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 #include <sys/cdefs.h> 33 34 #include <assert.h> 35 #include <errno.h> 36 #include <limits.h> 37 #include <stdbool.h> 38 #include <stddef.h> 39 #include <stdio.h> 40 #include <stdint.h> 41 #include <stdlib.h> 42 #include <string.h> 43 44 #include "citrus_namespace.h" 45 #include "citrus_bcs.h" 46 #include "citrus_region.h" 47 #include "citrus_memstream.h" 48 #include "citrus_prop.h" 49 50 typedef struct { 51 _citrus_prop_type_t type; 52 union { 53 const char *str; 54 int chr; 55 bool boolean; 56 uint64_t num; 57 } u; 58 } _citrus_prop_object_t; 59 60 static __inline void 61 _citrus_prop_object_init(_citrus_prop_object_t *obj, _citrus_prop_type_t type) 62 { 63 64 obj->type = type; 65 memset(&obj->u, 0, sizeof(obj->u)); 66 } 67 68 static __inline void 69 _citrus_prop_object_uninit(_citrus_prop_object_t *obj) 70 { 71 72 if (obj->type == _CITRUS_PROP_STR) 73 free(__DECONST(void *, obj->u.str)); 74 } 75 76 static const char *xdigit = "0123456789ABCDEF"; 77 78 #define _CITRUS_PROP_READ_UINT_COMMON(_func_, _type_, _max_) \ 79 static int \ 80 _citrus_prop_read_##_func_##_common(struct _memstream * __restrict ms, \ 81 _type_ * __restrict result, int base, int neg) \ 82 { \ 83 _type_ acc, cutoff; \ 84 int ch, cutlim, n; \ 85 char *p; \ 86 \ 87 acc = (_type_)0; \ 88 cutoff = _max_ / base; \ 89 cutlim = _max_ % base; \ 90 for (;;) { \ 91 ch = _memstream_getc(ms); \ 92 p = strchr(xdigit, _bcs_toupper(ch)); \ 93 if (p == NULL || (n = (p - xdigit)) >= base) \ 94 break; \ 95 if (acc > cutoff || (acc == cutoff && n > cutlim)) \ 96 break; \ 97 acc *= base; \ 98 acc += n; \ 99 } \ 100 _memstream_ungetc(ms, ch); \ 101 *result = neg ? -acc : acc; \ 102 return (0); \ 103 } 104 _CITRUS_PROP_READ_UINT_COMMON(chr, int, UCHAR_MAX) 105 _CITRUS_PROP_READ_UINT_COMMON(num, uint64_t, UINT64_MAX) 106 #undef _CITRUS_PROP_READ_UINT_COMMON 107 108 #define _CITRUS_PROP_READ_INT(_func_, _type_) \ 109 static int \ 110 _citrus_prop_read_##_func_(struct _memstream * __restrict ms, \ 111 _citrus_prop_object_t * __restrict obj) \ 112 { \ 113 int base, ch, neg; \ 114 \ 115 _memstream_skip_ws(ms); \ 116 ch = _memstream_getc(ms); \ 117 neg = 0; \ 118 switch (ch) { \ 119 case '-': \ 120 neg = 1; \ 121 case '+': \ 122 ch = _memstream_getc(ms); \ 123 } \ 124 base = 10; \ 125 if (ch == '0') { \ 126 base -= 2; \ 127 ch = _memstream_getc(ms); \ 128 if (ch == 'x' || ch == 'X') { \ 129 ch = _memstream_getc(ms); \ 130 if (_bcs_isxdigit(ch) == 0) { \ 131 _memstream_ungetc(ms, ch); \ 132 obj->u._func_ = 0; \ 133 return (0); \ 134 } \ 135 base += 8; \ 136 } \ 137 } else if (_bcs_isdigit(ch) == 0) \ 138 return (EINVAL); \ 139 _memstream_ungetc(ms, ch); \ 140 return (_citrus_prop_read_##_func_##_common \ 141 (ms, &obj->u._func_, base, neg)); \ 142 } 143 _CITRUS_PROP_READ_INT(chr, int) 144 _CITRUS_PROP_READ_INT(num, uint64_t) 145 #undef _CITRUS_PROP_READ_INT 146 147 static int 148 _citrus_prop_read_character_common(struct _memstream * __restrict ms, 149 int * __restrict result) 150 { 151 int base, ch; 152 153 ch = _memstream_getc(ms); 154 if (ch != '\\') 155 *result = ch; 156 else { 157 ch = _memstream_getc(ms); 158 base = 16; 159 switch (ch) { 160 case 'a': 161 *result = '\a'; 162 break; 163 case 'b': 164 *result = '\b'; 165 break; 166 case 'f': 167 *result = '\f'; 168 break; 169 case 'n': 170 *result = '\n'; 171 break; 172 case 'r': 173 *result = '\r'; 174 break; 175 case 't': 176 *result = '\t'; 177 break; 178 case 'v': 179 *result = '\v'; 180 break; 181 case '0': case '1': case '2': case '3': 182 case '4': case '5': case '6': case '7': 183 _memstream_ungetc(ms, ch); 184 base -= 8; 185 /*FALLTHROUGH*/ 186 case 'x': 187 return (_citrus_prop_read_chr_common(ms, result, 188 base, 0)); 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 } 448