1 /* $NetBSD: citrus_prop.c,v 1.4 2011/03/30 08:22:01 jruoho Exp $ */
2
3 /*-
4 * SPDX-License-Identifier: BSD-2-Clause
5 *
6 * Copyright (c)2006 Citrus Project,
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
29 *
30 */
31
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, int neg) \
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 = neg ? -acc : 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, neg; \
113 \
114 _memstream_skip_ws(ms); \
115 ch = _memstream_getc(ms); \
116 neg = 0; \
117 switch (ch) { \
118 case '-': \
119 neg = 1; \
120 case '+': \
121 ch = _memstream_getc(ms); \
122 } \
123 base = 10; \
124 if (ch == '0') { \
125 base -= 2; \
126 ch = _memstream_getc(ms); \
127 if (ch == 'x' || ch == 'X') { \
128 ch = _memstream_getc(ms); \
129 if (_bcs_isxdigit(ch) == 0) { \
130 _memstream_ungetc(ms, ch); \
131 obj->u._func_ = 0; \
132 return (0); \
133 } \
134 base += 8; \
135 } \
136 } else if (_bcs_isdigit(ch) == 0) \
137 return (EINVAL); \
138 _memstream_ungetc(ms, ch); \
139 return (_citrus_prop_read_##_func_##_common \
140 (ms, &obj->u._func_, base, neg)); \
141 }
142 _CITRUS_PROP_READ_INT(chr, int)
143 _CITRUS_PROP_READ_INT(num, uint64_t)
144 #undef _CITRUS_PROP_READ_INT
145
146 static int
147 _citrus_prop_read_character_common(struct _memstream * __restrict ms,
148 int * __restrict result)
149 {
150 int base, ch;
151
152 ch = _memstream_getc(ms);
153 if (ch != '\\')
154 *result = ch;
155 else {
156 ch = _memstream_getc(ms);
157 base = 16;
158 switch (ch) {
159 case 'a':
160 *result = '\a';
161 break;
162 case 'b':
163 *result = '\b';
164 break;
165 case 'f':
166 *result = '\f';
167 break;
168 case 'n':
169 *result = '\n';
170 break;
171 case 'r':
172 *result = '\r';
173 break;
174 case 't':
175 *result = '\t';
176 break;
177 case 'v':
178 *result = '\v';
179 break;
180 case '0': case '1': case '2': case '3':
181 case '4': case '5': case '6': case '7':
182 _memstream_ungetc(ms, ch);
183 base -= 8;
184 /*FALLTHROUGH*/
185 case 'x':
186 return (_citrus_prop_read_chr_common(ms, result,
187 base, 0));
188 /*NOTREACHED*/
189 default:
190 /* unknown escape */
191 *result = ch;
192 }
193 }
194 return (0);
195 }
196
197 static int
_citrus_prop_read_character(struct _memstream * __restrict ms,_citrus_prop_object_t * __restrict obj)198 _citrus_prop_read_character(struct _memstream * __restrict ms,
199 _citrus_prop_object_t * __restrict obj)
200 {
201 int ch, errnum;
202
203 _memstream_skip_ws(ms);
204 ch = _memstream_getc(ms);
205 if (ch != '\'') {
206 _memstream_ungetc(ms, ch);
207 return (_citrus_prop_read_chr(ms, obj));
208 }
209 errnum = _citrus_prop_read_character_common(ms, &ch);
210 if (errnum != 0)
211 return (errnum);
212 obj->u.chr = ch;
213 ch = _memstream_getc(ms);
214 if (ch != '\'')
215 return (EINVAL);
216 return (0);
217 }
218
219 static int
_citrus_prop_read_bool(struct _memstream * __restrict ms,_citrus_prop_object_t * __restrict obj)220 _citrus_prop_read_bool(struct _memstream * __restrict ms,
221 _citrus_prop_object_t * __restrict obj)
222 {
223
224 _memstream_skip_ws(ms);
225 switch (_bcs_tolower(_memstream_getc(ms))) {
226 case 't':
227 if (_bcs_tolower(_memstream_getc(ms)) == 'r' &&
228 _bcs_tolower(_memstream_getc(ms)) == 'u' &&
229 _bcs_tolower(_memstream_getc(ms)) == 'e') {
230 obj->u.boolean = true;
231 return (0);
232 }
233 break;
234 case 'f':
235 if (_bcs_tolower(_memstream_getc(ms)) == 'a' &&
236 _bcs_tolower(_memstream_getc(ms)) == 'l' &&
237 _bcs_tolower(_memstream_getc(ms)) == 's' &&
238 _bcs_tolower(_memstream_getc(ms)) == 'e') {
239 obj->u.boolean = false;
240 return (0);
241 }
242 }
243 return (EINVAL);
244 }
245
246 static int
_citrus_prop_read_str(struct _memstream * __restrict ms,_citrus_prop_object_t * __restrict obj)247 _citrus_prop_read_str(struct _memstream * __restrict ms,
248 _citrus_prop_object_t * __restrict obj)
249 {
250 int ch, errnum, quot;
251 char *s, *t;
252 #define _CITRUS_PROP_STR_BUFSIZ 512
253 size_t m, n;
254
255 m = _CITRUS_PROP_STR_BUFSIZ;
256 s = malloc(m);
257 if (s == NULL)
258 return (ENOMEM);
259 n = 0;
260 _memstream_skip_ws(ms);
261 quot = _memstream_getc(ms);
262 switch (quot) {
263 case EOF:
264 goto done;
265 /*NOTREACHED*/
266 case '\\':
267 _memstream_ungetc(ms, quot);
268 quot = EOF;
269 /*FALLTHROUGH*/
270 case '\"': case '\'':
271 break;
272 default:
273 s[n] = quot;
274 ++n, --m;
275 quot = EOF;
276 }
277 for (;;) {
278 if (m < 1) {
279 m = _CITRUS_PROP_STR_BUFSIZ;
280 t = realloc(s, n + m);
281 if (t == NULL) {
282 free(s);
283 return (ENOMEM);
284 }
285 s = t;
286 }
287 ch = _memstream_getc(ms);
288 if (quot == ch || (quot == EOF &&
289 (ch == ';' || _bcs_isspace(ch)))) {
290 done:
291 s[n] = '\0';
292 obj->u.str = (const char *)s;
293 return (0);
294 }
295 _memstream_ungetc(ms, ch);
296 errnum = _citrus_prop_read_character_common(ms, &ch);
297 if (errnum != 0) {
298 free(s);
299 return (errnum);
300 }
301 s[n] = ch;
302 ++n, --m;
303 }
304 free(s);
305 return (EINVAL);
306 #undef _CITRUS_PROP_STR_BUFSIZ
307 }
308
309 typedef int (*_citrus_prop_read_type_t)(struct _memstream * __restrict,
310 _citrus_prop_object_t * __restrict);
311
312 static const _citrus_prop_read_type_t readers[] = {
313 _citrus_prop_read_bool,
314 _citrus_prop_read_str,
315 _citrus_prop_read_character,
316 _citrus_prop_read_num,
317 };
318
319 static __inline int
_citrus_prop_read_symbol(struct _memstream * __restrict ms,char * __restrict s,size_t n)320 _citrus_prop_read_symbol(struct _memstream * __restrict ms,
321 char * __restrict s, size_t n)
322 {
323 int ch;
324 size_t m;
325
326 for (m = 0; m < n; ++m) {
327 ch = _memstream_getc(ms);
328 if (ch != '_' && _bcs_isalnum(ch) == 0)
329 goto name_found;
330 s[m] = ch;
331 }
332 ch = _memstream_getc(ms);
333 if (ch == '_' || _bcs_isalnum(ch) != 0)
334 return (EINVAL);
335
336 name_found:
337 _memstream_ungetc(ms, ch);
338 s[m] = '\0';
339
340 return (0);
341 }
342
343 static int
_citrus_prop_parse_element(struct _memstream * __restrict ms,const _citrus_prop_hint_t * __restrict hints,void * __restrict context)344 _citrus_prop_parse_element(struct _memstream * __restrict ms,
345 const _citrus_prop_hint_t * __restrict hints, void * __restrict context)
346 {
347 int ch, errnum;
348 #define _CITRUS_PROP_HINT_NAME_LEN_MAX 255
349 char name[_CITRUS_PROP_HINT_NAME_LEN_MAX + 1];
350 const _citrus_prop_hint_t *hint;
351 _citrus_prop_object_t ostart, oend;
352
353 errnum = _citrus_prop_read_symbol(ms, name, sizeof(name));
354 if (errnum != 0)
355 return (errnum);
356 for (hint = hints; hint->name != NULL; ++hint)
357 if (_citrus_bcs_strcasecmp(name, hint->name) == 0)
358 goto hint_found;
359 return (EINVAL);
360
361 hint_found:
362 _memstream_skip_ws(ms);
363 ch = _memstream_getc(ms);
364 if (ch != '=' && ch != ':')
365 _memstream_ungetc(ms, ch);
366 do {
367 _citrus_prop_object_init(&ostart, hint->type);
368 _citrus_prop_object_init(&oend, hint->type);
369 errnum = (*readers[hint->type])(ms, &ostart);
370 if (errnum != 0)
371 return (errnum);
372 _memstream_skip_ws(ms);
373 ch = _memstream_getc(ms);
374 switch (hint->type) {
375 case _CITRUS_PROP_BOOL:
376 /*FALLTHROUGH*/
377 case _CITRUS_PROP_STR:
378 break;
379 default:
380 if (ch != '-')
381 break;
382 errnum = (*readers[hint->type])(ms, &oend);
383 if (errnum != 0)
384 return (errnum);
385 _memstream_skip_ws(ms);
386 ch = _memstream_getc(ms);
387 }
388 #define CALL0(_func_) \
389 do { \
390 errnum = (*hint->cb._func_.func)(context, \
391 hint->name, ostart.u._func_); \
392 } while (0)
393 #define CALL1(_func_) \
394 do { \
395 errnum = (*hint->cb._func_.func)(context, \
396 hint->name, ostart.u._func_, oend.u._func_);\
397 } while (0)
398 switch (hint->type) {
399 case _CITRUS_PROP_BOOL:
400 CALL0(boolean);
401 break;
402 case _CITRUS_PROP_STR:
403 CALL0(str);
404 break;
405 case _CITRUS_PROP_CHR:
406 CALL1(chr);
407 break;
408 case _CITRUS_PROP_NUM:
409 CALL1(num);
410 break;
411 default:
412 abort();
413 /*NOTREACHED*/
414 }
415 #undef CALL0
416 #undef CALL1
417 _citrus_prop_object_uninit(&ostart);
418 _citrus_prop_object_uninit(&oend);
419 if (errnum != 0)
420 return (errnum);
421 } while (ch == ',');
422 if (ch != ';')
423 _memstream_ungetc(ms, ch);
424 return (0);
425 }
426
427 int
_citrus_prop_parse_variable(const _citrus_prop_hint_t * __restrict hints,void * __restrict context,const void * var,size_t lenvar)428 _citrus_prop_parse_variable(const _citrus_prop_hint_t * __restrict hints,
429 void * __restrict context, const void *var, size_t lenvar)
430 {
431 struct _memstream ms;
432 int ch, errnum;
433
434 _memstream_bind_ptr(&ms, __DECONST(void *, var), lenvar);
435 for (;;) {
436 _memstream_skip_ws(&ms);
437 ch = _memstream_getc(&ms);
438 if (ch == EOF || ch == '\0')
439 break;
440 _memstream_ungetc(&ms, ch);
441 errnum = _citrus_prop_parse_element(&ms, hints, context);
442 if (errnum != 0)
443 return (errnum);
444 }
445 return (0);
446 }
447