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