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