1 /* $NetBSD: citrus_prop.c,v 1.3 2006/11/22 23:47:21 tnozaki Exp $ */ 2 3 /*- 4 * Copyright (c)2006 Citrus Project, 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 * 28 */ 29 30 #include <sys/cdefs.h> 31 #if defined(LIBC_SCCS) && !defined(lint) 32 __RCSID("$NetBSD: citrus_prop.c,v 1.3 2006/11/22 23:47:21 tnozaki Exp $"); 33 #endif /* LIBC_SCCS and not lint */ 34 35 #include <assert.h> 36 #include <limits.h> 37 #include <errno.h> 38 #include <stddef.h> 39 #include <stdio.h> 40 #include <stdint.h> 41 #include <stdlib.h> 42 #include <string.h> 43 44 #include "citrus_namespace.h" 45 #include "citrus_bcs.h" 46 #include "citrus_region.h" 47 #include "citrus_memstream.h" 48 #include "citrus_prop.h" 49 50 typedef struct { 51 _citrus_prop_type_t type; 52 union { 53 const char *str; 54 int bool, chr; 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 _DIAGASSERT(obj != NULL); 63 64 obj->type = type; 65 memset(&obj->u, 0, sizeof(obj->u)); 66 } 67 68 static __inline void 69 _citrus_prop_object_uninit(_citrus_prop_object_t *obj) 70 { 71 _DIAGASSERT(obj != NULL); 72 73 if (obj->type == _CITRUS_PROP_STR) 74 free(__UNCONST(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 n, ch, cutlim; \ 86 char *p; \ 87 \ 88 _DIAGASSERT(ms != NULL); \ 89 _DIAGASSERT(result != NULL); \ 90 \ 91 acc = (_type_)0; \ 92 cutoff = _max_ / base; \ 93 cutlim = _max_ % base; \ 94 for (;;) { \ 95 ch = _memstream_getc(ms); \ 96 p = strchr(xdigit, _bcs_toupper(ch)); \ 97 if (p == NULL || (n = (p - xdigit)) >= base) \ 98 break; \ 99 if (acc > cutoff || (acc == cutoff && n > cutlim)) \ 100 break; \ 101 acc *= base; \ 102 acc += n; \ 103 } \ 104 _memstream_ungetc(ms, ch); \ 105 *result = acc; \ 106 return 0; \ 107 } 108 _CITRUS_PROP_READ_UINT_COMMON(chr, int, UCHAR_MAX) 109 _CITRUS_PROP_READ_UINT_COMMON(num, uint64_t, UINT64_MAX) 110 #undef _CITRUS_PROP_READ_UINT_COMMON 111 112 #define _CITRUS_PROP_READ_INT(_func_, _type_) \ 113 static int \ 114 _citrus_prop_read_##_func_(struct _memstream * __restrict ms, \ 115 _citrus_prop_object_t * __restrict obj) \ 116 { \ 117 int ch, neg, base; \ 118 \ 119 _DIAGASSERT(ms != NULL); \ 120 _DIAGASSERT(obj != NULL); \ 121 \ 122 _memstream_skip_ws(ms); \ 123 ch = _memstream_getc(ms); \ 124 neg = 0; \ 125 switch (ch) { \ 126 case '-': \ 127 neg = 1; \ 128 case '+': \ 129 ch = _memstream_getc(ms); \ 130 } \ 131 base = 10; \ 132 if (ch == '0') { \ 133 base -= 2; \ 134 ch = _memstream_getc(ms); \ 135 if (ch == 'x' || ch == 'X') { \ 136 ch = _memstream_getc(ms); \ 137 if (_bcs_isxdigit(ch) == 0) { \ 138 _memstream_ungetc(ms, ch); \ 139 obj->u._func_ = 0; \ 140 return 0; \ 141 } \ 142 base += 8; \ 143 } \ 144 } else if (_bcs_isdigit(ch) == 0) \ 145 return EINVAL; \ 146 _memstream_ungetc(ms, ch); \ 147 return _citrus_prop_read_##_func_##_common \ 148 (ms, &obj->u._func_, base); \ 149 } 150 _CITRUS_PROP_READ_INT(chr, int) 151 _CITRUS_PROP_READ_INT(num, uint64_t) 152 #undef _CITRUS_PROP_READ_INT 153 154 static int 155 _citrus_prop_read_character_common(struct _memstream * __restrict ms, 156 int * __restrict result) 157 { 158 int ch, base; 159 160 _DIAGASSERT(ms != NULL); 161 _DIAGASSERT(result != NULL); 162 163 ch = _memstream_getc(ms); 164 if (ch != '\\') { 165 *result = ch; 166 } else { 167 ch = _memstream_getc(ms); 168 base = 16; 169 switch (ch) { 170 case 'a': *result = '\a'; break; 171 case 'b': *result = '\b'; break; 172 case 'f': *result = '\f'; break; 173 case 'n': *result = '\n'; break; 174 case 'r': *result = '\r'; break; 175 case 't': *result = '\t'; break; 176 case 'v': *result = '\v'; break; 177 /*FALLTHROUGH*/ 178 case '0': case '1': case '2': case '3': 179 case '4': case '5': case '6': case '7': 180 _memstream_ungetc(ms, ch); 181 base -= 8; 182 case 'x': 183 return _citrus_prop_read_chr_common(ms, result, base); 184 185 default: 186 /* unknown escape */ 187 *result = ch; 188 } 189 } 190 return 0; 191 } 192 193 static int 194 _citrus_prop_read_character(struct _memstream * __restrict ms, 195 _citrus_prop_object_t * __restrict obj) 196 { 197 int ch, errnum; 198 199 _DIAGASSERT(ms != NULL); 200 _DIAGASSERT(obj != NULL); 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 _DIAGASSERT(ms != NULL); 223 _DIAGASSERT(obj != NULL); 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.bool = 1; 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.bool = 0; 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 errnum, quot, ch; 252 char *s, *t; 253 #define _CITRUS_PROP_STR_BUFSIZ 512 254 size_t n, m; 255 256 _DIAGASSERT(ms != NULL); 257 _DIAGASSERT(obj != NULL); 258 259 m = _CITRUS_PROP_STR_BUFSIZ; 260 s = malloc(m); 261 if (s == NULL) 262 return ENOMEM; 263 n = 0; 264 _memstream_skip_ws(ms); 265 quot = _memstream_getc(ms); 266 switch (quot) { 267 case EOF: 268 goto done; 269 case '\\': 270 _memstream_ungetc(ms, quot); 271 quot = EOF; 272 /*FALLTHROUGH*/ 273 case '\"': case '\'': 274 break; 275 default: 276 s[n] = quot; 277 ++n, --m; 278 quot = EOF; 279 } 280 for (;;) { 281 if (m < 1) { 282 m = _CITRUS_PROP_STR_BUFSIZ; 283 t = realloc(s, n + m); 284 if (t == NULL) { 285 free(s); 286 return ENOMEM; 287 } 288 s = t; 289 } 290 ch = _memstream_getc(ms); 291 if (quot == ch || (quot == EOF && 292 (ch == ';' || _bcs_isspace(ch)))) { 293 done: 294 s[n] = '\0'; 295 obj->u.str = (const char *)s; 296 return 0; 297 } 298 _memstream_ungetc(ms, ch); 299 errnum = _citrus_prop_read_character_common(ms, &ch); 300 if (errnum != 0) 301 return errnum; 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 _DIAGASSERT(ms != NULL); 328 _DIAGASSERT(s != NULL); 329 _DIAGASSERT(n > 0); 330 331 for (m = 0; m < n; ++m) { 332 ch = _memstream_getc(ms); 333 if (ch != '_' && _bcs_isalnum(ch) == 0) 334 goto name_found; 335 s[m] = ch; 336 } 337 ch = _memstream_getc(ms); 338 if (ch == '_' || _bcs_isalnum(ch) != 0) 339 return EINVAL; 340 341 name_found: 342 _memstream_ungetc(ms, ch); 343 s[m] = '\0'; 344 345 return 0; 346 } 347 348 static int 349 _citrus_prop_parse_element(struct _memstream * __restrict ms, 350 const _citrus_prop_hint_t * __restrict hints, 351 void ** __restrict context) 352 { 353 int ch, errnum; 354 #define _CITRUS_PROP_HINT_NAME_LEN_MAX 255 355 char name[_CITRUS_PROP_HINT_NAME_LEN_MAX + 1]; 356 const _citrus_prop_hint_t *hint; 357 _citrus_prop_object_t ostart, oend; 358 359 _DIAGASSERT(ms != NULL); 360 _DIAGASSERT(hints != NULL); 361 362 errnum = _citrus_prop_read_symbol(ms, name, sizeof(name)); 363 if (errnum != 0) 364 return errnum; 365 for (hint = hints; hint->name != NULL; ++hint) { 366 if (_citrus_bcs_strcasecmp(name, hint->name) == 0) 367 goto hint_found; 368 } 369 return EINVAL; 370 371 hint_found: 372 _memstream_skip_ws(ms); 373 ch = _memstream_getc(ms); 374 if (ch != '=' && ch != ':') 375 _memstream_ungetc(ms, ch); 376 do { 377 _citrus_prop_object_init(&ostart, hint->type); 378 _citrus_prop_object_init(&oend, hint->type); 379 errnum = (*readers[hint->type])(ms, &ostart); 380 if (errnum != 0) 381 return errnum; 382 _memstream_skip_ws(ms); 383 ch = _memstream_getc(ms); 384 switch (hint->type) { 385 case _CITRUS_PROP_BOOL: 386 case _CITRUS_PROP_STR: 387 break; 388 default: 389 if (ch != '-') 390 break; 391 errnum = (*readers[hint->type])(ms, &oend); 392 if (errnum != 0) 393 return errnum; 394 _memstream_skip_ws(ms); 395 ch = _memstream_getc(ms); 396 } 397 #define CALL0(_func_) \ 398 do { \ 399 _DIAGASSERT(hint->cb._func_.func != NULL); \ 400 errnum = (*hint->cb._func_.func)(context, \ 401 hint->name, ostart.u._func_); \ 402 } while (/*CONSTCOND*/0) 403 #define CALL1(_func_) \ 404 do { \ 405 _DIAGASSERT(hint->cb._func_.func != NULL); \ 406 errnum = (*hint->cb._func_.func)(context, \ 407 hint->name, ostart.u._func_, oend.u._func_);\ 408 } while (/*CONSTCOND*/0) 409 switch (hint->type) { 410 case _CITRUS_PROP_BOOL: CALL0(bool); break; 411 case _CITRUS_PROP_STR : CALL0( str); break; 412 case _CITRUS_PROP_CHR : CALL1( chr); break; 413 case _CITRUS_PROP_NUM : CALL1( num); break; 414 default: 415 abort(); 416 /*NOTREACHED*/ 417 } 418 #undef CALL0 419 #undef CALL1 420 _citrus_prop_object_uninit(&ostart); 421 _citrus_prop_object_uninit(&oend); 422 if (errnum != 0) 423 return errnum; 424 } while (ch == ','); 425 if (ch != ';') 426 _memstream_ungetc(ms, ch); 427 return 0; 428 } 429 430 int 431 _citrus_prop_parse_variable(const _citrus_prop_hint_t * __restrict hints, 432 void * __restrict context, const void *var, size_t lenvar) 433 { 434 struct _memstream ms; 435 int errnum, ch; 436 437 _DIAGASSERT(hints != NULL); 438 439 _memstream_bind_ptr(&ms, __UNCONST(var), lenvar); 440 for (;;) { 441 _memstream_skip_ws(&ms); 442 ch = _memstream_getc(&ms); 443 if (ch == EOF || ch == '\0') 444 break; 445 _memstream_ungetc(&ms, ch); 446 errnum = _citrus_prop_parse_element( 447 &ms, hints, (void **)&context); 448 if (errnum != 0) 449 return errnum; 450 } 451 return 0; 452 } 453