xref: /freebsd/lib/libc/iconv/citrus_prop.c (revision e0c4386e)
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 
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, int neg)			\
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 = neg ? -acc : 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, neg;					\
113 								\
114 	_memstream_skip_ws(ms);					\
115 	ch = _memstream_getc(ms);				\
116 	neg = 0;						\
117 	switch (ch) {						\
118 	case '-':						\
119 		neg = 1;					\
120 	case '+':						\
121 		ch = _memstream_getc(ms);			\
122 	}							\
123 	base = 10;						\
124 	if (ch == '0') {					\
125 		base -= 2;					\
126 		ch = _memstream_getc(ms);			\
127 		if (ch == 'x' || ch == 'X') {			\
128 			ch = _memstream_getc(ms);		\
129 			if (_bcs_isxdigit(ch) == 0) {		\
130 				_memstream_ungetc(ms, ch);	\
131 				obj->u._func_ = 0;		\
132 				return (0);			\
133 			}					\
134 			base += 8;				\
135 		}						\
136 	} else if (_bcs_isdigit(ch) == 0)			\
137 		return (EINVAL);				\
138 	_memstream_ungetc(ms, ch);				\
139 	return (_citrus_prop_read_##_func_##_common		\
140 	    (ms, &obj->u._func_, base, neg));			\
141 }
142 _CITRUS_PROP_READ_INT(chr, int)
143 _CITRUS_PROP_READ_INT(num, uint64_t)
144 #undef _CITRUS_PROP_READ_INT
145 
146 static int
147 _citrus_prop_read_character_common(struct _memstream * __restrict ms,
148     int * __restrict result)
149 {
150 	int base, ch;
151 
152 	ch = _memstream_getc(ms);
153 	if (ch != '\\')
154 		*result = ch;
155 	else {
156 		ch = _memstream_getc(ms);
157 		base = 16;
158 		switch (ch) {
159 		case 'a':
160 			*result = '\a';
161 			break;
162 		case 'b':
163 			*result = '\b';
164 			break;
165 		case 'f':
166 			*result = '\f';
167 			break;
168 		case 'n':
169 			*result = '\n';
170 			break;
171 		case 'r':
172 			*result = '\r';
173 			break;
174 		case 't':
175 			*result = '\t';
176 			break;
177 		case 'v':
178 			*result = '\v';
179 			break;
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 			/*FALLTHROUGH*/
185 		case 'x':
186 			return (_citrus_prop_read_chr_common(ms, result,
187 			    base, 0));
188 			/*NOTREACHED*/
189 		default:
190 			/* unknown escape */
191 			*result = ch;
192 		}
193 	}
194 	return (0);
195 }
196 
197 static int
198 _citrus_prop_read_character(struct _memstream * __restrict ms,
199     _citrus_prop_object_t * __restrict obj)
200 {
201 	int ch, errnum;
202 
203 	_memstream_skip_ws(ms);
204 	ch = _memstream_getc(ms);
205 	if (ch != '\'') {
206 		_memstream_ungetc(ms, ch);
207 		return (_citrus_prop_read_chr(ms, obj));
208 	}
209 	errnum = _citrus_prop_read_character_common(ms, &ch);
210 	if (errnum != 0)
211 		return (errnum);
212 	obj->u.chr = ch;
213 	ch = _memstream_getc(ms);
214 	if (ch != '\'')
215 		return (EINVAL);
216 	return (0);
217 }
218 
219 static int
220 _citrus_prop_read_bool(struct _memstream * __restrict ms,
221     _citrus_prop_object_t * __restrict obj)
222 {
223 
224 	_memstream_skip_ws(ms);
225 	switch (_bcs_tolower(_memstream_getc(ms))) {
226 	case 't':
227 		if (_bcs_tolower(_memstream_getc(ms)) == 'r' &&
228 		    _bcs_tolower(_memstream_getc(ms)) == 'u' &&
229 		    _bcs_tolower(_memstream_getc(ms)) == 'e') {
230 			obj->u.boolean = true;
231 			return (0);
232 		}
233 		break;
234 	case 'f':
235 		if (_bcs_tolower(_memstream_getc(ms)) == 'a' &&
236 		    _bcs_tolower(_memstream_getc(ms)) == 'l' &&
237 		    _bcs_tolower(_memstream_getc(ms)) == 's' &&
238 		    _bcs_tolower(_memstream_getc(ms)) == 'e') {
239 			obj->u.boolean = false;
240 			return (0);
241 		}
242 	}
243 	return (EINVAL);
244 }
245 
246 static int
247 _citrus_prop_read_str(struct _memstream * __restrict ms,
248     _citrus_prop_object_t * __restrict obj)
249 {
250 	int ch, errnum, quot;
251 	char *s, *t;
252 #define _CITRUS_PROP_STR_BUFSIZ	512
253 	size_t m, n;
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 		/*NOTREACHED*/
266 	case '\\':
267 		_memstream_ungetc(ms, quot);
268 		quot = EOF;
269 		/*FALLTHROUGH*/
270 	case '\"': case '\'':
271 		break;
272 	default:
273 		s[n] = quot;
274 		++n, --m;
275 		quot = EOF;
276 	}
277 	for (;;) {
278 		if (m < 1) {
279 			m = _CITRUS_PROP_STR_BUFSIZ;
280 			t = realloc(s, n + m);
281 			if (t == NULL) {
282 				free(s);
283 				return (ENOMEM);
284 			}
285 			s = t;
286 		}
287 		ch = _memstream_getc(ms);
288 		if (quot == ch || (quot == EOF &&
289 		    (ch == ';' || _bcs_isspace(ch)))) {
290 done:
291 			s[n] = '\0';
292 			obj->u.str = (const char *)s;
293 			return (0);
294 		}
295 		_memstream_ungetc(ms, ch);
296 		errnum = _citrus_prop_read_character_common(ms, &ch);
297 		if (errnum != 0) {
298 			free(s);
299 			return (errnum);
300 		}
301 		s[n] = ch;
302 		++n, --m;
303 	}
304 	free(s);
305 	return (EINVAL);
306 #undef _CITRUS_PROP_STR_BUFSIZ
307 }
308 
309 typedef int (*_citrus_prop_read_type_t)(struct _memstream * __restrict,
310     _citrus_prop_object_t * __restrict);
311 
312 static const _citrus_prop_read_type_t readers[] = {
313 	_citrus_prop_read_bool,
314 	_citrus_prop_read_str,
315 	_citrus_prop_read_character,
316 	_citrus_prop_read_num,
317 };
318 
319 static __inline int
320 _citrus_prop_read_symbol(struct _memstream * __restrict ms,
321     char * __restrict s, size_t n)
322 {
323 	int ch;
324 	size_t m;
325 
326 	for (m = 0; m < n; ++m) {
327 		ch = _memstream_getc(ms);
328 		if (ch != '_' && _bcs_isalnum(ch) == 0)
329 			goto name_found;
330 		s[m] = ch;
331 	}
332 	ch = _memstream_getc(ms);
333 	if (ch == '_' || _bcs_isalnum(ch) != 0)
334 		return (EINVAL);
335 
336 name_found:
337 	_memstream_ungetc(ms, ch);
338 	s[m] = '\0';
339 
340 	return (0);
341 }
342 
343 static int
344 _citrus_prop_parse_element(struct _memstream * __restrict ms,
345     const _citrus_prop_hint_t * __restrict hints, void * __restrict context)
346 {
347 	int ch, errnum;
348 #define _CITRUS_PROP_HINT_NAME_LEN_MAX	255
349 	char name[_CITRUS_PROP_HINT_NAME_LEN_MAX + 1];
350 	const _citrus_prop_hint_t *hint;
351 	_citrus_prop_object_t ostart, oend;
352 
353 	errnum = _citrus_prop_read_symbol(ms, name, sizeof(name));
354 	if (errnum != 0)
355 		return (errnum);
356 	for (hint = hints; hint->name != NULL; ++hint)
357 		if (_citrus_bcs_strcasecmp(name, hint->name) == 0)
358 			goto hint_found;
359 	return (EINVAL);
360 
361 hint_found:
362 	_memstream_skip_ws(ms);
363 	ch = _memstream_getc(ms);
364 	if (ch != '=' && ch != ':')
365 		_memstream_ungetc(ms, ch);
366 	do {
367 		_citrus_prop_object_init(&ostart, hint->type);
368 		_citrus_prop_object_init(&oend, hint->type);
369 		errnum = (*readers[hint->type])(ms, &ostart);
370 		if (errnum != 0)
371 			return (errnum);
372 		_memstream_skip_ws(ms);
373 		ch = _memstream_getc(ms);
374 		switch (hint->type) {
375 		case _CITRUS_PROP_BOOL:
376 			/*FALLTHROUGH*/
377 		case _CITRUS_PROP_STR:
378 			break;
379 		default:
380 			if (ch != '-')
381 				break;
382 			errnum = (*readers[hint->type])(ms, &oend);
383 			if (errnum != 0)
384 				return (errnum);
385 			_memstream_skip_ws(ms);
386 			ch = _memstream_getc(ms);
387 		}
388 #define CALL0(_func_)					\
389 do {							\
390 	errnum = (*hint->cb._func_.func)(context,	\
391 	    hint->name,	ostart.u._func_);		\
392 } while (0)
393 #define CALL1(_func_)					\
394 do {							\
395 	errnum = (*hint->cb._func_.func)(context,	\
396 	    hint->name,	ostart.u._func_, oend.u._func_);\
397 } while (0)
398 		switch (hint->type) {
399 		case _CITRUS_PROP_BOOL:
400 			CALL0(boolean);
401 			break;
402 		case _CITRUS_PROP_STR:
403 			CALL0(str);
404 			break;
405 		case _CITRUS_PROP_CHR:
406 			CALL1(chr);
407 			break;
408 		case _CITRUS_PROP_NUM:
409 			CALL1(num);
410 			break;
411 		default:
412 			abort();
413 			/*NOTREACHED*/
414 		}
415 #undef CALL0
416 #undef CALL1
417 		_citrus_prop_object_uninit(&ostart);
418 		_citrus_prop_object_uninit(&oend);
419 		if (errnum != 0)
420 			return (errnum);
421 	} while (ch == ',');
422 	if (ch != ';')
423 		_memstream_ungetc(ms, ch);
424 	return (0);
425 }
426 
427 int
428 _citrus_prop_parse_variable(const _citrus_prop_hint_t * __restrict hints,
429     void * __restrict context, const void *var, size_t lenvar)
430 {
431 	struct _memstream ms;
432 	int ch, errnum;
433 
434 	_memstream_bind_ptr(&ms, __DECONST(void *, var), lenvar);
435 	for (;;) {
436 		_memstream_skip_ws(&ms);
437 		ch = _memstream_getc(&ms);
438 		if (ch == EOF || ch == '\0')
439 			break;
440 		_memstream_ungetc(&ms, ch);
441 		errnum = _citrus_prop_parse_element(&ms, hints, context);
442 		if (errnum != 0)
443 			return (errnum);
444 	}
445 	return (0);
446 }
447