citrus_prop.c revision 267829
1/* $FreeBSD: releng/10.0/lib/libc/iconv/citrus_prop.c 267829 2014-06-24 19:05:08Z delphij $ */
2/* $NetBSD: citrus_prop.c,v 1.3 2006/11/22 23:47:21 tnozaki 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
49typedef 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
59static __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
67static __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
75static const char *xdigit = "0123456789ABCDEF";
76
77#define _CITRUS_PROP_READ_UINT_COMMON(_func_, _type_, _max_)		\
78static 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_)			\
108static 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));			\
141}
142_CITRUS_PROP_READ_INT(chr, int)
143_CITRUS_PROP_READ_INT(num, uint64_t)
144#undef _CITRUS_PROP_READ_INT
145
146static 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, base));
187			/*NOTREACHED*/
188		default:
189			/* unknown escape */
190			*result = ch;
191		}
192	}
193	return (0);
194}
195
196static int
197_citrus_prop_read_character(struct _memstream * __restrict ms,
198    _citrus_prop_object_t * __restrict obj)
199{
200	int ch, errnum;
201
202	_memstream_skip_ws(ms);
203	ch = _memstream_getc(ms);
204	if (ch != '\'') {
205		_memstream_ungetc(ms, ch);
206		return (_citrus_prop_read_chr(ms, obj));
207	}
208	errnum = _citrus_prop_read_character_common(ms, &ch);
209	if (errnum != 0)
210		return (errnum);
211	obj->u.chr = ch;
212	ch = _memstream_getc(ms);
213	if (ch != '\'')
214		return (EINVAL);
215	return (0);
216}
217
218static int
219_citrus_prop_read_bool(struct _memstream * __restrict ms,
220    _citrus_prop_object_t * __restrict obj)
221{
222
223	_memstream_skip_ws(ms);
224	switch (_bcs_tolower(_memstream_getc(ms))) {
225	case 't':
226		if (_bcs_tolower(_memstream_getc(ms)) == 'r' &&
227		    _bcs_tolower(_memstream_getc(ms)) == 'u' &&
228		    _bcs_tolower(_memstream_getc(ms)) == 'e') {
229			obj->u.boolean = true;
230			return (0);
231		}
232		break;
233	case 'f':
234		if (_bcs_tolower(_memstream_getc(ms)) == 'a' &&
235		    _bcs_tolower(_memstream_getc(ms)) == 'l' &&
236		    _bcs_tolower(_memstream_getc(ms)) == 's' &&
237		    _bcs_tolower(_memstream_getc(ms)) == 'e') {
238			obj->u.boolean = false;
239			return (0);
240		}
241	}
242	return (EINVAL);
243}
244
245static int
246_citrus_prop_read_str(struct _memstream * __restrict ms,
247    _citrus_prop_object_t * __restrict obj)
248{
249	int ch, errnum, quot;
250	char *s, *t;
251#define _CITRUS_PROP_STR_BUFSIZ	512
252	size_t m, n;
253
254	m = _CITRUS_PROP_STR_BUFSIZ;
255	s = malloc(m);
256	if (s == NULL)
257		return (ENOMEM);
258	n = 0;
259	_memstream_skip_ws(ms);
260	quot = _memstream_getc(ms);
261	switch (quot) {
262	case EOF:
263		goto done;
264		/*NOTREACHED*/
265	case '\\':
266		_memstream_ungetc(ms, quot);
267		quot = EOF;
268		/*FALLTHROUGH*/
269	case '\"': case '\'':
270		break;
271	default:
272		s[n] = quot;
273		++n, --m;
274		quot = EOF;
275	}
276	for (;;) {
277		if (m < 1) {
278			m = _CITRUS_PROP_STR_BUFSIZ;
279			t = realloc(s, n + m);
280			if (t == NULL) {
281				free(s);
282				return (ENOMEM);
283			}
284			s = t;
285		}
286		ch = _memstream_getc(ms);
287		if (quot == ch || (quot == EOF &&
288		    (ch == ';' || _bcs_isspace(ch)))) {
289done:
290			s[n] = '\0';
291			obj->u.str = (const char *)s;
292			return (0);
293		}
294		_memstream_ungetc(ms, ch);
295		errnum = _citrus_prop_read_character_common(ms, &ch);
296		if (errnum != 0)
297			return (errnum);
298		s[n] = ch;
299		++n, --m;
300	}
301	free(s);
302	return (EINVAL);
303#undef _CITRUS_PROP_STR_BUFSIZ
304}
305
306typedef int (*_citrus_prop_read_type_t)(struct _memstream * __restrict,
307    _citrus_prop_object_t * __restrict);
308
309static const _citrus_prop_read_type_t readers[] = {
310	_citrus_prop_read_bool,
311	_citrus_prop_read_str,
312	_citrus_prop_read_character,
313	_citrus_prop_read_num,
314};
315
316static __inline int
317_citrus_prop_read_symbol(struct _memstream * __restrict ms,
318    char * __restrict s, size_t n)
319{
320	int ch;
321	size_t m;
322
323	for (m = 0; m < n; ++m) {
324		ch = _memstream_getc(ms);
325		if (ch != '_' && _bcs_isalnum(ch) == 0)
326			goto name_found;
327		s[m] = ch;
328	}
329	ch = _memstream_getc(ms);
330	if (ch == '_' || _bcs_isalnum(ch) != 0)
331		return (EINVAL);
332
333name_found:
334	_memstream_ungetc(ms, ch);
335	s[m] = '\0';
336
337	return (0);
338}
339
340static int
341_citrus_prop_parse_element(struct _memstream * __restrict ms,
342    const _citrus_prop_hint_t * __restrict hints, void * __restrict context)
343{
344	int ch, errnum;
345#define _CITRUS_PROP_HINT_NAME_LEN_MAX	255
346	char name[_CITRUS_PROP_HINT_NAME_LEN_MAX + 1];
347	const _citrus_prop_hint_t *hint;
348	_citrus_prop_object_t ostart, oend;
349
350	errnum = _citrus_prop_read_symbol(ms, name, sizeof(name));
351	if (errnum != 0)
352		return (errnum);
353	for (hint = hints; hint->name != NULL; ++hint)
354		if (_citrus_bcs_strcasecmp(name, hint->name) == 0)
355			goto hint_found;
356	return (EINVAL);
357
358hint_found:
359	_memstream_skip_ws(ms);
360	ch = _memstream_getc(ms);
361	if (ch != '=' && ch != ':')
362		_memstream_ungetc(ms, ch);
363	do {
364		_citrus_prop_object_init(&ostart, hint->type);
365		_citrus_prop_object_init(&oend, hint->type);
366		errnum = (*readers[hint->type])(ms, &ostart);
367		if (errnum != 0)
368			return (errnum);
369		_memstream_skip_ws(ms);
370		ch = _memstream_getc(ms);
371		switch (hint->type) {
372		case _CITRUS_PROP_BOOL:
373			/*FALLTHROUGH*/
374		case _CITRUS_PROP_STR:
375			break;
376		default:
377			if (ch != '-')
378				break;
379			errnum = (*readers[hint->type])(ms, &oend);
380			if (errnum != 0)
381				return (errnum);
382			_memstream_skip_ws(ms);
383			ch = _memstream_getc(ms);
384		}
385#define CALL0(_func_)					\
386do {							\
387	errnum = (*hint->cb._func_.func)(context,	\
388	    hint->name,	ostart.u._func_);		\
389} while (0)
390#define CALL1(_func_)					\
391do {							\
392	errnum = (*hint->cb._func_.func)(context,	\
393	    hint->name,	ostart.u._func_, oend.u._func_);\
394} while (0)
395		switch (hint->type) {
396		case _CITRUS_PROP_BOOL:
397			CALL0(boolean);
398			break;
399		case _CITRUS_PROP_STR:
400			CALL0(str);
401			break;
402		case _CITRUS_PROP_CHR:
403			CALL1(chr);
404			break;
405		case _CITRUS_PROP_NUM:
406			CALL1(num);
407			break;
408		default:
409			abort();
410			/*NOTREACHED*/
411		}
412#undef CALL0
413#undef CALL1
414		_citrus_prop_object_uninit(&ostart);
415		_citrus_prop_object_uninit(&oend);
416		if (errnum != 0)
417			return (errnum);
418	} while (ch == ',');
419	if (ch != ';')
420		_memstream_ungetc(ms, ch);
421	return (0);
422}
423
424int
425_citrus_prop_parse_variable(const _citrus_prop_hint_t * __restrict hints,
426    void * __restrict context, const void *var, size_t lenvar)
427{
428	struct _memstream ms;
429	int ch, errnum;
430
431	_memstream_bind_ptr(&ms, __DECONST(void *, var), lenvar);
432	for (;;) {
433		_memstream_skip_ws(&ms);
434		ch = _memstream_getc(&ms);
435		if (ch == EOF || ch == '\0')
436			break;
437		_memstream_ungetc(&ms, ch);
438		errnum = _citrus_prop_parse_element(&ms, hints, context);
439		if (errnum != 0)
440			return (errnum);
441	}
442	return (0);
443}
444