xref: /netbsd-src/lib/libc/citrus/citrus_prop.c (revision b1c86f5f087524e68db12794ee9c3e3da1ab17a0)
1 /* $NetBSD: citrus_prop.c,v 1.3 2006/11/22 23:47:21 tnozaki Exp $ */
2 
3 /*-
4  * Copyright (c)2006 Citrus Project,
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 AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  *
28  */
29 
30 #include <sys/cdefs.h>
31 #if defined(LIBC_SCCS) && !defined(lint)
32 __RCSID("$NetBSD: citrus_prop.c,v 1.3 2006/11/22 23:47:21 tnozaki Exp $");
33 #endif /* LIBC_SCCS and not lint */
34 
35 #include <assert.h>
36 #include <limits.h>
37 #include <errno.h>
38 #include <stddef.h>
39 #include <stdio.h>
40 #include <stdint.h>
41 #include <stdlib.h>
42 #include <string.h>
43 
44 #include "citrus_namespace.h"
45 #include "citrus_bcs.h"
46 #include "citrus_region.h"
47 #include "citrus_memstream.h"
48 #include "citrus_prop.h"
49 
50 typedef struct {
51 	_citrus_prop_type_t type;
52 	union {
53 		const char *str;
54 		int bool, chr;
55 		uint64_t num;
56 	} u;
57 } _citrus_prop_object_t;
58 
59 static __inline void
60 _citrus_prop_object_init(_citrus_prop_object_t *obj, _citrus_prop_type_t type)
61 {
62 	_DIAGASSERT(obj != NULL);
63 
64 	obj->type = type;
65 	memset(&obj->u, 0, sizeof(obj->u));
66 }
67 
68 static __inline void
69 _citrus_prop_object_uninit(_citrus_prop_object_t *obj)
70 {
71 	_DIAGASSERT(obj != NULL);
72 
73 	if (obj->type == _CITRUS_PROP_STR)
74 		free(__UNCONST(obj->u.str));
75 }
76 
77 static const char *xdigit = "0123456789ABCDEF";
78 
79 #define _CITRUS_PROP_READ_UINT_COMMON(_func_, _type_, _max_)		\
80 static int								\
81 _citrus_prop_read_##_func_##_common(struct _memstream * __restrict ms,	\
82 	_type_ * __restrict result, int base)				\
83 {									\
84 	_type_ acc, cutoff;						\
85 	int n, ch, cutlim;						\
86 	char *p;							\
87 									\
88 	_DIAGASSERT(ms != NULL);					\
89 	_DIAGASSERT(result != NULL);					\
90 									\
91 	acc = (_type_)0;						\
92 	cutoff = _max_ / base;						\
93 	cutlim = _max_ % base;						\
94 	for (;;) {							\
95 		ch = _memstream_getc(ms);				\
96 		p = strchr(xdigit, _bcs_toupper(ch));			\
97 		if (p == NULL || (n = (p - xdigit)) >= base)		\
98 			break;						\
99 		if (acc > cutoff || (acc == cutoff && n > cutlim))	\
100 			break;						\
101 		acc *= base;						\
102 		acc += n;						\
103 	}								\
104 	_memstream_ungetc(ms, ch);					\
105 	*result = acc;							\
106 	return 0;							\
107 }
108 _CITRUS_PROP_READ_UINT_COMMON(chr, int, UCHAR_MAX)
109 _CITRUS_PROP_READ_UINT_COMMON(num, uint64_t, UINT64_MAX)
110 #undef _CITRUS_PROP_READ_UINT_COMMON
111 
112 #define _CITRUS_PROP_READ_INT(_func_, _type_)			\
113 static int							\
114 _citrus_prop_read_##_func_(struct _memstream * __restrict ms,	\
115 	_citrus_prop_object_t * __restrict obj)			\
116 {								\
117 	int ch, neg, base;					\
118 								\
119 	_DIAGASSERT(ms != NULL);				\
120 	_DIAGASSERT(obj != NULL);				\
121 								\
122 	_memstream_skip_ws(ms);					\
123 	ch = _memstream_getc(ms);				\
124 	neg = 0;						\
125 	switch (ch) {						\
126 	case '-':						\
127 		neg = 1;					\
128 	case '+':						\
129 		ch = _memstream_getc(ms);			\
130 	}							\
131 	base = 10;						\
132 	if (ch == '0') {					\
133 		base -= 2;					\
134 		ch = _memstream_getc(ms);			\
135 		if (ch == 'x' || ch == 'X') {			\
136 			ch = _memstream_getc(ms);		\
137 			if (_bcs_isxdigit(ch) == 0) {		\
138 				_memstream_ungetc(ms, ch);	\
139 				obj->u._func_ = 0;		\
140 				return 0;			\
141 			}					\
142 			base += 8;				\
143 		}						\
144 	} else if (_bcs_isdigit(ch) == 0)			\
145 		return EINVAL;					\
146 	_memstream_ungetc(ms, ch);				\
147 	return _citrus_prop_read_##_func_##_common		\
148 	    (ms, &obj->u._func_, base);				\
149 }
150 _CITRUS_PROP_READ_INT(chr, int)
151 _CITRUS_PROP_READ_INT(num, uint64_t)
152 #undef _CITRUS_PROP_READ_INT
153 
154 static int
155 _citrus_prop_read_character_common(struct _memstream * __restrict ms,
156 	int * __restrict result)
157 {
158 	int ch, base;
159 
160 	_DIAGASSERT(ms != NULL);
161 	_DIAGASSERT(result != NULL);
162 
163 	ch = _memstream_getc(ms);
164 	if (ch != '\\') {
165 		*result = ch;
166 	} else {
167 		ch = _memstream_getc(ms);
168 		base = 16;
169 		switch (ch) {
170 		case 'a': *result = '\a'; break;
171 		case 'b': *result = '\b'; break;
172 		case 'f': *result = '\f'; break;
173 		case 'n': *result = '\n'; break;
174 		case 'r': *result = '\r'; break;
175 		case 't': *result = '\t'; break;
176 		case 'v': *result = '\v'; break;
177 		/*FALLTHROUGH*/
178 		case '0': case '1': case '2': case '3':
179 		case '4': case '5': case '6': case '7':
180 			_memstream_ungetc(ms, ch);
181 			base -= 8;
182 		case 'x':
183 			return _citrus_prop_read_chr_common(ms, result, base);
184 
185 		default:
186 			/* unknown escape */
187 			*result = ch;
188 		}
189 	}
190 	return 0;
191 }
192 
193 static int
194 _citrus_prop_read_character(struct _memstream * __restrict ms,
195 	_citrus_prop_object_t * __restrict obj)
196 {
197 	int ch, errnum;
198 
199 	_DIAGASSERT(ms != NULL);
200 	_DIAGASSERT(obj != NULL);
201 
202 	_memstream_skip_ws(ms);
203 	ch = _memstream_getc(ms);
204 	if (ch != '\'') {
205 		_memstream_ungetc(ms, ch);
206 		return _citrus_prop_read_chr(ms, obj);
207 	}
208 	errnum = _citrus_prop_read_character_common(ms, &ch);
209 	if (errnum != 0)
210 		return errnum;
211 	obj->u.chr = ch;
212 	ch = _memstream_getc(ms);
213 	if (ch != '\'')
214 		return EINVAL;
215 	return 0;
216 }
217 
218 static int
219 _citrus_prop_read_bool(struct _memstream * __restrict ms,
220 	_citrus_prop_object_t * __restrict obj)
221 {
222 	_DIAGASSERT(ms != NULL);
223 	_DIAGASSERT(obj != NULL);
224 
225 	_memstream_skip_ws(ms);
226 	switch (_bcs_tolower(_memstream_getc(ms))) {
227 	case 't':
228 		if (_bcs_tolower(_memstream_getc(ms)) == 'r' &&
229 		    _bcs_tolower(_memstream_getc(ms)) == 'u' &&
230 		    _bcs_tolower(_memstream_getc(ms)) == 'e') {
231 			obj->u.bool = 1;
232 			return 0;
233 		}
234 		break;
235 	case 'f':
236 		if (_bcs_tolower(_memstream_getc(ms)) == 'a' &&
237 		    _bcs_tolower(_memstream_getc(ms)) == 'l' &&
238 		    _bcs_tolower(_memstream_getc(ms)) == 's' &&
239 		    _bcs_tolower(_memstream_getc(ms)) == 'e') {
240 			obj->u.bool = 0;
241 			return 0;
242 		}
243 	}
244 	return EINVAL;
245 }
246 
247 static int
248 _citrus_prop_read_str(struct _memstream * __restrict ms,
249 	_citrus_prop_object_t * __restrict obj)
250 {
251 	int errnum, quot, ch;
252 	char *s, *t;
253 #define _CITRUS_PROP_STR_BUFSIZ	512
254 	size_t n, m;
255 
256 	_DIAGASSERT(ms != NULL);
257 	_DIAGASSERT(obj != NULL);
258 
259 	m = _CITRUS_PROP_STR_BUFSIZ;
260 	s = malloc(m);
261 	if (s == NULL)
262 		return ENOMEM;
263 	n = 0;
264 	_memstream_skip_ws(ms);
265 	quot = _memstream_getc(ms);
266 	switch (quot) {
267 	case EOF:
268 		goto done;
269 	case '\\':
270 		_memstream_ungetc(ms, quot);
271 		quot = EOF;
272 	/*FALLTHROUGH*/
273 	case '\"': case '\'':
274 		break;
275 	default:
276 		s[n] = quot;
277 		++n, --m;
278 		quot = EOF;
279 	}
280 	for (;;) {
281 		if (m < 1) {
282 			m = _CITRUS_PROP_STR_BUFSIZ;
283 			t = realloc(s, n + m);
284 			if (t == NULL) {
285 				free(s);
286 				return ENOMEM;
287 			}
288 			s = t;
289 		}
290 		ch = _memstream_getc(ms);
291 		if (quot == ch || (quot == EOF &&
292 		    (ch == ';' || _bcs_isspace(ch)))) {
293 done:
294 			s[n] = '\0';
295 			obj->u.str = (const char *)s;
296 			return 0;
297 		}
298 		_memstream_ungetc(ms, ch);
299 		errnum = _citrus_prop_read_character_common(ms, &ch);
300 		if (errnum != 0)
301 			return errnum;
302 		s[n] = ch;
303 		++n, --m;
304 	}
305 	free(s);
306 	return EINVAL;
307 #undef _CITRUS_PROP_STR_BUFSIZ
308 }
309 
310 typedef int (*_citrus_prop_read_type_t)(struct _memstream * __restrict,
311 	_citrus_prop_object_t * __restrict);
312 
313 static const _citrus_prop_read_type_t readers[] = {
314 	_citrus_prop_read_bool,
315 	_citrus_prop_read_str,
316 	_citrus_prop_read_character,
317 	_citrus_prop_read_num,
318 };
319 
320 static __inline int
321 _citrus_prop_read_symbol(struct _memstream * __restrict ms,
322 	char * __restrict s, size_t n)
323 {
324 	int ch;
325 	size_t m;
326 
327 	_DIAGASSERT(ms != NULL);
328 	_DIAGASSERT(s != NULL);
329 	_DIAGASSERT(n > 0);
330 
331 	for (m = 0; m < n; ++m) {
332 		ch = _memstream_getc(ms);
333 		if (ch != '_' && _bcs_isalnum(ch) == 0)
334 			goto name_found;
335 		s[m] = ch;
336 	}
337 	ch = _memstream_getc(ms);
338 	if (ch == '_' || _bcs_isalnum(ch) != 0)
339 		return EINVAL;
340 
341 name_found:
342 	_memstream_ungetc(ms, ch);
343 	s[m] = '\0';
344 
345 	return 0;
346 }
347 
348 static int
349 _citrus_prop_parse_element(struct _memstream * __restrict ms,
350 	const _citrus_prop_hint_t * __restrict hints,
351 	void ** __restrict context)
352 {
353 	int ch, errnum;
354 #define _CITRUS_PROP_HINT_NAME_LEN_MAX	255
355 	char name[_CITRUS_PROP_HINT_NAME_LEN_MAX + 1];
356 	const _citrus_prop_hint_t *hint;
357 	_citrus_prop_object_t ostart, oend;
358 
359 	_DIAGASSERT(ms != NULL);
360 	_DIAGASSERT(hints != NULL);
361 
362 	errnum = _citrus_prop_read_symbol(ms, name, sizeof(name));
363 	if (errnum != 0)
364 		return errnum;
365 	for (hint = hints; hint->name != NULL; ++hint) {
366 		if (_citrus_bcs_strcasecmp(name, hint->name) == 0)
367 			goto hint_found;
368 	}
369 	return EINVAL;
370 
371 hint_found:
372 	_memstream_skip_ws(ms);
373 	ch = _memstream_getc(ms);
374 	if (ch != '=' && ch != ':')
375 		_memstream_ungetc(ms, ch);
376 	do {
377 		_citrus_prop_object_init(&ostart, hint->type);
378 		_citrus_prop_object_init(&oend, hint->type);
379 		errnum = (*readers[hint->type])(ms, &ostart);
380 		if (errnum != 0)
381 			return errnum;
382 		_memstream_skip_ws(ms);
383 		ch = _memstream_getc(ms);
384 		switch (hint->type) {
385 		case _CITRUS_PROP_BOOL:
386 		case _CITRUS_PROP_STR:
387 			break;
388 		default:
389 			if (ch != '-')
390 				break;
391 			errnum = (*readers[hint->type])(ms, &oend);
392 			if (errnum != 0)
393 				return errnum;
394 			_memstream_skip_ws(ms);
395 			ch = _memstream_getc(ms);
396 		}
397 #define CALL0(_func_)					\
398 do {							\
399 	_DIAGASSERT(hint->cb._func_.func != NULL);	\
400 	errnum = (*hint->cb._func_.func)(context,	\
401 	    hint->name,	ostart.u._func_);		\
402 } while (/*CONSTCOND*/0)
403 #define CALL1(_func_)					\
404 do {							\
405 	_DIAGASSERT(hint->cb._func_.func != NULL);	\
406 	errnum = (*hint->cb._func_.func)(context,	\
407 	    hint->name,	ostart.u._func_, oend.u._func_);\
408 } while (/*CONSTCOND*/0)
409 		switch (hint->type) {
410 		case _CITRUS_PROP_BOOL: CALL0(bool); break;
411 		case _CITRUS_PROP_STR : CALL0( str); break;
412 		case _CITRUS_PROP_CHR : CALL1( chr); break;
413 		case _CITRUS_PROP_NUM : CALL1( num); break;
414 		default:
415 			abort();
416 			/*NOTREACHED*/
417 		}
418 #undef CALL0
419 #undef CALL1
420 		_citrus_prop_object_uninit(&ostart);
421 		_citrus_prop_object_uninit(&oend);
422 		if (errnum != 0)
423 			return errnum;
424 	} while (ch == ',');
425 	if (ch != ';')
426 		_memstream_ungetc(ms, ch);
427 	return 0;
428 }
429 
430 int
431 _citrus_prop_parse_variable(const _citrus_prop_hint_t * __restrict hints,
432 	void * __restrict context, const void *var, size_t lenvar)
433 {
434 	struct _memstream ms;
435 	int errnum, ch;
436 
437 	_DIAGASSERT(hints != NULL);
438 
439 	_memstream_bind_ptr(&ms, __UNCONST(var), lenvar);
440 	for (;;) {
441 		_memstream_skip_ws(&ms);
442 		ch = _memstream_getc(&ms);
443 		if (ch == EOF || ch == '\0')
444 			break;
445 		_memstream_ungetc(&ms, ch);
446 		errnum = _citrus_prop_parse_element(
447 		    &ms, hints, (void **)&context);
448 		if (errnum != 0)
449 			return errnum;
450 	}
451 	return 0;
452 }
453