kernel - Stir longer in arc4random
[dragonfly.git] / lib / libc / citrus / citrus_prop.c
1 /* $FreeBSD: head/lib/libc/iconv/citrus_prop.c 281798 2015-04-20 22:09:50Z pfg $ */
2 /* $NetBSD: citrus_prop.c,v 1.4 2011/03/30 08:22:01 jruoho 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
49 typedef 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
59 static __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
67 static __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
75 static const char *xdigit = "0123456789ABCDEF";
76
77 #define _CITRUS_PROP_READ_UINT_COMMON(_func_, _type_, _max_)            \
78 static 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_)                   \
108 static int                                                      \
109 _citrus_prop_read_##_func_(struct _memstream * __restrict ms,   \
110     _citrus_prop_object_t * __restrict obj)                     \
111 {                                                               \
112         int base, ch;                                           \
113                                                                 \
114         _memstream_skip_ws(ms);                                 \
115         ch = _memstream_getc(ms);                               \
116         switch (ch) {                                           \
117         case '+':                                               \
118                 ch = _memstream_getc(ms);                       \
119         }                                                       \
120         base = 10;                                              \
121         if (ch == '0') {                                        \
122                 base -= 2;                                      \
123                 ch = _memstream_getc(ms);                       \
124                 if (ch == 'x' || ch == 'X') {                   \
125                         ch = _memstream_getc(ms);               \
126                         if (_bcs_isxdigit(ch) == 0) {           \
127                                 _memstream_ungetc(ms, ch);      \
128                                 obj->u._func_ = 0;              \
129                                 return (0);                     \
130                         }                                       \
131                         base += 8;                              \
132                 }                                               \
133         } else if (_bcs_isdigit(ch) == 0)                       \
134                 return (EINVAL);                                \
135         _memstream_ungetc(ms, ch);                              \
136         return (_citrus_prop_read_##_func_##_common             \
137             (ms, &obj->u._func_, base));                        \
138 }
139 _CITRUS_PROP_READ_INT(chr, int)
140 _CITRUS_PROP_READ_INT(num, uint64_t)
141 #undef _CITRUS_PROP_READ_INT
142
143 static int
144 _citrus_prop_read_character_common(struct _memstream * __restrict ms,
145     int * __restrict result)
146 {
147         int base, ch;
148
149         ch = _memstream_getc(ms);
150         if (ch != '\\')
151                 *result = ch;
152         else {
153                 ch = _memstream_getc(ms);
154                 base = 16;
155                 switch (ch) {
156                 case 'a':
157                         *result = '\a';
158                         break;
159                 case 'b':
160                         *result = '\b';
161                         break;
162                 case 'f':
163                         *result = '\f';
164                         break;
165                 case 'n':
166                         *result = '\n';
167                         break;
168                 case 'r':
169                         *result = '\r';
170                         break;
171                 case 't':
172                         *result = '\t';
173                         break;
174                 case 'v':
175                         *result = '\v';
176                         break;
177                 case '0': case '1': case '2': case '3':
178                 case '4': case '5': case '6': case '7':
179                         _memstream_ungetc(ms, ch);
180                         base -= 8;
181                         /*FALLTHROUGH*/
182                 case 'x':
183                         return (_citrus_prop_read_chr_common(ms, result, base));
184                         /*NOTREACHED*/
185                 default:
186                         /* unknown escape */
187                         *result = ch;
188                 }
189         }
190         return (0);
191 }
192
193 static int
194 _citrus_prop_read_character(struct _memstream * __restrict ms,
195     _citrus_prop_object_t * __restrict obj)
196 {
197         int ch, errnum;
198
199         _memstream_skip_ws(ms);
200         ch = _memstream_getc(ms);
201         if (ch != '\'') {
202                 _memstream_ungetc(ms, ch);
203                 return (_citrus_prop_read_chr(ms, obj));
204         }
205         errnum = _citrus_prop_read_character_common(ms, &ch);
206         if (errnum != 0)
207                 return (errnum);
208         obj->u.chr = ch;
209         ch = _memstream_getc(ms);
210         if (ch != '\'')
211                 return (EINVAL);
212         return (0);
213 }
214
215 static int
216 _citrus_prop_read_bool(struct _memstream * __restrict ms,
217     _citrus_prop_object_t * __restrict obj)
218 {
219
220         _memstream_skip_ws(ms);
221         switch (_bcs_tolower(_memstream_getc(ms))) {
222         case 't':
223                 if (_bcs_tolower(_memstream_getc(ms)) == 'r' &&
224                     _bcs_tolower(_memstream_getc(ms)) == 'u' &&
225                     _bcs_tolower(_memstream_getc(ms)) == 'e') {
226                         obj->u.boolean = true;
227                         return (0);
228                 }
229                 break;
230         case 'f':
231                 if (_bcs_tolower(_memstream_getc(ms)) == 'a' &&
232                     _bcs_tolower(_memstream_getc(ms)) == 'l' &&
233                     _bcs_tolower(_memstream_getc(ms)) == 's' &&
234                     _bcs_tolower(_memstream_getc(ms)) == 'e') {
235                         obj->u.boolean = false;
236                         return (0);
237                 }
238         }
239         return (EINVAL);
240 }
241
242 static int
243 _citrus_prop_read_str(struct _memstream * __restrict ms,
244     _citrus_prop_object_t * __restrict obj)
245 {
246         int ch, errnum, quot;
247         char *s, *t;
248 #define _CITRUS_PROP_STR_BUFSIZ 512
249         size_t m, n;
250
251         m = _CITRUS_PROP_STR_BUFSIZ;
252         s = malloc(m);
253         if (s == NULL)
254                 return (ENOMEM);
255         n = 0;
256         _memstream_skip_ws(ms);
257         quot = _memstream_getc(ms);
258         switch (quot) {
259         case EOF:
260                 goto done;
261                 /*NOTREACHED*/
262         case '\\':
263                 _memstream_ungetc(ms, quot);
264                 quot = EOF;
265                 /*FALLTHROUGH*/
266         case '\"': case '\'':
267                 break;
268         default:
269                 s[n] = quot;
270                 ++n, --m;
271                 quot = EOF;
272         }
273         for (;;) {
274                 if (m < 1) {
275                         m = _CITRUS_PROP_STR_BUFSIZ;
276                         t = realloc(s, n + m);
277                         if (t == NULL) {
278                                 free(s);
279                                 return (ENOMEM);
280                         }
281                         s = t;
282                 }
283                 ch = _memstream_getc(ms);
284                 if (quot == ch || (quot == EOF &&
285                     (ch == ';' || _bcs_isspace(ch)))) {
286 done:
287                         s[n] = '\0';
288                         obj->u.str = (const char *)s;
289                         return (0);
290                 }
291                 _memstream_ungetc(ms, ch);
292                 errnum = _citrus_prop_read_character_common(ms, &ch);
293                 if (errnum != 0) {
294                         free(s);
295                         return (errnum);
296                 }
297                 s[n] = ch;
298                 ++n, --m;
299         }
300         free(s);
301         return (EINVAL);
302 #undef _CITRUS_PROP_STR_BUFSIZ
303 }
304
305 typedef int (*_citrus_prop_read_type_t)(struct _memstream * __restrict,
306     _citrus_prop_object_t * __restrict);
307
308 static const _citrus_prop_read_type_t readers[] = {
309         _citrus_prop_read_bool,
310         _citrus_prop_read_str,
311         _citrus_prop_read_character,
312         _citrus_prop_read_num,
313 };
314
315 static __inline int
316 _citrus_prop_read_symbol(struct _memstream * __restrict ms,
317     char * __restrict s, size_t n)
318 {
319         int ch;
320         size_t m;
321
322         for (m = 0; m < n; ++m) {
323                 ch = _memstream_getc(ms);
324                 if (ch != '_' && _bcs_isalnum(ch) == 0)
325                         goto name_found;
326                 s[m] = ch;
327         }
328         ch = _memstream_getc(ms);
329         if (ch == '_' || _bcs_isalnum(ch) != 0)
330                 return (EINVAL);
331
332 name_found:
333         _memstream_ungetc(ms, ch);
334         s[m] = '\0';
335
336         return (0);
337 }
338
339 static int
340 _citrus_prop_parse_element(struct _memstream * __restrict ms,
341     const _citrus_prop_hint_t * __restrict hints, void * __restrict context)
342 {
343         int ch, errnum;
344 #define _CITRUS_PROP_HINT_NAME_LEN_MAX  255
345         char name[_CITRUS_PROP_HINT_NAME_LEN_MAX + 1];
346         const _citrus_prop_hint_t *hint;
347         _citrus_prop_object_t ostart, oend;
348
349         errnum = _citrus_prop_read_symbol(ms, name, sizeof(name));
350         if (errnum != 0)
351                 return (errnum);
352         for (hint = hints; hint->name != NULL; ++hint)
353                 if (_citrus_bcs_strcasecmp(name, hint->name) == 0)
354                         goto hint_found;
355         return (EINVAL);
356
357 hint_found:
358         _memstream_skip_ws(ms);
359         ch = _memstream_getc(ms);
360         if (ch != '=' && ch != ':')
361                 _memstream_ungetc(ms, ch);
362         do {
363                 _citrus_prop_object_init(&ostart, hint->type);
364                 _citrus_prop_object_init(&oend, hint->type);
365                 errnum = (*readers[hint->type])(ms, &ostart);
366                 if (errnum != 0)
367                         return (errnum);
368                 _memstream_skip_ws(ms);
369                 ch = _memstream_getc(ms);
370                 switch (hint->type) {
371                 case _CITRUS_PROP_BOOL:
372                         /*FALLTHROUGH*/
373                 case _CITRUS_PROP_STR:
374                         break;
375                 default:
376                         if (ch != '-')
377                                 break;
378                         errnum = (*readers[hint->type])(ms, &oend);
379                         if (errnum != 0)
380                                 return (errnum);
381                         _memstream_skip_ws(ms);
382                         ch = _memstream_getc(ms);
383                 }
384 #define CALL0(_func_)                                   \
385 do {                                                    \
386         errnum = (*hint->cb._func_.func)(context,       \
387             hint->name, ostart.u._func_);               \
388 } while (0)
389 #define CALL1(_func_)                                   \
390 do {                                                    \
391         errnum = (*hint->cb._func_.func)(context,       \
392             hint->name, ostart.u._func_, oend.u._func_);\
393 } while (0)
394                 switch (hint->type) {
395                 case _CITRUS_PROP_BOOL:
396                         CALL0(boolean);
397                         break;
398                 case _CITRUS_PROP_STR:
399                         CALL0(str);
400                         break;
401                 case _CITRUS_PROP_CHR:
402                         CALL1(chr);
403                         break;
404                 case _CITRUS_PROP_NUM:
405                         CALL1(num);
406                         break;
407                 default:
408                         abort();
409                         /*NOTREACHED*/
410                 }
411 #undef CALL0
412 #undef CALL1
413                 _citrus_prop_object_uninit(&ostart);
414                 _citrus_prop_object_uninit(&oend);
415                 if (errnum != 0)
416                         return (errnum);
417         } while (ch == ',');
418         if (ch != ';')
419                 _memstream_ungetc(ms, ch);
420         return (0);
421 }
422
423 int
424 _citrus_prop_parse_variable(const _citrus_prop_hint_t * __restrict hints,
425     void * __restrict context, const void *var, size_t lenvar)
426 {
427         struct _memstream ms;
428         int ch, errnum;
429
430         _memstream_bind_ptr(&ms, __DECONST(void *, var), lenvar);
431         for (;;) {
432                 _memstream_skip_ws(&ms);
433                 ch = _memstream_getc(&ms);
434                 if (ch == EOF || ch == '\0')
435                         break;
436                 _memstream_ungetc(&ms, ch);
437                 errnum = _citrus_prop_parse_element(&ms, hints, context);
438                 if (errnum != 0)
439                         return (errnum);
440         }
441         return (0);
442 }