1219019Sgabor/* $FreeBSD$ */ 2219019Sgabor/* $NetBSD: citrus_prop.c,v 1.3 2006/11/22 23:47:21 tnozaki Exp $ */ 3219019Sgabor 4219019Sgabor/*- 5219019Sgabor * Copyright (c)2006 Citrus Project, 6219019Sgabor * All rights reserved. 7219019Sgabor * 8219019Sgabor * Redistribution and use in source and binary forms, with or without 9219019Sgabor * modification, are permitted provided that the following conditions 10219019Sgabor * are met: 11219019Sgabor * 1. Redistributions of source code must retain the above copyright 12219019Sgabor * notice, this list of conditions and the following disclaimer. 13219019Sgabor * 2. Redistributions in binary form must reproduce the above copyright 14219019Sgabor * notice, this list of conditions and the following disclaimer in the 15219019Sgabor * documentation and/or other materials provided with the distribution. 16219019Sgabor * 17219019Sgabor * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18219019Sgabor * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19219019Sgabor * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20219019Sgabor * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21219019Sgabor * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22219019Sgabor * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23219019Sgabor * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24219019Sgabor * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25219019Sgabor * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26219019Sgabor * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27219019Sgabor * SUCH DAMAGE. 28219019Sgabor * 29219019Sgabor */ 30219019Sgabor 31219019Sgabor#include <sys/cdefs.h> 32219019Sgabor 33219019Sgabor#include <assert.h> 34219019Sgabor#include <errno.h> 35219019Sgabor#include <limits.h> 36219019Sgabor#include <stdbool.h> 37219019Sgabor#include <stddef.h> 38219019Sgabor#include <stdio.h> 39219019Sgabor#include <stdint.h> 40219019Sgabor#include <stdlib.h> 41219019Sgabor#include <string.h> 42219019Sgabor 43219019Sgabor#include "citrus_namespace.h" 44219019Sgabor#include "citrus_bcs.h" 45219019Sgabor#include "citrus_region.h" 46219019Sgabor#include "citrus_memstream.h" 47219019Sgabor#include "citrus_prop.h" 48219019Sgabor 49219019Sgabortypedef struct { 50219019Sgabor _citrus_prop_type_t type; 51219019Sgabor union { 52219019Sgabor const char *str; 53219019Sgabor int chr; 54219019Sgabor bool boolean; 55219019Sgabor uint64_t num; 56219019Sgabor } u; 57219019Sgabor} _citrus_prop_object_t; 58219019Sgabor 59219019Sgaborstatic __inline void 60219019Sgabor_citrus_prop_object_init(_citrus_prop_object_t *obj, _citrus_prop_type_t type) 61219019Sgabor{ 62219019Sgabor 63219019Sgabor obj->type = type; 64219019Sgabor memset(&obj->u, 0, sizeof(obj->u)); 65219019Sgabor} 66219019Sgabor 67219019Sgaborstatic __inline void 68219019Sgabor_citrus_prop_object_uninit(_citrus_prop_object_t *obj) 69219019Sgabor{ 70219019Sgabor 71219019Sgabor if (obj->type == _CITRUS_PROP_STR) 72219019Sgabor free(__DECONST(void *, obj->u.str)); 73219019Sgabor} 74219019Sgabor 75219019Sgaborstatic const char *xdigit = "0123456789ABCDEF"; 76219019Sgabor 77219019Sgabor#define _CITRUS_PROP_READ_UINT_COMMON(_func_, _type_, _max_) \ 78219019Sgaborstatic int \ 79219019Sgabor_citrus_prop_read_##_func_##_common(struct _memstream * __restrict ms, \ 80219019Sgabor _type_ * __restrict result, int base) \ 81219019Sgabor{ \ 82219019Sgabor _type_ acc, cutoff; \ 83219019Sgabor int ch, cutlim, n; \ 84219019Sgabor char *p; \ 85219019Sgabor \ 86219019Sgabor acc = (_type_)0; \ 87219019Sgabor cutoff = _max_ / base; \ 88219019Sgabor cutlim = _max_ % base; \ 89219019Sgabor for (;;) { \ 90219019Sgabor ch = _memstream_getc(ms); \ 91219019Sgabor p = strchr(xdigit, _bcs_toupper(ch)); \ 92219019Sgabor if (p == NULL || (n = (p - xdigit)) >= base) \ 93219019Sgabor break; \ 94219019Sgabor if (acc > cutoff || (acc == cutoff && n > cutlim)) \ 95219019Sgabor break; \ 96219019Sgabor acc *= base; \ 97219019Sgabor acc += n; \ 98219019Sgabor } \ 99219019Sgabor _memstream_ungetc(ms, ch); \ 100219019Sgabor *result = acc; \ 101219019Sgabor return (0); \ 102219019Sgabor} 103219019Sgabor_CITRUS_PROP_READ_UINT_COMMON(chr, int, UCHAR_MAX) 104219019Sgabor_CITRUS_PROP_READ_UINT_COMMON(num, uint64_t, UINT64_MAX) 105219019Sgabor#undef _CITRUS_PROP_READ_UINT_COMMON 106219019Sgabor 107219019Sgabor#define _CITRUS_PROP_READ_INT(_func_, _type_) \ 108219019Sgaborstatic int \ 109219019Sgabor_citrus_prop_read_##_func_(struct _memstream * __restrict ms, \ 110219019Sgabor _citrus_prop_object_t * __restrict obj) \ 111219019Sgabor{ \ 112219019Sgabor int base, ch, neg; \ 113219019Sgabor \ 114219019Sgabor _memstream_skip_ws(ms); \ 115219019Sgabor ch = _memstream_getc(ms); \ 116219019Sgabor neg = 0; \ 117219019Sgabor switch (ch) { \ 118219019Sgabor case '-': \ 119219019Sgabor neg = 1; \ 120219019Sgabor case '+': \ 121219019Sgabor ch = _memstream_getc(ms); \ 122219019Sgabor } \ 123219019Sgabor base = 10; \ 124219019Sgabor if (ch == '0') { \ 125219019Sgabor base -= 2; \ 126219019Sgabor ch = _memstream_getc(ms); \ 127219019Sgabor if (ch == 'x' || ch == 'X') { \ 128219019Sgabor ch = _memstream_getc(ms); \ 129219019Sgabor if (_bcs_isxdigit(ch) == 0) { \ 130219019Sgabor _memstream_ungetc(ms, ch); \ 131219019Sgabor obj->u._func_ = 0; \ 132219019Sgabor return (0); \ 133219019Sgabor } \ 134219019Sgabor base += 8; \ 135219019Sgabor } \ 136219019Sgabor } else if (_bcs_isdigit(ch) == 0) \ 137219019Sgabor return (EINVAL); \ 138219019Sgabor _memstream_ungetc(ms, ch); \ 139219019Sgabor return (_citrus_prop_read_##_func_##_common \ 140219019Sgabor (ms, &obj->u._func_, base)); \ 141219019Sgabor} 142219019Sgabor_CITRUS_PROP_READ_INT(chr, int) 143219019Sgabor_CITRUS_PROP_READ_INT(num, uint64_t) 144219019Sgabor#undef _CITRUS_PROP_READ_INT 145219019Sgabor 146219019Sgaborstatic int 147219019Sgabor_citrus_prop_read_character_common(struct _memstream * __restrict ms, 148219019Sgabor int * __restrict result) 149219019Sgabor{ 150219019Sgabor int base, ch; 151219019Sgabor 152219019Sgabor ch = _memstream_getc(ms); 153219019Sgabor if (ch != '\\') 154219019Sgabor *result = ch; 155219019Sgabor else { 156219019Sgabor ch = _memstream_getc(ms); 157219019Sgabor base = 16; 158219019Sgabor switch (ch) { 159219019Sgabor case 'a': 160219019Sgabor *result = '\a'; 161219019Sgabor break; 162219019Sgabor case 'b': 163219019Sgabor *result = '\b'; 164219019Sgabor break; 165219019Sgabor case 'f': 166219019Sgabor *result = '\f'; 167219019Sgabor break; 168219019Sgabor case 'n': 169219019Sgabor *result = '\n'; 170219019Sgabor break; 171219019Sgabor case 'r': 172219019Sgabor *result = '\r'; 173219019Sgabor break; 174219019Sgabor case 't': 175219019Sgabor *result = '\t'; 176219019Sgabor break; 177219019Sgabor case 'v': 178219019Sgabor *result = '\v'; 179219019Sgabor break; 180219019Sgabor case '0': case '1': case '2': case '3': 181219019Sgabor case '4': case '5': case '6': case '7': 182219019Sgabor _memstream_ungetc(ms, ch); 183219019Sgabor base -= 8; 184219019Sgabor /*FALLTHROUGH*/ 185219019Sgabor case 'x': 186219019Sgabor return (_citrus_prop_read_chr_common(ms, result, base)); 187219019Sgabor /*NOTREACHED*/ 188219019Sgabor default: 189219019Sgabor /* unknown escape */ 190219019Sgabor *result = ch; 191219019Sgabor } 192219019Sgabor } 193219019Sgabor return (0); 194219019Sgabor} 195219019Sgabor 196219019Sgaborstatic int 197219019Sgabor_citrus_prop_read_character(struct _memstream * __restrict ms, 198219019Sgabor _citrus_prop_object_t * __restrict obj) 199219019Sgabor{ 200219019Sgabor int ch, errnum; 201219019Sgabor 202219019Sgabor _memstream_skip_ws(ms); 203219019Sgabor ch = _memstream_getc(ms); 204219019Sgabor if (ch != '\'') { 205219019Sgabor _memstream_ungetc(ms, ch); 206219019Sgabor return (_citrus_prop_read_chr(ms, obj)); 207219019Sgabor } 208219019Sgabor errnum = _citrus_prop_read_character_common(ms, &ch); 209219019Sgabor if (errnum != 0) 210219019Sgabor return (errnum); 211219019Sgabor obj->u.chr = ch; 212219019Sgabor ch = _memstream_getc(ms); 213219019Sgabor if (ch != '\'') 214219019Sgabor return (EINVAL); 215219019Sgabor return (0); 216219019Sgabor} 217219019Sgabor 218219019Sgaborstatic int 219219019Sgabor_citrus_prop_read_bool(struct _memstream * __restrict ms, 220219019Sgabor _citrus_prop_object_t * __restrict obj) 221219019Sgabor{ 222219019Sgabor 223219019Sgabor _memstream_skip_ws(ms); 224219019Sgabor switch (_bcs_tolower(_memstream_getc(ms))) { 225219019Sgabor case 't': 226219019Sgabor if (_bcs_tolower(_memstream_getc(ms)) == 'r' && 227219019Sgabor _bcs_tolower(_memstream_getc(ms)) == 'u' && 228219019Sgabor _bcs_tolower(_memstream_getc(ms)) == 'e') { 229219019Sgabor obj->u.boolean = true; 230219019Sgabor return (0); 231219019Sgabor } 232219019Sgabor break; 233219019Sgabor case 'f': 234219019Sgabor if (_bcs_tolower(_memstream_getc(ms)) == 'a' && 235219019Sgabor _bcs_tolower(_memstream_getc(ms)) == 'l' && 236219019Sgabor _bcs_tolower(_memstream_getc(ms)) == 's' && 237219019Sgabor _bcs_tolower(_memstream_getc(ms)) == 'e') { 238219019Sgabor obj->u.boolean = false; 239219019Sgabor return (0); 240219019Sgabor } 241219019Sgabor } 242219019Sgabor return (EINVAL); 243219019Sgabor} 244219019Sgabor 245219019Sgaborstatic int 246219019Sgabor_citrus_prop_read_str(struct _memstream * __restrict ms, 247219019Sgabor _citrus_prop_object_t * __restrict obj) 248219019Sgabor{ 249219019Sgabor int ch, errnum, quot; 250219019Sgabor char *s, *t; 251219019Sgabor#define _CITRUS_PROP_STR_BUFSIZ 512 252219019Sgabor size_t m, n; 253219019Sgabor 254219019Sgabor m = _CITRUS_PROP_STR_BUFSIZ; 255219019Sgabor s = malloc(m); 256219019Sgabor if (s == NULL) 257219019Sgabor return (ENOMEM); 258219019Sgabor n = 0; 259219019Sgabor _memstream_skip_ws(ms); 260219019Sgabor quot = _memstream_getc(ms); 261219019Sgabor switch (quot) { 262219019Sgabor case EOF: 263219019Sgabor goto done; 264219019Sgabor /*NOTREACHED*/ 265219019Sgabor case '\\': 266219019Sgabor _memstream_ungetc(ms, quot); 267219019Sgabor quot = EOF; 268219019Sgabor /*FALLTHROUGH*/ 269219019Sgabor case '\"': case '\'': 270219019Sgabor break; 271219019Sgabor default: 272219019Sgabor s[n] = quot; 273219019Sgabor ++n, --m; 274219019Sgabor quot = EOF; 275219019Sgabor } 276219019Sgabor for (;;) { 277219019Sgabor if (m < 1) { 278219019Sgabor m = _CITRUS_PROP_STR_BUFSIZ; 279219019Sgabor t = realloc(s, n + m); 280219019Sgabor if (t == NULL) { 281219019Sgabor free(s); 282219019Sgabor return (ENOMEM); 283219019Sgabor } 284219019Sgabor s = t; 285219019Sgabor } 286219019Sgabor ch = _memstream_getc(ms); 287219019Sgabor if (quot == ch || (quot == EOF && 288219019Sgabor (ch == ';' || _bcs_isspace(ch)))) { 289219019Sgabordone: 290219019Sgabor s[n] = '\0'; 291219019Sgabor obj->u.str = (const char *)s; 292219019Sgabor return (0); 293219019Sgabor } 294219019Sgabor _memstream_ungetc(ms, ch); 295219019Sgabor errnum = _citrus_prop_read_character_common(ms, &ch); 296219019Sgabor if (errnum != 0) 297219019Sgabor return (errnum); 298219019Sgabor s[n] = ch; 299219019Sgabor ++n, --m; 300219019Sgabor } 301219019Sgabor free(s); 302219019Sgabor return (EINVAL); 303219019Sgabor#undef _CITRUS_PROP_STR_BUFSIZ 304219019Sgabor} 305219019Sgabor 306219019Sgabortypedef int (*_citrus_prop_read_type_t)(struct _memstream * __restrict, 307219019Sgabor _citrus_prop_object_t * __restrict); 308219019Sgabor 309219019Sgaborstatic const _citrus_prop_read_type_t readers[] = { 310219019Sgabor _citrus_prop_read_bool, 311219019Sgabor _citrus_prop_read_str, 312219019Sgabor _citrus_prop_read_character, 313219019Sgabor _citrus_prop_read_num, 314219019Sgabor}; 315219019Sgabor 316219019Sgaborstatic __inline int 317219019Sgabor_citrus_prop_read_symbol(struct _memstream * __restrict ms, 318219019Sgabor char * __restrict s, size_t n) 319219019Sgabor{ 320219019Sgabor int ch; 321219019Sgabor size_t m; 322219019Sgabor 323219019Sgabor for (m = 0; m < n; ++m) { 324219019Sgabor ch = _memstream_getc(ms); 325219019Sgabor if (ch != '_' && _bcs_isalnum(ch) == 0) 326219019Sgabor goto name_found; 327219019Sgabor s[m] = ch; 328219019Sgabor } 329219019Sgabor ch = _memstream_getc(ms); 330219019Sgabor if (ch == '_' || _bcs_isalnum(ch) != 0) 331219019Sgabor return (EINVAL); 332219019Sgabor 333219019Sgaborname_found: 334219019Sgabor _memstream_ungetc(ms, ch); 335219019Sgabor s[m] = '\0'; 336219019Sgabor 337219019Sgabor return (0); 338219019Sgabor} 339219019Sgabor 340219019Sgaborstatic int 341219019Sgabor_citrus_prop_parse_element(struct _memstream * __restrict ms, 342219019Sgabor const _citrus_prop_hint_t * __restrict hints, void ** __restrict context) 343219019Sgabor{ 344219019Sgabor int ch, errnum; 345219019Sgabor#define _CITRUS_PROP_HINT_NAME_LEN_MAX 255 346219019Sgabor char name[_CITRUS_PROP_HINT_NAME_LEN_MAX + 1]; 347219019Sgabor const _citrus_prop_hint_t *hint; 348219019Sgabor _citrus_prop_object_t ostart, oend; 349219019Sgabor 350219019Sgabor errnum = _citrus_prop_read_symbol(ms, name, sizeof(name)); 351219019Sgabor if (errnum != 0) 352219019Sgabor return (errnum); 353219019Sgabor for (hint = hints; hint->name != NULL; ++hint) 354219019Sgabor if (_citrus_bcs_strcasecmp(name, hint->name) == 0) 355219019Sgabor goto hint_found; 356219019Sgabor return (EINVAL); 357219019Sgabor 358219019Sgaborhint_found: 359219019Sgabor _memstream_skip_ws(ms); 360219019Sgabor ch = _memstream_getc(ms); 361219019Sgabor if (ch != '=' && ch != ':') 362219019Sgabor _memstream_ungetc(ms, ch); 363219019Sgabor do { 364219019Sgabor _citrus_prop_object_init(&ostart, hint->type); 365219019Sgabor _citrus_prop_object_init(&oend, hint->type); 366219019Sgabor errnum = (*readers[hint->type])(ms, &ostart); 367219019Sgabor if (errnum != 0) 368219019Sgabor return (errnum); 369219019Sgabor _memstream_skip_ws(ms); 370219019Sgabor ch = _memstream_getc(ms); 371219019Sgabor switch (hint->type) { 372219019Sgabor case _CITRUS_PROP_BOOL: 373219019Sgabor /*FALLTHROUGH*/ 374219019Sgabor case _CITRUS_PROP_STR: 375219019Sgabor break; 376219019Sgabor default: 377219019Sgabor if (ch != '-') 378219019Sgabor break; 379219019Sgabor errnum = (*readers[hint->type])(ms, &oend); 380219019Sgabor if (errnum != 0) 381219019Sgabor return (errnum); 382219019Sgabor _memstream_skip_ws(ms); 383219019Sgabor ch = _memstream_getc(ms); 384219019Sgabor } 385219019Sgabor#define CALL0(_func_) \ 386219019Sgabordo { \ 387219019Sgabor errnum = (*hint->cb._func_.func)(context, \ 388219019Sgabor hint->name, ostart.u._func_); \ 389219019Sgabor} while (0) 390219019Sgabor#define CALL1(_func_) \ 391219019Sgabordo { \ 392219019Sgabor errnum = (*hint->cb._func_.func)(context, \ 393219019Sgabor hint->name, ostart.u._func_, oend.u._func_);\ 394219019Sgabor} while (0) 395219019Sgabor switch (hint->type) { 396219019Sgabor case _CITRUS_PROP_BOOL: 397219019Sgabor CALL0(boolean); 398219019Sgabor break; 399219019Sgabor case _CITRUS_PROP_STR: 400219019Sgabor CALL0(str); 401219019Sgabor break; 402219019Sgabor case _CITRUS_PROP_CHR: 403219019Sgabor CALL1(chr); 404219019Sgabor break; 405219019Sgabor case _CITRUS_PROP_NUM: 406219019Sgabor CALL1(num); 407219019Sgabor break; 408219019Sgabor default: 409219019Sgabor abort(); 410219019Sgabor /*NOTREACHED*/ 411219019Sgabor } 412219019Sgabor#undef CALL0 413219019Sgabor#undef CALL1 414219019Sgabor _citrus_prop_object_uninit(&ostart); 415219019Sgabor _citrus_prop_object_uninit(&oend); 416219019Sgabor if (errnum != 0) 417219019Sgabor return (errnum); 418219019Sgabor } while (ch == ','); 419219019Sgabor if (ch != ';') 420219019Sgabor _memstream_ungetc(ms, ch); 421219019Sgabor return (0); 422219019Sgabor} 423219019Sgabor 424219019Sgaborint 425219019Sgabor_citrus_prop_parse_variable(const _citrus_prop_hint_t * __restrict hints, 426219019Sgabor void * __restrict context, const void *var, size_t lenvar) 427219019Sgabor{ 428219019Sgabor struct _memstream ms; 429219019Sgabor int ch, errnum; 430219019Sgabor 431219019Sgabor _memstream_bind_ptr(&ms, __DECONST(void *, var), lenvar); 432219019Sgabor for (;;) { 433219019Sgabor _memstream_skip_ws(&ms); 434219019Sgabor ch = _memstream_getc(&ms); 435219019Sgabor if (ch == EOF || ch == '\0') 436219019Sgabor break; 437219019Sgabor _memstream_ungetc(&ms, ch); 438219019Sgabor errnum = _citrus_prop_parse_element( 439219019Sgabor &ms, hints, (void ** __restrict)context); 440219019Sgabor if (errnum != 0) 441219019Sgabor return (errnum); 442219019Sgabor } 443219019Sgabor return (0); 444219019Sgabor} 445