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
_citrus_prop_object_init(_citrus_prop_object_t * obj,_citrus_prop_type_t type)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
_citrus_prop_object_uninit(_citrus_prop_object_t * obj)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 }
_CITRUS_PROP_READ_UINT_COMMON(chr,int,UCHAR_MAX)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
_citrus_prop_read_character(struct _memstream * __restrict ms,_citrus_prop_object_t * __restrict obj)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
_citrus_prop_read_bool(struct _memstream * __restrict ms,_citrus_prop_object_t * __restrict obj)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
_citrus_prop_read_str(struct _memstream * __restrict ms,_citrus_prop_object_t * __restrict obj)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
_citrus_prop_read_symbol(struct _memstream * __restrict ms,char * __restrict s,size_t n)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
_citrus_prop_parse_element(struct _memstream * __restrict ms,const _citrus_prop_hint_t * __restrict hints,void * __restrict context)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
_citrus_prop_parse_variable(const _citrus_prop_hint_t * __restrict hints,void * __restrict context,const void * var,size_t lenvar)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 }
443