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) \ 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 = 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)); \ 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, base)); 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