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