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 return (errnum); 298 s[n] = ch; 299 ++n, --m; 300 } 301 free(s); 302 return (EINVAL); 303 #undef _CITRUS_PROP_STR_BUFSIZ 304 } 305 306 typedef int (*_citrus_prop_read_type_t)(struct _memstream * __restrict, 307 _citrus_prop_object_t * __restrict); 308 309 static const _citrus_prop_read_type_t readers[] = { 310 _citrus_prop_read_bool, 311 _citrus_prop_read_str, 312 _citrus_prop_read_character, 313 _citrus_prop_read_num, 314 }; 315 316 static __inline int 317 _citrus_prop_read_symbol(struct _memstream * __restrict ms, 318 char * __restrict s, size_t n) 319 { 320 int ch; 321 size_t m; 322 323 for (m = 0; m < n; ++m) { 324 ch = _memstream_getc(ms); 325 if (ch != '_' && _bcs_isalnum(ch) == 0) 326 goto name_found; 327 s[m] = ch; 328 } 329 ch = _memstream_getc(ms); 330 if (ch == '_' || _bcs_isalnum(ch) != 0) 331 return (EINVAL); 332 333 name_found: 334 _memstream_ungetc(ms, ch); 335 s[m] = '\0'; 336 337 return (0); 338 } 339 340 static int 341 _citrus_prop_parse_element(struct _memstream * __restrict ms, 342 const _citrus_prop_hint_t * __restrict hints, void * __restrict context) 343 { 344 int ch, errnum; 345 #define _CITRUS_PROP_HINT_NAME_LEN_MAX 255 346 char name[_CITRUS_PROP_HINT_NAME_LEN_MAX + 1]; 347 const _citrus_prop_hint_t *hint; 348 _citrus_prop_object_t ostart, oend; 349 350 errnum = _citrus_prop_read_symbol(ms, name, sizeof(name)); 351 if (errnum != 0) 352 return (errnum); 353 for (hint = hints; hint->name != NULL; ++hint) 354 if (_citrus_bcs_strcasecmp(name, hint->name) == 0) 355 goto hint_found; 356 return (EINVAL); 357 358 hint_found: 359 _memstream_skip_ws(ms); 360 ch = _memstream_getc(ms); 361 if (ch != '=' && ch != ':') 362 _memstream_ungetc(ms, ch); 363 do { 364 _citrus_prop_object_init(&ostart, hint->type); 365 _citrus_prop_object_init(&oend, hint->type); 366 errnum = (*readers[hint->type])(ms, &ostart); 367 if (errnum != 0) 368 return (errnum); 369 _memstream_skip_ws(ms); 370 ch = _memstream_getc(ms); 371 switch (hint->type) { 372 case _CITRUS_PROP_BOOL: 373 /*FALLTHROUGH*/ 374 case _CITRUS_PROP_STR: 375 break; 376 default: 377 if (ch != '-') 378 break; 379 errnum = (*readers[hint->type])(ms, &oend); 380 if (errnum != 0) 381 return (errnum); 382 _memstream_skip_ws(ms); 383 ch = _memstream_getc(ms); 384 } 385 #define CALL0(_func_) \ 386 do { \ 387 errnum = (*hint->cb._func_.func)(context, \ 388 hint->name, ostart.u._func_); \ 389 } while (0) 390 #define CALL1(_func_) \ 391 do { \ 392 errnum = (*hint->cb._func_.func)(context, \ 393 hint->name, ostart.u._func_, oend.u._func_);\ 394 } while (0) 395 switch (hint->type) { 396 case _CITRUS_PROP_BOOL: 397 CALL0(boolean); 398 break; 399 case _CITRUS_PROP_STR: 400 CALL0(str); 401 break; 402 case _CITRUS_PROP_CHR: 403 CALL1(chr); 404 break; 405 case _CITRUS_PROP_NUM: 406 CALL1(num); 407 break; 408 default: 409 abort(); 410 /*NOTREACHED*/ 411 } 412 #undef CALL0 413 #undef CALL1 414 _citrus_prop_object_uninit(&ostart); 415 _citrus_prop_object_uninit(&oend); 416 if (errnum != 0) 417 return (errnum); 418 } while (ch == ','); 419 if (ch != ';') 420 _memstream_ungetc(ms, ch); 421 return (0); 422 } 423 424 int 425 _citrus_prop_parse_variable(const _citrus_prop_hint_t * __restrict hints, 426 void * __restrict context, const void *var, size_t lenvar) 427 { 428 struct _memstream ms; 429 int ch, errnum; 430 431 _memstream_bind_ptr(&ms, __DECONST(void *, var), lenvar); 432 for (;;) { 433 _memstream_skip_ws(&ms); 434 ch = _memstream_getc(&ms); 435 if (ch == EOF || ch == '\0') 436 break; 437 _memstream_ungetc(&ms, ch); 438 errnum = _citrus_prop_parse_element(&ms, hints, context); 439 if (errnum != 0) 440 return (errnum); 441 } 442 return (0); 443 } 444