xref: /freebsd/lib/libc/iconv/citrus_prop.c (revision 63f537551380d2dab29fa402ad1269feae17e594)
1 /* $NetBSD: citrus_prop.c,v 1.4 2011/03/30 08:22:01 jruoho Exp $ */
2 
3 /*-
4  * SPDX-License-Identifier: BSD-2-Clause
5  *
6  * Copyright (c)2006 Citrus Project,
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  *
30  */
31 
32 #include <sys/cdefs.h>
33 
34 #include <assert.h>
35 #include <errno.h>
36 #include <limits.h>
37 #include <stdbool.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 chr;
55 		bool boolean;
56 		uint64_t num;
57 	} u;
58 } _citrus_prop_object_t;
59 
60 static __inline void
61 _citrus_prop_object_init(_citrus_prop_object_t *obj, _citrus_prop_type_t type)
62 {
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 
72 	if (obj->type == _CITRUS_PROP_STR)
73 		free(__DECONST(void *, obj->u.str));
74 }
75 
76 static const char *xdigit = "0123456789ABCDEF";
77 
78 #define _CITRUS_PROP_READ_UINT_COMMON(_func_, _type_, _max_)		\
79 static int								\
80 _citrus_prop_read_##_func_##_common(struct _memstream * __restrict ms,	\
81     _type_ * __restrict result, int base, int neg)			\
82 {									\
83 	_type_ acc, cutoff;						\
84 	int ch, cutlim, n;						\
85 	char *p;							\
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 = neg ? -acc : 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 base, ch, neg;					\
114 								\
115 	_memstream_skip_ws(ms);					\
116 	ch = _memstream_getc(ms);				\
117 	neg = 0;						\
118 	switch (ch) {						\
119 	case '-':						\
120 		neg = 1;					\
121 	case '+':						\
122 		ch = _memstream_getc(ms);			\
123 	}							\
124 	base = 10;						\
125 	if (ch == '0') {					\
126 		base -= 2;					\
127 		ch = _memstream_getc(ms);			\
128 		if (ch == 'x' || ch == 'X') {			\
129 			ch = _memstream_getc(ms);		\
130 			if (_bcs_isxdigit(ch) == 0) {		\
131 				_memstream_ungetc(ms, ch);	\
132 				obj->u._func_ = 0;		\
133 				return (0);			\
134 			}					\
135 			base += 8;				\
136 		}						\
137 	} else if (_bcs_isdigit(ch) == 0)			\
138 		return (EINVAL);				\
139 	_memstream_ungetc(ms, ch);				\
140 	return (_citrus_prop_read_##_func_##_common		\
141 	    (ms, &obj->u._func_, base, neg));			\
142 }
143 _CITRUS_PROP_READ_INT(chr, int)
144 _CITRUS_PROP_READ_INT(num, uint64_t)
145 #undef _CITRUS_PROP_READ_INT
146 
147 static int
148 _citrus_prop_read_character_common(struct _memstream * __restrict ms,
149     int * __restrict result)
150 {
151 	int base, ch;
152 
153 	ch = _memstream_getc(ms);
154 	if (ch != '\\')
155 		*result = ch;
156 	else {
157 		ch = _memstream_getc(ms);
158 		base = 16;
159 		switch (ch) {
160 		case 'a':
161 			*result = '\a';
162 			break;
163 		case 'b':
164 			*result = '\b';
165 			break;
166 		case 'f':
167 			*result = '\f';
168 			break;
169 		case 'n':
170 			*result = '\n';
171 			break;
172 		case 'r':
173 			*result = '\r';
174 			break;
175 		case 't':
176 			*result = '\t';
177 			break;
178 		case 'v':
179 			*result = '\v';
180 			break;
181 		case '0': case '1': case '2': case '3':
182 		case '4': case '5': case '6': case '7':
183 			_memstream_ungetc(ms, ch);
184 			base -= 8;
185 			/*FALLTHROUGH*/
186 		case 'x':
187 			return (_citrus_prop_read_chr_common(ms, result,
188 			    base, 0));
189 			/*NOTREACHED*/
190 		default:
191 			/* unknown escape */
192 			*result = ch;
193 		}
194 	}
195 	return (0);
196 }
197 
198 static int
199 _citrus_prop_read_character(struct _memstream * __restrict ms,
200     _citrus_prop_object_t * __restrict obj)
201 {
202 	int ch, errnum;
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
221 _citrus_prop_read_bool(struct _memstream * __restrict ms,
222     _citrus_prop_object_t * __restrict obj)
223 {
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.boolean = true;
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.boolean = false;
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 ch, errnum, quot;
252 	char *s, *t;
253 #define _CITRUS_PROP_STR_BUFSIZ	512
254 	size_t m, n;
255 
256 	m = _CITRUS_PROP_STR_BUFSIZ;
257 	s = malloc(m);
258 	if (s == NULL)
259 		return (ENOMEM);
260 	n = 0;
261 	_memstream_skip_ws(ms);
262 	quot = _memstream_getc(ms);
263 	switch (quot) {
264 	case EOF:
265 		goto done;
266 		/*NOTREACHED*/
267 	case '\\':
268 		_memstream_ungetc(ms, quot);
269 		quot = EOF;
270 		/*FALLTHROUGH*/
271 	case '\"': case '\'':
272 		break;
273 	default:
274 		s[n] = quot;
275 		++n, --m;
276 		quot = EOF;
277 	}
278 	for (;;) {
279 		if (m < 1) {
280 			m = _CITRUS_PROP_STR_BUFSIZ;
281 			t = realloc(s, n + m);
282 			if (t == NULL) {
283 				free(s);
284 				return (ENOMEM);
285 			}
286 			s = t;
287 		}
288 		ch = _memstream_getc(ms);
289 		if (quot == ch || (quot == EOF &&
290 		    (ch == ';' || _bcs_isspace(ch)))) {
291 done:
292 			s[n] = '\0';
293 			obj->u.str = (const char *)s;
294 			return (0);
295 		}
296 		_memstream_ungetc(ms, ch);
297 		errnum = _citrus_prop_read_character_common(ms, &ch);
298 		if (errnum != 0) {
299 			free(s);
300 			return (errnum);
301 		}
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 	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, void * __restrict context)
347 {
348 	int ch, errnum;
349 #define _CITRUS_PROP_HINT_NAME_LEN_MAX	255
350 	char name[_CITRUS_PROP_HINT_NAME_LEN_MAX + 1];
351 	const _citrus_prop_hint_t *hint;
352 	_citrus_prop_object_t ostart, oend;
353 
354 	errnum = _citrus_prop_read_symbol(ms, name, sizeof(name));
355 	if (errnum != 0)
356 		return (errnum);
357 	for (hint = hints; hint->name != NULL; ++hint)
358 		if (_citrus_bcs_strcasecmp(name, hint->name) == 0)
359 			goto hint_found;
360 	return (EINVAL);
361 
362 hint_found:
363 	_memstream_skip_ws(ms);
364 	ch = _memstream_getc(ms);
365 	if (ch != '=' && ch != ':')
366 		_memstream_ungetc(ms, ch);
367 	do {
368 		_citrus_prop_object_init(&ostart, hint->type);
369 		_citrus_prop_object_init(&oend, hint->type);
370 		errnum = (*readers[hint->type])(ms, &ostart);
371 		if (errnum != 0)
372 			return (errnum);
373 		_memstream_skip_ws(ms);
374 		ch = _memstream_getc(ms);
375 		switch (hint->type) {
376 		case _CITRUS_PROP_BOOL:
377 			/*FALLTHROUGH*/
378 		case _CITRUS_PROP_STR:
379 			break;
380 		default:
381 			if (ch != '-')
382 				break;
383 			errnum = (*readers[hint->type])(ms, &oend);
384 			if (errnum != 0)
385 				return (errnum);
386 			_memstream_skip_ws(ms);
387 			ch = _memstream_getc(ms);
388 		}
389 #define CALL0(_func_)					\
390 do {							\
391 	errnum = (*hint->cb._func_.func)(context,	\
392 	    hint->name,	ostart.u._func_);		\
393 } while (0)
394 #define CALL1(_func_)					\
395 do {							\
396 	errnum = (*hint->cb._func_.func)(context,	\
397 	    hint->name,	ostart.u._func_, oend.u._func_);\
398 } while (0)
399 		switch (hint->type) {
400 		case _CITRUS_PROP_BOOL:
401 			CALL0(boolean);
402 			break;
403 		case _CITRUS_PROP_STR:
404 			CALL0(str);
405 			break;
406 		case _CITRUS_PROP_CHR:
407 			CALL1(chr);
408 			break;
409 		case _CITRUS_PROP_NUM:
410 			CALL1(num);
411 			break;
412 		default:
413 			abort();
414 			/*NOTREACHED*/
415 		}
416 #undef CALL0
417 #undef CALL1
418 		_citrus_prop_object_uninit(&ostart);
419 		_citrus_prop_object_uninit(&oend);
420 		if (errnum != 0)
421 			return (errnum);
422 	} while (ch == ',');
423 	if (ch != ';')
424 		_memstream_ungetc(ms, ch);
425 	return (0);
426 }
427 
428 int
429 _citrus_prop_parse_variable(const _citrus_prop_hint_t * __restrict hints,
430     void * __restrict context, const void *var, size_t lenvar)
431 {
432 	struct _memstream ms;
433 	int ch, errnum;
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(&ms, hints, context);
443 		if (errnum != 0)
444 			return (errnum);
445 	}
446 	return (0);
447 }
448