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 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 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 } 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 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 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 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 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 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 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