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