1/*	$NetBSD: data.c,v 1.5 2008/05/10 15:31:05 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 * 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.5 2008/05/10 15:31:05 martin Exp $");
45#endif /* not lint */
46
47static void *
48alloc(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 */
64static char *
65strdup_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
80int
81init_data(struct tpctl_data *data)
82{
83	TAILQ_INIT(&data->list);
84
85	return (0);
86}
87
88int
89read_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
254int
255write_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
317void
318write_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
336void
337free_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
358int
359replace_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
384struct wsmouse_calibcoords *
385search_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