1 /* Copyright (c) 2013, Vsevolod Stakhov 2 * All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions are met: 6 * * Redistributions of source code must retain the above copyright 7 * notice, this list of conditions and the following disclaimer. 8 * * Redistributions in binary form must reproduce the above copyright 9 * notice, this list of conditions and the following disclaimer in the 10 * documentation and/or other materials provided with the distribution. 11 * 12 * THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY 13 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 14 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 15 * DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY 16 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 17 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 18 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 19 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 20 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 21 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 22 */ 23 24 #include <math.h> 25 #include "ucl.h" 26 #include "ucl_internal.h" 27 #include "ucl_chartable.h" 28 29 /** 30 * @file ucl_parser.c 31 * The implementation of ucl parser 32 */ 33 34 struct ucl_parser_saved_state { 35 unsigned int line; 36 unsigned int column; 37 size_t remain; 38 const unsigned char *pos; 39 }; 40 41 /** 42 * Move up to len characters 43 * @param parser 44 * @param begin 45 * @param len 46 * @return new position in chunk 47 */ 48 #define ucl_chunk_skipc(chunk, p) \ 49 do { \ 50 if (p == chunk->end) { \ 51 break; \ 52 } \ 53 if (*(p) == '\n') { \ 54 (chunk)->line++; \ 55 (chunk)->column = 0; \ 56 } \ 57 else \ 58 (chunk)->column++; \ 59 (p++); \ 60 (chunk)->pos++; \ 61 (chunk)->remain--; \ 62 } while (0) 63 64 static inline void 65 ucl_set_err(struct ucl_parser *parser, int code, const char *str, UT_string **err) 66 { 67 const char *fmt_string, *filename; 68 struct ucl_chunk *chunk = parser->chunks; 69 70 if (parser->cur_file) { 71 filename = parser->cur_file; 72 } 73 else { 74 filename = "<unknown>"; 75 } 76 77 if (chunk->pos < chunk->end) { 78 if (isgraph(*chunk->pos)) { 79 fmt_string = "error while parsing %s: " 80 "line: %d, column: %d - '%s', character: '%c'"; 81 } 82 else { 83 fmt_string = "error while parsing %s: " 84 "line: %d, column: %d - '%s', character: '0x%02x'"; 85 } 86 ucl_create_err(err, fmt_string, 87 filename, chunk->line, chunk->column, 88 str, *chunk->pos); 89 } 90 else { 91 ucl_create_err(err, "error while parsing %s: at the end of chunk: %s", 92 filename, str); 93 } 94 95 parser->err_code = code; 96 parser->state = UCL_STATE_ERROR; 97 } 98 99 static void 100 ucl_save_comment(struct ucl_parser *parser, const char *begin, size_t len) 101 { 102 ucl_object_t *nobj; 103 104 if (len > 0 && begin != NULL) { 105 nobj = ucl_object_fromstring_common(begin, len, 0); 106 107 if (parser->last_comment) { 108 /* We need to append data to an existing object */ 109 DL_APPEND(parser->last_comment, nobj); 110 } 111 else { 112 parser->last_comment = nobj; 113 } 114 } 115 } 116 117 static void 118 ucl_attach_comment(struct ucl_parser *parser, ucl_object_t *obj, bool before) 119 { 120 if (parser->last_comment) { 121 ucl_object_insert_key(parser->comments, parser->last_comment, 122 (const char *) &obj, sizeof(void *), true); 123 124 if (before) { 125 parser->last_comment->flags |= UCL_OBJECT_INHERITED; 126 } 127 128 parser->last_comment = NULL; 129 } 130 } 131 132 /** 133 * Skip all comments from the current pos resolving nested and multiline comments 134 * @param parser 135 * @return 136 */ 137 static bool 138 ucl_skip_comments(struct ucl_parser *parser) 139 { 140 struct ucl_chunk *chunk = parser->chunks; 141 const unsigned char *p, *beg = NULL; 142 int comments_nested = 0; 143 bool quoted = false; 144 145 p = chunk->pos; 146 147 start: 148 if (chunk->remain > 0 && *p == '#') { 149 if (parser->state != UCL_STATE_SCOMMENT && 150 parser->state != UCL_STATE_MCOMMENT) { 151 beg = p; 152 153 while (p < chunk->end) { 154 if (*p == '\n') { 155 if (parser->flags & UCL_PARSER_SAVE_COMMENTS) { 156 ucl_save_comment(parser, beg, p - beg); 157 beg = NULL; 158 } 159 160 ucl_chunk_skipc(chunk, p); 161 162 goto start; 163 } 164 ucl_chunk_skipc(chunk, p); 165 } 166 } 167 } 168 else if (chunk->remain >= 2 && *p == '/' && p[1] == '*') { 169 beg = p; 170 comments_nested ++; 171 ucl_chunk_skipc (chunk, p); 172 ucl_chunk_skipc (chunk, p); 173 while (p < chunk->end) { 174 if (*p == '"' && *(p - 1) != '\\') { 175 /* begin or end double-quoted string */ 176 quoted = !quoted; 177 ucl_chunk_skipc (chunk, p); 178 } 179 else if (quoted) { 180 /* quoted character */ 181 ucl_chunk_skipc (chunk, p); 182 } 183 else if (chunk->remain >= 2 && *p == '*' && p[1] == '/') { 184 /* end of comment */ 185 ucl_chunk_skipc (chunk, p); 186 ucl_chunk_skipc (chunk, p); 187 comments_nested --; 188 if (comments_nested == 0) { 189 if (parser->flags & UCL_PARSER_SAVE_COMMENTS) { 190 ucl_save_comment (parser, beg, p - beg + 1); 191 beg = NULL; 192 } 193 goto start; 194 } 195 } 196 else if (chunk->remain >= 2 && *p == '/' && p[1] == '*') { 197 /* start of nested comment */ 198 comments_nested ++; 199 ucl_chunk_skipc (chunk, p); 200 ucl_chunk_skipc (chunk, p); 201 } 202 else { 203 /* anything else */ 204 ucl_chunk_skipc (chunk, p); 205 } 206 } 207 if (comments_nested != 0) { 208 ucl_set_err(parser, UCL_ENESTED, 209 "unfinished multiline comment", &parser->err); 210 return false; 211 } 212 } 213 214 if (beg && p > beg && (parser->flags & UCL_PARSER_SAVE_COMMENTS)) { 215 ucl_save_comment(parser, beg, p - beg); 216 } 217 218 return true; 219 } 220 221 /** 222 * Return multiplier for a character 223 * @param c multiplier character 224 * @param is_bytes if true use 1024 multiplier 225 * @return multiplier 226 */ 227 static inline unsigned long 228 ucl_lex_num_multiplier(const unsigned char c, bool is_bytes) 229 { 230 const struct { 231 char c; 232 long mult_normal; 233 long mult_bytes; 234 } multipliers[] = { 235 {'m', 1000 * 1000, 1024 * 1024}, 236 {'k', 1000, 1024}, 237 {'g', 1000 * 1000 * 1000, 1024 * 1024 * 1024}}; 238 int i; 239 240 for (i = 0; i < 3; i++) { 241 if (tolower(c) == multipliers[i].c) { 242 if (is_bytes) { 243 return multipliers[i].mult_bytes; 244 } 245 return multipliers[i].mult_normal; 246 } 247 } 248 249 return 1; 250 } 251 252 253 /** 254 * Return multiplier for time scaling 255 * @param c 256 * @return 257 */ 258 static inline double 259 ucl_lex_time_multiplier(const unsigned char c) 260 { 261 const struct { 262 char c; 263 double mult; 264 } multipliers[] = { 265 {'m', 60}, 266 {'h', 60 * 60}, 267 {'d', 60 * 60 * 24}, 268 {'w', 60 * 60 * 24 * 7}, 269 {'y', 60 * 60 * 24 * 365}}; 270 int i; 271 272 for (i = 0; i < 5; i++) { 273 if (tolower(c) == multipliers[i].c) { 274 return multipliers[i].mult; 275 } 276 } 277 278 return 1; 279 } 280 281 /** 282 * Return true if a character is a end of an atom 283 * @param c 284 * @return 285 */ 286 static inline bool 287 ucl_lex_is_atom_end(const unsigned char c) 288 { 289 return ucl_test_character(c, UCL_CHARACTER_VALUE_END); 290 } 291 292 static inline bool 293 ucl_lex_is_comment(const unsigned char c1, const unsigned char c2) 294 { 295 if (c1 == '/') { 296 if (c2 == '*') { 297 return true; 298 } 299 } 300 else if (c1 == '#') { 301 return true; 302 } 303 return false; 304 } 305 306 /** 307 * Check variable found 308 * @param parser 309 * @param ptr 310 * @param remain 311 * @param out_len 312 * @param strict 313 * @param found 314 * @return 315 */ 316 static inline const char * 317 ucl_check_variable_safe(struct ucl_parser *parser, const char *ptr, size_t remain, 318 size_t *out_len, bool strict, bool *found) 319 { 320 struct ucl_variable *var; 321 unsigned char *dst; 322 size_t dstlen; 323 bool need_free = false; 324 325 LL_FOREACH(parser->variables, var) 326 { 327 if (strict) { 328 if (remain == var->var_len) { 329 if (memcmp(ptr, var->var, var->var_len) == 0) { 330 *out_len += var->value_len; 331 *found = true; 332 return (ptr + var->var_len); 333 } 334 } 335 } 336 else { 337 if (remain >= var->var_len) { 338 if (memcmp(ptr, var->var, var->var_len) == 0) { 339 *out_len += var->value_len; 340 *found = true; 341 return (ptr + var->var_len); 342 } 343 } 344 } 345 } 346 347 /* XXX: can only handle ${VAR} */ 348 if (!(*found) && parser->var_handler != NULL && strict) { 349 /* Call generic handler */ 350 if (parser->var_handler(ptr, remain, &dst, &dstlen, &need_free, 351 parser->var_data)) { 352 *found = true; 353 *out_len = dstlen; 354 355 if (need_free) { 356 free(dst); 357 } 358 return (ptr + remain); 359 } 360 } 361 362 return ptr; 363 } 364 365 /** 366 * Check for a variable in a given string 367 * @param parser 368 * @param ptr 369 * @param remain 370 * @param out_len 371 * @param vars_found 372 * @return 373 */ 374 static const char * 375 ucl_check_variable(struct ucl_parser *parser, const char *ptr, 376 size_t remain, size_t *out_len, bool *vars_found) 377 { 378 const char *p, *end, *ret = ptr; 379 bool found = false; 380 381 if (*ptr == '{') { 382 /* We need to match the variable enclosed in braces */ 383 p = ptr + 1; 384 end = ptr + remain; 385 while (p < end) { 386 if (*p == '}') { 387 ret = ucl_check_variable_safe(parser, ptr + 1, p - ptr - 1, 388 out_len, true, &found); 389 if (found) { 390 /* {} must be excluded actually */ 391 ret++; 392 if (!*vars_found) { 393 *vars_found = true; 394 } 395 } 396 else { 397 *out_len += 2; 398 } 399 break; 400 } 401 p++; 402 } 403 if (p == end) { 404 (*out_len)++; 405 } 406 } 407 else if (*ptr != '$') { 408 /* Not count escaped dollar sign */ 409 ret = ucl_check_variable_safe(parser, ptr, remain, out_len, false, &found); 410 if (found && !*vars_found) { 411 *vars_found = true; 412 } 413 if (!found) { 414 (*out_len)++; 415 } 416 } 417 else { 418 ret++; 419 (*out_len)++; 420 } 421 422 return ret; 423 } 424 425 /** 426 * Expand a single variable 427 * @param parser 428 * @param ptr 429 * @param in_len 430 * @param dest 431 * @param out_len 432 * @return 433 */ 434 static const char * 435 ucl_expand_single_variable(struct ucl_parser *parser, const char *ptr, 436 size_t in_len, unsigned char **dest, size_t out_len) 437 { 438 unsigned char *d = *dest, *dst; 439 const char *p = ptr + 1, *ret; 440 struct ucl_variable *var; 441 size_t dstlen; 442 bool need_free = false; 443 bool found = false; 444 bool strict = false; 445 446 ret = ptr + 1; 447 /* For the $ sign */ 448 in_len--; 449 450 if (*p == '$') { 451 *d++ = *p++; 452 *dest = d; 453 return p; 454 } 455 else if (*p == '{') { 456 p++; 457 in_len--; 458 strict = true; 459 ret += 2; 460 } 461 462 LL_FOREACH(parser->variables, var) 463 { 464 if (out_len >= var->value_len && in_len >= (var->var_len + (strict ? 1 : 0))) { 465 if (memcmp(p, var->var, var->var_len) == 0) { 466 if (!strict || p[var->var_len] == '}') { 467 memcpy(d, var->value, var->value_len); 468 ret += var->var_len; 469 d += var->value_len; 470 found = true; 471 break; 472 } 473 } 474 } 475 } 476 477 if (!found) { 478 if (strict && parser->var_handler != NULL) { 479 dstlen = out_len; 480 481 if (parser->var_handler(p, in_len, &dst, &dstlen, &need_free, 482 parser->var_data)) { 483 if (dstlen > out_len) { 484 /* We do not have enough space! */ 485 if (need_free) { 486 free(dst); 487 } 488 } 489 else { 490 memcpy(d, dst, dstlen); 491 ret += in_len; 492 d += dstlen; 493 found = true; 494 495 if (need_free) { 496 free(dst); 497 } 498 } 499 } 500 } 501 502 /* Leave variable as is, in this case we use dest */ 503 if (!found) { 504 if (strict && out_len >= 2) { 505 /* Copy '${' */ 506 memcpy(d, ptr, 2); 507 d += 2; 508 ret--; 509 } 510 else { 511 memcpy(d, ptr, 1); 512 d++; 513 } 514 } 515 } 516 517 *dest = d; 518 return ret; 519 } 520 521 /** 522 * Expand variables in string 523 * @param parser 524 * @param dst 525 * @param src 526 * @param in_len 527 * @return 528 */ 529 static ssize_t 530 ucl_expand_variable(struct ucl_parser *parser, unsigned char **dst, 531 const char *src, size_t in_len) 532 { 533 const char *p, *end = src + in_len; 534 unsigned char *d, *d_end; 535 size_t out_len = 0; 536 bool vars_found = false; 537 538 if (parser->flags & UCL_PARSER_DISABLE_MACRO) { 539 *dst = NULL; 540 return in_len; 541 } 542 543 p = src; 544 while (p != end) { 545 if (*p == '$' && p + 1 != end) { 546 p = ucl_check_variable(parser, p + 1, end - p - 1, &out_len, &vars_found); 547 } 548 else { 549 p++; 550 out_len++; 551 } 552 } 553 554 if (!vars_found) { 555 /* Trivial case */ 556 *dst = NULL; 557 return in_len; 558 } 559 560 *dst = UCL_ALLOC(out_len + 1); 561 if (*dst == NULL) { 562 return in_len; 563 } 564 565 d = *dst; 566 d_end = d + out_len; 567 p = src; 568 while (p != end && d != d_end) { 569 if (*p == '$' && p + 1 != end) { 570 p = ucl_expand_single_variable(parser, p, end - p, &d, d_end - d); 571 } 572 else { 573 *d++ = *p++; 574 } 575 } 576 577 *d = '\0'; 578 579 return out_len; 580 } 581 582 /** 583 * Store or copy pointer to the trash stack 584 * @param parser parser object 585 * @param src src string 586 * @param dst destination buffer (trash stack pointer) 587 * @param dst_const const destination pointer (e.g. value of object) 588 * @param in_len input length 589 * @param need_unescape need to unescape source (and copy it) 590 * @param need_lowercase need to lowercase value (and copy) 591 * @param need_expand need to expand variables (and copy as well) 592 * @param unescape_squote unescape single quoted string 593 * @return output length (excluding \0 symbol) 594 */ 595 static inline ssize_t 596 ucl_copy_or_store_ptr(struct ucl_parser *parser, 597 const unsigned char *src, unsigned char **dst, 598 const char **dst_const, size_t in_len, 599 bool need_unescape, bool need_lowercase, bool need_expand, 600 bool unescape_squote) 601 { 602 ssize_t ret = -1, tret; 603 unsigned char *tmp; 604 605 if (need_unescape || need_lowercase || 606 (need_expand && parser->variables != NULL) || 607 !(parser->flags & UCL_PARSER_ZEROCOPY)) { 608 /* Copy string */ 609 *dst = UCL_ALLOC(in_len + 1); 610 if (*dst == NULL) { 611 ucl_set_err(parser, UCL_EINTERNAL, "cannot allocate memory for a string", 612 &parser->err); 613 return false; 614 } 615 if (need_lowercase) { 616 ret = ucl_strlcpy_tolower(*dst, src, in_len + 1); 617 } 618 else { 619 ret = ucl_strlcpy_unsafe(*dst, src, in_len + 1); 620 } 621 622 if (need_unescape) { 623 if (!unescape_squote) { 624 ret = ucl_unescape_json_string(*dst, ret); 625 } 626 else { 627 ret = ucl_unescape_squoted_string(*dst, ret); 628 } 629 } 630 631 if (need_expand) { 632 tmp = *dst; 633 tret = ret; 634 ret = ucl_expand_variable(parser, dst, tmp, ret); 635 if (*dst == NULL) { 636 /* Nothing to expand */ 637 *dst = tmp; 638 ret = tret; 639 } 640 else { 641 /* Free unexpanded value */ 642 UCL_FREE(in_len + 1, tmp); 643 } 644 } 645 *dst_const = *dst; 646 } 647 else { 648 *dst_const = src; 649 ret = in_len; 650 } 651 652 return ret; 653 } 654 655 /** 656 * Create and append an object at the specified level 657 * @param parser 658 * @param is_array 659 * @param level 660 * @return 661 */ 662 static inline ucl_object_t * 663 ucl_parser_add_container(ucl_object_t *obj, struct ucl_parser *parser, 664 bool is_array, uint32_t level, bool has_obrace) 665 { 666 struct ucl_stack *st; 667 ucl_object_t *nobj; 668 669 if (obj == NULL) { 670 nobj = ucl_object_new_full(is_array ? UCL_ARRAY : UCL_OBJECT, parser->chunks->priority); 671 if (nobj == NULL) { 672 goto enomem0; 673 } 674 } 675 else { 676 if (obj->type == (is_array ? UCL_OBJECT : UCL_ARRAY)) { 677 /* Bad combination for merge: array and object */ 678 ucl_set_err(parser, UCL_EMERGE, 679 "cannot merge an object with an array", 680 &parser->err); 681 682 return NULL; 683 } 684 nobj = obj; 685 nobj->type = is_array ? UCL_ARRAY : UCL_OBJECT; 686 } 687 688 if (!is_array) { 689 if (nobj->value.ov == NULL) { 690 nobj->value.ov = ucl_hash_create(parser->flags & UCL_PARSER_KEY_LOWERCASE); 691 if (nobj->value.ov == NULL) { 692 goto enomem1; 693 } 694 } 695 parser->state = UCL_STATE_KEY; 696 } 697 else { 698 parser->state = UCL_STATE_VALUE; 699 } 700 701 st = UCL_ALLOC(sizeof(struct ucl_stack)); 702 703 if (st == NULL) { 704 goto enomem1; 705 } 706 707 st->obj = nobj; 708 709 if (level >= UINT16_MAX) { 710 ucl_set_err(parser, UCL_ENESTED, 711 "objects are nesting too deep (over 65535 limit)", 712 &parser->err); 713 if (nobj != obj) { 714 ucl_object_unref(obj); 715 } 716 717 UCL_FREE(sizeof(struct ucl_stack), st); 718 719 return NULL; 720 } 721 722 723 st->e.params.level = level; 724 st->e.params.line = parser->chunks->line; 725 st->chunk = parser->chunks; 726 727 if (has_obrace) { 728 st->e.params.flags = UCL_STACK_HAS_OBRACE; 729 } 730 else { 731 st->e.params.flags = 0; 732 } 733 734 LL_PREPEND(parser->stack, st); 735 parser->cur_obj = nobj; 736 737 return nobj; 738 enomem1: 739 if (nobj != obj) 740 ucl_object_unref(nobj); 741 enomem0: 742 ucl_set_err(parser, UCL_EINTERNAL, "cannot allocate memory for an object", 743 &parser->err); 744 return NULL; 745 } 746 747 int ucl_maybe_parse_number(ucl_object_t *obj, 748 const char *start, const char *end, const char **pos, 749 bool allow_double, bool number_bytes, bool allow_time) 750 { 751 const char *p = start, *c = start; 752 char *endptr; 753 bool got_dot = false, got_exp = false, need_double = false, 754 is_time = false, valid_start = false, is_hex = false; 755 int is_neg = 0; 756 double dv = 0; 757 int64_t lv = 0; 758 759 if (*p == '-') { 760 is_neg = 1; 761 c++; 762 p++; 763 } 764 while (p < end) { 765 if (is_hex && isxdigit(*p)) { 766 p++; 767 } 768 else if (isdigit(*p)) { 769 valid_start = true; 770 p++; 771 } 772 else if (!is_hex && (*p == 'x' || *p == 'X')) { 773 is_hex = true; 774 allow_double = false; 775 c = p + 1; 776 p++; 777 } 778 else if (allow_double) { 779 if (p == c) { 780 /* Empty digits sequence, not a number */ 781 *pos = start; 782 return EINVAL; 783 } 784 else if (*p == '.') { 785 if (got_dot) { 786 /* Double dots, not a number */ 787 *pos = start; 788 return EINVAL; 789 } 790 else { 791 got_dot = true; 792 need_double = true; 793 p++; 794 } 795 } 796 else if (*p == 'e' || *p == 'E') { 797 if (got_exp) { 798 /* Double exp, not a number */ 799 *pos = start; 800 return EINVAL; 801 } 802 else { 803 got_exp = true; 804 need_double = true; 805 p++; 806 if (p >= end) { 807 *pos = start; 808 return EINVAL; 809 } 810 if (!isdigit(*p) && *p != '+' && *p != '-') { 811 /* Wrong exponent sign */ 812 *pos = start; 813 return EINVAL; 814 } 815 else { 816 p++; 817 } 818 } 819 } 820 else { 821 /* Got the end of the number, need to check */ 822 break; 823 } 824 } 825 else if (!allow_double && *p == '.') { 826 /* Unexpected dot */ 827 *pos = start; 828 return EINVAL; 829 } 830 else { 831 break; 832 } 833 } 834 835 if (!valid_start || p == c) { 836 *pos = start; 837 return EINVAL; 838 } 839 840 char numbuf[128]; 841 842 if ((size_t) (p - c + 1) >= sizeof(numbuf)) { 843 *pos = start; 844 return EINVAL; 845 } 846 847 if (is_neg) { 848 numbuf[0] = '-'; 849 ucl_strlcpy(&numbuf[1], c, p - c + 1); 850 } 851 else { 852 ucl_strlcpy(numbuf, c, p - c + 1); 853 } 854 855 errno = 0; 856 if (need_double) { 857 dv = strtod(numbuf, &endptr); 858 } 859 else { 860 if (is_hex) { 861 lv = strtoimax(numbuf, &endptr, 16); 862 } 863 else { 864 lv = strtoimax(numbuf, &endptr, 10); 865 } 866 } 867 if (errno == ERANGE) { 868 *pos = start; 869 return ERANGE; 870 } 871 872 /* Now check endptr and move it from numbuf to the real ending */ 873 if (endptr != NULL) { 874 long shift = endptr - numbuf - is_neg; 875 endptr = (char *) c + shift; 876 } 877 if (endptr >= end) { 878 p = end; 879 goto set_obj; 880 } 881 if (endptr == NULL || ucl_lex_is_atom_end(*endptr) || *endptr == '\0') { 882 p = endptr; 883 goto set_obj; 884 } 885 886 if (endptr < end && endptr != start) { 887 p = endptr; 888 switch (*p) { 889 case 'm': 890 case 'M': 891 case 'g': 892 case 'G': 893 case 'k': 894 case 'K': 895 if (end - p >= 2) { 896 if (p[1] == 's' || p[1] == 'S') { 897 /* Milliseconds */ 898 if (!need_double) { 899 need_double = true; 900 dv = lv; 901 } 902 is_time = true; 903 if (p[0] == 'm' || p[0] == 'M') { 904 dv /= 1000.; 905 } 906 else { 907 dv *= ucl_lex_num_multiplier(*p, false); 908 } 909 p += 2; 910 if (end - p > 0 && !ucl_lex_is_atom_end(*p)) { 911 *pos = start; 912 return EINVAL; 913 } 914 goto set_obj; 915 } 916 else if (number_bytes || (p[1] == 'b' || p[1] == 'B')) { 917 /* Bytes */ 918 if (need_double) { 919 need_double = false; 920 lv = dv; 921 } 922 lv *= ucl_lex_num_multiplier(*p, true); 923 p += 2; 924 if (end - p > 0 && !ucl_lex_is_atom_end(*p)) { 925 *pos = start; 926 return EINVAL; 927 } 928 goto set_obj; 929 } 930 else if (ucl_lex_is_atom_end(p[1])) { 931 if (need_double) { 932 dv *= ucl_lex_num_multiplier(*p, false); 933 } 934 else { 935 lv *= ucl_lex_num_multiplier(*p, number_bytes); 936 } 937 p++; 938 goto set_obj; 939 } 940 else if (allow_time && end - p >= 3) { 941 if (tolower(p[0]) == 'm' && 942 tolower(p[1]) == 'i' && 943 tolower(p[2]) == 'n') { 944 /* Minutes */ 945 if (!need_double) { 946 need_double = true; 947 dv = lv; 948 } 949 is_time = true; 950 dv *= 60.; 951 p += 3; 952 if (end - p > 0 && !ucl_lex_is_atom_end(*p)) { 953 *pos = start; 954 return EINVAL; 955 } 956 goto set_obj; 957 } 958 } 959 } 960 else { 961 if (need_double) { 962 dv *= ucl_lex_num_multiplier(*p, false); 963 } 964 else { 965 lv *= ucl_lex_num_multiplier(*p, number_bytes); 966 } 967 p++; 968 if (end - p > 0 && !ucl_lex_is_atom_end(*p)) { 969 *pos = start; 970 return EINVAL; 971 } 972 goto set_obj; 973 } 974 break; 975 case 'S': 976 case 's': 977 if (allow_time && 978 (p == end - 1 || ucl_lex_is_atom_end(p[1]))) { 979 if (!need_double) { 980 need_double = true; 981 dv = lv; 982 } 983 p++; 984 is_time = true; 985 goto set_obj; 986 } 987 break; 988 case 'h': 989 case 'H': 990 case 'd': 991 case 'D': 992 case 'w': 993 case 'W': 994 case 'Y': 995 case 'y': 996 if (allow_time && 997 (p == end - 1 || ucl_lex_is_atom_end(p[1]))) { 998 if (!need_double) { 999 need_double = true; 1000 dv = lv; 1001 } 1002 is_time = true; 1003 dv *= ucl_lex_time_multiplier(*p); 1004 p++; 1005 goto set_obj; 1006 } 1007 break; 1008 case '\t': 1009 case ' ': 1010 while (p < end && ucl_test_character(*p, UCL_CHARACTER_WHITESPACE)) { 1011 p++; 1012 } 1013 if (p == end || ucl_lex_is_atom_end(*p)) 1014 goto set_obj; 1015 break; 1016 } 1017 } 1018 else if (endptr == end) { 1019 /* Just a number at the end of chunk */ 1020 p = end; 1021 goto set_obj; 1022 } 1023 1024 *pos = c; 1025 return EINVAL; 1026 1027 set_obj: 1028 if (obj != NULL) { 1029 if (allow_double && (need_double || is_time)) { 1030 if (!is_time) { 1031 obj->type = UCL_FLOAT; 1032 } 1033 else { 1034 obj->type = UCL_TIME; 1035 } 1036 obj->value.dv = dv; 1037 } 1038 else { 1039 obj->type = UCL_INT; 1040 obj->value.iv = lv; 1041 } 1042 } 1043 *pos = p; 1044 return 0; 1045 } 1046 1047 /** 1048 * Parse possible number 1049 * @param parser 1050 * @param chunk 1051 * @param obj 1052 * @return true if a number has been parsed 1053 */ 1054 static bool 1055 ucl_lex_number(struct ucl_parser *parser, 1056 struct ucl_chunk *chunk, ucl_object_t *obj) 1057 { 1058 const unsigned char *pos; 1059 int ret; 1060 1061 ret = ucl_maybe_parse_number(obj, chunk->pos, chunk->end, (const char **) &pos, 1062 true, false, ((parser->flags & UCL_PARSER_NO_TIME) == 0)); 1063 1064 if (ret == 0) { 1065 chunk->remain -= pos - chunk->pos; 1066 chunk->column += pos - chunk->pos; 1067 chunk->pos = pos; 1068 return true; 1069 } 1070 else if (ret == ERANGE) { 1071 ucl_set_err(parser, UCL_ESYNTAX, "numeric value out of range", 1072 &parser->err); 1073 } 1074 1075 return false; 1076 } 1077 1078 /** 1079 * Parse quoted string with possible escapes 1080 * @param parser 1081 * @param chunk 1082 * @param need_unescape 1083 * @param ucl_escape 1084 * @param var_expand 1085 * @return true if a string has been parsed 1086 */ 1087 static bool 1088 ucl_lex_json_string(struct ucl_parser *parser, 1089 struct ucl_chunk *chunk, 1090 bool *need_unescape, 1091 bool *ucl_escape, 1092 bool *var_expand) 1093 { 1094 const unsigned char *p = chunk->pos; 1095 unsigned char c; 1096 int i; 1097 1098 while (p < chunk->end) { 1099 c = *p; 1100 if (c < 0x1F) { 1101 /* Unmasked control character */ 1102 if (c == '\n') { 1103 ucl_set_err(parser, UCL_ESYNTAX, "unexpected newline", 1104 &parser->err); 1105 } 1106 else { 1107 ucl_set_err(parser, UCL_ESYNTAX, "unexpected control character", 1108 &parser->err); 1109 } 1110 return false; 1111 } 1112 else if (c == '\\') { 1113 ucl_chunk_skipc(chunk, p); 1114 if (p >= chunk->end) { 1115 ucl_set_err(parser, UCL_ESYNTAX, "unfinished escape character", 1116 &parser->err); 1117 return false; 1118 } 1119 c = *p; 1120 if (ucl_test_character(c, UCL_CHARACTER_ESCAPE)) { 1121 if (c == 'u') { 1122 ucl_chunk_skipc(chunk, p); 1123 for (i = 0; i < 4 && p < chunk->end; i++) { 1124 if (!isxdigit(*p)) { 1125 ucl_set_err(parser, UCL_ESYNTAX, "invalid utf escape", 1126 &parser->err); 1127 return false; 1128 } 1129 ucl_chunk_skipc(chunk, p); 1130 } 1131 if (p >= chunk->end) { 1132 ucl_set_err(parser, UCL_ESYNTAX, 1133 "unfinished escape character", 1134 &parser->err); 1135 return false; 1136 } 1137 } 1138 else { 1139 ucl_chunk_skipc(chunk, p); 1140 } 1141 } 1142 *need_unescape = true; 1143 *ucl_escape = true; 1144 continue; 1145 } 1146 else if (c == '"') { 1147 ucl_chunk_skipc(chunk, p); 1148 return true; 1149 } 1150 else if (ucl_test_character(c, UCL_CHARACTER_UCL_UNSAFE)) { 1151 *ucl_escape = true; 1152 } 1153 else if (c == '$') { 1154 *var_expand = true; 1155 } 1156 ucl_chunk_skipc(chunk, p); 1157 } 1158 1159 ucl_set_err(parser, UCL_ESYNTAX, 1160 "no quote at the end of json string", 1161 &parser->err); 1162 return false; 1163 } 1164 1165 /** 1166 * Process single quoted string 1167 * @param parser 1168 * @param chunk 1169 * @param need_unescape 1170 * @return 1171 */ 1172 static bool 1173 ucl_lex_squoted_string(struct ucl_parser *parser, 1174 struct ucl_chunk *chunk, bool *need_unescape) 1175 { 1176 const unsigned char *p = chunk->pos; 1177 unsigned char c; 1178 1179 while (p < chunk->end) { 1180 c = *p; 1181 if (c == '\\') { 1182 ucl_chunk_skipc(chunk, p); 1183 1184 if (p >= chunk->end) { 1185 ucl_set_err(parser, UCL_ESYNTAX, 1186 "unfinished escape character", 1187 &parser->err); 1188 return false; 1189 } 1190 else { 1191 ucl_chunk_skipc(chunk, p); 1192 } 1193 1194 *need_unescape = true; 1195 continue; 1196 } 1197 else if (c == '\'') { 1198 ucl_chunk_skipc(chunk, p); 1199 return true; 1200 } 1201 1202 ucl_chunk_skipc(chunk, p); 1203 } 1204 1205 ucl_set_err(parser, UCL_ESYNTAX, 1206 "no quote at the end of single quoted string", 1207 &parser->err); 1208 return false; 1209 } 1210 1211 static void 1212 ucl_parser_append_elt(struct ucl_parser *parser, ucl_hash_t *cont, 1213 ucl_object_t *top, 1214 ucl_object_t *elt) 1215 { 1216 ucl_object_t *nobj; 1217 1218 if ((parser->flags & UCL_PARSER_NO_IMPLICIT_ARRAYS) == 0) { 1219 /* Implicit array */ 1220 top->flags |= UCL_OBJECT_MULTIVALUE; 1221 DL_APPEND(top, elt); 1222 parser->stack->obj->len++; 1223 } 1224 else { 1225 if ((top->flags & UCL_OBJECT_MULTIVALUE) != 0) { 1226 /* Just add to the explicit array */ 1227 ucl_array_append(top, elt); 1228 } 1229 else { 1230 /* Convert to an array */ 1231 nobj = ucl_object_typed_new(UCL_ARRAY); 1232 nobj->key = top->key; 1233 nobj->keylen = top->keylen; 1234 nobj->flags |= UCL_OBJECT_MULTIVALUE; 1235 ucl_array_append(nobj, top); 1236 ucl_array_append(nobj, elt); 1237 ucl_hash_replace(cont, top, nobj); 1238 } 1239 } 1240 } 1241 1242 bool ucl_parser_process_object_element(struct ucl_parser *parser, ucl_object_t *nobj) 1243 { 1244 ucl_hash_t *container; 1245 ucl_object_t *tobj = NULL, *cur; 1246 char errmsg[256]; 1247 1248 container = parser->stack->obj->value.ov; 1249 1250 DL_FOREACH(parser->stack->obj, cur) 1251 { 1252 if (cur->type == UCL_OBJECT) { 1253 tobj = __DECONST(ucl_object_t *, ucl_hash_search_obj(cur->value.ov, nobj)); 1254 1255 if (tobj != NULL) { 1256 /* 1257 * Check if we have found an object in the same container. 1258 * If not, we should probably ignore it as we cannot replace it 1259 * effectively and we definitely should not unref it. 1260 */ 1261 if (cur->value.ov != container) { 1262 tobj = NULL; 1263 continue; 1264 } 1265 break; 1266 } 1267 } 1268 } 1269 1270 1271 if (tobj == NULL) { 1272 container = ucl_hash_insert_object(container, nobj, 1273 parser->flags & UCL_PARSER_KEY_LOWERCASE); 1274 if (container == NULL) { 1275 return false; 1276 } 1277 nobj->prev = nobj; 1278 nobj->next = NULL; 1279 parser->stack->obj->len++; 1280 } 1281 else { 1282 unsigned priold = ucl_object_get_priority(tobj), 1283 prinew = ucl_object_get_priority(nobj); 1284 switch (parser->chunks->strategy) { 1285 1286 case UCL_DUPLICATE_APPEND: 1287 /* 1288 * The logic here is the following: 1289 * 1290 * - if we have two objects with the same priority, then we form an 1291 * implicit or explicit array 1292 * - if a new object has bigger priority, then we overwrite an old one 1293 * - if a new object has lower priority, then we ignore it 1294 */ 1295 /* Special case for inherited objects */ 1296 if (tobj->flags & UCL_OBJECT_INHERITED) { 1297 prinew = priold + 1; 1298 } 1299 1300 if (priold == prinew) { 1301 ucl_parser_append_elt(parser, container, tobj, nobj); 1302 } 1303 else if (priold > prinew) { 1304 /* 1305 * We add this new object to a list of trash objects just to ensure 1306 * that it won't come to any real object 1307 * XXX: rather inefficient approach 1308 */ 1309 DL_APPEND(parser->trash_objs, nobj); 1310 } 1311 else { 1312 ucl_hash_replace(container, tobj, nobj); 1313 ucl_object_unref(tobj); 1314 } 1315 1316 break; 1317 1318 case UCL_DUPLICATE_REWRITE: 1319 /* We just rewrite old values regardless of priority */ 1320 ucl_hash_replace(container, tobj, nobj); 1321 ucl_object_unref(tobj); 1322 1323 break; 1324 1325 case UCL_DUPLICATE_ERROR: 1326 snprintf(errmsg, sizeof(errmsg), 1327 "duplicate element for key '%s' found", 1328 nobj->key); 1329 ucl_set_err(parser, UCL_EMERGE, errmsg, &parser->err); 1330 return false; 1331 1332 case UCL_DUPLICATE_MERGE: 1333 /* 1334 * Here we do have some old object so we just push it on top of objects stack 1335 * Check priority and then perform the merge on the remaining objects 1336 */ 1337 if (tobj->type == UCL_OBJECT || tobj->type == UCL_ARRAY) { 1338 ucl_object_unref(nobj); 1339 nobj = tobj; 1340 } 1341 else if (priold == prinew) { 1342 ucl_parser_append_elt(parser, container, tobj, nobj); 1343 } 1344 else if (priold > prinew) { 1345 /* 1346 * We add this new object to a list of trash objects just to ensure 1347 * that it won't come to any real object 1348 * XXX: rather inefficient approach 1349 */ 1350 DL_APPEND(parser->trash_objs, nobj); 1351 } 1352 else { 1353 ucl_hash_replace(container, tobj, nobj); 1354 ucl_object_unref(tobj); 1355 } 1356 break; 1357 } 1358 } 1359 1360 parser->stack->obj->value.ov = container; 1361 parser->cur_obj = nobj; 1362 ucl_attach_comment(parser, nobj, false); 1363 1364 return true; 1365 } 1366 1367 /** 1368 * Parse a key in an object 1369 * @param parser 1370 * @param chunk 1371 * @param next_key 1372 * @param end_of_object 1373 * @return true if a key has been parsed 1374 */ 1375 static bool 1376 ucl_parse_key(struct ucl_parser *parser, struct ucl_chunk *chunk, 1377 bool *next_key, bool *end_of_object, bool *got_content) 1378 { 1379 const unsigned char *p, *c = NULL, *end, *t; 1380 const char *key = NULL; 1381 bool got_quote = false, got_eq = false, got_semicolon = false, 1382 need_unescape = false, ucl_escape = false, var_expand = false, 1383 got_sep = false; 1384 ucl_object_t *nobj; 1385 ssize_t keylen; 1386 1387 p = chunk->pos; 1388 1389 if (*p == '.' && !(parser->flags & UCL_PARSER_DISABLE_MACRO)) { 1390 ucl_chunk_skipc(chunk, p); 1391 parser->prev_state = parser->state; 1392 parser->state = UCL_STATE_MACRO_NAME; 1393 *end_of_object = false; 1394 return true; 1395 } 1396 while (p < chunk->end) { 1397 /* 1398 * A key must start with alpha, number, '/' or '_' and end with space character 1399 */ 1400 if (c == NULL) { 1401 if (chunk->remain >= 2 && ucl_lex_is_comment(p[0], p[1])) { 1402 if (!ucl_skip_comments(parser)) { 1403 return false; 1404 } 1405 p = chunk->pos; 1406 } 1407 else if (ucl_test_character(*p, UCL_CHARACTER_WHITESPACE_UNSAFE)) { 1408 ucl_chunk_skipc(chunk, p); 1409 } 1410 else if (ucl_test_character(*p, UCL_CHARACTER_KEY_START)) { 1411 /* The first symbol */ 1412 c = p; 1413 ucl_chunk_skipc(chunk, p); 1414 *got_content = true; 1415 } 1416 else if (*p == '"') { 1417 /* JSON style key */ 1418 c = p + 1; 1419 got_quote = true; 1420 *got_content = true; 1421 ucl_chunk_skipc(chunk, p); 1422 } 1423 else if (*p == '}') { 1424 /* We have actually end of an object */ 1425 *end_of_object = true; 1426 return true; 1427 } 1428 else if (*p == '.' && !(parser->flags & UCL_PARSER_DISABLE_MACRO)) { 1429 ucl_chunk_skipc(chunk, p); 1430 parser->prev_state = parser->state; 1431 parser->state = UCL_STATE_MACRO_NAME; 1432 return true; 1433 } 1434 else { 1435 /* Invalid identifier */ 1436 ucl_set_err(parser, UCL_ESYNTAX, "key must begin with a letter", 1437 &parser->err); 1438 return false; 1439 } 1440 } 1441 else { 1442 /* Parse the body of a key */ 1443 if (!got_quote) { 1444 if (ucl_test_character(*p, UCL_CHARACTER_KEY)) { 1445 *got_content = true; 1446 ucl_chunk_skipc(chunk, p); 1447 } 1448 else if (ucl_test_character(*p, UCL_CHARACTER_KEY_SEP)) { 1449 end = p; 1450 break; 1451 } 1452 else { 1453 ucl_set_err(parser, UCL_ESYNTAX, "invalid character in a key", 1454 &parser->err); 1455 return false; 1456 } 1457 } 1458 else { 1459 /* We need to parse json like quoted string */ 1460 if (!ucl_lex_json_string(parser, chunk, &need_unescape, &ucl_escape, &var_expand)) { 1461 return false; 1462 } 1463 /* Always escape keys obtained via json */ 1464 end = chunk->pos - 1; 1465 p = chunk->pos; 1466 break; 1467 } 1468 } 1469 } 1470 1471 if (p >= chunk->end && *got_content) { 1472 ucl_set_err(parser, UCL_ESYNTAX, "unfinished key", &parser->err); 1473 return false; 1474 } 1475 else if (!*got_content) { 1476 return true; 1477 } 1478 *end_of_object = false; 1479 /* We are now at the end of the key, need to parse the rest */ 1480 while (p < chunk->end) { 1481 if (ucl_test_character(*p, UCL_CHARACTER_WHITESPACE)) { 1482 ucl_chunk_skipc(chunk, p); 1483 } 1484 else if (*p == '=') { 1485 if (!got_eq && !got_semicolon) { 1486 ucl_chunk_skipc(chunk, p); 1487 got_eq = true; 1488 } 1489 else { 1490 ucl_set_err(parser, UCL_ESYNTAX, "unexpected '=' character", 1491 &parser->err); 1492 return false; 1493 } 1494 } 1495 else if (*p == ':') { 1496 if (!got_eq && !got_semicolon) { 1497 ucl_chunk_skipc(chunk, p); 1498 got_semicolon = true; 1499 } 1500 else { 1501 ucl_set_err(parser, UCL_ESYNTAX, "unexpected ':' character", 1502 &parser->err); 1503 return false; 1504 } 1505 } 1506 else if (chunk->remain >= 2 && ucl_lex_is_comment(p[0], p[1])) { 1507 /* Check for comment */ 1508 if (!ucl_skip_comments(parser)) { 1509 return false; 1510 } 1511 p = chunk->pos; 1512 } 1513 else { 1514 /* Start value */ 1515 break; 1516 } 1517 } 1518 1519 if (p >= chunk->end && got_content) { 1520 ucl_set_err(parser, UCL_ESYNTAX, "unfinished key", &parser->err); 1521 return false; 1522 } 1523 1524 got_sep = got_semicolon || got_eq; 1525 1526 if (!got_sep) { 1527 /* 1528 * Maybe we have more keys nested, so search for termination character. 1529 * Possible choices: 1530 * 1) key1 key2 ... keyN [:=] value <- we treat that as error 1531 * 2) key1 ... keyN {} or [] <- we treat that as nested objects 1532 * 3) key1 value[;,\n] <- we treat that as linear object 1533 */ 1534 t = p; 1535 *next_key = false; 1536 while (ucl_test_character(*t, UCL_CHARACTER_WHITESPACE)) { 1537 t++; 1538 } 1539 /* Check first non-space character after a key */ 1540 if (*t != '{' && *t != '[') { 1541 while (t < chunk->end) { 1542 if (*t == ',' || *t == ';' || *t == '\n' || *t == '\r') { 1543 break; 1544 } 1545 else if (*t == '{' || *t == '[') { 1546 *next_key = true; 1547 break; 1548 } 1549 t++; 1550 } 1551 } 1552 } 1553 1554 /* Create a new object */ 1555 nobj = ucl_object_new_full(UCL_NULL, parser->chunks->priority); 1556 if (nobj == NULL) { 1557 return false; 1558 } 1559 keylen = ucl_copy_or_store_ptr(parser, c, &nobj->trash_stack[UCL_TRASH_KEY], 1560 &key, end - c, need_unescape, parser->flags & UCL_PARSER_KEY_LOWERCASE, 1561 false, false); 1562 if (keylen == -1) { 1563 ucl_object_unref(nobj); 1564 return false; 1565 } 1566 else if (keylen == 0) { 1567 ucl_set_err(parser, UCL_ESYNTAX, "empty keys are not allowed", &parser->err); 1568 ucl_object_unref(nobj); 1569 return false; 1570 } 1571 1572 nobj->key = key; 1573 nobj->keylen = keylen; 1574 1575 if (!ucl_parser_process_object_element(parser, nobj)) { 1576 return false; 1577 } 1578 1579 if (ucl_escape) { 1580 nobj->flags |= UCL_OBJECT_NEED_KEY_ESCAPE; 1581 } 1582 1583 1584 return true; 1585 } 1586 1587 /** 1588 * Parse a cl string 1589 * @param parser 1590 * @param chunk 1591 * @param var_expand 1592 * @param need_unescape 1593 * @return true if a key has been parsed 1594 */ 1595 static bool 1596 ucl_parse_string_value(struct ucl_parser *parser, 1597 struct ucl_chunk *chunk, bool *var_expand, bool *need_unescape) 1598 { 1599 const unsigned char *p; 1600 enum { 1601 UCL_BRACE_ROUND = 0, 1602 UCL_BRACE_SQUARE, 1603 UCL_BRACE_FIGURE 1604 }; 1605 int braces[3][2] = {{0, 0}, {0, 0}, {0, 0}}; 1606 1607 p = chunk->pos; 1608 1609 while (p < chunk->end) { 1610 1611 /* Skip pairs of figure braces */ 1612 if (*p == '{') { 1613 braces[UCL_BRACE_FIGURE][0]++; 1614 } 1615 else if (*p == '}') { 1616 braces[UCL_BRACE_FIGURE][1]++; 1617 if (braces[UCL_BRACE_FIGURE][1] <= braces[UCL_BRACE_FIGURE][0]) { 1618 /* This is not a termination symbol, continue */ 1619 ucl_chunk_skipc(chunk, p); 1620 continue; 1621 } 1622 } 1623 /* Skip pairs of square braces */ 1624 else if (*p == '[') { 1625 braces[UCL_BRACE_SQUARE][0]++; 1626 } 1627 else if (*p == ']') { 1628 braces[UCL_BRACE_SQUARE][1]++; 1629 if (braces[UCL_BRACE_SQUARE][1] <= braces[UCL_BRACE_SQUARE][0]) { 1630 /* This is not a termination symbol, continue */ 1631 ucl_chunk_skipc(chunk, p); 1632 continue; 1633 } 1634 } 1635 else if (*p == '$') { 1636 *var_expand = true; 1637 } 1638 else if (*p == '\\') { 1639 *need_unescape = true; 1640 ucl_chunk_skipc(chunk, p); 1641 if (p < chunk->end) { 1642 ucl_chunk_skipc(chunk, p); 1643 } 1644 continue; 1645 } 1646 1647 if (ucl_lex_is_atom_end(*p) || (chunk->remain >= 2 && ucl_lex_is_comment(p[0], p[1]))) { 1648 break; 1649 } 1650 ucl_chunk_skipc(chunk, p); 1651 } 1652 1653 return true; 1654 } 1655 1656 /** 1657 * Parse multiline string ending with \n{term}\n 1658 * @param parser 1659 * @param chunk 1660 * @param term 1661 * @param term_len 1662 * @param beg 1663 * @param var_expand 1664 * @return size of multiline string or 0 in case of error 1665 */ 1666 static int 1667 ucl_parse_multiline_string(struct ucl_parser *parser, 1668 struct ucl_chunk *chunk, const unsigned char *term, 1669 int term_len, unsigned char const **beg, 1670 bool *var_expand) 1671 { 1672 const unsigned char *p, *c, *tend; 1673 bool newline = false; 1674 int len = 0; 1675 1676 p = chunk->pos; 1677 1678 c = p; 1679 1680 while (p < chunk->end) { 1681 if (newline) { 1682 if (chunk->end - p < term_len) { 1683 return 0; 1684 } 1685 else if (memcmp(p, term, term_len) == 0) { 1686 tend = p + term_len; 1687 if (tend < chunk->end && *tend != '\n' && *tend != ';' && *tend != ',') { 1688 /* Incomplete terminator */ 1689 ucl_chunk_skipc(chunk, p); 1690 continue; 1691 } 1692 len = p - c; 1693 chunk->remain -= term_len; 1694 chunk->pos = p + term_len; 1695 chunk->column = term_len; 1696 *beg = c; 1697 break; 1698 } 1699 } 1700 if (*p == '\n') { 1701 newline = true; 1702 } 1703 else { 1704 if (*p == '$') { 1705 *var_expand = true; 1706 } 1707 newline = false; 1708 } 1709 ucl_chunk_skipc(chunk, p); 1710 } 1711 1712 return len; 1713 } 1714 1715 static inline ucl_object_t * 1716 ucl_parser_get_container(struct ucl_parser *parser) 1717 { 1718 ucl_object_t *t, *obj = NULL; 1719 1720 if (parser == NULL || parser->stack == NULL || parser->stack->obj == NULL) { 1721 return NULL; 1722 } 1723 1724 if (parser->stack->obj->type == UCL_ARRAY) { 1725 /* Object must be allocated */ 1726 obj = ucl_object_new_full(UCL_NULL, parser->chunks->priority); 1727 t = parser->stack->obj; 1728 1729 if (!ucl_array_append(t, obj)) { 1730 ucl_object_unref(obj); 1731 return NULL; 1732 } 1733 1734 parser->cur_obj = obj; 1735 ucl_attach_comment(parser, obj, false); 1736 } 1737 else { 1738 /* Object has been already allocated */ 1739 obj = parser->cur_obj; 1740 } 1741 1742 return obj; 1743 } 1744 1745 /** 1746 * Handle value data 1747 * @param parser 1748 * @param chunk 1749 * @return 1750 */ 1751 static bool 1752 ucl_parse_value(struct ucl_parser *parser, struct ucl_chunk *chunk) 1753 { 1754 const unsigned char *p, *c; 1755 ucl_object_t *obj = NULL; 1756 unsigned int stripped_spaces; 1757 ssize_t str_len; 1758 bool need_unescape = false, ucl_escape = false, var_expand = false; 1759 1760 p = chunk->pos; 1761 1762 /* Skip any spaces and comments */ 1763 if (ucl_test_character(*p, UCL_CHARACTER_WHITESPACE_UNSAFE) || 1764 (chunk->remain >= 2 && ucl_lex_is_comment(p[0], p[1]))) { 1765 while (p < chunk->end && ucl_test_character(*p, UCL_CHARACTER_WHITESPACE_UNSAFE)) { 1766 ucl_chunk_skipc(chunk, p); 1767 } 1768 if (!ucl_skip_comments(parser)) { 1769 return false; 1770 } 1771 p = chunk->pos; 1772 } 1773 1774 while (p < chunk->end) { 1775 c = p; 1776 switch (*p) { 1777 case '"': 1778 ucl_chunk_skipc(chunk, p); 1779 1780 if (!ucl_lex_json_string(parser, chunk, &need_unescape, &ucl_escape, 1781 &var_expand)) { 1782 return false; 1783 } 1784 1785 obj = ucl_parser_get_container(parser); 1786 if (!obj) { 1787 return false; 1788 } 1789 1790 str_len = chunk->pos - c - 2; 1791 obj->type = UCL_STRING; 1792 if ((str_len = ucl_copy_or_store_ptr(parser, c + 1, 1793 &obj->trash_stack[UCL_TRASH_VALUE], 1794 &obj->value.sv, str_len, need_unescape, false, 1795 var_expand, false)) == -1) { 1796 return false; 1797 } 1798 1799 obj->len = str_len; 1800 parser->state = UCL_STATE_AFTER_VALUE; 1801 1802 return true; 1803 break; 1804 case '\'': 1805 ucl_chunk_skipc(chunk, p); 1806 1807 if (!ucl_lex_squoted_string(parser, chunk, &need_unescape)) { 1808 return false; 1809 } 1810 1811 obj = ucl_parser_get_container(parser); 1812 if (!obj) { 1813 return false; 1814 } 1815 1816 str_len = chunk->pos - c - 2; 1817 obj->type = UCL_STRING; 1818 obj->flags |= UCL_OBJECT_SQUOTED; 1819 1820 if ((str_len = ucl_copy_or_store_ptr(parser, c + 1, 1821 &obj->trash_stack[UCL_TRASH_VALUE], 1822 &obj->value.sv, str_len, need_unescape, false, 1823 var_expand, true)) == -1) { 1824 return false; 1825 } 1826 1827 obj->len = str_len; 1828 1829 parser->state = UCL_STATE_AFTER_VALUE; 1830 1831 return true; 1832 break; 1833 case '{': 1834 obj = ucl_parser_get_container(parser); 1835 if (obj == NULL) { 1836 parser->state = UCL_STATE_ERROR; 1837 ucl_set_err(parser, UCL_ESYNTAX, "object value must be a part of an object", 1838 &parser->err); 1839 return false; 1840 } 1841 /* We have a new object */ 1842 if (parser->stack) { 1843 obj = ucl_parser_add_container(obj, parser, false, 1844 parser->stack->e.params.level, true); 1845 } 1846 else { 1847 return false; 1848 } 1849 if (obj == NULL) { 1850 return false; 1851 } 1852 1853 ucl_chunk_skipc(chunk, p); 1854 1855 return true; 1856 break; 1857 case '[': 1858 obj = ucl_parser_get_container(parser); 1859 if (obj == NULL) { 1860 parser->state = UCL_STATE_ERROR; 1861 ucl_set_err(parser, UCL_ESYNTAX, "array value must be a part of an object", 1862 &parser->err); 1863 return false; 1864 } 1865 /* We have a new array */ 1866 if (parser->stack) { 1867 obj = ucl_parser_add_container(obj, parser, true, 1868 parser->stack->e.params.level, true); 1869 } 1870 else { 1871 return false; 1872 } 1873 1874 if (obj == NULL) { 1875 return false; 1876 } 1877 1878 ucl_chunk_skipc(chunk, p); 1879 1880 return true; 1881 break; 1882 case ']': 1883 /* We have the array ending */ 1884 if (parser->stack && parser->stack->obj->type == UCL_ARRAY) { 1885 parser->state = UCL_STATE_AFTER_VALUE; 1886 return true; 1887 } 1888 else { 1889 goto parse_string; 1890 } 1891 break; 1892 case '<': 1893 obj = ucl_parser_get_container(parser); 1894 if (obj == NULL) { 1895 parser->state = UCL_STATE_ERROR; 1896 ucl_set_err(parser, UCL_ESYNTAX, "multiline value must be a part of an object", 1897 &parser->err); 1898 return false; 1899 } 1900 /* We have something like multiline value, which must be <<[A-Z]+\n */ 1901 if (chunk->end - p > 3) { 1902 if (memcmp(p, "<<", 2) == 0) { 1903 p += 2; 1904 /* We allow only uppercase characters in multiline definitions */ 1905 while (p < chunk->end && *p >= 'A' && *p <= 'Z') { 1906 p++; 1907 } 1908 if (p == chunk->end) { 1909 ucl_set_err(parser, UCL_ESYNTAX, 1910 "unterminated multiline value", &parser->err); 1911 return false; 1912 } 1913 if (*p == '\n') { 1914 /* Set chunk positions and start multiline parsing */ 1915 chunk->remain -= p - c + 1; 1916 c += 2; 1917 chunk->pos = p + 1; 1918 chunk->column = 0; 1919 chunk->line++; 1920 if ((str_len = ucl_parse_multiline_string(parser, chunk, c, 1921 p - c, &c, &var_expand)) == 0) { 1922 ucl_set_err(parser, UCL_ESYNTAX, 1923 "unterminated multiline value", &parser->err); 1924 return false; 1925 } 1926 1927 obj->type = UCL_STRING; 1928 obj->flags |= UCL_OBJECT_MULTILINE; 1929 if ((str_len = ucl_copy_or_store_ptr(parser, c, 1930 &obj->trash_stack[UCL_TRASH_VALUE], 1931 &obj->value.sv, str_len - 1, false, 1932 false, var_expand, false)) == -1) { 1933 return false; 1934 } 1935 obj->len = str_len; 1936 1937 parser->state = UCL_STATE_AFTER_VALUE; 1938 1939 return true; 1940 } 1941 } 1942 } 1943 /* Fallback to ordinary strings */ 1944 /* FALLTHRU */ 1945 default: 1946 parse_string: 1947 if (obj == NULL) { 1948 obj = ucl_parser_get_container(parser); 1949 } 1950 1951 if (obj == NULL) { 1952 parser->state = UCL_STATE_ERROR; 1953 ucl_set_err(parser, UCL_ESYNTAX, "value must be a part of an object", 1954 &parser->err); 1955 return false; 1956 } 1957 1958 /* Parse atom */ 1959 if (ucl_test_character(*p, UCL_CHARACTER_VALUE_DIGIT_START)) { 1960 if (!ucl_lex_number(parser, chunk, obj)) { 1961 if (parser->state == UCL_STATE_ERROR) { 1962 return false; 1963 } 1964 } 1965 else { 1966 parser->state = UCL_STATE_AFTER_VALUE; 1967 return true; 1968 } 1969 /* Fallback to normal string */ 1970 } 1971 1972 if (!ucl_parse_string_value(parser, chunk, &var_expand, 1973 &need_unescape)) { 1974 return false; 1975 } 1976 /* Cut trailing spaces */ 1977 stripped_spaces = 0; 1978 while (ucl_test_character(*(chunk->pos - 1 - stripped_spaces), 1979 UCL_CHARACTER_WHITESPACE)) { 1980 stripped_spaces++; 1981 } 1982 str_len = chunk->pos - c - stripped_spaces; 1983 if (str_len <= 0) { 1984 ucl_set_err(parser, UCL_ESYNTAX, "string value must not be empty", 1985 &parser->err); 1986 return false; 1987 } 1988 else if (str_len == 4 && memcmp(c, "null", 4) == 0) { 1989 obj->len = 0; 1990 obj->type = UCL_NULL; 1991 } 1992 else if (str_len == 3 && memcmp(c, "nan", 3) == 0) { 1993 obj->len = 0; 1994 obj->type = UCL_FLOAT; 1995 obj->value.dv = NAN; 1996 } 1997 else if (str_len == 3 && memcmp(c, "inf", 3) == 0) { 1998 obj->len = 0; 1999 obj->type = UCL_FLOAT; 2000 obj->value.dv = INFINITY; 2001 } 2002 else if (!ucl_maybe_parse_boolean(obj, c, str_len)) { 2003 obj->type = UCL_STRING; 2004 if ((str_len = ucl_copy_or_store_ptr(parser, c, 2005 &obj->trash_stack[UCL_TRASH_VALUE], 2006 &obj->value.sv, str_len, need_unescape, 2007 false, var_expand, false)) == -1) { 2008 return false; 2009 } 2010 obj->len = str_len; 2011 } 2012 2013 parser->state = UCL_STATE_AFTER_VALUE; 2014 2015 return true; 2016 break; 2017 } 2018 } 2019 2020 return true; 2021 } 2022 2023 /** 2024 * Handle after value data 2025 * @param parser 2026 * @param chunk 2027 * @return 2028 */ 2029 static bool 2030 ucl_parse_after_value(struct ucl_parser *parser, struct ucl_chunk *chunk) 2031 { 2032 const unsigned char *p; 2033 bool got_sep = false; 2034 struct ucl_stack *st; 2035 2036 p = chunk->pos; 2037 2038 while (p < chunk->end) { 2039 if (ucl_test_character(*p, UCL_CHARACTER_WHITESPACE)) { 2040 /* Skip whitespaces */ 2041 ucl_chunk_skipc(chunk, p); 2042 } 2043 else if (chunk->remain >= 2 && ucl_lex_is_comment(p[0], p[1])) { 2044 /* Skip comment */ 2045 if (!ucl_skip_comments(parser)) { 2046 return false; 2047 } 2048 /* Treat comment as a separator */ 2049 got_sep = true; 2050 p = chunk->pos; 2051 } 2052 else if (ucl_test_character(*p, UCL_CHARACTER_VALUE_END)) { 2053 if (*p == '}' || *p == ']') { 2054 if (parser->stack == NULL) { 2055 ucl_set_err(parser, UCL_ESYNTAX, 2056 "end of array or object detected without corresponding start", 2057 &parser->err); 2058 return false; 2059 } 2060 if ((*p == '}' && parser->stack->obj->type == UCL_OBJECT) || 2061 (*p == ']' && parser->stack->obj->type == UCL_ARRAY)) { 2062 2063 /* Pop all nested objects from a stack */ 2064 st = parser->stack; 2065 2066 if (!(st->e.params.flags & UCL_STACK_HAS_OBRACE)) { 2067 parser->err_code = UCL_EUNPAIRED; 2068 ucl_create_err(&parser->err, 2069 "%s:%d object closed with } is not opened with { at line %d", 2070 chunk->fname ? chunk->fname : "memory", 2071 parser->chunks->line, st->e.params.line); 2072 2073 return false; 2074 } 2075 2076 if ((st->e.params.flags & UCL_STACK_AUTOMATIC)) { 2077 st->e.params.flags = 0; 2078 } 2079 else { 2080 parser->stack = st->next; 2081 UCL_FREE(sizeof(struct ucl_stack), st); 2082 } 2083 2084 if (parser->cur_obj) { 2085 ucl_attach_comment(parser, parser->cur_obj, true); 2086 } 2087 2088 while (parser->stack != NULL) { 2089 st = parser->stack; 2090 2091 if (st->next == NULL) { 2092 break; 2093 } 2094 else if (st->next->e.params.level == st->e.params.level) { 2095 break; 2096 } 2097 2098 2099 parser->stack = st->next; 2100 parser->cur_obj = st->obj; 2101 UCL_FREE(sizeof(struct ucl_stack), st); 2102 } 2103 } 2104 else { 2105 ucl_set_err(parser, UCL_ESYNTAX, 2106 "unexpected terminating symbol detected", 2107 &parser->err); 2108 return false; 2109 } 2110 2111 if (parser->stack == NULL) { 2112 /* Ignore everything after a top object */ 2113 return true; 2114 } 2115 else { 2116 ucl_chunk_skipc(chunk, p); 2117 } 2118 got_sep = true; 2119 } 2120 else { 2121 /* Got a separator */ 2122 got_sep = true; 2123 ucl_chunk_skipc(chunk, p); 2124 } 2125 } 2126 else { 2127 /* Anything else */ 2128 if (!got_sep) { 2129 ucl_set_err(parser, UCL_ESYNTAX, "delimiter is missing", 2130 &parser->err); 2131 return false; 2132 } 2133 return true; 2134 } 2135 } 2136 2137 return true; 2138 } 2139 2140 static bool 2141 ucl_skip_macro_as_comment(struct ucl_parser *parser, 2142 struct ucl_chunk *chunk) 2143 { 2144 const unsigned char *p, *c; 2145 enum { 2146 macro_skip_start = 0, 2147 macro_has_symbols, 2148 macro_has_obrace, 2149 macro_has_quote, 2150 macro_has_backslash, 2151 macro_has_sqbrace, 2152 macro_save 2153 } state = macro_skip_start, 2154 prev_state = macro_skip_start; 2155 2156 p = chunk->pos; 2157 c = chunk->pos; 2158 2159 while (p < chunk->end) { 2160 switch (state) { 2161 case macro_skip_start: 2162 if (!ucl_test_character(*p, UCL_CHARACTER_WHITESPACE)) { 2163 state = macro_has_symbols; 2164 } 2165 else if (ucl_test_character(*p, UCL_CHARACTER_WHITESPACE_UNSAFE)) { 2166 state = macro_save; 2167 continue; 2168 } 2169 2170 ucl_chunk_skipc(chunk, p); 2171 break; 2172 2173 case macro_has_symbols: 2174 if (*p == '{') { 2175 state = macro_has_sqbrace; 2176 } 2177 else if (*p == '(') { 2178 state = macro_has_obrace; 2179 } 2180 else if (*p == '"') { 2181 state = macro_has_quote; 2182 } 2183 else if (*p == '\n') { 2184 state = macro_save; 2185 continue; 2186 } 2187 2188 ucl_chunk_skipc(chunk, p); 2189 break; 2190 2191 case macro_has_obrace: 2192 if (*p == '\\') { 2193 prev_state = state; 2194 state = macro_has_backslash; 2195 } 2196 else if (*p == ')') { 2197 state = macro_has_symbols; 2198 } 2199 2200 ucl_chunk_skipc(chunk, p); 2201 break; 2202 2203 case macro_has_sqbrace: 2204 if (*p == '\\') { 2205 prev_state = state; 2206 state = macro_has_backslash; 2207 } 2208 else if (*p == '}') { 2209 state = macro_save; 2210 } 2211 2212 ucl_chunk_skipc(chunk, p); 2213 break; 2214 2215 case macro_has_quote: 2216 if (*p == '\\') { 2217 prev_state = state; 2218 state = macro_has_backslash; 2219 } 2220 else if (*p == '"') { 2221 state = macro_save; 2222 } 2223 2224 ucl_chunk_skipc(chunk, p); 2225 break; 2226 2227 case macro_has_backslash: 2228 state = prev_state; 2229 ucl_chunk_skipc(chunk, p); 2230 break; 2231 2232 case macro_save: 2233 if (parser->flags & UCL_PARSER_SAVE_COMMENTS) { 2234 ucl_save_comment(parser, c, p - c); 2235 } 2236 2237 return true; 2238 } 2239 } 2240 2241 return false; 2242 } 2243 2244 /** 2245 * Handle macro data 2246 * @param parser 2247 * @param chunk 2248 * @param marco 2249 * @param macro_start 2250 * @param macro_len 2251 * @return 2252 */ 2253 static bool 2254 ucl_parse_macro_value(struct ucl_parser *parser, 2255 struct ucl_chunk *chunk, struct ucl_macro *macro, 2256 unsigned char const **macro_start, size_t *macro_len) 2257 { 2258 const unsigned char *p, *c; 2259 bool need_unescape = false, ucl_escape = false, var_expand = false; 2260 2261 p = chunk->pos; 2262 2263 switch (*p) { 2264 case '"': 2265 /* We have macro value encoded in quotes */ 2266 c = p; 2267 ucl_chunk_skipc(chunk, p); 2268 if (!ucl_lex_json_string(parser, chunk, &need_unescape, &ucl_escape, &var_expand)) { 2269 return false; 2270 } 2271 2272 *macro_start = c + 1; 2273 *macro_len = chunk->pos - c - 2; 2274 p = chunk->pos; 2275 break; 2276 case '{': 2277 /* We got a multiline macro body */ 2278 ucl_chunk_skipc(chunk, p); 2279 /* Skip spaces at the beginning */ 2280 while (p < chunk->end) { 2281 if (ucl_test_character(*p, UCL_CHARACTER_WHITESPACE_UNSAFE)) { 2282 ucl_chunk_skipc(chunk, p); 2283 } 2284 else { 2285 break; 2286 } 2287 } 2288 c = p; 2289 while (p < chunk->end) { 2290 if (*p == '}') { 2291 break; 2292 } 2293 ucl_chunk_skipc(chunk, p); 2294 } 2295 *macro_start = c; 2296 *macro_len = p - c; 2297 ucl_chunk_skipc(chunk, p); 2298 break; 2299 default: 2300 /* Macro is not enclosed in quotes or braces */ 2301 c = p; 2302 while (p < chunk->end) { 2303 if (ucl_lex_is_atom_end(*p)) { 2304 break; 2305 } 2306 ucl_chunk_skipc(chunk, p); 2307 } 2308 *macro_start = c; 2309 *macro_len = p - c; 2310 break; 2311 } 2312 2313 /* We are at the end of a macro */ 2314 /* Skip ';' and space characters and return to previous state */ 2315 while (p < chunk->end) { 2316 if (!ucl_test_character(*p, UCL_CHARACTER_WHITESPACE_UNSAFE) && *p != ';') { 2317 break; 2318 } 2319 ucl_chunk_skipc(chunk, p); 2320 } 2321 return true; 2322 } 2323 2324 /** 2325 * Parse macro arguments as UCL object 2326 * @param parser parser structure 2327 * @param chunk the current data chunk 2328 * @return 2329 */ 2330 static ucl_object_t * 2331 ucl_parse_macro_arguments(struct ucl_parser *parser, 2332 struct ucl_chunk *chunk) 2333 { 2334 ucl_object_t *res = NULL; 2335 struct ucl_parser *params_parser; 2336 int obraces = 1, ebraces = 0, state = 0; 2337 const unsigned char *p, *c; 2338 size_t args_len = 0; 2339 struct ucl_parser_saved_state saved; 2340 2341 saved.column = chunk->column; 2342 saved.line = chunk->line; 2343 saved.pos = chunk->pos; 2344 saved.remain = chunk->remain; 2345 p = chunk->pos; 2346 2347 if (*p != '(' || chunk->remain < 2) { 2348 return NULL; 2349 } 2350 2351 /* Set begin and start */ 2352 ucl_chunk_skipc(chunk, p); 2353 c = p; 2354 2355 while ((p) < (chunk)->end) { 2356 switch (state) { 2357 case 0: 2358 /* Parse symbols and check for '(', ')' and '"' */ 2359 if (*p == '(') { 2360 obraces++; 2361 } 2362 else if (*p == ')') { 2363 ebraces++; 2364 } 2365 else if (*p == '"') { 2366 state = 1; 2367 } 2368 /* Check pairing */ 2369 if (obraces == ebraces) { 2370 state = 99; 2371 } 2372 else { 2373 args_len++; 2374 } 2375 /* Check overflow */ 2376 if (chunk->remain == 0) { 2377 goto restore_chunk; 2378 } 2379 ucl_chunk_skipc(chunk, p); 2380 break; 2381 case 1: 2382 /* We have quote character, so skip all but quotes */ 2383 if (*p == '"' && *(p - 1) != '\\') { 2384 state = 0; 2385 } 2386 if (chunk->remain == 0) { 2387 goto restore_chunk; 2388 } 2389 args_len++; 2390 ucl_chunk_skipc(chunk, p); 2391 break; 2392 case 99: 2393 /* 2394 * We have read the full body of arguments, so we need to parse and set 2395 * object from that 2396 */ 2397 params_parser = ucl_parser_new(parser->flags); 2398 if (!ucl_parser_add_chunk(params_parser, c, args_len)) { 2399 ucl_set_err(parser, UCL_ESYNTAX, "macro arguments parsing error", 2400 &parser->err); 2401 } 2402 else { 2403 res = ucl_parser_get_object(params_parser); 2404 } 2405 ucl_parser_free(params_parser); 2406 2407 return res; 2408 2409 break; 2410 } 2411 } 2412 2413 return res; 2414 2415 restore_chunk: 2416 chunk->column = saved.column; 2417 chunk->line = saved.line; 2418 chunk->pos = saved.pos; 2419 chunk->remain = saved.remain; 2420 2421 return NULL; 2422 } 2423 2424 #define SKIP_SPACES_COMMENTS(parser, chunk, p) \ 2425 do { \ 2426 while ((p) < (chunk)->end) { \ 2427 if (!ucl_test_character(*(p), UCL_CHARACTER_WHITESPACE_UNSAFE)) { \ 2428 if ((chunk)->remain >= 2 && ucl_lex_is_comment((p)[0], (p)[1])) { \ 2429 if (!ucl_skip_comments(parser)) { \ 2430 return false; \ 2431 } \ 2432 p = (chunk)->pos; \ 2433 } \ 2434 break; \ 2435 } \ 2436 ucl_chunk_skipc(chunk, p); \ 2437 } \ 2438 } while (0) 2439 2440 /** 2441 * Handle the main states of rcl parser 2442 * @param parser parser structure 2443 * @return true if chunk has been parsed and false in case of error 2444 */ 2445 static bool 2446 ucl_state_machine(struct ucl_parser *parser) 2447 { 2448 ucl_object_t *obj, *macro_args; 2449 struct ucl_chunk *chunk = parser->chunks; 2450 const unsigned char *p, *c = NULL, *macro_start = NULL; 2451 unsigned char *macro_escaped; 2452 size_t macro_len = 0; 2453 struct ucl_macro *macro = NULL; 2454 bool next_key = false, end_of_object = false, got_content = false, ret; 2455 2456 if (parser->top_obj == NULL) { 2457 parser->state = UCL_STATE_INIT; 2458 } 2459 2460 p = chunk->pos; 2461 while (chunk->pos < chunk->end) { 2462 switch (parser->state) { 2463 case UCL_STATE_INIT: 2464 /* 2465 * At the init state we can either go to the parse array or object 2466 * if we got [ or { correspondingly or can just treat new data as 2467 * a key of newly created object 2468 */ 2469 if (!ucl_skip_comments(parser)) { 2470 parser->prev_state = parser->state; 2471 parser->state = UCL_STATE_ERROR; 2472 return false; 2473 } 2474 else { 2475 bool seen_obrace = false; 2476 2477 /* Skip any spaces */ 2478 while (p < chunk->end && ucl_test_character(*p, 2479 UCL_CHARACTER_WHITESPACE_UNSAFE)) { 2480 ucl_chunk_skipc(chunk, p); 2481 } 2482 2483 p = chunk->pos; 2484 2485 if (p < chunk->end) { 2486 if (*p == '[') { 2487 parser->state = UCL_STATE_VALUE; 2488 ucl_chunk_skipc(chunk, p); 2489 seen_obrace = true; 2490 } 2491 else { 2492 2493 if (*p == '{') { 2494 ucl_chunk_skipc(chunk, p); 2495 parser->state = UCL_STATE_KEY_OBRACE; 2496 seen_obrace = true; 2497 } 2498 else { 2499 parser->state = UCL_STATE_KEY; 2500 } 2501 } 2502 } 2503 2504 if (parser->top_obj == NULL) { 2505 if (parser->state == UCL_STATE_VALUE) { 2506 obj = ucl_parser_add_container(NULL, parser, true, 0, 2507 seen_obrace); 2508 } 2509 else { 2510 obj = ucl_parser_add_container(NULL, parser, false, 0, 2511 seen_obrace); 2512 } 2513 2514 if (obj == NULL) { 2515 return false; 2516 } 2517 2518 parser->top_obj = obj; 2519 parser->cur_obj = obj; 2520 } 2521 } 2522 break; 2523 case UCL_STATE_KEY_OBRACE: 2524 parser->stack->e.params.flags |= UCL_STACK_HAS_OBRACE | UCL_STACK_AUTOMATIC; 2525 /* FALLTHROUGHT */ 2526 case UCL_STATE_KEY: 2527 /* Skip any spaces */ 2528 while (p < chunk->end && ucl_test_character(*p, UCL_CHARACTER_WHITESPACE_UNSAFE)) { 2529 ucl_chunk_skipc(chunk, p); 2530 } 2531 if (p == chunk->end || *p == '}') { 2532 /* We have the end of an object */ 2533 parser->state = UCL_STATE_AFTER_VALUE; 2534 continue; 2535 } 2536 if (parser->stack == NULL) { 2537 /* No objects are on stack, but we want to parse a key */ 2538 ucl_set_err(parser, UCL_ESYNTAX, "top object is finished but the parser " 2539 "expects a key", 2540 &parser->err); 2541 parser->prev_state = parser->state; 2542 parser->state = UCL_STATE_ERROR; 2543 return false; 2544 } 2545 2546 got_content = false; 2547 2548 if (!ucl_parse_key(parser, chunk, &next_key, &end_of_object, &got_content)) { 2549 parser->prev_state = parser->state; 2550 parser->state = UCL_STATE_ERROR; 2551 return false; 2552 } 2553 2554 if (end_of_object) { 2555 p = chunk->pos; 2556 parser->state = UCL_STATE_AFTER_VALUE; 2557 continue; 2558 } 2559 else if (parser->state != UCL_STATE_MACRO_NAME) { 2560 if (next_key && parser->stack->obj->type == UCL_OBJECT) { 2561 /* Parse more keys and nest objects accordingly */ 2562 obj = ucl_parser_add_container(parser->cur_obj, 2563 parser, 2564 false, 2565 parser->stack->e.params.level + 1, 2566 parser->state == UCL_STATE_KEY_OBRACE); 2567 if (obj == NULL) { 2568 return false; 2569 } 2570 } 2571 else if (got_content) { 2572 /* Do not switch state if we have not read any content */ 2573 parser->state = UCL_STATE_VALUE; 2574 } 2575 } 2576 else { 2577 c = chunk->pos; 2578 } 2579 p = chunk->pos; 2580 break; 2581 case UCL_STATE_VALUE: 2582 /* We need to check what we do have */ 2583 if (!parser->cur_obj || !ucl_parse_value(parser, chunk)) { 2584 parser->prev_state = parser->state; 2585 parser->state = UCL_STATE_ERROR; 2586 return false; 2587 } 2588 /* State is set in ucl_parse_value call */ 2589 p = chunk->pos; 2590 break; 2591 case UCL_STATE_AFTER_VALUE: 2592 if (!ucl_parse_after_value(parser, chunk)) { 2593 parser->prev_state = parser->state; 2594 parser->state = UCL_STATE_ERROR; 2595 return false; 2596 } 2597 2598 if (parser->stack != NULL) { 2599 if (parser->stack->obj->type == UCL_OBJECT) { 2600 parser->state = UCL_STATE_KEY; 2601 } 2602 else { 2603 /* Array */ 2604 parser->state = UCL_STATE_VALUE; 2605 } 2606 } 2607 else { 2608 /* Skip everything at the end */ 2609 return true; 2610 } 2611 2612 p = chunk->pos; 2613 break; 2614 case UCL_STATE_MACRO_NAME: 2615 if (parser->flags & UCL_PARSER_DISABLE_MACRO) { 2616 if (!ucl_skip_macro_as_comment(parser, chunk)) { 2617 /* We have invalid macro */ 2618 ucl_create_err(&parser->err, 2619 "error at %s:%d at column %d: invalid macro", 2620 chunk->fname ? chunk->fname : "memory", 2621 chunk->line, 2622 chunk->column); 2623 parser->state = UCL_STATE_ERROR; 2624 return false; 2625 } 2626 else { 2627 p = chunk->pos; 2628 parser->state = parser->prev_state; 2629 } 2630 } 2631 else { 2632 if (!ucl_test_character(*p, UCL_CHARACTER_WHITESPACE_UNSAFE) && 2633 *p != '(') { 2634 ucl_chunk_skipc(chunk, p); 2635 } 2636 else { 2637 if (c != NULL && p - c > 0) { 2638 /* We got macro name */ 2639 macro_len = (size_t) (p - c); 2640 HASH_FIND(hh, parser->macroes, c, macro_len, macro); 2641 if (macro == NULL) { 2642 ucl_create_err(&parser->err, 2643 "error at %s:%d at column %d: " 2644 "unknown macro: '%.*s', character: '%c'", 2645 chunk->fname ? chunk->fname : "memory", 2646 chunk->line, 2647 chunk->column, 2648 (int) (p - c), 2649 c, 2650 *chunk->pos); 2651 parser->state = UCL_STATE_ERROR; 2652 return false; 2653 } 2654 /* Now we need to skip all spaces */ 2655 SKIP_SPACES_COMMENTS(parser, chunk, p); 2656 parser->state = UCL_STATE_MACRO; 2657 } 2658 else { 2659 /* We have invalid macro name */ 2660 ucl_create_err(&parser->err, 2661 "error at %s:%d at column %d: invalid macro name", 2662 chunk->fname ? chunk->fname : "memory", 2663 chunk->line, 2664 chunk->column); 2665 parser->state = UCL_STATE_ERROR; 2666 return false; 2667 } 2668 } 2669 } 2670 break; 2671 case UCL_STATE_MACRO: 2672 if (*chunk->pos == '(') { 2673 macro_args = ucl_parse_macro_arguments(parser, chunk); 2674 p = chunk->pos; 2675 if (macro_args) { 2676 SKIP_SPACES_COMMENTS(parser, chunk, p); 2677 } 2678 } 2679 else { 2680 macro_args = NULL; 2681 } 2682 if (!ucl_parse_macro_value(parser, chunk, macro, 2683 ¯o_start, ¯o_len)) { 2684 parser->prev_state = parser->state; 2685 parser->state = UCL_STATE_ERROR; 2686 return false; 2687 } 2688 macro_len = ucl_expand_variable(parser, ¯o_escaped, 2689 macro_start, macro_len); 2690 parser->state = parser->prev_state; 2691 2692 if (macro_escaped == NULL && macro != NULL) { 2693 if (macro->is_context) { 2694 ret = macro->h.context_handler(macro_start, macro_len, 2695 macro_args, 2696 parser->top_obj, 2697 macro->ud); 2698 } 2699 else { 2700 ret = macro->h.handler(macro_start, macro_len, macro_args, 2701 macro->ud); 2702 } 2703 } 2704 else if (macro != NULL) { 2705 if (macro->is_context) { 2706 ret = macro->h.context_handler(macro_escaped, macro_len, 2707 macro_args, 2708 parser->top_obj, 2709 macro->ud); 2710 } 2711 else { 2712 ret = macro->h.handler(macro_escaped, macro_len, macro_args, 2713 macro->ud); 2714 } 2715 2716 UCL_FREE(macro_len + 1, macro_escaped); 2717 } 2718 else { 2719 ret = false; 2720 ucl_set_err(parser, UCL_EINTERNAL, 2721 "internal error: parser has macro undefined", &parser->err); 2722 } 2723 2724 /* 2725 * Chunk can be modified within macro handler 2726 */ 2727 chunk = parser->chunks; 2728 p = chunk->pos; 2729 2730 if (macro_args) { 2731 ucl_object_unref(macro_args); 2732 } 2733 2734 if (!ret) { 2735 return false; 2736 } 2737 break; 2738 case UCL_STATE_ERROR: 2739 /* Already in the error state */ 2740 return false; 2741 default: 2742 ucl_set_err(parser, UCL_EINTERNAL, 2743 "internal error: parser is in an unknown state", &parser->err); 2744 parser->state = UCL_STATE_ERROR; 2745 return false; 2746 } 2747 } 2748 2749 if (parser->last_comment) { 2750 if (parser->cur_obj) { 2751 ucl_attach_comment(parser, parser->cur_obj, true); 2752 } 2753 else if (parser->stack && parser->stack->obj) { 2754 ucl_attach_comment(parser, parser->stack->obj, true); 2755 } 2756 else if (parser->top_obj) { 2757 ucl_attach_comment(parser, parser->top_obj, true); 2758 } 2759 else { 2760 ucl_object_unref(parser->last_comment); 2761 } 2762 } 2763 2764 if (parser->stack != NULL && parser->state != UCL_STATE_ERROR) { 2765 struct ucl_stack *st; 2766 bool has_error = false; 2767 2768 LL_FOREACH(parser->stack, st) 2769 { 2770 if (st->chunk != parser->chunks) { 2771 break; /* Not our chunk, give up */ 2772 } 2773 if (st->e.params.flags & UCL_STACK_HAS_OBRACE) { 2774 if (parser->err == NULL) { 2775 utstring_new(parser->err); 2776 } 2777 2778 utstring_printf(parser->err, "%s:%d unmatched open brace at %d; ", 2779 chunk->fname ? chunk->fname : "memory", 2780 parser->chunks->line, 2781 st->e.params.line); 2782 2783 has_error = true; 2784 } 2785 } 2786 2787 if (has_error) { 2788 parser->err_code = UCL_EUNPAIRED; 2789 2790 return false; 2791 } 2792 } 2793 2794 return true; 2795 } 2796 2797 #define UPRM_SAFE(fn, a, b, c, el) \ 2798 do { \ 2799 if (!fn(a, b, c, a)) \ 2800 goto el; \ 2801 } while (0) 2802 2803 struct ucl_parser * 2804 ucl_parser_new(int flags) 2805 { 2806 struct ucl_parser *parser; 2807 2808 parser = UCL_ALLOC(sizeof(struct ucl_parser)); 2809 if (parser == NULL) { 2810 return NULL; 2811 } 2812 2813 memset(parser, 0, sizeof(struct ucl_parser)); 2814 2815 UPRM_SAFE(ucl_parser_register_macro, parser, "include", ucl_include_handler, e0); 2816 UPRM_SAFE(ucl_parser_register_macro, parser, "try_include", ucl_try_include_handler, e0); 2817 UPRM_SAFE(ucl_parser_register_macro, parser, "includes", ucl_includes_handler, e0); 2818 UPRM_SAFE(ucl_parser_register_macro, parser, "priority", ucl_priority_handler, e0); 2819 UPRM_SAFE(ucl_parser_register_macro, parser, "load", ucl_load_handler, e0); 2820 UPRM_SAFE(ucl_parser_register_context_macro, parser, "inherit", ucl_inherit_handler, e0); 2821 2822 parser->flags = flags; 2823 parser->includepaths = NULL; 2824 2825 if (flags & UCL_PARSER_SAVE_COMMENTS) { 2826 parser->comments = ucl_object_typed_new(UCL_OBJECT); 2827 } 2828 2829 if (!(flags & UCL_PARSER_NO_FILEVARS)) { 2830 /* Initial assumption about filevars */ 2831 ucl_parser_set_filevars(parser, NULL, false); 2832 } 2833 2834 return parser; 2835 e0: 2836 ucl_parser_free(parser); 2837 return NULL; 2838 } 2839 2840 bool ucl_parser_set_default_priority(struct ucl_parser *parser, unsigned prio) 2841 { 2842 if (parser == NULL) { 2843 return false; 2844 } 2845 2846 parser->default_priority = prio; 2847 2848 return true; 2849 } 2850 2851 int ucl_parser_get_default_priority(struct ucl_parser *parser) 2852 { 2853 if (parser == NULL) { 2854 return -1; 2855 } 2856 2857 return parser->default_priority; 2858 } 2859 2860 bool ucl_parser_register_macro(struct ucl_parser *parser, const char *macro, 2861 ucl_macro_handler handler, void *ud) 2862 { 2863 struct ucl_macro *new; 2864 2865 if (macro == NULL || handler == NULL) { 2866 return false; 2867 } 2868 2869 new = UCL_ALLOC(sizeof(struct ucl_macro)); 2870 if (new == NULL) { 2871 return false; 2872 } 2873 2874 memset(new, 0, sizeof(struct ucl_macro)); 2875 new->h.handler = handler; 2876 new->name = UCL_STRDUP(macro); 2877 if (new->name == NULL) { 2878 UCL_FREE(sizeof(struct ucl_macro), new); 2879 return false; 2880 } 2881 new->ud = ud; 2882 HASH_ADD_KEYPTR(hh, parser->macroes, new->name, strlen(new->name), new); 2883 return true; 2884 } 2885 2886 bool ucl_parser_register_context_macro(struct ucl_parser *parser, const char *macro, 2887 ucl_context_macro_handler handler, void *ud) 2888 { 2889 struct ucl_macro *new; 2890 2891 if (macro == NULL || handler == NULL) { 2892 return false; 2893 } 2894 2895 new = UCL_ALLOC(sizeof(struct ucl_macro)); 2896 if (new == NULL) { 2897 return false; 2898 } 2899 2900 memset(new, 0, sizeof(struct ucl_macro)); 2901 new->h.context_handler = handler; 2902 new->name = UCL_STRDUP(macro); 2903 if (new->name == NULL) { 2904 UCL_FREE(sizeof(struct ucl_macro), new); 2905 return false; 2906 } 2907 new->ud = ud; 2908 new->is_context = true; 2909 HASH_ADD_KEYPTR(hh, parser->macroes, new->name, strlen(new->name), new); 2910 return true; 2911 } 2912 2913 void ucl_parser_register_variable(struct ucl_parser *parser, const char *var, 2914 const char *value) 2915 { 2916 struct ucl_variable *new = NULL, *cur; 2917 2918 if (var == NULL) { 2919 return; 2920 } 2921 2922 /* Find whether a variable already exists */ 2923 LL_FOREACH(parser->variables, cur) 2924 { 2925 if (strcmp(cur->var, var) == 0) { 2926 new = cur; 2927 break; 2928 } 2929 } 2930 2931 if (value == NULL) { 2932 2933 if (new != NULL) { 2934 /* Remove variable */ 2935 DL_DELETE(parser->variables, new); 2936 UCL_FREE(new->var_len + 1, new->var); 2937 UCL_FREE(new->value_len + 1, new->value); 2938 UCL_FREE(sizeof(struct ucl_variable), new); 2939 } 2940 else { 2941 /* Do nothing */ 2942 return; 2943 } 2944 } 2945 else { 2946 if (new == NULL) { 2947 new = UCL_ALLOC(sizeof(struct ucl_variable)); 2948 if (new == NULL) { 2949 return; 2950 } 2951 memset(new, 0, sizeof(struct ucl_variable)); 2952 new->var = UCL_STRDUP(var); 2953 new->var_len = strlen(var); 2954 new->value = UCL_STRDUP(value); 2955 new->value_len = strlen(value); 2956 2957 DL_APPEND(parser->variables, new); 2958 } 2959 else { 2960 UCL_FREE(new->value_len + 1, new->value); 2961 new->value = UCL_STRDUP(value); 2962 new->value_len = strlen(value); 2963 } 2964 } 2965 } 2966 2967 void ucl_parser_set_variables_handler(struct ucl_parser *parser, 2968 ucl_variable_handler handler, void *ud) 2969 { 2970 parser->var_handler = handler; 2971 parser->var_data = ud; 2972 } 2973 2974 bool ucl_parser_add_chunk_full(struct ucl_parser *parser, const unsigned char *data, 2975 size_t len, unsigned priority, enum ucl_duplicate_strategy strat, 2976 enum ucl_parse_type parse_type) 2977 { 2978 struct ucl_chunk *chunk; 2979 struct ucl_parser_special_handler *special_handler; 2980 2981 if (parser == NULL) { 2982 return false; 2983 } 2984 2985 if (data == NULL && len != 0) { 2986 ucl_create_err(&parser->err, "invalid chunk added"); 2987 return false; 2988 } 2989 2990 if (parser->state != UCL_STATE_ERROR) { 2991 chunk = UCL_ALLOC(sizeof(struct ucl_chunk)); 2992 if (chunk == NULL) { 2993 ucl_create_err(&parser->err, "cannot allocate chunk structure"); 2994 return false; 2995 } 2996 2997 memset(chunk, 0, sizeof(*chunk)); 2998 2999 /* Apply all matching handlers from the first to the last */ 3000 LL_FOREACH(parser->special_handlers, special_handler) 3001 { 3002 if ((special_handler->flags & UCL_SPECIAL_HANDLER_PREPROCESS_ALL) || 3003 (len >= special_handler->magic_len && 3004 memcmp(data, special_handler->magic, special_handler->magic_len) == 0)) { 3005 unsigned char *ndata = NULL; 3006 size_t nlen = 0; 3007 3008 if (!special_handler->handler(parser, data, len, &ndata, &nlen, 3009 special_handler->user_data)) { 3010 UCL_FREE(sizeof(struct ucl_chunk), chunk); 3011 ucl_create_err(&parser->err, "call for external handler failed"); 3012 3013 return false; 3014 } 3015 3016 struct ucl_parser_special_handler_chain *nchain; 3017 nchain = UCL_ALLOC(sizeof(*nchain)); 3018 nchain->begin = ndata; 3019 nchain->len = nlen; 3020 nchain->special_handler = special_handler; 3021 3022 /* Free order is reversed */ 3023 LL_PREPEND(chunk->special_handlers, nchain); 3024 3025 data = ndata; 3026 len = nlen; 3027 } 3028 } 3029 3030 if (parse_type == UCL_PARSE_AUTO && len > 0) { 3031 /* We need to detect parse type by the first symbol */ 3032 if ((*data & 0x80) == 0x80) { 3033 parse_type = UCL_PARSE_MSGPACK; 3034 } 3035 else if (*data == '(') { 3036 parse_type = UCL_PARSE_CSEXP; 3037 } 3038 else { 3039 parse_type = UCL_PARSE_UCL; 3040 } 3041 } 3042 3043 chunk->begin = data; 3044 chunk->remain = len; 3045 chunk->pos = chunk->begin; 3046 chunk->end = chunk->begin + len; 3047 chunk->line = 1; 3048 chunk->column = 0; 3049 chunk->priority = priority; 3050 chunk->strategy = strat; 3051 chunk->parse_type = parse_type; 3052 3053 if (parser->cur_file) { 3054 chunk->fname = strdup(parser->cur_file); 3055 } 3056 3057 LL_PREPEND(parser->chunks, chunk); 3058 parser->recursion++; 3059 3060 if (parser->recursion > UCL_MAX_RECURSION) { 3061 ucl_create_err(&parser->err, "maximum include nesting limit is reached: %d", 3062 parser->recursion); 3063 return false; 3064 } 3065 3066 if (len > 0) { 3067 /* Need to parse something */ 3068 switch (parse_type) { 3069 default: 3070 case UCL_PARSE_UCL: 3071 return ucl_state_machine(parser); 3072 case UCL_PARSE_MSGPACK: 3073 return ucl_parse_msgpack(parser); 3074 case UCL_PARSE_CSEXP: 3075 return ucl_parse_csexp(parser); 3076 } 3077 } 3078 else { 3079 /* Just add empty chunk and go forward */ 3080 if (parser->top_obj == NULL) { 3081 /* 3082 * In case of empty object, create one to indicate that we've 3083 * read something 3084 */ 3085 parser->top_obj = ucl_object_new_full(UCL_OBJECT, priority); 3086 } 3087 3088 return true; 3089 } 3090 } 3091 3092 ucl_create_err(&parser->err, "a parser is in an invalid state"); 3093 3094 return false; 3095 } 3096 3097 bool ucl_parser_add_chunk_priority(struct ucl_parser *parser, 3098 const unsigned char *data, size_t len, unsigned priority) 3099 { 3100 /* We dereference parser, so this check is essential */ 3101 if (parser == NULL) { 3102 return false; 3103 } 3104 3105 return ucl_parser_add_chunk_full(parser, data, len, 3106 priority, UCL_DUPLICATE_APPEND, UCL_PARSE_UCL); 3107 } 3108 3109 bool ucl_parser_add_chunk(struct ucl_parser *parser, const unsigned char *data, 3110 size_t len) 3111 { 3112 if (parser == NULL) { 3113 return false; 3114 } 3115 3116 return ucl_parser_add_chunk_full(parser, data, len, 3117 parser->default_priority, UCL_DUPLICATE_APPEND, UCL_PARSE_UCL); 3118 } 3119 3120 bool ucl_parser_insert_chunk(struct ucl_parser *parser, const unsigned char *data, 3121 size_t len) 3122 { 3123 if (parser == NULL || parser->top_obj == NULL) { 3124 return false; 3125 } 3126 3127 bool res; 3128 struct ucl_chunk *chunk; 3129 3130 int state = parser->state; 3131 parser->state = UCL_STATE_INIT; 3132 3133 /* Prevent inserted chunks from unintentionally closing the current object */ 3134 if (parser->stack != NULL && parser->stack->next != NULL) { 3135 parser->stack->e.params.level = parser->stack->next->e.params.level; 3136 } 3137 3138 res = ucl_parser_add_chunk_full(parser, data, len, parser->chunks->priority, 3139 parser->chunks->strategy, parser->chunks->parse_type); 3140 3141 /* Remove chunk from the stack */ 3142 chunk = parser->chunks; 3143 if (chunk != NULL) { 3144 parser->chunks = chunk->next; 3145 ucl_chunk_free(chunk); 3146 parser->recursion--; 3147 } 3148 3149 parser->state = state; 3150 3151 return res; 3152 } 3153 3154 bool ucl_parser_add_string_priority(struct ucl_parser *parser, const char *data, 3155 size_t len, unsigned priority) 3156 { 3157 if (data == NULL) { 3158 ucl_create_err(&parser->err, "invalid string added"); 3159 return false; 3160 } 3161 if (len == 0) { 3162 len = strlen(data); 3163 } 3164 3165 return ucl_parser_add_chunk_priority(parser, 3166 (const unsigned char *) data, len, priority); 3167 } 3168 3169 bool ucl_parser_add_string(struct ucl_parser *parser, const char *data, 3170 size_t len) 3171 { 3172 if (parser == NULL) { 3173 return false; 3174 } 3175 3176 return ucl_parser_add_string_priority(parser, 3177 (const unsigned char *) data, len, parser->default_priority); 3178 } 3179 3180 bool ucl_set_include_path(struct ucl_parser *parser, ucl_object_t *paths) 3181 { 3182 if (parser == NULL || paths == NULL || paths->type != UCL_ARRAY) { 3183 return false; 3184 } 3185 3186 if (parser->includepaths == NULL) { 3187 parser->includepaths = ucl_object_copy(paths); 3188 } 3189 else { 3190 ucl_object_unref(parser->includepaths); 3191 parser->includepaths = ucl_object_copy(paths); 3192 } 3193 3194 if (parser->includepaths == NULL) { 3195 return false; 3196 } 3197 3198 return true; 3199 } 3200 3201 unsigned char ucl_parser_chunk_peek(struct ucl_parser *parser) 3202 { 3203 if (parser == NULL || parser->chunks == NULL || parser->chunks->pos == NULL || parser->chunks->end == NULL || 3204 parser->chunks->pos == parser->chunks->end) { 3205 return 0; 3206 } 3207 3208 return (*parser->chunks->pos); 3209 } 3210 3211 bool ucl_parser_chunk_skip(struct ucl_parser *parser) 3212 { 3213 if (parser == NULL || parser->chunks == NULL || parser->chunks->pos == NULL || parser->chunks->end == NULL || 3214 parser->chunks->pos == parser->chunks->end) { 3215 return false; 3216 } 3217 3218 const unsigned char *p = parser->chunks->pos; 3219 ucl_chunk_skipc(parser->chunks, p); 3220 if (parser->chunks->pos != NULL) return true; 3221 return false; 3222 } 3223 3224 ucl_object_t * 3225 ucl_parser_get_current_stack_object(struct ucl_parser *parser, unsigned int depth) 3226 { 3227 ucl_object_t *obj; 3228 3229 if (parser == NULL || parser->stack == NULL) { 3230 return NULL; 3231 } 3232 3233 struct ucl_stack *stack = parser->stack; 3234 if (stack == NULL || stack->obj == NULL || ucl_object_type(stack->obj) != UCL_OBJECT) { 3235 return NULL; 3236 } 3237 3238 for (unsigned int i = 0; i < depth; ++i) { 3239 stack = stack->next; 3240 if (stack == NULL || stack->obj == NULL || ucl_object_type(stack->obj) != UCL_OBJECT) { 3241 return NULL; 3242 } 3243 } 3244 3245 obj = ucl_object_ref(stack->obj); 3246 return obj; 3247 } 3248