Initial import of binutils 2.22 on the new vendor branch
[dragonfly.git] / lib / libc / citrus / citrus_prop.c
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 $ */
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 <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>
39
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"
45
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;
54
55 static __inline void
56 _citrus_prop_object_init(_citrus_prop_object_t *obj, _citrus_prop_type_t type)
57 {
58         _DIAGASSERT(obj != NULL);
59
60         obj->type = type;
61         memset(&obj->u, 0, sizeof(obj->u));
62 }
63
64 static __inline void
65 _citrus_prop_object_uninit(_citrus_prop_object_t *obj)
66 {
67         _DIAGASSERT(obj != NULL);
68
69         if (obj->type == _CITRUS_PROP_STR)
70                 free(__DECONST(void *, obj->u.str));
71 }
72
73 static const char *xdigit = "0123456789ABCDEF";
74
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;                                                        \
83                                                                         \
84         _DIAGASSERT(ms != NULL);                                        \
85         _DIAGASSERT(result != NULL);                                    \
86                                                                         \
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;                                                       \
103 }
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
107
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)                 \
112 {                                                               \
113         int ch, neg, base;                                      \
114                                                                 \
115         _DIAGASSERT(ms != NULL);                                \
116         _DIAGASSERT(obj != NULL);                               \
117                                                                 \
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);                       \
126         }                                                       \
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;                       \
137                         }                                       \
138                         base += 8;                              \
139                 }                                               \
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);                         \
145 }
146 _CITRUS_PROP_READ_INT(chr, int)
147 _CITRUS_PROP_READ_INT(num, uint64_t)
148 #undef _CITRUS_PROP_READ_INT
149
150 static int
151 _citrus_prop_read_character_common(struct _memstream * __restrict ms,
152         int * __restrict result)
153 {
154         int ch, base;
155
156         _DIAGASSERT(ms != NULL);
157         _DIAGASSERT(result != NULL);
158
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);
180                         
181                 default:
182                         /* unknown escape */
183                         *result = ch;
184                 }
185         }
186         return 0;
187 }
188
189 static int
190 _citrus_prop_read_character(struct _memstream * __restrict ms,
191         _citrus_prop_object_t * __restrict obj)
192 {
193         int ch, errnum;
194
195         _DIAGASSERT(ms != NULL);
196         _DIAGASSERT(obj != NULL);
197
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);
203         }
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;
212 }
213
214 static int
215 _citrus_prop_read_bool(struct _memstream * __restrict ms,
216         _citrus_prop_object_t * __restrict obj)
217 {
218         _DIAGASSERT(ms != NULL);
219         _DIAGASSERT(obj != NULL);
220
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;
229                 }
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;
238                 }
239         }
240         return EINVAL;
241 }
242
243 static int
244 _citrus_prop_read_str(struct _memstream * __restrict ms,
245         _citrus_prop_object_t * __restrict obj)
246 {
247         int errnum, quot, ch;
248         char *s, *t;
249 #define _CITRUS_PROP_STR_BUFSIZ 512
250         size_t n, m;
251
252         _DIAGASSERT(ms != NULL);
253         _DIAGASSERT(obj != NULL);
254
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;
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)))) {
289 done:
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
306 typedef int (*_citrus_prop_read_type_t)(struct _memstream * __restrict,
307         _citrus_prop_object_t * __restrict);
308
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,
314 };
315
316 static __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         _DIAGASSERT(ms != NULL);
324         _DIAGASSERT(s != NULL);
325         _DIAGASSERT(n > 0);
326
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;
332         }
333         ch = _memstream_getc(ms);
334         if (ch == '_' || _bcs_isalnum(ch) != 0)
335                 return EINVAL;
336
337 name_found:
338         _memstream_ungetc(ms, ch);
339         s[m] = '\0';
340
341         return 0;
342 }
343
344 static int
345 _citrus_prop_parse_element(struct _memstream * __restrict ms,
346         const _citrus_prop_hint_t * __restrict hints,
347         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         _DIAGASSERT(ms != NULL);
356         _DIAGASSERT(hints != NULL);
357
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;
364         }
365         return EINVAL;
366
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);
392                 }
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*/
413                 }
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;
424 }
425
426 int
427 _citrus_prop_parse_variable(const _citrus_prop_hint_t * __restrict hints,
428         void * __restrict context, const void *var, size_t lenvar)
429 {
430         struct _memstream ms;
431         int errnum, ch;
432
433         _DIAGASSERT(hints != NULL);
434
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;
446         }
447         return 0;
448 }