1 /* $FreeBSD$ */ 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 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 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, int neg) \ 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 = neg ? -acc : acc; \ 103 return (0); \ 104 } 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, neg)); \ 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, 189 base, 0)); 190 /*NOTREACHED*/ 191 default: 192 /* unknown escape */ 193 *result = ch; 194 } 195 } 196 return (0); 197 } 198 199 static int 200 _citrus_prop_read_character(struct _memstream * __restrict ms, 201 _citrus_prop_object_t * __restrict obj) 202 { 203 int ch, errnum; 204 205 _memstream_skip_ws(ms); 206 ch = _memstream_getc(ms); 207 if (ch != '\'') { 208 _memstream_ungetc(ms, ch); 209 return (_citrus_prop_read_chr(ms, obj)); 210 } 211 errnum = _citrus_prop_read_character_common(ms, &ch); 212 if (errnum != 0) 213 return (errnum); 214 obj->u.chr = ch; 215 ch = _memstream_getc(ms); 216 if (ch != '\'') 217 return (EINVAL); 218 return (0); 219 } 220 221 static int 222 _citrus_prop_read_bool(struct _memstream * __restrict ms, 223 _citrus_prop_object_t * __restrict obj) 224 { 225 226 _memstream_skip_ws(ms); 227 switch (_bcs_tolower(_memstream_getc(ms))) { 228 case 't': 229 if (_bcs_tolower(_memstream_getc(ms)) == 'r' && 230 _bcs_tolower(_memstream_getc(ms)) == 'u' && 231 _bcs_tolower(_memstream_getc(ms)) == 'e') { 232 obj->u.boolean = true; 233 return (0); 234 } 235 break; 236 case 'f': 237 if (_bcs_tolower(_memstream_getc(ms)) == 'a' && 238 _bcs_tolower(_memstream_getc(ms)) == 'l' && 239 _bcs_tolower(_memstream_getc(ms)) == 's' && 240 _bcs_tolower(_memstream_getc(ms)) == 'e') { 241 obj->u.boolean = false; 242 return (0); 243 } 244 } 245 return (EINVAL); 246 } 247 248 static int 249 _citrus_prop_read_str(struct _memstream * __restrict ms, 250 _citrus_prop_object_t * __restrict obj) 251 { 252 int ch, errnum, quot; 253 char *s, *t; 254 #define _CITRUS_PROP_STR_BUFSIZ 512 255 size_t m, n; 256 257 m = _CITRUS_PROP_STR_BUFSIZ; 258 s = malloc(m); 259 if (s == NULL) 260 return (ENOMEM); 261 n = 0; 262 _memstream_skip_ws(ms); 263 quot = _memstream_getc(ms); 264 switch (quot) { 265 case EOF: 266 goto done; 267 /*NOTREACHED*/ 268 case '\\': 269 _memstream_ungetc(ms, quot); 270 quot = EOF; 271 /*FALLTHROUGH*/ 272 case '\"': case '\'': 273 break; 274 default: 275 s[n] = quot; 276 ++n, --m; 277 quot = EOF; 278 } 279 for (;;) { 280 if (m < 1) { 281 m = _CITRUS_PROP_STR_BUFSIZ; 282 t = realloc(s, n + m); 283 if (t == NULL) { 284 free(s); 285 return (ENOMEM); 286 } 287 s = t; 288 } 289 ch = _memstream_getc(ms); 290 if (quot == ch || (quot == EOF && 291 (ch == ';' || _bcs_isspace(ch)))) { 292 done: 293 s[n] = '\0'; 294 obj->u.str = (const char *)s; 295 return (0); 296 } 297 _memstream_ungetc(ms, ch); 298 errnum = _citrus_prop_read_character_common(ms, &ch); 299 if (errnum != 0) { 300 free(s); 301 return (errnum); 302 } 303 s[n] = ch; 304 ++n, --m; 305 } 306 free(s); 307 return (EINVAL); 308 #undef _CITRUS_PROP_STR_BUFSIZ 309 } 310 311 typedef int (*_citrus_prop_read_type_t)(struct _memstream * __restrict, 312 _citrus_prop_object_t * __restrict); 313 314 static const _citrus_prop_read_type_t readers[] = { 315 _citrus_prop_read_bool, 316 _citrus_prop_read_str, 317 _citrus_prop_read_character, 318 _citrus_prop_read_num, 319 }; 320 321 static __inline int 322 _citrus_prop_read_symbol(struct _memstream * __restrict ms, 323 char * __restrict s, size_t n) 324 { 325 int ch; 326 size_t m; 327 328 for (m = 0; m < n; ++m) { 329 ch = _memstream_getc(ms); 330 if (ch != '_' && _bcs_isalnum(ch) == 0) 331 goto name_found; 332 s[m] = ch; 333 } 334 ch = _memstream_getc(ms); 335 if (ch == '_' || _bcs_isalnum(ch) != 0) 336 return (EINVAL); 337 338 name_found: 339 _memstream_ungetc(ms, ch); 340 s[m] = '\0'; 341 342 return (0); 343 } 344 345 static int 346 _citrus_prop_parse_element(struct _memstream * __restrict ms, 347 const _citrus_prop_hint_t * __restrict hints, void * __restrict context) 348 { 349 int ch, errnum; 350 #define _CITRUS_PROP_HINT_NAME_LEN_MAX 255 351 char name[_CITRUS_PROP_HINT_NAME_LEN_MAX + 1]; 352 const _citrus_prop_hint_t *hint; 353 _citrus_prop_object_t ostart, oend; 354 355 errnum = _citrus_prop_read_symbol(ms, name, sizeof(name)); 356 if (errnum != 0) 357 return (errnum); 358 for (hint = hints; hint->name != NULL; ++hint) 359 if (_citrus_bcs_strcasecmp(name, hint->name) == 0) 360 goto hint_found; 361 return (EINVAL); 362 363 hint_found: 364 _memstream_skip_ws(ms); 365 ch = _memstream_getc(ms); 366 if (ch != '=' && ch != ':') 367 _memstream_ungetc(ms, ch); 368 do { 369 _citrus_prop_object_init(&ostart, hint->type); 370 _citrus_prop_object_init(&oend, hint->type); 371 errnum = (*readers[hint->type])(ms, &ostart); 372 if (errnum != 0) 373 return (errnum); 374 _memstream_skip_ws(ms); 375 ch = _memstream_getc(ms); 376 switch (hint->type) { 377 case _CITRUS_PROP_BOOL: 378 /*FALLTHROUGH*/ 379 case _CITRUS_PROP_STR: 380 break; 381 default: 382 if (ch != '-') 383 break; 384 errnum = (*readers[hint->type])(ms, &oend); 385 if (errnum != 0) 386 return (errnum); 387 _memstream_skip_ws(ms); 388 ch = _memstream_getc(ms); 389 } 390 #define CALL0(_func_) \ 391 do { \ 392 errnum = (*hint->cb._func_.func)(context, \ 393 hint->name, ostart.u._func_); \ 394 } while (0) 395 #define CALL1(_func_) \ 396 do { \ 397 errnum = (*hint->cb._func_.func)(context, \ 398 hint->name, ostart.u._func_, oend.u._func_);\ 399 } while (0) 400 switch (hint->type) { 401 case _CITRUS_PROP_BOOL: 402 CALL0(boolean); 403 break; 404 case _CITRUS_PROP_STR: 405 CALL0(str); 406 break; 407 case _CITRUS_PROP_CHR: 408 CALL1(chr); 409 break; 410 case _CITRUS_PROP_NUM: 411 CALL1(num); 412 break; 413 default: 414 abort(); 415 /*NOTREACHED*/ 416 } 417 #undef CALL0 418 #undef CALL1 419 _citrus_prop_object_uninit(&ostart); 420 _citrus_prop_object_uninit(&oend); 421 if (errnum != 0) 422 return (errnum); 423 } while (ch == ','); 424 if (ch != ';') 425 _memstream_ungetc(ms, ch); 426 return (0); 427 } 428 429 int 430 _citrus_prop_parse_variable(const _citrus_prop_hint_t * __restrict hints, 431 void * __restrict context, const void *var, size_t lenvar) 432 { 433 struct _memstream ms; 434 int ch, errnum; 435 436 _memstream_bind_ptr(&ms, __DECONST(void *, var), lenvar); 437 for (;;) { 438 _memstream_skip_ws(&ms); 439 ch = _memstream_getc(&ms); 440 if (ch == EOF || ch == '\0') 441 break; 442 _memstream_ungetc(&ms, ch); 443 errnum = _citrus_prop_parse_element(&ms, hints, context); 444 if (errnum != 0) 445 return (errnum); 446 } 447 return (0); 448 } 449