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