1 /* $FreeBSD: stable/12/lib/libc/iconv/citrus_prop.c 326193 2017-11-25 17:12:48Z pfg $ */
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
_citrus_prop_object_init(_citrus_prop_object_t * obj,_citrus_prop_type_t type)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
_citrus_prop_object_uninit(_citrus_prop_object_t * obj)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 }
_CITRUS_PROP_READ_UINT_COMMON(chr,int,UCHAR_MAX)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
_citrus_prop_read_character(struct _memstream * __restrict ms,_citrus_prop_object_t * __restrict obj)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
_citrus_prop_read_bool(struct _memstream * __restrict ms,_citrus_prop_object_t * __restrict obj)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
_citrus_prop_read_str(struct _memstream * __restrict ms,_citrus_prop_object_t * __restrict obj)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
_citrus_prop_read_symbol(struct _memstream * __restrict ms,char * __restrict s,size_t n)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
_citrus_prop_parse_element(struct _memstream * __restrict ms,const _citrus_prop_hint_t * __restrict hints,void * __restrict context)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
_citrus_prop_parse_variable(const _citrus_prop_hint_t * __restrict hints,void * __restrict context,const void * var,size_t lenvar)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 }
448