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