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