Sync Citrus iconv support with NetBSD.
[dragonfly.git] / lib / libc / citrus / citrus_prop.c
blob64c7506c1e7d9210caf3460ff7dc7a40abde4093
1 /* $NetBSD: citrus_prop.c,v 1.3 2006/11/22 23:47:21 tnozaki Exp $ */
2 /* $DragonFly: src/lib/libc/citrus/citrus_prop.c,v 1.1 2008/04/10 10:21:01 hasso Exp $ */
4 /*-
5 * Copyright (c)2006 Citrus Project,
6 * All rights reserved.
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.
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.
31 #include <assert.h>
32 #include <limits.h>
33 #include <errno.h>
34 #include <stddef.h>
35 #include <stdio.h>
36 #include <stdint.h>
37 #include <stdlib.h>
38 #include <string.h>
40 #include "citrus_namespace.h"
41 #include "citrus_bcs.h"
42 #include "citrus_region.h"
43 #include "citrus_memstream.h"
44 #include "citrus_prop.h"
46 typedef struct {
47 _citrus_prop_type_t type;
48 union {
49 const char *str;
50 int bool, chr;
51 uint64_t num;
52 } u;
53 } _citrus_prop_object_t;
55 static __inline void
56 _citrus_prop_object_init(_citrus_prop_object_t *obj, _citrus_prop_type_t type)
58 _DIAGASSERT(obj != NULL);
60 obj->type = type;
61 memset(&obj->u, 0, sizeof(obj->u));
64 static __inline void
65 _citrus_prop_object_uninit(_citrus_prop_object_t *obj)
67 _DIAGASSERT(obj != NULL);
69 if (obj->type == _CITRUS_PROP_STR)
70 free(__DECONST(void *, obj->u.str));
73 static const char *xdigit = "0123456789ABCDEF";
75 #define _CITRUS_PROP_READ_UINT_COMMON(_func_, _type_, _max_) \
76 static int \
77 _citrus_prop_read_##_func_##_common(struct _memstream * __restrict ms, \
78 _type_ * __restrict result, int base) \
79 { \
80 _type_ acc, cutoff; \
81 int n, ch, cutlim; \
82 char *p; \
84 _DIAGASSERT(ms != NULL); \
85 _DIAGASSERT(result != NULL); \
87 acc = (_type_)0; \
88 cutoff = _max_ / base; \
89 cutlim = _max_ % base; \
90 for (;;) { \
91 ch = _memstream_getc(ms); \
92 p = strchr(xdigit, _bcs_toupper(ch)); \
93 if (p == NULL || (n = (p - xdigit)) >= base) \
94 break; \
95 if (acc > cutoff || (acc == cutoff && n > cutlim)) \
96 break; \
97 acc *= base; \
98 acc += n; \
99 } \
100 _memstream_ungetc(ms, ch); \
101 *result = acc; \
102 return 0; \
104 _CITRUS_PROP_READ_UINT_COMMON(chr, int, UCHAR_MAX)
105 _CITRUS_PROP_READ_UINT_COMMON(num, uint64_t, UINT64_MAX)
106 #undef _CITRUS_PROP_READ_UINT_COMMON
108 #define _CITRUS_PROP_READ_INT(_func_, _type_) \
109 static int \
110 _citrus_prop_read_##_func_(struct _memstream * __restrict ms, \
111 _citrus_prop_object_t * __restrict obj) \
113 int ch, neg, base; \
115 _DIAGASSERT(ms != NULL); \
116 _DIAGASSERT(obj != NULL); \
118 _memstream_skip_ws(ms); \
119 ch = _memstream_getc(ms); \
120 neg = 0; \
121 switch (ch) { \
122 case '-': \
123 neg = 1; \
124 case '+': \
125 ch = _memstream_getc(ms); \
127 base = 10; \
128 if (ch == '0') { \
129 base -= 2; \
130 ch = _memstream_getc(ms); \
131 if (ch == 'x' || ch == 'X') { \
132 ch = _memstream_getc(ms); \
133 if (_bcs_isxdigit(ch) == 0) { \
134 _memstream_ungetc(ms, ch); \
135 obj->u._func_ = 0; \
136 return 0; \
138 base += 8; \
140 } else if (_bcs_isdigit(ch) == 0) \
141 return EINVAL; \
142 _memstream_ungetc(ms, ch); \
143 return _citrus_prop_read_##_func_##_common \
144 (ms, &obj->u._func_, base); \
146 _CITRUS_PROP_READ_INT(chr, int)
147 _CITRUS_PROP_READ_INT(num, uint64_t)
148 #undef _CITRUS_PROP_READ_INT
150 static int
151 _citrus_prop_read_character_common(struct _memstream * __restrict ms,
152 int * __restrict result)
154 int ch, base;
156 _DIAGASSERT(ms != NULL);
157 _DIAGASSERT(result != NULL);
159 ch = _memstream_getc(ms);
160 if (ch != '\\') {
161 *result = ch;
162 } else {
163 ch = _memstream_getc(ms);
164 base = 16;
165 switch (ch) {
166 case 'a': *result = '\a'; break;
167 case 'b': *result = '\b'; break;
168 case 'f': *result = '\f'; break;
169 case 'n': *result = '\n'; break;
170 case 'r': *result = '\r'; break;
171 case 't': *result = '\t'; break;
172 case 'v': *result = '\v'; break;
173 /*FALLTHROUGH*/
174 case '0': case '1': case '2': case '3':
175 case '4': case '5': case '6': case '7':
176 _memstream_ungetc(ms, ch);
177 base -= 8;
178 case 'x':
179 return _citrus_prop_read_chr_common(ms, result, base);
181 default:
182 /* unknown escape */
183 *result = ch;
186 return 0;
189 static int
190 _citrus_prop_read_character(struct _memstream * __restrict ms,
191 _citrus_prop_object_t * __restrict obj)
193 int ch, errnum;
195 _DIAGASSERT(ms != NULL);
196 _DIAGASSERT(obj != NULL);
198 _memstream_skip_ws(ms);
199 ch = _memstream_getc(ms);
200 if (ch != '\'') {
201 _memstream_ungetc(ms, ch);
202 return _citrus_prop_read_chr(ms, obj);
204 errnum = _citrus_prop_read_character_common(ms, &ch);
205 if (errnum != 0)
206 return errnum;
207 obj->u.chr = ch;
208 ch = _memstream_getc(ms);
209 if (ch != '\'')
210 return EINVAL;
211 return 0;
214 static int
215 _citrus_prop_read_bool(struct _memstream * __restrict ms,
216 _citrus_prop_object_t * __restrict obj)
218 _DIAGASSERT(ms != NULL);
219 _DIAGASSERT(obj != NULL);
221 _memstream_skip_ws(ms);
222 switch (_bcs_tolower(_memstream_getc(ms))) {
223 case 't':
224 if (_bcs_tolower(_memstream_getc(ms)) == 'r' &&
225 _bcs_tolower(_memstream_getc(ms)) == 'u' &&
226 _bcs_tolower(_memstream_getc(ms)) == 'e') {
227 obj->u.bool = 1;
228 return 0;
230 break;
231 case 'f':
232 if (_bcs_tolower(_memstream_getc(ms)) == 'a' &&
233 _bcs_tolower(_memstream_getc(ms)) == 'l' &&
234 _bcs_tolower(_memstream_getc(ms)) == 's' &&
235 _bcs_tolower(_memstream_getc(ms)) == 'e') {
236 obj->u.bool = 0;
237 return 0;
240 return EINVAL;
243 static int
244 _citrus_prop_read_str(struct _memstream * __restrict ms,
245 _citrus_prop_object_t * __restrict obj)
247 int errnum, quot, ch;
248 char *s, *t;
249 #define _CITRUS_PROP_STR_BUFSIZ 512
250 size_t n, m;
252 _DIAGASSERT(ms != NULL);
253 _DIAGASSERT(obj != NULL);
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 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;
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;
284 s = t;
286 ch = _memstream_getc(ms);
287 if (quot == ch || (quot == EOF &&
288 (ch == ';' || _bcs_isspace(ch)))) {
289 done:
290 s[n] = '\0';
291 obj->u.str = (const char *)s;
292 return 0;
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;
301 free(s);
302 return EINVAL;
303 #undef _CITRUS_PROP_STR_BUFSIZ
306 typedef int (*_citrus_prop_read_type_t)(struct _memstream * __restrict,
307 _citrus_prop_object_t * __restrict);
309 static 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,
316 static __inline int
317 _citrus_prop_read_symbol(struct _memstream * __restrict ms,
318 char * __restrict s, size_t n)
320 int ch;
321 size_t m;
323 _DIAGASSERT(ms != NULL);
324 _DIAGASSERT(s != NULL);
325 _DIAGASSERT(n > 0);
327 for (m = 0; m < n; ++m) {
328 ch = _memstream_getc(ms);
329 if (ch != '_' && _bcs_isalnum(ch) == 0)
330 goto name_found;
331 s[m] = ch;
333 ch = _memstream_getc(ms);
334 if (ch == '_' || _bcs_isalnum(ch) != 0)
335 return EINVAL;
337 name_found:
338 _memstream_ungetc(ms, ch);
339 s[m] = '\0';
341 return 0;
344 static int
345 _citrus_prop_parse_element(struct _memstream * __restrict ms,
346 const _citrus_prop_hint_t * __restrict hints,
347 void ** __restrict context)
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;
355 _DIAGASSERT(ms != NULL);
356 _DIAGASSERT(hints != NULL);
358 errnum = _citrus_prop_read_symbol(ms, name, sizeof(name));
359 if (errnum != 0)
360 return errnum;
361 for (hint = hints; hint->name != NULL; ++hint) {
362 if (_citrus_bcs_strcasecmp(name, hint->name) == 0)
363 goto hint_found;
365 return EINVAL;
367 hint_found:
368 _memstream_skip_ws(ms);
369 ch = _memstream_getc(ms);
370 if (ch != '=' && ch != ':')
371 _memstream_ungetc(ms, ch);
372 do {
373 _citrus_prop_object_init(&ostart, hint->type);
374 _citrus_prop_object_init(&oend, hint->type);
375 errnum = (*readers[hint->type])(ms, &ostart);
376 if (errnum != 0)
377 return errnum;
378 _memstream_skip_ws(ms);
379 ch = _memstream_getc(ms);
380 switch (hint->type) {
381 case _CITRUS_PROP_BOOL:
382 case _CITRUS_PROP_STR:
383 break;
384 default:
385 if (ch != '-')
386 break;
387 errnum = (*readers[hint->type])(ms, &oend);
388 if (errnum != 0)
389 return errnum;
390 _memstream_skip_ws(ms);
391 ch = _memstream_getc(ms);
393 #define CALL0(_func_) \
394 do { \
395 _DIAGASSERT(hint->cb._func_.func != NULL); \
396 errnum = (*hint->cb._func_.func)(context, \
397 hint->name, ostart.u._func_); \
398 } while (/*CONSTCOND*/0)
399 #define CALL1(_func_) \
400 do { \
401 _DIAGASSERT(hint->cb._func_.func != NULL); \
402 errnum = (*hint->cb._func_.func)(context, \
403 hint->name, ostart.u._func_, oend.u._func_);\
404 } while (/*CONSTCOND*/0)
405 switch (hint->type) {
406 case _CITRUS_PROP_BOOL: CALL0(bool); break;
407 case _CITRUS_PROP_STR : CALL0( str); break;
408 case _CITRUS_PROP_CHR : CALL1( chr); break;
409 case _CITRUS_PROP_NUM : CALL1( num); break;
410 default:
411 abort();
412 /*NOTREACHED*/
414 #undef CALL0
415 #undef CALL1
416 _citrus_prop_object_uninit(&ostart);
417 _citrus_prop_object_uninit(&oend);
418 if (errnum != 0)
419 return errnum;
420 } while (ch == ',');
421 if (ch != ';')
422 _memstream_ungetc(ms, ch);
423 return 0;
427 _citrus_prop_parse_variable(const _citrus_prop_hint_t * __restrict hints,
428 void * __restrict context, const void *var, size_t lenvar)
430 struct _memstream ms;
431 int errnum, ch;
433 _DIAGASSERT(hints != NULL);
435 _memstream_bind_ptr(&ms, __DECONST(void *, var), lenvar);
436 for (;;) {
437 _memstream_skip_ws(&ms);
438 ch = _memstream_getc(&ms);
439 if (ch == EOF || ch == '\0')
440 break;
441 _memstream_ungetc(&ms, ch);
442 errnum = _citrus_prop_parse_element(
443 &ms, hints, (void **)&context);
444 if (errnum != 0)
445 return errnum;
447 return 0;