1219019Sgabor/* $FreeBSD$ */
2219019Sgabor/* $NetBSD: citrus_prop.c,v 1.3 2006/11/22 23:47:21 tnozaki Exp $ */
3219019Sgabor
4219019Sgabor/*-
5219019Sgabor * Copyright (c)2006 Citrus Project,
6219019Sgabor * All rights reserved.
7219019Sgabor *
8219019Sgabor * Redistribution and use in source and binary forms, with or without
9219019Sgabor * modification, are permitted provided that the following conditions
10219019Sgabor * are met:
11219019Sgabor * 1. Redistributions of source code must retain the above copyright
12219019Sgabor *    notice, this list of conditions and the following disclaimer.
13219019Sgabor * 2. Redistributions in binary form must reproduce the above copyright
14219019Sgabor *    notice, this list of conditions and the following disclaimer in the
15219019Sgabor *    documentation and/or other materials provided with the distribution.
16219019Sgabor *
17219019Sgabor * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18219019Sgabor * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19219019Sgabor * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20219019Sgabor * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21219019Sgabor * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22219019Sgabor * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23219019Sgabor * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24219019Sgabor * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25219019Sgabor * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26219019Sgabor * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27219019Sgabor * SUCH DAMAGE.
28219019Sgabor *
29219019Sgabor */
30219019Sgabor
31219019Sgabor#include <sys/cdefs.h>
32219019Sgabor
33219019Sgabor#include <assert.h>
34219019Sgabor#include <errno.h>
35219019Sgabor#include <limits.h>
36219019Sgabor#include <stdbool.h>
37219019Sgabor#include <stddef.h>
38219019Sgabor#include <stdio.h>
39219019Sgabor#include <stdint.h>
40219019Sgabor#include <stdlib.h>
41219019Sgabor#include <string.h>
42219019Sgabor
43219019Sgabor#include "citrus_namespace.h"
44219019Sgabor#include "citrus_bcs.h"
45219019Sgabor#include "citrus_region.h"
46219019Sgabor#include "citrus_memstream.h"
47219019Sgabor#include "citrus_prop.h"
48219019Sgabor
49219019Sgabortypedef struct {
50219019Sgabor	_citrus_prop_type_t type;
51219019Sgabor	union {
52219019Sgabor		const char *str;
53219019Sgabor		int chr;
54219019Sgabor		bool boolean;
55219019Sgabor		uint64_t num;
56219019Sgabor	} u;
57219019Sgabor} _citrus_prop_object_t;
58219019Sgabor
59219019Sgaborstatic __inline void
60219019Sgabor_citrus_prop_object_init(_citrus_prop_object_t *obj, _citrus_prop_type_t type)
61219019Sgabor{
62219019Sgabor
63219019Sgabor	obj->type = type;
64219019Sgabor	memset(&obj->u, 0, sizeof(obj->u));
65219019Sgabor}
66219019Sgabor
67219019Sgaborstatic __inline void
68219019Sgabor_citrus_prop_object_uninit(_citrus_prop_object_t *obj)
69219019Sgabor{
70219019Sgabor
71219019Sgabor	if (obj->type == _CITRUS_PROP_STR)
72219019Sgabor		free(__DECONST(void *, obj->u.str));
73219019Sgabor}
74219019Sgabor
75219019Sgaborstatic const char *xdigit = "0123456789ABCDEF";
76219019Sgabor
77219019Sgabor#define _CITRUS_PROP_READ_UINT_COMMON(_func_, _type_, _max_)		\
78219019Sgaborstatic int								\
79219019Sgabor_citrus_prop_read_##_func_##_common(struct _memstream * __restrict ms,	\
80219019Sgabor    _type_ * __restrict result, int base)				\
81219019Sgabor{									\
82219019Sgabor	_type_ acc, cutoff;						\
83219019Sgabor	int ch, cutlim, n;						\
84219019Sgabor	char *p;							\
85219019Sgabor									\
86219019Sgabor	acc = (_type_)0;						\
87219019Sgabor	cutoff = _max_ / base;						\
88219019Sgabor	cutlim = _max_ % base;						\
89219019Sgabor	for (;;) {							\
90219019Sgabor		ch = _memstream_getc(ms);				\
91219019Sgabor		p = strchr(xdigit, _bcs_toupper(ch));			\
92219019Sgabor		if (p == NULL || (n = (p - xdigit)) >= base)		\
93219019Sgabor			break;						\
94219019Sgabor		if (acc > cutoff || (acc == cutoff && n > cutlim))	\
95219019Sgabor			break;						\
96219019Sgabor		acc *= base;						\
97219019Sgabor		acc += n;						\
98219019Sgabor	}								\
99219019Sgabor	_memstream_ungetc(ms, ch);					\
100219019Sgabor	*result = acc;							\
101219019Sgabor	return (0);							\
102219019Sgabor}
103219019Sgabor_CITRUS_PROP_READ_UINT_COMMON(chr, int, UCHAR_MAX)
104219019Sgabor_CITRUS_PROP_READ_UINT_COMMON(num, uint64_t, UINT64_MAX)
105219019Sgabor#undef _CITRUS_PROP_READ_UINT_COMMON
106219019Sgabor
107219019Sgabor#define _CITRUS_PROP_READ_INT(_func_, _type_)			\
108219019Sgaborstatic int							\
109219019Sgabor_citrus_prop_read_##_func_(struct _memstream * __restrict ms,	\
110219019Sgabor    _citrus_prop_object_t * __restrict obj)			\
111219019Sgabor{								\
112219019Sgabor	int base, ch, neg;					\
113219019Sgabor								\
114219019Sgabor	_memstream_skip_ws(ms);					\
115219019Sgabor	ch = _memstream_getc(ms);				\
116219019Sgabor	neg = 0;						\
117219019Sgabor	switch (ch) {						\
118219019Sgabor	case '-':						\
119219019Sgabor		neg = 1;					\
120219019Sgabor	case '+':						\
121219019Sgabor		ch = _memstream_getc(ms);			\
122219019Sgabor	}							\
123219019Sgabor	base = 10;						\
124219019Sgabor	if (ch == '0') {					\
125219019Sgabor		base -= 2;					\
126219019Sgabor		ch = _memstream_getc(ms);			\
127219019Sgabor		if (ch == 'x' || ch == 'X') {			\
128219019Sgabor			ch = _memstream_getc(ms);		\
129219019Sgabor			if (_bcs_isxdigit(ch) == 0) {		\
130219019Sgabor				_memstream_ungetc(ms, ch);	\
131219019Sgabor				obj->u._func_ = 0;		\
132219019Sgabor				return (0);			\
133219019Sgabor			}					\
134219019Sgabor			base += 8;				\
135219019Sgabor		}						\
136219019Sgabor	} else if (_bcs_isdigit(ch) == 0)			\
137219019Sgabor		return (EINVAL);				\
138219019Sgabor	_memstream_ungetc(ms, ch);				\
139219019Sgabor	return (_citrus_prop_read_##_func_##_common		\
140219019Sgabor	    (ms, &obj->u._func_, base));			\
141219019Sgabor}
142219019Sgabor_CITRUS_PROP_READ_INT(chr, int)
143219019Sgabor_CITRUS_PROP_READ_INT(num, uint64_t)
144219019Sgabor#undef _CITRUS_PROP_READ_INT
145219019Sgabor
146219019Sgaborstatic int
147219019Sgabor_citrus_prop_read_character_common(struct _memstream * __restrict ms,
148219019Sgabor    int * __restrict result)
149219019Sgabor{
150219019Sgabor	int base, ch;
151219019Sgabor
152219019Sgabor	ch = _memstream_getc(ms);
153219019Sgabor	if (ch != '\\')
154219019Sgabor		*result = ch;
155219019Sgabor	else {
156219019Sgabor		ch = _memstream_getc(ms);
157219019Sgabor		base = 16;
158219019Sgabor		switch (ch) {
159219019Sgabor		case 'a':
160219019Sgabor			*result = '\a';
161219019Sgabor			break;
162219019Sgabor		case 'b':
163219019Sgabor			*result = '\b';
164219019Sgabor			break;
165219019Sgabor		case 'f':
166219019Sgabor			*result = '\f';
167219019Sgabor			break;
168219019Sgabor		case 'n':
169219019Sgabor			*result = '\n';
170219019Sgabor			break;
171219019Sgabor		case 'r':
172219019Sgabor			*result = '\r';
173219019Sgabor			break;
174219019Sgabor		case 't':
175219019Sgabor			*result = '\t';
176219019Sgabor			break;
177219019Sgabor		case 'v':
178219019Sgabor			*result = '\v';
179219019Sgabor			break;
180219019Sgabor		case '0': case '1': case '2': case '3':
181219019Sgabor		case '4': case '5': case '6': case '7':
182219019Sgabor			_memstream_ungetc(ms, ch);
183219019Sgabor			base -= 8;
184219019Sgabor			/*FALLTHROUGH*/
185219019Sgabor		case 'x':
186219019Sgabor			return (_citrus_prop_read_chr_common(ms, result, base));
187219019Sgabor			/*NOTREACHED*/
188219019Sgabor		default:
189219019Sgabor			/* unknown escape */
190219019Sgabor			*result = ch;
191219019Sgabor		}
192219019Sgabor	}
193219019Sgabor	return (0);
194219019Sgabor}
195219019Sgabor
196219019Sgaborstatic int
197219019Sgabor_citrus_prop_read_character(struct _memstream * __restrict ms,
198219019Sgabor    _citrus_prop_object_t * __restrict obj)
199219019Sgabor{
200219019Sgabor	int ch, errnum;
201219019Sgabor
202219019Sgabor	_memstream_skip_ws(ms);
203219019Sgabor	ch = _memstream_getc(ms);
204219019Sgabor	if (ch != '\'') {
205219019Sgabor		_memstream_ungetc(ms, ch);
206219019Sgabor		return (_citrus_prop_read_chr(ms, obj));
207219019Sgabor	}
208219019Sgabor	errnum = _citrus_prop_read_character_common(ms, &ch);
209219019Sgabor	if (errnum != 0)
210219019Sgabor		return (errnum);
211219019Sgabor	obj->u.chr = ch;
212219019Sgabor	ch = _memstream_getc(ms);
213219019Sgabor	if (ch != '\'')
214219019Sgabor		return (EINVAL);
215219019Sgabor	return (0);
216219019Sgabor}
217219019Sgabor
218219019Sgaborstatic int
219219019Sgabor_citrus_prop_read_bool(struct _memstream * __restrict ms,
220219019Sgabor    _citrus_prop_object_t * __restrict obj)
221219019Sgabor{
222219019Sgabor
223219019Sgabor	_memstream_skip_ws(ms);
224219019Sgabor	switch (_bcs_tolower(_memstream_getc(ms))) {
225219019Sgabor	case 't':
226219019Sgabor		if (_bcs_tolower(_memstream_getc(ms)) == 'r' &&
227219019Sgabor		    _bcs_tolower(_memstream_getc(ms)) == 'u' &&
228219019Sgabor		    _bcs_tolower(_memstream_getc(ms)) == 'e') {
229219019Sgabor			obj->u.boolean = true;
230219019Sgabor			return (0);
231219019Sgabor		}
232219019Sgabor		break;
233219019Sgabor	case 'f':
234219019Sgabor		if (_bcs_tolower(_memstream_getc(ms)) == 'a' &&
235219019Sgabor		    _bcs_tolower(_memstream_getc(ms)) == 'l' &&
236219019Sgabor		    _bcs_tolower(_memstream_getc(ms)) == 's' &&
237219019Sgabor		    _bcs_tolower(_memstream_getc(ms)) == 'e') {
238219019Sgabor			obj->u.boolean = false;
239219019Sgabor			return (0);
240219019Sgabor		}
241219019Sgabor	}
242219019Sgabor	return (EINVAL);
243219019Sgabor}
244219019Sgabor
245219019Sgaborstatic int
246219019Sgabor_citrus_prop_read_str(struct _memstream * __restrict ms,
247219019Sgabor    _citrus_prop_object_t * __restrict obj)
248219019Sgabor{
249219019Sgabor	int ch, errnum, quot;
250219019Sgabor	char *s, *t;
251219019Sgabor#define _CITRUS_PROP_STR_BUFSIZ	512
252219019Sgabor	size_t m, n;
253219019Sgabor
254219019Sgabor	m = _CITRUS_PROP_STR_BUFSIZ;
255219019Sgabor	s = malloc(m);
256219019Sgabor	if (s == NULL)
257219019Sgabor		return (ENOMEM);
258219019Sgabor	n = 0;
259219019Sgabor	_memstream_skip_ws(ms);
260219019Sgabor	quot = _memstream_getc(ms);
261219019Sgabor	switch (quot) {
262219019Sgabor	case EOF:
263219019Sgabor		goto done;
264219019Sgabor		/*NOTREACHED*/
265219019Sgabor	case '\\':
266219019Sgabor		_memstream_ungetc(ms, quot);
267219019Sgabor		quot = EOF;
268219019Sgabor		/*FALLTHROUGH*/
269219019Sgabor	case '\"': case '\'':
270219019Sgabor		break;
271219019Sgabor	default:
272219019Sgabor		s[n] = quot;
273219019Sgabor		++n, --m;
274219019Sgabor		quot = EOF;
275219019Sgabor	}
276219019Sgabor	for (;;) {
277219019Sgabor		if (m < 1) {
278219019Sgabor			m = _CITRUS_PROP_STR_BUFSIZ;
279219019Sgabor			t = realloc(s, n + m);
280219019Sgabor			if (t == NULL) {
281219019Sgabor				free(s);
282219019Sgabor				return (ENOMEM);
283219019Sgabor			}
284219019Sgabor			s = t;
285219019Sgabor		}
286219019Sgabor		ch = _memstream_getc(ms);
287219019Sgabor		if (quot == ch || (quot == EOF &&
288219019Sgabor		    (ch == ';' || _bcs_isspace(ch)))) {
289219019Sgabordone:
290219019Sgabor			s[n] = '\0';
291219019Sgabor			obj->u.str = (const char *)s;
292219019Sgabor			return (0);
293219019Sgabor		}
294219019Sgabor		_memstream_ungetc(ms, ch);
295219019Sgabor		errnum = _citrus_prop_read_character_common(ms, &ch);
296219019Sgabor		if (errnum != 0)
297219019Sgabor			return (errnum);
298219019Sgabor		s[n] = ch;
299219019Sgabor		++n, --m;
300219019Sgabor	}
301219019Sgabor	free(s);
302219019Sgabor	return (EINVAL);
303219019Sgabor#undef _CITRUS_PROP_STR_BUFSIZ
304219019Sgabor}
305219019Sgabor
306219019Sgabortypedef int (*_citrus_prop_read_type_t)(struct _memstream * __restrict,
307219019Sgabor    _citrus_prop_object_t * __restrict);
308219019Sgabor
309219019Sgaborstatic const _citrus_prop_read_type_t readers[] = {
310219019Sgabor	_citrus_prop_read_bool,
311219019Sgabor	_citrus_prop_read_str,
312219019Sgabor	_citrus_prop_read_character,
313219019Sgabor	_citrus_prop_read_num,
314219019Sgabor};
315219019Sgabor
316219019Sgaborstatic __inline int
317219019Sgabor_citrus_prop_read_symbol(struct _memstream * __restrict ms,
318219019Sgabor    char * __restrict s, size_t n)
319219019Sgabor{
320219019Sgabor	int ch;
321219019Sgabor	size_t m;
322219019Sgabor
323219019Sgabor	for (m = 0; m < n; ++m) {
324219019Sgabor		ch = _memstream_getc(ms);
325219019Sgabor		if (ch != '_' && _bcs_isalnum(ch) == 0)
326219019Sgabor			goto name_found;
327219019Sgabor		s[m] = ch;
328219019Sgabor	}
329219019Sgabor	ch = _memstream_getc(ms);
330219019Sgabor	if (ch == '_' || _bcs_isalnum(ch) != 0)
331219019Sgabor		return (EINVAL);
332219019Sgabor
333219019Sgaborname_found:
334219019Sgabor	_memstream_ungetc(ms, ch);
335219019Sgabor	s[m] = '\0';
336219019Sgabor
337219019Sgabor	return (0);
338219019Sgabor}
339219019Sgabor
340219019Sgaborstatic int
341219019Sgabor_citrus_prop_parse_element(struct _memstream * __restrict ms,
342219019Sgabor    const _citrus_prop_hint_t * __restrict hints, void ** __restrict context)
343219019Sgabor{
344219019Sgabor	int ch, errnum;
345219019Sgabor#define _CITRUS_PROP_HINT_NAME_LEN_MAX	255
346219019Sgabor	char name[_CITRUS_PROP_HINT_NAME_LEN_MAX + 1];
347219019Sgabor	const _citrus_prop_hint_t *hint;
348219019Sgabor	_citrus_prop_object_t ostart, oend;
349219019Sgabor
350219019Sgabor	errnum = _citrus_prop_read_symbol(ms, name, sizeof(name));
351219019Sgabor	if (errnum != 0)
352219019Sgabor		return (errnum);
353219019Sgabor	for (hint = hints; hint->name != NULL; ++hint)
354219019Sgabor		if (_citrus_bcs_strcasecmp(name, hint->name) == 0)
355219019Sgabor			goto hint_found;
356219019Sgabor	return (EINVAL);
357219019Sgabor
358219019Sgaborhint_found:
359219019Sgabor	_memstream_skip_ws(ms);
360219019Sgabor	ch = _memstream_getc(ms);
361219019Sgabor	if (ch != '=' && ch != ':')
362219019Sgabor		_memstream_ungetc(ms, ch);
363219019Sgabor	do {
364219019Sgabor		_citrus_prop_object_init(&ostart, hint->type);
365219019Sgabor		_citrus_prop_object_init(&oend, hint->type);
366219019Sgabor		errnum = (*readers[hint->type])(ms, &ostart);
367219019Sgabor		if (errnum != 0)
368219019Sgabor			return (errnum);
369219019Sgabor		_memstream_skip_ws(ms);
370219019Sgabor		ch = _memstream_getc(ms);
371219019Sgabor		switch (hint->type) {
372219019Sgabor		case _CITRUS_PROP_BOOL:
373219019Sgabor			/*FALLTHROUGH*/
374219019Sgabor		case _CITRUS_PROP_STR:
375219019Sgabor			break;
376219019Sgabor		default:
377219019Sgabor			if (ch != '-')
378219019Sgabor				break;
379219019Sgabor			errnum = (*readers[hint->type])(ms, &oend);
380219019Sgabor			if (errnum != 0)
381219019Sgabor				return (errnum);
382219019Sgabor			_memstream_skip_ws(ms);
383219019Sgabor			ch = _memstream_getc(ms);
384219019Sgabor		}
385219019Sgabor#define CALL0(_func_)					\
386219019Sgabordo {							\
387219019Sgabor	errnum = (*hint->cb._func_.func)(context,	\
388219019Sgabor	    hint->name,	ostart.u._func_);		\
389219019Sgabor} while (0)
390219019Sgabor#define CALL1(_func_)					\
391219019Sgabordo {							\
392219019Sgabor	errnum = (*hint->cb._func_.func)(context,	\
393219019Sgabor	    hint->name,	ostart.u._func_, oend.u._func_);\
394219019Sgabor} while (0)
395219019Sgabor		switch (hint->type) {
396219019Sgabor		case _CITRUS_PROP_BOOL:
397219019Sgabor			CALL0(boolean);
398219019Sgabor			break;
399219019Sgabor		case _CITRUS_PROP_STR:
400219019Sgabor			CALL0(str);
401219019Sgabor			break;
402219019Sgabor		case _CITRUS_PROP_CHR:
403219019Sgabor			CALL1(chr);
404219019Sgabor			break;
405219019Sgabor		case _CITRUS_PROP_NUM:
406219019Sgabor			CALL1(num);
407219019Sgabor			break;
408219019Sgabor		default:
409219019Sgabor			abort();
410219019Sgabor			/*NOTREACHED*/
411219019Sgabor		}
412219019Sgabor#undef CALL0
413219019Sgabor#undef CALL1
414219019Sgabor		_citrus_prop_object_uninit(&ostart);
415219019Sgabor		_citrus_prop_object_uninit(&oend);
416219019Sgabor		if (errnum != 0)
417219019Sgabor			return (errnum);
418219019Sgabor	} while (ch == ',');
419219019Sgabor	if (ch != ';')
420219019Sgabor		_memstream_ungetc(ms, ch);
421219019Sgabor	return (0);
422219019Sgabor}
423219019Sgabor
424219019Sgaborint
425219019Sgabor_citrus_prop_parse_variable(const _citrus_prop_hint_t * __restrict hints,
426219019Sgabor    void * __restrict context, const void *var, size_t lenvar)
427219019Sgabor{
428219019Sgabor	struct _memstream ms;
429219019Sgabor	int ch, errnum;
430219019Sgabor
431219019Sgabor	_memstream_bind_ptr(&ms, __DECONST(void *, var), lenvar);
432219019Sgabor	for (;;) {
433219019Sgabor		_memstream_skip_ws(&ms);
434219019Sgabor		ch = _memstream_getc(&ms);
435219019Sgabor		if (ch == EOF || ch == '\0')
436219019Sgabor			break;
437219019Sgabor		_memstream_ungetc(&ms, ch);
438219019Sgabor		errnum = _citrus_prop_parse_element(
439219019Sgabor		    &ms, hints, (void ** __restrict)context);
440219019Sgabor		if (errnum != 0)
441219019Sgabor			return (errnum);
442219019Sgabor	}
443219019Sgabor	return (0);
444219019Sgabor}
445