]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/libc/iconv/citrus_prop.c
Merge llvm-project main llvmorg-14-init-18294-gdb01b123d012
[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, int neg)                      \
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 = neg ? -acc : 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, neg));                   \
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,
189                             base, 0));
190                         /*NOTREACHED*/
191                 default:
192                         /* unknown escape */
193                         *result = ch;
194                 }
195         }
196         return (0);
197 }
198
199 static int
200 _citrus_prop_read_character(struct _memstream * __restrict ms,
201     _citrus_prop_object_t * __restrict obj)
202 {
203         int ch, errnum;
204
205         _memstream_skip_ws(ms);
206         ch = _memstream_getc(ms);
207         if (ch != '\'') {
208                 _memstream_ungetc(ms, ch);
209                 return (_citrus_prop_read_chr(ms, obj));
210         }
211         errnum = _citrus_prop_read_character_common(ms, &ch);
212         if (errnum != 0)
213                 return (errnum);
214         obj->u.chr = ch;
215         ch = _memstream_getc(ms);
216         if (ch != '\'')
217                 return (EINVAL);
218         return (0);
219 }
220
221 static int
222 _citrus_prop_read_bool(struct _memstream * __restrict ms,
223     _citrus_prop_object_t * __restrict obj)
224 {
225
226         _memstream_skip_ws(ms);
227         switch (_bcs_tolower(_memstream_getc(ms))) {
228         case 't':
229                 if (_bcs_tolower(_memstream_getc(ms)) == 'r' &&
230                     _bcs_tolower(_memstream_getc(ms)) == 'u' &&
231                     _bcs_tolower(_memstream_getc(ms)) == 'e') {
232                         obj->u.boolean = true;
233                         return (0);
234                 }
235                 break;
236         case 'f':
237                 if (_bcs_tolower(_memstream_getc(ms)) == 'a' &&
238                     _bcs_tolower(_memstream_getc(ms)) == 'l' &&
239                     _bcs_tolower(_memstream_getc(ms)) == 's' &&
240                     _bcs_tolower(_memstream_getc(ms)) == 'e') {
241                         obj->u.boolean = false;
242                         return (0);
243                 }
244         }
245         return (EINVAL);
246 }
247
248 static int
249 _citrus_prop_read_str(struct _memstream * __restrict ms,
250     _citrus_prop_object_t * __restrict obj)
251 {
252         int ch, errnum, quot;
253         char *s, *t;
254 #define _CITRUS_PROP_STR_BUFSIZ 512
255         size_t m, n;
256
257         m = _CITRUS_PROP_STR_BUFSIZ;
258         s = malloc(m);
259         if (s == NULL)
260                 return (ENOMEM);
261         n = 0;
262         _memstream_skip_ws(ms);
263         quot = _memstream_getc(ms);
264         switch (quot) {
265         case EOF:
266                 goto done;
267                 /*NOTREACHED*/
268         case '\\':
269                 _memstream_ungetc(ms, quot);
270                 quot = EOF;
271                 /*FALLTHROUGH*/
272         case '\"': case '\'':
273                 break;
274         default:
275                 s[n] = quot;
276                 ++n, --m;
277                 quot = EOF;
278         }
279         for (;;) {
280                 if (m < 1) {
281                         m = _CITRUS_PROP_STR_BUFSIZ;
282                         t = realloc(s, n + m);
283                         if (t == NULL) {
284                                 free(s);
285                                 return (ENOMEM);
286                         }
287                         s = t;
288                 }
289                 ch = _memstream_getc(ms);
290                 if (quot == ch || (quot == EOF &&
291                     (ch == ';' || _bcs_isspace(ch)))) {
292 done:
293                         s[n] = '\0';
294                         obj->u.str = (const char *)s;
295                         return (0);
296                 }
297                 _memstream_ungetc(ms, ch);
298                 errnum = _citrus_prop_read_character_common(ms, &ch);
299                 if (errnum != 0) {
300                         free(s);
301                         return (errnum);
302                 }
303                 s[n] = ch;
304                 ++n, --m;
305         }
306         free(s);
307         return (EINVAL);
308 #undef _CITRUS_PROP_STR_BUFSIZ
309 }
310
311 typedef int (*_citrus_prop_read_type_t)(struct _memstream * __restrict,
312     _citrus_prop_object_t * __restrict);
313
314 static const _citrus_prop_read_type_t readers[] = {
315         _citrus_prop_read_bool,
316         _citrus_prop_read_str,
317         _citrus_prop_read_character,
318         _citrus_prop_read_num,
319 };
320
321 static __inline int
322 _citrus_prop_read_symbol(struct _memstream * __restrict ms,
323     char * __restrict s, size_t n)
324 {
325         int ch;
326         size_t m;
327
328         for (m = 0; m < n; ++m) {
329                 ch = _memstream_getc(ms);
330                 if (ch != '_' && _bcs_isalnum(ch) == 0)
331                         goto name_found;
332                 s[m] = ch;
333         }
334         ch = _memstream_getc(ms);
335         if (ch == '_' || _bcs_isalnum(ch) != 0)
336                 return (EINVAL);
337
338 name_found:
339         _memstream_ungetc(ms, ch);
340         s[m] = '\0';
341
342         return (0);
343 }
344
345 static int
346 _citrus_prop_parse_element(struct _memstream * __restrict ms,
347     const _citrus_prop_hint_t * __restrict hints, 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         errnum = _citrus_prop_read_symbol(ms, name, sizeof(name));
356         if (errnum != 0)
357                 return (errnum);
358         for (hint = hints; hint->name != NULL; ++hint)
359                 if (_citrus_bcs_strcasecmp(name, hint->name) == 0)
360                         goto hint_found;
361         return (EINVAL);
362
363 hint_found:
364         _memstream_skip_ws(ms);
365         ch = _memstream_getc(ms);
366         if (ch != '=' && ch != ':')
367                 _memstream_ungetc(ms, ch);
368         do {
369                 _citrus_prop_object_init(&ostart, hint->type);
370                 _citrus_prop_object_init(&oend, hint->type);
371                 errnum = (*readers[hint->type])(ms, &ostart);
372                 if (errnum != 0)
373                         return (errnum);
374                 _memstream_skip_ws(ms);
375                 ch = _memstream_getc(ms);
376                 switch (hint->type) {
377                 case _CITRUS_PROP_BOOL:
378                         /*FALLTHROUGH*/
379                 case _CITRUS_PROP_STR:
380                         break;
381                 default:
382                         if (ch != '-')
383                                 break;
384                         errnum = (*readers[hint->type])(ms, &oend);
385                         if (errnum != 0)
386                                 return (errnum);
387                         _memstream_skip_ws(ms);
388                         ch = _memstream_getc(ms);
389                 }
390 #define CALL0(_func_)                                   \
391 do {                                                    \
392         errnum = (*hint->cb._func_.func)(context,       \
393             hint->name, ostart.u._func_);               \
394 } while (0)
395 #define CALL1(_func_)                                   \
396 do {                                                    \
397         errnum = (*hint->cb._func_.func)(context,       \
398             hint->name, ostart.u._func_, oend.u._func_);\
399 } while (0)
400                 switch (hint->type) {
401                 case _CITRUS_PROP_BOOL:
402                         CALL0(boolean);
403                         break;
404                 case _CITRUS_PROP_STR:
405                         CALL0(str);
406                         break;
407                 case _CITRUS_PROP_CHR:
408                         CALL1(chr);
409                         break;
410                 case _CITRUS_PROP_NUM:
411                         CALL1(num);
412                         break;
413                 default:
414                         abort();
415                         /*NOTREACHED*/
416                 }
417 #undef CALL0
418 #undef CALL1
419                 _citrus_prop_object_uninit(&ostart);
420                 _citrus_prop_object_uninit(&oend);
421                 if (errnum != 0)
422                         return (errnum);
423         } while (ch == ',');
424         if (ch != ';')
425                 _memstream_ungetc(ms, ch);
426         return (0);
427 }
428
429 int
430 _citrus_prop_parse_variable(const _citrus_prop_hint_t * __restrict hints,
431     void * __restrict context, const void *var, size_t lenvar)
432 {
433         struct _memstream ms;
434         int ch, errnum;
435
436         _memstream_bind_ptr(&ms, __DECONST(void *, var), lenvar);
437         for (;;) {
438                 _memstream_skip_ws(&ms);
439                 ch = _memstream_getc(&ms);
440                 if (ch == EOF || ch == '\0')
441                         break;
442                 _memstream_ungetc(&ms, ch);
443                 errnum = _citrus_prop_parse_element(&ms, hints, context);
444                 if (errnum != 0)
445                         return (errnum);
446         }
447         return (0);
448 }