xref: /netbsd/usr.sbin/tpctl/data.c (revision 50010e74)
1 /*	$NetBSD: data.c,v 1.6 2009/04/28 10:57:24 nonaka Exp $	*/
2 
3 /*-
4  * Copyright (c) 2002 TAKEMRUA Shin
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. Neither the name of The NetBSD Foundation nor the names of its
16  *    contributors may be used to endorse or promote products derived
17  *    from this software without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 #include <stdio.h>
33 #include <strings.h>
34 #include <stdlib.h>
35 #include <time.h>
36 #include <fcntl.h>
37 #include <unistd.h>
38 #include <sys/param.h>
39 
40 #include "tpctl.h"
41 
42 #ifndef lint
43 #include <sys/cdefs.h>
44 __RCSID("$NetBSD: data.c,v 1.6 2009/04/28 10:57:24 nonaka Exp $");
45 #endif /* not lint */
46 
47 static void *
alloc(int size)48 alloc(int size)
49 {
50 	void *res;
51 
52 	if ((res = malloc(size)) == NULL) {
53 		perror(getprogname());
54 		exit(EXIT_FAILURE);
55 	}
56 
57 	return (res);
58 }
59 
60 /*
61  * duplicate string
62  * trailing white space will be removed.
63  */
64 static char *
strdup_prune(char * s)65 strdup_prune(char *s)
66 {
67 	char *res, *tail;
68 
69 	tail = &s[strlen(s) - 1];
70 	while (s <= tail && strchr(" \t", *tail) != NULL)
71 		tail--;
72 
73 	res = alloc(tail - s + 2);
74 	memcpy(res, s, tail - s + 1);
75 	res[tail - s + 1] = '\0';
76 
77 	return (res);
78 }
79 
80 int
init_data(struct tpctl_data * data)81 init_data(struct tpctl_data *data)
82 {
83 	TAILQ_INIT(&data->list);
84 
85 	return (0);
86 }
87 
88 int
read_data(const char * filename,struct tpctl_data * data)89 read_data(const char *filename, struct tpctl_data *data)
90 {
91 	int res, len, n, i, t;
92 	char buf[MAXDATALEN + 2], *p, *p2;
93 	FILE *fp;
94 	struct tpctl_data_elem *elem;
95 
96 	data->lineno = 0;
97 
98 	if ((fp = fopen(filename, "r")) == NULL)
99 		return (ERR_NOFILE);
100 
101 	while (fgets(buf, sizeof(buf), fp) != NULL) {
102 		data->lineno++;
103 		buf[MAXDATALEN + 1] = '\0';
104 		len = strlen(buf);
105 		if (MAXDATALEN < len) {
106 			res = ERR_SYNTAX;
107 			goto exit_func;
108 		}
109 
110 		/* prune trailing space and newline */;
111 		p = &buf[len - 1];
112 		while (buf <= p && strchr(" \t\n\r", *p) != NULL)
113 			*p-- = '\0';
114 
115 		/* skip space */;
116 		p = buf;
117 		while (*p != '\0' && strchr(" \t", *p) != NULL)
118 			p++;
119 
120 		/* comment or empty line */
121 		if (*p == '#' || *p == '\0') {
122 			elem = alloc(sizeof(*elem));
123 			elem->type = TPCTL_COMMENT;
124 			elem->name = strdup_prune(buf);
125 			TAILQ_INSERT_TAIL(&data->list, elem, link);
126 			continue;
127 		}
128 
129 		/* calibration parameter */
130 		elem = alloc(sizeof(*elem));
131 		elem->type = TPCTL_CALIBCOORDS;
132 		p2 = p;
133 		while (*p2 != ',' && *p2 != '\0')
134 			p2++;
135 		if (*p2 != ',') {
136 			/* missing ',' */
137 			res = ERR_SYNTAX;
138 			free(elem);
139 			goto exit_func;
140 		}
141 		*p2 = '\0';
142 		elem->name = strdup_prune(p);
143 		if (search_data(data, elem->name) != NULL) {
144 			free(elem);
145 			res = ERR_DUPNAME;
146 			goto exit_func;
147 		}
148 		TAILQ_INSERT_TAIL(&data->list, elem, link);
149 		p = p2 + 1;
150 
151 		/*
152 		 * minX, maxX, minY, maxY
153 		 */
154 		for (i = 0; i < 4; i++) {
155 			t = strtol(p, &p2, 0);
156 			if (p == p2) {
157 				res = ERR_SYNTAX;
158 				goto exit_func;
159 			}
160 			p = p2;
161 			while (*p != '\0' && strchr(" \t", *p) != NULL)
162 				p++;
163 			if (*p != ',') {
164 				res = ERR_SYNTAX;
165 				goto exit_func;
166 			}
167 			p++;
168 			switch (i % 4) {
169 			case 0:
170 				elem->calibcoords.minx = t;
171 				break;
172 			case 1:
173 				elem->calibcoords.miny = t;
174 				break;
175 			case 2:
176 				elem->calibcoords.maxx = t;
177 				break;
178 			case 3:
179 				elem->calibcoords.maxy = t;
180 				break;
181 			}
182 		}
183 
184 		/*
185 		 * number of samples
186 		 */
187 		n = strtol(p, &p2, 0);
188 		if (p == p2) {
189 			res = ERR_SYNTAX;
190 			goto exit_func;
191 		}
192 		p = p2;
193 		while (*p != '\0' && strchr(" \t", *p) != NULL)
194 			p++;
195 
196 		if (WSMOUSE_CALIBCOORDS_MAX < n) {
197 			res = ERR_SYNTAX;
198 			goto exit_func;
199 		}
200 		elem->calibcoords.samplelen = n;
201 
202 		/*
203 		 * samples
204 		 */
205 		for (i = 0; i < n * 4; i++) {
206 			if (*p != ',') {
207 				res = ERR_SYNTAX;
208 				goto exit_func;
209 			}
210 			p++;
211 			t = strtol(p, &p2, 0);
212 			if (p == p2) {
213 				res = ERR_SYNTAX;
214 				goto exit_func;
215 			}
216 			p = p2;
217 			while (*p != '\0' && strchr(" \t", *p) != NULL)
218 				p++;
219 			switch (i % 4) {
220 			case 0:
221 				elem->calibcoords.samples[i / 4].rawx = t;
222 				break;
223 			case 1:
224 				elem->calibcoords.samples[i / 4].rawy = t;
225 				break;
226 			case 2:
227 				elem->calibcoords.samples[i / 4].x = t;
228 				break;
229 			case 3:
230 				elem->calibcoords.samples[i / 4].y = t;
231 				break;
232 			}
233 		}
234 		if (*p != '\0') {
235 			res = ERR_SYNTAX;
236 			goto exit_func;
237 		}
238 	}
239 
240 	if (ferror(fp))
241 		res = ERR_IO;
242 	else
243 		res = ERR_NONE;
244 
245  exit_func:
246 	fclose(fp);
247 	if (res != ERR_NONE) {
248 		free_data(data);
249 	}
250 
251 	return (res);
252 }
253 
254 int
write_data(const char * filename,struct tpctl_data * data)255 write_data(const char *filename, struct tpctl_data *data)
256 {
257 	int res, fd;
258 	FILE *fp;
259 	struct tpctl_data_elem *elem;
260 	char *p, tempfile[MAXPATHLEN + 1];
261 
262 	fd = 0;		/* XXXGCC -Wuninitialized [hpcarm] */
263 
264 	if (filename == NULL) {
265 		fp = stdout;
266 	} else {
267 		strncpy(tempfile, filename, MAXPATHLEN);
268 		tempfile[MAXPATHLEN] = '\0';
269 		if ((p = strrchr(tempfile, '/')) == NULL) {
270 			strcpy(tempfile, TPCTL_TMP_FILENAME);
271 		} else {
272 			p++;
273 			if (MAXPATHLEN <
274 			    p - tempfile + strlen(TPCTL_TMP_FILENAME))
275 				return (ERR_NOFILE);/* file name is too long */
276 			strcat(tempfile, TPCTL_TMP_FILENAME);
277 		}
278 		if ((fd = open(tempfile, O_RDWR|O_CREAT|O_EXCL, 0644)) < 0) {
279 			fprintf(stderr, "%s: can't create %s\n",
280 			    getprogname(), tempfile);
281 			return (ERR_NOFILE);
282 		}
283 		if ((fp = fdopen(fd, "w")) == NULL) {
284 			perror("fdopen");
285 			exit(EXIT_FAILURE);
286 		}
287 	}
288 
289 	TAILQ_FOREACH(elem, &data->list, link) {
290 		switch (elem->type) {
291 		case TPCTL_CALIBCOORDS:
292 			write_coords(fp, elem->name, &elem->calibcoords);
293 			break;
294 		case TPCTL_COMMENT:
295 			fprintf(fp, "%s\n", elem->name);
296 			break;
297 		default:
298 			fprintf(stderr, "%s: internal error\n", getprogname());
299 			exit(EXIT_FAILURE);
300 			break;
301 		}
302 	}
303 
304 	if (filename != NULL) {
305 		fclose(fp);
306 		close(fd);
307 		if (rename(tempfile, filename) < 0) {
308 			unlink(tempfile);
309 			return (ERR_NOFILE);
310 		}
311 	}
312 	res = ERR_NONE;
313 
314 	return (res);
315 }
316 
317 void
write_coords(FILE * fp,char * name,struct wsmouse_calibcoords * coords)318 write_coords(FILE *fp, char *name, struct wsmouse_calibcoords *coords)
319 {
320 	int i;
321 
322 	fprintf(fp, "%s,%d,%d,%d,%d,%d", name,
323 		coords->minx, coords->miny,
324 		coords->maxx, coords->maxy,
325 		coords->samplelen);
326 	for (i = 0; i < coords->samplelen; i++) {
327 		fprintf(fp, ",%d,%d,%d,%d",
328 		    coords->samples[i].rawx,
329 		    coords->samples[i].rawy,
330 		    coords->samples[i].x,
331 		    coords->samples[i].y);
332 	}
333 	fprintf(fp, "\n");
334 }
335 
336 void
free_data(struct tpctl_data * data)337 free_data(struct tpctl_data *data)
338 {
339 	struct tpctl_data_elem *elem;
340 
341 	while (!TAILQ_EMPTY(&data->list)) {
342 		elem = TAILQ_FIRST(&data->list);
343 		TAILQ_REMOVE(&data->list, elem, link);
344 
345 		switch (elem->type) {
346 		case TPCTL_CALIBCOORDS:
347 		case TPCTL_COMMENT:
348 			free(elem->name);
349 			break;
350 		default:
351 			fprintf(stderr, "%s: internal error\n", getprogname());
352 			exit(EXIT_FAILURE);
353 			break;
354 		}
355 	}
356 }
357 
358 int
replace_data(struct tpctl_data * data,char * name,struct wsmouse_calibcoords * calibcoords)359 replace_data(struct tpctl_data *data, char *name, struct wsmouse_calibcoords *calibcoords)
360 {
361 	struct tpctl_data_elem *elem;
362 
363 	TAILQ_FOREACH(elem, &data->list, link) {
364 		if (elem->type == TPCTL_CALIBCOORDS &&
365 		    strcmp(name, elem->name) == 0) {
366 			elem->calibcoords = *calibcoords;
367 			return (0);
368 		}
369 	}
370 
371 	elem = alloc(sizeof(*elem));
372 	elem->type = TPCTL_CALIBCOORDS;
373 	elem->name = strdup(name);
374 	elem->calibcoords = *calibcoords;
375 	if (elem->name == NULL) {
376 		perror(getprogname());
377 		exit(EXIT_FAILURE);
378 	}
379 	TAILQ_INSERT_TAIL(&data->list, elem, link);
380 
381 	return (1);
382 }
383 
384 struct wsmouse_calibcoords *
search_data(struct tpctl_data * data,char * name)385 search_data(struct tpctl_data *data, char *name)
386 {
387 	struct tpctl_data_elem *elem;
388 
389 	TAILQ_FOREACH(elem, &data->list, link) {
390 		if (elem->type == TPCTL_CALIBCOORDS &&
391 		    strcmp(name, elem->name) == 0) {
392 			return (&elem->calibcoords);
393 		}
394 	}
395 
396 	return (NULL);
397 }
398