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