1 /* $NetBSD: citrus_prop.c,v 1.3 2006/11/22 23:47:21 tnozaki Exp $ */ 2 /* $DragonFly: src/lib/libc/citrus/citrus_prop.c,v 1.1 2008/04/10 10:21:01 hasso 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 <assert.h> 32 #include <limits.h> 33 #include <errno.h> 34 #include <stddef.h> 35 #include <stdio.h> 36 #include <stdint.h> 37 #include <stdlib.h> 38 #include <string.h> 39 40 #include "citrus_namespace.h" 41 #include "citrus_bcs.h" 42 #include "citrus_region.h" 43 #include "citrus_memstream.h" 44 #include "citrus_prop.h" 45 46 typedef struct { 47 _citrus_prop_type_t type; 48 union { 49 const char *str; 50 int bool, chr; 51 uint64_t num; 52 } u; 53 } _citrus_prop_object_t; 54 55 static __inline void 56 _citrus_prop_object_init(_citrus_prop_object_t *obj, _citrus_prop_type_t type) 57 { 58 _DIAGASSERT(obj != NULL); 59 60 obj->type = type; 61 memset(&obj->u, 0, sizeof(obj->u)); 62 } 63 64 static __inline void 65 _citrus_prop_object_uninit(_citrus_prop_object_t *obj) 66 { 67 _DIAGASSERT(obj != NULL); 68 69 if (obj->type == _CITRUS_PROP_STR) 70 free(__DECONST(void *, obj->u.str)); 71 } 72 73 static const char *xdigit = "0123456789ABCDEF"; 74 75 #define _CITRUS_PROP_READ_UINT_COMMON(_func_, _type_, _max_) \ 76 static int \ 77 _citrus_prop_read_##_func_##_common(struct _memstream * __restrict ms, \ 78 _type_ * __restrict result, int base) \ 79 { \ 80 _type_ acc, cutoff; \ 81 int n, ch, cutlim; \ 82 char *p; \ 83 \ 84 _DIAGASSERT(ms != NULL); \ 85 _DIAGASSERT(result != NULL); \ 86 \ 87 acc = (_type_)0; \ 88 cutoff = _max_ / base; \ 89 cutlim = _max_ % base; \ 90 for (;;) { \ 91 ch = _memstream_getc(ms); \ 92 p = strchr(xdigit, _bcs_toupper(ch)); \ 93 if (p == NULL || (n = (p - xdigit)) >= base) \ 94 break; \ 95 if (acc > cutoff || (acc == cutoff && n > cutlim)) \ 96 break; \ 97 acc *= base; \ 98 acc += n; \ 99 } \ 100 _memstream_ungetc(ms, ch); \ 101 *result = acc; \ 102 return 0; \ 103 } 104 _CITRUS_PROP_READ_UINT_COMMON(chr, int, UCHAR_MAX) 105 _CITRUS_PROP_READ_UINT_COMMON(num, uint64_t, UINT64_MAX) 106 #undef _CITRUS_PROP_READ_UINT_COMMON 107 108 #define _CITRUS_PROP_READ_INT(_func_, _type_) \ 109 static int \ 110 _citrus_prop_read_##_func_(struct _memstream * __restrict ms, \ 111 _citrus_prop_object_t * __restrict obj) \ 112 { \ 113 int ch, neg, base; \ 114 \ 115 _DIAGASSERT(ms != NULL); \ 116 _DIAGASSERT(obj != NULL); \ 117 \ 118 _memstream_skip_ws(ms); \ 119 ch = _memstream_getc(ms); \ 120 neg = 0; \ 121 switch (ch) { \ 122 case '-': \ 123 neg = 1; \ 124 case '+': \ 125 ch = _memstream_getc(ms); \ 126 } \ 127 base = 10; \ 128 if (ch == '0') { \ 129 base -= 2; \ 130 ch = _memstream_getc(ms); \ 131 if (ch == 'x' || ch == 'X') { \ 132 ch = _memstream_getc(ms); \ 133 if (_bcs_isxdigit(ch) == 0) { \ 134 _memstream_ungetc(ms, ch); \ 135 obj->u._func_ = 0; \ 136 return 0; \ 137 } \ 138 base += 8; \ 139 } \ 140 } else if (_bcs_isdigit(ch) == 0) \ 141 return EINVAL; \ 142 _memstream_ungetc(ms, ch); \ 143 return _citrus_prop_read_##_func_##_common \ 144 (ms, &obj->u._func_, base); \ 145 } 146 _CITRUS_PROP_READ_INT(chr, int) 147 _CITRUS_PROP_READ_INT(num, uint64_t) 148 #undef _CITRUS_PROP_READ_INT 149 150 static int 151 _citrus_prop_read_character_common(struct _memstream * __restrict ms, 152 int * __restrict result) 153 { 154 int ch, base; 155 156 _DIAGASSERT(ms != NULL); 157 _DIAGASSERT(result != NULL); 158 159 ch = _memstream_getc(ms); 160 if (ch != '\\') { 161 *result = ch; 162 } else { 163 ch = _memstream_getc(ms); 164 base = 16; 165 switch (ch) { 166 case 'a': *result = '\a'; break; 167 case 'b': *result = '\b'; break; 168 case 'f': *result = '\f'; break; 169 case 'n': *result = '\n'; break; 170 case 'r': *result = '\r'; break; 171 case 't': *result = '\t'; break; 172 case 'v': *result = '\v'; break; 173 /*FALLTHROUGH*/ 174 case '0': case '1': case '2': case '3': 175 case '4': case '5': case '6': case '7': 176 _memstream_ungetc(ms, ch); 177 base -= 8; 178 case 'x': 179 return _citrus_prop_read_chr_common(ms, result, base); 180 181 default: 182 /* unknown escape */ 183 *result = ch; 184 } 185 } 186 return 0; 187 } 188 189 static int 190 _citrus_prop_read_character(struct _memstream * __restrict ms, 191 _citrus_prop_object_t * __restrict obj) 192 { 193 int ch, errnum; 194 195 _DIAGASSERT(ms != NULL); 196 _DIAGASSERT(obj != NULL); 197 198 _memstream_skip_ws(ms); 199 ch = _memstream_getc(ms); 200 if (ch != '\'') { 201 _memstream_ungetc(ms, ch); 202 return _citrus_prop_read_chr(ms, obj); 203 } 204 errnum = _citrus_prop_read_character_common(ms, &ch); 205 if (errnum != 0) 206 return errnum; 207 obj->u.chr = ch; 208 ch = _memstream_getc(ms); 209 if (ch != '\'') 210 return EINVAL; 211 return 0; 212 } 213 214 static int 215 _citrus_prop_read_bool(struct _memstream * __restrict ms, 216 _citrus_prop_object_t * __restrict obj) 217 { 218 _DIAGASSERT(ms != NULL); 219 _DIAGASSERT(obj != NULL); 220 221 _memstream_skip_ws(ms); 222 switch (_bcs_tolower(_memstream_getc(ms))) { 223 case 't': 224 if (_bcs_tolower(_memstream_getc(ms)) == 'r' && 225 _bcs_tolower(_memstream_getc(ms)) == 'u' && 226 _bcs_tolower(_memstream_getc(ms)) == 'e') { 227 obj->u.bool = 1; 228 return 0; 229 } 230 break; 231 case 'f': 232 if (_bcs_tolower(_memstream_getc(ms)) == 'a' && 233 _bcs_tolower(_memstream_getc(ms)) == 'l' && 234 _bcs_tolower(_memstream_getc(ms)) == 's' && 235 _bcs_tolower(_memstream_getc(ms)) == 'e') { 236 obj->u.bool = 0; 237 return 0; 238 } 239 } 240 return EINVAL; 241 } 242 243 static int 244 _citrus_prop_read_str(struct _memstream * __restrict ms, 245 _citrus_prop_object_t * __restrict obj) 246 { 247 int errnum, quot, ch; 248 char *s, *t; 249 #define _CITRUS_PROP_STR_BUFSIZ 512 250 size_t n, m; 251 252 _DIAGASSERT(ms != NULL); 253 _DIAGASSERT(obj != NULL); 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 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 _DIAGASSERT(ms != NULL); 324 _DIAGASSERT(s != NULL); 325 _DIAGASSERT(n > 0); 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, 347 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 _DIAGASSERT(ms != NULL); 356 _DIAGASSERT(hints != NULL); 357 358 errnum = _citrus_prop_read_symbol(ms, name, sizeof(name)); 359 if (errnum != 0) 360 return errnum; 361 for (hint = hints; hint->name != NULL; ++hint) { 362 if (_citrus_bcs_strcasecmp(name, hint->name) == 0) 363 goto hint_found; 364 } 365 return EINVAL; 366 367 hint_found: 368 _memstream_skip_ws(ms); 369 ch = _memstream_getc(ms); 370 if (ch != '=' && ch != ':') 371 _memstream_ungetc(ms, ch); 372 do { 373 _citrus_prop_object_init(&ostart, hint->type); 374 _citrus_prop_object_init(&oend, hint->type); 375 errnum = (*readers[hint->type])(ms, &ostart); 376 if (errnum != 0) 377 return errnum; 378 _memstream_skip_ws(ms); 379 ch = _memstream_getc(ms); 380 switch (hint->type) { 381 case _CITRUS_PROP_BOOL: 382 case _CITRUS_PROP_STR: 383 break; 384 default: 385 if (ch != '-') 386 break; 387 errnum = (*readers[hint->type])(ms, &oend); 388 if (errnum != 0) 389 return errnum; 390 _memstream_skip_ws(ms); 391 ch = _memstream_getc(ms); 392 } 393 #define CALL0(_func_) \ 394 do { \ 395 _DIAGASSERT(hint->cb._func_.func != NULL); \ 396 errnum = (*hint->cb._func_.func)(context, \ 397 hint->name, ostart.u._func_); \ 398 } while (/*CONSTCOND*/0) 399 #define CALL1(_func_) \ 400 do { \ 401 _DIAGASSERT(hint->cb._func_.func != NULL); \ 402 errnum = (*hint->cb._func_.func)(context, \ 403 hint->name, ostart.u._func_, oend.u._func_);\ 404 } while (/*CONSTCOND*/0) 405 switch (hint->type) { 406 case _CITRUS_PROP_BOOL: CALL0(bool); break; 407 case _CITRUS_PROP_STR : CALL0( str); break; 408 case _CITRUS_PROP_CHR : CALL1( chr); break; 409 case _CITRUS_PROP_NUM : CALL1( num); 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 errnum, ch; 432 433 _DIAGASSERT(hints != NULL); 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( 443 &ms, hints, (void **)&context); 444 if (errnum != 0) 445 return errnum; 446 } 447 return 0; 448 } 449