1 /* $FreeBSD: head/lib/libc/iconv/citrus_prop.c 219019 2011-02-25 00:04:39Z gabor $ */ 2 /* $NetBSD: citrus_prop.c,v 1.3 2006/11/22 23:47:21 tnozaki 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; \ 113 \ 114 _memstream_skip_ws(ms); \ 115 ch = _memstream_getc(ms); \ 116 switch (ch) { \ 117 case '+': \ 118 ch = _memstream_getc(ms); \ 119 } \ 120 base = 10; \ 121 if (ch == '0') { \ 122 base -= 2; \ 123 ch = _memstream_getc(ms); \ 124 if (ch == 'x' || ch == 'X') { \ 125 ch = _memstream_getc(ms); \ 126 if (_bcs_isxdigit(ch) == 0) { \ 127 _memstream_ungetc(ms, ch); \ 128 obj->u._func_ = 0; \ 129 return (0); \ 130 } \ 131 base += 8; \ 132 } \ 133 } else if (_bcs_isdigit(ch) == 0) \ 134 return (EINVAL); \ 135 _memstream_ungetc(ms, ch); \ 136 return (_citrus_prop_read_##_func_##_common \ 137 (ms, &obj->u._func_, base)); \ 138 } 139 _CITRUS_PROP_READ_INT(chr, int) 140 _CITRUS_PROP_READ_INT(num, uint64_t) 141 #undef _CITRUS_PROP_READ_INT 142 143 static int 144 _citrus_prop_read_character_common(struct _memstream * __restrict ms, 145 int * __restrict result) 146 { 147 int base, ch; 148 149 ch = _memstream_getc(ms); 150 if (ch != '\\') 151 *result = ch; 152 else { 153 ch = _memstream_getc(ms); 154 base = 16; 155 switch (ch) { 156 case 'a': 157 *result = '\a'; 158 break; 159 case 'b': 160 *result = '\b'; 161 break; 162 case 'f': 163 *result = '\f'; 164 break; 165 case 'n': 166 *result = '\n'; 167 break; 168 case 'r': 169 *result = '\r'; 170 break; 171 case 't': 172 *result = '\t'; 173 break; 174 case 'v': 175 *result = '\v'; 176 break; 177 case '0': case '1': case '2': case '3': 178 case '4': case '5': case '6': case '7': 179 _memstream_ungetc(ms, ch); 180 base -= 8; 181 /*FALLTHROUGH*/ 182 case 'x': 183 return (_citrus_prop_read_chr_common(ms, result, base)); 184 /*NOTREACHED*/ 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 _memstream_skip_ws(ms); 200 ch = _memstream_getc(ms); 201 if (ch != '\'') { 202 _memstream_ungetc(ms, ch); 203 return (_citrus_prop_read_chr(ms, obj)); 204 } 205 errnum = _citrus_prop_read_character_common(ms, &ch); 206 if (errnum != 0) 207 return (errnum); 208 obj->u.chr = ch; 209 ch = _memstream_getc(ms); 210 if (ch != '\'') 211 return (EINVAL); 212 return (0); 213 } 214 215 static int 216 _citrus_prop_read_bool(struct _memstream * __restrict ms, 217 _citrus_prop_object_t * __restrict obj) 218 { 219 220 _memstream_skip_ws(ms); 221 switch (_bcs_tolower(_memstream_getc(ms))) { 222 case 't': 223 if (_bcs_tolower(_memstream_getc(ms)) == 'r' && 224 _bcs_tolower(_memstream_getc(ms)) == 'u' && 225 _bcs_tolower(_memstream_getc(ms)) == 'e') { 226 obj->u.boolean = true; 227 return (0); 228 } 229 break; 230 case 'f': 231 if (_bcs_tolower(_memstream_getc(ms)) == 'a' && 232 _bcs_tolower(_memstream_getc(ms)) == 'l' && 233 _bcs_tolower(_memstream_getc(ms)) == 's' && 234 _bcs_tolower(_memstream_getc(ms)) == 'e') { 235 obj->u.boolean = false; 236 return (0); 237 } 238 } 239 return (EINVAL); 240 } 241 242 static int 243 _citrus_prop_read_str(struct _memstream * __restrict ms, 244 _citrus_prop_object_t * __restrict obj) 245 { 246 int ch, errnum, quot; 247 char *s, *t; 248 #define _CITRUS_PROP_STR_BUFSIZ 512 249 size_t m, n; 250 251 m = _CITRUS_PROP_STR_BUFSIZ; 252 s = malloc(m); 253 if (s == NULL) 254 return (ENOMEM); 255 n = 0; 256 _memstream_skip_ws(ms); 257 quot = _memstream_getc(ms); 258 switch (quot) { 259 case EOF: 260 goto done; 261 /*NOTREACHED*/ 262 case '\\': 263 _memstream_ungetc(ms, quot); 264 quot = EOF; 265 /*FALLTHROUGH*/ 266 case '\"': case '\'': 267 break; 268 default: 269 s[n] = quot; 270 ++n, --m; 271 quot = EOF; 272 } 273 for (;;) { 274 if (m < 1) { 275 m = _CITRUS_PROP_STR_BUFSIZ; 276 t = realloc(s, n + m); 277 if (t == NULL) { 278 free(s); 279 return (ENOMEM); 280 } 281 s = t; 282 } 283 ch = _memstream_getc(ms); 284 if (quot == ch || (quot == EOF && 285 (ch == ';' || _bcs_isspace(ch)))) { 286 done: 287 s[n] = '\0'; 288 obj->u.str = (const char *)s; 289 return (0); 290 } 291 _memstream_ungetc(ms, ch); 292 errnum = _citrus_prop_read_character_common(ms, &ch); 293 if (errnum != 0) 294 return (errnum); 295 s[n] = ch; 296 ++n, --m; 297 } 298 free(s); 299 return (EINVAL); 300 #undef _CITRUS_PROP_STR_BUFSIZ 301 } 302 303 typedef int (*_citrus_prop_read_type_t)(struct _memstream * __restrict, 304 _citrus_prop_object_t * __restrict); 305 306 static const _citrus_prop_read_type_t readers[] = { 307 _citrus_prop_read_bool, 308 _citrus_prop_read_str, 309 _citrus_prop_read_character, 310 _citrus_prop_read_num, 311 }; 312 313 static __inline int 314 _citrus_prop_read_symbol(struct _memstream * __restrict ms, 315 char * __restrict s, size_t n) 316 { 317 int ch; 318 size_t m; 319 320 for (m = 0; m < n; ++m) { 321 ch = _memstream_getc(ms); 322 if (ch != '_' && _bcs_isalnum(ch) == 0) 323 goto name_found; 324 s[m] = ch; 325 } 326 ch = _memstream_getc(ms); 327 if (ch == '_' || _bcs_isalnum(ch) != 0) 328 return (EINVAL); 329 330 name_found: 331 _memstream_ungetc(ms, ch); 332 s[m] = '\0'; 333 334 return (0); 335 } 336 337 static int 338 _citrus_prop_parse_element(struct _memstream * __restrict ms, 339 const _citrus_prop_hint_t * __restrict hints, void ** __restrict context) 340 { 341 int ch, errnum; 342 #define _CITRUS_PROP_HINT_NAME_LEN_MAX 255 343 char name[_CITRUS_PROP_HINT_NAME_LEN_MAX + 1]; 344 const _citrus_prop_hint_t *hint; 345 _citrus_prop_object_t ostart, oend; 346 347 errnum = _citrus_prop_read_symbol(ms, name, sizeof(name)); 348 if (errnum != 0) 349 return (errnum); 350 for (hint = hints; hint->name != NULL; ++hint) 351 if (_citrus_bcs_strcasecmp(name, hint->name) == 0) 352 goto hint_found; 353 return (EINVAL); 354 355 hint_found: 356 _memstream_skip_ws(ms); 357 ch = _memstream_getc(ms); 358 if (ch != '=' && ch != ':') 359 _memstream_ungetc(ms, ch); 360 do { 361 _citrus_prop_object_init(&ostart, hint->type); 362 _citrus_prop_object_init(&oend, hint->type); 363 errnum = (*readers[hint->type])(ms, &ostart); 364 if (errnum != 0) 365 return (errnum); 366 _memstream_skip_ws(ms); 367 ch = _memstream_getc(ms); 368 switch (hint->type) { 369 case _CITRUS_PROP_BOOL: 370 /*FALLTHROUGH*/ 371 case _CITRUS_PROP_STR: 372 break; 373 default: 374 if (ch != '-') 375 break; 376 errnum = (*readers[hint->type])(ms, &oend); 377 if (errnum != 0) 378 return (errnum); 379 _memstream_skip_ws(ms); 380 ch = _memstream_getc(ms); 381 } 382 #define CALL0(_func_) \ 383 do { \ 384 errnum = (*hint->cb._func_.func)(context, \ 385 hint->name, ostart.u._func_); \ 386 } while (0) 387 #define CALL1(_func_) \ 388 do { \ 389 errnum = (*hint->cb._func_.func)(context, \ 390 hint->name, ostart.u._func_, oend.u._func_);\ 391 } while (0) 392 switch (hint->type) { 393 case _CITRUS_PROP_BOOL: 394 CALL0(boolean); 395 break; 396 case _CITRUS_PROP_STR: 397 CALL0(str); 398 break; 399 case _CITRUS_PROP_CHR: 400 CALL1(chr); 401 break; 402 case _CITRUS_PROP_NUM: 403 CALL1(num); 404 break; 405 default: 406 abort(); 407 /*NOTREACHED*/ 408 } 409 #undef CALL0 410 #undef CALL1 411 _citrus_prop_object_uninit(&ostart); 412 _citrus_prop_object_uninit(&oend); 413 if (errnum != 0) 414 return (errnum); 415 } while (ch == ','); 416 if (ch != ';') 417 _memstream_ungetc(ms, ch); 418 return (0); 419 } 420 421 int 422 _citrus_prop_parse_variable(const _citrus_prop_hint_t * __restrict hints, 423 void * __restrict context, const void *var, size_t lenvar) 424 { 425 struct _memstream ms; 426 int ch, errnum; 427 428 _memstream_bind_ptr(&ms, __DECONST(void *, var), lenvar); 429 for (;;) { 430 _memstream_skip_ws(&ms); 431 ch = _memstream_getc(&ms); 432 if (ch == EOF || ch == '\0') 433 break; 434 _memstream_ungetc(&ms, ch); 435 errnum = _citrus_prop_parse_element( 436 &ms, hints, (void ** __restrict)context); 437 if (errnum != 0) 438 return (errnum); 439 } 440 return (0); 441 } 442