ucl_parser.c revision 301339
1262395Sbapt/* Copyright (c) 2013, Vsevolod Stakhov 2262395Sbapt * All rights reserved. 3262395Sbapt * 4262395Sbapt * Redistribution and use in source and binary forms, with or without 5262395Sbapt * modification, are permitted provided that the following conditions are met: 6262395Sbapt * * Redistributions of source code must retain the above copyright 7262395Sbapt * notice, this list of conditions and the following disclaimer. 8262395Sbapt * * Redistributions in binary form must reproduce the above copyright 9262395Sbapt * notice, this list of conditions and the following disclaimer in the 10262395Sbapt * documentation and/or other materials provided with the distribution. 11262395Sbapt * 12262395Sbapt * THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY 13262395Sbapt * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 14262395Sbapt * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 15262395Sbapt * DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY 16262395Sbapt * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 17262395Sbapt * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 18262395Sbapt * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 19262395Sbapt * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 20262395Sbapt * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 21262395Sbapt * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 22262395Sbapt */ 23262395Sbapt 24262395Sbapt#include "ucl.h" 25262395Sbapt#include "ucl_internal.h" 26262395Sbapt#include "ucl_chartable.h" 27262395Sbapt 28262395Sbapt/** 29275223Sbapt * @file ucl_parser.c 30275223Sbapt * The implementation of ucl parser 31262395Sbapt */ 32262395Sbapt 33262395Sbaptstruct ucl_parser_saved_state { 34262395Sbapt unsigned int line; 35262395Sbapt unsigned int column; 36262395Sbapt size_t remain; 37262395Sbapt const unsigned char *pos; 38262395Sbapt}; 39262395Sbapt 40262395Sbapt/** 41262395Sbapt * Move up to len characters 42262395Sbapt * @param parser 43262395Sbapt * @param begin 44262395Sbapt * @param len 45262395Sbapt * @return new position in chunk 46262395Sbapt */ 47262395Sbapt#define ucl_chunk_skipc(chunk, p) do{ \ 48262395Sbapt if (*(p) == '\n') { \ 49262395Sbapt (chunk)->line ++; \ 50262395Sbapt (chunk)->column = 0; \ 51262395Sbapt } \ 52262395Sbapt else (chunk)->column ++; \ 53262395Sbapt (p++); \ 54262395Sbapt (chunk)->pos ++; \ 55262395Sbapt (chunk)->remain --; \ 56262395Sbapt } while (0) 57262395Sbapt 58262395Sbaptstatic inline void 59275223Sbaptucl_set_err (struct ucl_parser *parser, int code, const char *str, UT_string **err) 60262395Sbapt{ 61275223Sbapt const char *fmt_string, *filename; 62275223Sbapt struct ucl_chunk *chunk = parser->chunks; 63275223Sbapt 64275223Sbapt if (parser->cur_file) { 65275223Sbapt filename = parser->cur_file; 66275223Sbapt } 67275223Sbapt else { 68275223Sbapt filename = "<unknown>"; 69275223Sbapt } 70290071Sbapt 71262395Sbapt if (chunk->pos < chunk->end) { 72262395Sbapt if (isgraph (*chunk->pos)) { 73275223Sbapt fmt_string = "error while parsing %s: " 74275223Sbapt "line: %d, column: %d - '%s', character: '%c'"; 75262395Sbapt } 76262395Sbapt else { 77275223Sbapt fmt_string = "error while parsing %s: " 78275223Sbapt "line: %d, column: %d - '%s', character: '0x%02x'"; 79262395Sbapt } 80275223Sbapt ucl_create_err (err, fmt_string, 81275223Sbapt filename, chunk->line, chunk->column, 82275223Sbapt str, *chunk->pos); 83262395Sbapt } 84262395Sbapt else { 85275223Sbapt ucl_create_err (err, "error while parsing %s: at the end of chunk: %s", 86275223Sbapt filename, str); 87262395Sbapt } 88290071Sbapt 89290071Sbapt parser->err_code = code; 90262395Sbapt} 91262395Sbapt 92298166Sbaptstatic void 93298166Sbaptucl_save_comment (struct ucl_parser *parser, const char *begin, size_t len) 94298166Sbapt{ 95298166Sbapt ucl_object_t *nobj; 96298166Sbapt 97298166Sbapt if (len > 0 && begin != NULL) { 98298166Sbapt nobj = ucl_object_fromstring_common (begin, len, 0); 99298166Sbapt 100298166Sbapt if (parser->last_comment) { 101298166Sbapt /* We need to append data to an existing object */ 102298166Sbapt DL_APPEND (parser->last_comment, nobj); 103298166Sbapt } 104298166Sbapt else { 105298166Sbapt parser->last_comment = nobj; 106298166Sbapt } 107298166Sbapt } 108298166Sbapt} 109298166Sbapt 110298166Sbaptstatic void 111298166Sbaptucl_attach_comment (struct ucl_parser *parser, ucl_object_t *obj, bool before) 112298166Sbapt{ 113298166Sbapt if (parser->last_comment) { 114298166Sbapt ucl_object_insert_key (parser->comments, parser->last_comment, 115298166Sbapt (const char *)&obj, sizeof (void *), true); 116298166Sbapt 117298166Sbapt if (before) { 118298166Sbapt parser->last_comment->flags |= UCL_OBJECT_INHERITED; 119298166Sbapt } 120298166Sbapt 121298166Sbapt parser->last_comment = NULL; 122298166Sbapt } 123298166Sbapt} 124298166Sbapt 125262395Sbapt/** 126262395Sbapt * Skip all comments from the current pos resolving nested and multiline comments 127262395Sbapt * @param parser 128262395Sbapt * @return 129262395Sbapt */ 130262395Sbaptstatic bool 131262395Sbaptucl_skip_comments (struct ucl_parser *parser) 132262395Sbapt{ 133262395Sbapt struct ucl_chunk *chunk = parser->chunks; 134298166Sbapt const unsigned char *p, *beg = NULL; 135262395Sbapt int comments_nested = 0; 136275223Sbapt bool quoted = false; 137262395Sbapt 138262395Sbapt p = chunk->pos; 139262395Sbapt 140262395Sbaptstart: 141275223Sbapt if (chunk->remain > 0 && *p == '#') { 142262395Sbapt if (parser->state != UCL_STATE_SCOMMENT && 143262395Sbapt parser->state != UCL_STATE_MCOMMENT) { 144298166Sbapt beg = p; 145298166Sbapt 146262395Sbapt while (p < chunk->end) { 147262395Sbapt if (*p == '\n') { 148298166Sbapt if (parser->flags & UCL_PARSER_SAVE_COMMENTS) { 149298166Sbapt ucl_save_comment (parser, beg, p - beg); 150298166Sbapt beg = NULL; 151298166Sbapt } 152298166Sbapt 153262395Sbapt ucl_chunk_skipc (chunk, p); 154298166Sbapt 155262395Sbapt goto start; 156262395Sbapt } 157262395Sbapt ucl_chunk_skipc (chunk, p); 158262395Sbapt } 159262395Sbapt } 160262395Sbapt } 161275223Sbapt else if (chunk->remain >= 2 && *p == '/') { 162262395Sbapt if (p[1] == '*') { 163298166Sbapt beg = p; 164262395Sbapt ucl_chunk_skipc (chunk, p); 165262395Sbapt comments_nested ++; 166262395Sbapt ucl_chunk_skipc (chunk, p); 167262395Sbapt 168262395Sbapt while (p < chunk->end) { 169275223Sbapt if (*p == '"' && *(p - 1) != '\\') { 170275223Sbapt quoted = !quoted; 171275223Sbapt } 172275223Sbapt 173275223Sbapt if (!quoted) { 174275223Sbapt if (*p == '*') { 175275223Sbapt ucl_chunk_skipc (chunk, p); 176275223Sbapt if (*p == '/') { 177275223Sbapt comments_nested --; 178275223Sbapt if (comments_nested == 0) { 179298166Sbapt if (parser->flags & UCL_PARSER_SAVE_COMMENTS) { 180298166Sbapt ucl_save_comment (parser, beg, p - beg + 1); 181298166Sbapt beg = NULL; 182298166Sbapt } 183298166Sbapt 184275223Sbapt ucl_chunk_skipc (chunk, p); 185275223Sbapt goto start; 186275223Sbapt } 187262395Sbapt } 188275223Sbapt ucl_chunk_skipc (chunk, p); 189262395Sbapt } 190275223Sbapt else if (p[0] == '/' && chunk->remain >= 2 && p[1] == '*') { 191275223Sbapt comments_nested ++; 192275223Sbapt ucl_chunk_skipc (chunk, p); 193275223Sbapt ucl_chunk_skipc (chunk, p); 194275223Sbapt continue; 195275223Sbapt } 196262395Sbapt } 197298166Sbapt 198262395Sbapt ucl_chunk_skipc (chunk, p); 199262395Sbapt } 200262395Sbapt if (comments_nested != 0) { 201275223Sbapt ucl_set_err (parser, UCL_ENESTED, 202275223Sbapt "unfinished multiline comment", &parser->err); 203262395Sbapt return false; 204262395Sbapt } 205262395Sbapt } 206262395Sbapt } 207262395Sbapt 208298166Sbapt if (beg && p > beg && (parser->flags & UCL_PARSER_SAVE_COMMENTS)) { 209298166Sbapt ucl_save_comment (parser, beg, p - beg); 210298166Sbapt } 211298166Sbapt 212262395Sbapt return true; 213262395Sbapt} 214262395Sbapt 215262395Sbapt/** 216262395Sbapt * Return multiplier for a character 217262395Sbapt * @param c multiplier character 218262395Sbapt * @param is_bytes if true use 1024 multiplier 219262395Sbapt * @return multiplier 220262395Sbapt */ 221262395Sbaptstatic inline unsigned long 222262395Sbaptucl_lex_num_multiplier (const unsigned char c, bool is_bytes) { 223262395Sbapt const struct { 224262395Sbapt char c; 225262395Sbapt long mult_normal; 226262395Sbapt long mult_bytes; 227262395Sbapt } multipliers[] = { 228262395Sbapt {'m', 1000 * 1000, 1024 * 1024}, 229262395Sbapt {'k', 1000, 1024}, 230262395Sbapt {'g', 1000 * 1000 * 1000, 1024 * 1024 * 1024} 231262395Sbapt }; 232262395Sbapt int i; 233262395Sbapt 234262395Sbapt for (i = 0; i < 3; i ++) { 235262395Sbapt if (tolower (c) == multipliers[i].c) { 236262395Sbapt if (is_bytes) { 237262395Sbapt return multipliers[i].mult_bytes; 238262395Sbapt } 239262395Sbapt return multipliers[i].mult_normal; 240262395Sbapt } 241262395Sbapt } 242262395Sbapt 243262395Sbapt return 1; 244262395Sbapt} 245262395Sbapt 246262395Sbapt 247262395Sbapt/** 248262395Sbapt * Return multiplier for time scaling 249262395Sbapt * @param c 250262395Sbapt * @return 251262395Sbapt */ 252262395Sbaptstatic inline double 253262395Sbaptucl_lex_time_multiplier (const unsigned char c) { 254262395Sbapt const struct { 255262395Sbapt char c; 256262395Sbapt double mult; 257262395Sbapt } multipliers[] = { 258262395Sbapt {'m', 60}, 259262395Sbapt {'h', 60 * 60}, 260262395Sbapt {'d', 60 * 60 * 24}, 261262395Sbapt {'w', 60 * 60 * 24 * 7}, 262298166Sbapt {'y', 60 * 60 * 24 * 365} 263262395Sbapt }; 264262395Sbapt int i; 265262395Sbapt 266262395Sbapt for (i = 0; i < 5; i ++) { 267262395Sbapt if (tolower (c) == multipliers[i].c) { 268262395Sbapt return multipliers[i].mult; 269262395Sbapt } 270262395Sbapt } 271262395Sbapt 272262395Sbapt return 1; 273262395Sbapt} 274262395Sbapt 275262395Sbapt/** 276262395Sbapt * Return true if a character is a end of an atom 277262395Sbapt * @param c 278262395Sbapt * @return 279262395Sbapt */ 280262395Sbaptstatic inline bool 281262395Sbaptucl_lex_is_atom_end (const unsigned char c) 282262395Sbapt{ 283262395Sbapt return ucl_test_character (c, UCL_CHARACTER_VALUE_END); 284262395Sbapt} 285262395Sbapt 286262395Sbaptstatic inline bool 287262395Sbaptucl_lex_is_comment (const unsigned char c1, const unsigned char c2) 288262395Sbapt{ 289262395Sbapt if (c1 == '/') { 290262395Sbapt if (c2 == '*') { 291262395Sbapt return true; 292262395Sbapt } 293262395Sbapt } 294262395Sbapt else if (c1 == '#') { 295262395Sbapt return true; 296262395Sbapt } 297262395Sbapt return false; 298262395Sbapt} 299262395Sbapt 300262395Sbapt/** 301262395Sbapt * Check variable found 302262395Sbapt * @param parser 303262395Sbapt * @param ptr 304262395Sbapt * @param remain 305262395Sbapt * @param out_len 306262395Sbapt * @param strict 307262395Sbapt * @param found 308262395Sbapt * @return 309262395Sbapt */ 310262395Sbaptstatic inline const char * 311262395Sbaptucl_check_variable_safe (struct ucl_parser *parser, const char *ptr, size_t remain, 312262395Sbapt size_t *out_len, bool strict, bool *found) 313262395Sbapt{ 314262395Sbapt struct ucl_variable *var; 315266636Sbapt unsigned char *dst; 316266636Sbapt size_t dstlen; 317266636Sbapt bool need_free = false; 318262395Sbapt 319262395Sbapt LL_FOREACH (parser->variables, var) { 320262395Sbapt if (strict) { 321262395Sbapt if (remain == var->var_len) { 322262395Sbapt if (memcmp (ptr, var->var, var->var_len) == 0) { 323262395Sbapt *out_len += var->value_len; 324262395Sbapt *found = true; 325262395Sbapt return (ptr + var->var_len); 326262395Sbapt } 327262395Sbapt } 328262395Sbapt } 329262395Sbapt else { 330262395Sbapt if (remain >= var->var_len) { 331262395Sbapt if (memcmp (ptr, var->var, var->var_len) == 0) { 332262395Sbapt *out_len += var->value_len; 333262395Sbapt *found = true; 334262395Sbapt return (ptr + var->var_len); 335262395Sbapt } 336262395Sbapt } 337262395Sbapt } 338262395Sbapt } 339262395Sbapt 340266636Sbapt /* XXX: can only handle ${VAR} */ 341266636Sbapt if (!(*found) && parser->var_handler != NULL && strict) { 342266636Sbapt /* Call generic handler */ 343266636Sbapt if (parser->var_handler (ptr, remain, &dst, &dstlen, &need_free, 344266636Sbapt parser->var_data)) { 345266636Sbapt *found = true; 346266636Sbapt if (need_free) { 347266636Sbapt free (dst); 348266636Sbapt } 349266636Sbapt return (ptr + remain); 350266636Sbapt } 351266636Sbapt } 352266636Sbapt 353262395Sbapt return ptr; 354262395Sbapt} 355262395Sbapt 356262395Sbapt/** 357262395Sbapt * Check for a variable in a given string 358262395Sbapt * @param parser 359262395Sbapt * @param ptr 360262395Sbapt * @param remain 361262395Sbapt * @param out_len 362262395Sbapt * @param vars_found 363262395Sbapt * @return 364262395Sbapt */ 365262395Sbaptstatic const char * 366266636Sbaptucl_check_variable (struct ucl_parser *parser, const char *ptr, 367266636Sbapt size_t remain, size_t *out_len, bool *vars_found) 368262395Sbapt{ 369262395Sbapt const char *p, *end, *ret = ptr; 370262395Sbapt bool found = false; 371262395Sbapt 372262395Sbapt if (*ptr == '{') { 373262395Sbapt /* We need to match the variable enclosed in braces */ 374262395Sbapt p = ptr + 1; 375262395Sbapt end = ptr + remain; 376262395Sbapt while (p < end) { 377262395Sbapt if (*p == '}') { 378266636Sbapt ret = ucl_check_variable_safe (parser, ptr + 1, p - ptr - 1, 379266636Sbapt out_len, true, &found); 380262395Sbapt if (found) { 381262395Sbapt /* {} must be excluded actually */ 382262395Sbapt ret ++; 383262395Sbapt if (!*vars_found) { 384262395Sbapt *vars_found = true; 385262395Sbapt } 386262395Sbapt } 387262395Sbapt else { 388262395Sbapt *out_len += 2; 389262395Sbapt } 390262395Sbapt break; 391262395Sbapt } 392262395Sbapt p ++; 393262395Sbapt } 394262395Sbapt } 395262395Sbapt else if (*ptr != '$') { 396262395Sbapt /* Not count escaped dollar sign */ 397262395Sbapt ret = ucl_check_variable_safe (parser, ptr, remain, out_len, false, &found); 398262395Sbapt if (found && !*vars_found) { 399262395Sbapt *vars_found = true; 400262395Sbapt } 401262395Sbapt if (!found) { 402262395Sbapt (*out_len) ++; 403262395Sbapt } 404262395Sbapt } 405262395Sbapt else { 406262395Sbapt ret ++; 407262395Sbapt (*out_len) ++; 408262395Sbapt } 409262395Sbapt 410262395Sbapt return ret; 411262395Sbapt} 412262395Sbapt 413262395Sbapt/** 414262395Sbapt * Expand a single variable 415262395Sbapt * @param parser 416262395Sbapt * @param ptr 417262395Sbapt * @param remain 418262395Sbapt * @param dest 419262395Sbapt * @return 420262395Sbapt */ 421262395Sbaptstatic const char * 422262395Sbaptucl_expand_single_variable (struct ucl_parser *parser, const char *ptr, 423262395Sbapt size_t remain, unsigned char **dest) 424262395Sbapt{ 425266636Sbapt unsigned char *d = *dest, *dst; 426262395Sbapt const char *p = ptr + 1, *ret; 427262395Sbapt struct ucl_variable *var; 428266636Sbapt size_t dstlen; 429266636Sbapt bool need_free = false; 430262395Sbapt bool found = false; 431266636Sbapt bool strict = false; 432262395Sbapt 433262395Sbapt ret = ptr + 1; 434262395Sbapt remain --; 435262395Sbapt 436262395Sbapt if (*p == '$') { 437262395Sbapt *d++ = *p++; 438262395Sbapt *dest = d; 439262395Sbapt return p; 440262395Sbapt } 441262395Sbapt else if (*p == '{') { 442262395Sbapt p ++; 443266636Sbapt strict = true; 444262395Sbapt ret += 2; 445262395Sbapt remain -= 2; 446262395Sbapt } 447262395Sbapt 448262395Sbapt LL_FOREACH (parser->variables, var) { 449262395Sbapt if (remain >= var->var_len) { 450262395Sbapt if (memcmp (p, var->var, var->var_len) == 0) { 451262395Sbapt memcpy (d, var->value, var->value_len); 452262395Sbapt ret += var->var_len; 453262395Sbapt d += var->value_len; 454262395Sbapt found = true; 455262395Sbapt break; 456262395Sbapt } 457262395Sbapt } 458262395Sbapt } 459262395Sbapt if (!found) { 460266636Sbapt if (strict && parser->var_handler != NULL) { 461266636Sbapt if (parser->var_handler (ptr, remain, &dst, &dstlen, &need_free, 462266636Sbapt parser->var_data)) { 463266636Sbapt memcpy (d, dst, dstlen); 464266636Sbapt ret += dstlen; 465266636Sbapt d += remain; 466266636Sbapt found = true; 467266636Sbapt } 468266636Sbapt } 469266636Sbapt 470266636Sbapt /* Leave variable as is */ 471266636Sbapt if (!found) { 472268831Sbapt if (strict) { 473268831Sbapt /* Copy '${' */ 474268831Sbapt memcpy (d, ptr, 2); 475268831Sbapt d += 2; 476268831Sbapt ret --; 477268831Sbapt } 478268831Sbapt else { 479268831Sbapt memcpy (d, ptr, 1); 480268831Sbapt d ++; 481268831Sbapt } 482266636Sbapt } 483262395Sbapt } 484262395Sbapt 485262395Sbapt *dest = d; 486262395Sbapt return ret; 487262395Sbapt} 488262395Sbapt 489262395Sbapt/** 490262395Sbapt * Expand variables in string 491262395Sbapt * @param parser 492262395Sbapt * @param dst 493262395Sbapt * @param src 494262395Sbapt * @param in_len 495262395Sbapt * @return 496262395Sbapt */ 497262395Sbaptstatic ssize_t 498262395Sbaptucl_expand_variable (struct ucl_parser *parser, unsigned char **dst, 499262395Sbapt const char *src, size_t in_len) 500262395Sbapt{ 501262395Sbapt const char *p, *end = src + in_len; 502262395Sbapt unsigned char *d; 503262395Sbapt size_t out_len = 0; 504262395Sbapt bool vars_found = false; 505262395Sbapt 506298166Sbapt if (parser->flags & UCL_PARSER_DISABLE_MACRO) { 507298166Sbapt *dst = NULL; 508298166Sbapt return in_len; 509298166Sbapt } 510298166Sbapt 511262395Sbapt p = src; 512262395Sbapt while (p != end) { 513262395Sbapt if (*p == '$') { 514262395Sbapt p = ucl_check_variable (parser, p + 1, end - p - 1, &out_len, &vars_found); 515262395Sbapt } 516262395Sbapt else { 517262395Sbapt p ++; 518262395Sbapt out_len ++; 519262395Sbapt } 520262395Sbapt } 521262395Sbapt 522262395Sbapt if (!vars_found) { 523262395Sbapt /* Trivial case */ 524262395Sbapt *dst = NULL; 525262395Sbapt return in_len; 526262395Sbapt } 527262395Sbapt 528262395Sbapt *dst = UCL_ALLOC (out_len + 1); 529262395Sbapt if (*dst == NULL) { 530262395Sbapt return in_len; 531262395Sbapt } 532262395Sbapt 533262395Sbapt d = *dst; 534262395Sbapt p = src; 535262395Sbapt while (p != end) { 536262395Sbapt if (*p == '$') { 537262395Sbapt p = ucl_expand_single_variable (parser, p, end - p, &d); 538262395Sbapt } 539262395Sbapt else { 540262395Sbapt *d++ = *p++; 541262395Sbapt } 542262395Sbapt } 543262395Sbapt 544262395Sbapt *d = '\0'; 545262395Sbapt 546262395Sbapt return out_len; 547262395Sbapt} 548262395Sbapt 549262395Sbapt/** 550262395Sbapt * Store or copy pointer to the trash stack 551262395Sbapt * @param parser parser object 552262395Sbapt * @param src src string 553262395Sbapt * @param dst destination buffer (trash stack pointer) 554262395Sbapt * @param dst_const const destination pointer (e.g. value of object) 555262395Sbapt * @param in_len input length 556262395Sbapt * @param need_unescape need to unescape source (and copy it) 557262395Sbapt * @param need_lowercase need to lowercase value (and copy) 558262395Sbapt * @param need_expand need to expand variables (and copy as well) 559262395Sbapt * @return output length (excluding \0 symbol) 560262395Sbapt */ 561262395Sbaptstatic inline ssize_t 562262395Sbaptucl_copy_or_store_ptr (struct ucl_parser *parser, 563262395Sbapt const unsigned char *src, unsigned char **dst, 564262395Sbapt const char **dst_const, size_t in_len, 565262395Sbapt bool need_unescape, bool need_lowercase, bool need_expand) 566262395Sbapt{ 567262395Sbapt ssize_t ret = -1, tret; 568262395Sbapt unsigned char *tmp; 569262395Sbapt 570262395Sbapt if (need_unescape || need_lowercase || 571262395Sbapt (need_expand && parser->variables != NULL) || 572262395Sbapt !(parser->flags & UCL_PARSER_ZEROCOPY)) { 573262395Sbapt /* Copy string */ 574262395Sbapt *dst = UCL_ALLOC (in_len + 1); 575262395Sbapt if (*dst == NULL) { 576290071Sbapt ucl_set_err (parser, UCL_EINTERNAL, "cannot allocate memory for a string", 577275223Sbapt &parser->err); 578262395Sbapt return false; 579262395Sbapt } 580262395Sbapt if (need_lowercase) { 581262395Sbapt ret = ucl_strlcpy_tolower (*dst, src, in_len + 1); 582262395Sbapt } 583262395Sbapt else { 584262395Sbapt ret = ucl_strlcpy_unsafe (*dst, src, in_len + 1); 585262395Sbapt } 586262395Sbapt 587262395Sbapt if (need_unescape) { 588262395Sbapt ret = ucl_unescape_json_string (*dst, ret); 589262395Sbapt } 590262395Sbapt if (need_expand) { 591262395Sbapt tmp = *dst; 592262395Sbapt tret = ret; 593262395Sbapt ret = ucl_expand_variable (parser, dst, tmp, ret); 594262395Sbapt if (*dst == NULL) { 595262395Sbapt /* Nothing to expand */ 596262395Sbapt *dst = tmp; 597262395Sbapt ret = tret; 598262395Sbapt } 599275223Sbapt else { 600275223Sbapt /* Free unexpanded value */ 601275223Sbapt UCL_FREE (in_len + 1, tmp); 602275223Sbapt } 603262395Sbapt } 604262395Sbapt *dst_const = *dst; 605262395Sbapt } 606262395Sbapt else { 607262395Sbapt *dst_const = src; 608262395Sbapt ret = in_len; 609262395Sbapt } 610262395Sbapt 611262395Sbapt return ret; 612262395Sbapt} 613262395Sbapt 614262395Sbapt/** 615262395Sbapt * Create and append an object at the specified level 616262395Sbapt * @param parser 617262395Sbapt * @param is_array 618262395Sbapt * @param level 619262395Sbapt * @return 620262395Sbapt */ 621262395Sbaptstatic inline ucl_object_t * 622290071Sbaptucl_parser_add_container (ucl_object_t *obj, struct ucl_parser *parser, 623290071Sbapt bool is_array, int level) 624262395Sbapt{ 625262395Sbapt struct ucl_stack *st; 626262395Sbapt 627262395Sbapt if (!is_array) { 628262395Sbapt if (obj == NULL) { 629275223Sbapt obj = ucl_object_new_full (UCL_OBJECT, parser->chunks->priority); 630262395Sbapt } 631262395Sbapt else { 632262395Sbapt obj->type = UCL_OBJECT; 633262395Sbapt } 634290071Sbapt if (obj->value.ov == NULL) { 635290071Sbapt obj->value.ov = ucl_hash_create (parser->flags & UCL_PARSER_KEY_LOWERCASE); 636290071Sbapt } 637262395Sbapt parser->state = UCL_STATE_KEY; 638262395Sbapt } 639262395Sbapt else { 640262395Sbapt if (obj == NULL) { 641275223Sbapt obj = ucl_object_new_full (UCL_ARRAY, parser->chunks->priority); 642262395Sbapt } 643262395Sbapt else { 644262395Sbapt obj->type = UCL_ARRAY; 645262395Sbapt } 646262395Sbapt parser->state = UCL_STATE_VALUE; 647262395Sbapt } 648262395Sbapt 649262395Sbapt st = UCL_ALLOC (sizeof (struct ucl_stack)); 650298166Sbapt 651263648Sbapt if (st == NULL) { 652290071Sbapt ucl_set_err (parser, UCL_EINTERNAL, "cannot allocate memory for an object", 653275223Sbapt &parser->err); 654275223Sbapt ucl_object_unref (obj); 655263648Sbapt return NULL; 656263648Sbapt } 657298166Sbapt 658262395Sbapt st->obj = obj; 659262395Sbapt st->level = level; 660262395Sbapt LL_PREPEND (parser->stack, st); 661262395Sbapt parser->cur_obj = obj; 662262395Sbapt 663262395Sbapt return obj; 664262395Sbapt} 665262395Sbapt 666262395Sbaptint 667262395Sbaptucl_maybe_parse_number (ucl_object_t *obj, 668263648Sbapt const char *start, const char *end, const char **pos, 669263648Sbapt bool allow_double, bool number_bytes, bool allow_time) 670262395Sbapt{ 671262395Sbapt const char *p = start, *c = start; 672262395Sbapt char *endptr; 673262395Sbapt bool got_dot = false, got_exp = false, need_double = false, 674263648Sbapt is_time = false, valid_start = false, is_hex = false, 675262395Sbapt is_neg = false; 676262395Sbapt double dv = 0; 677262395Sbapt int64_t lv = 0; 678262395Sbapt 679262395Sbapt if (*p == '-') { 680262395Sbapt is_neg = true; 681262395Sbapt c ++; 682262395Sbapt p ++; 683262395Sbapt } 684262395Sbapt while (p < end) { 685262395Sbapt if (is_hex && isxdigit (*p)) { 686262395Sbapt p ++; 687262395Sbapt } 688262395Sbapt else if (isdigit (*p)) { 689262395Sbapt valid_start = true; 690262395Sbapt p ++; 691262395Sbapt } 692262395Sbapt else if (!is_hex && (*p == 'x' || *p == 'X')) { 693262395Sbapt is_hex = true; 694262395Sbapt allow_double = false; 695262395Sbapt c = p + 1; 696262395Sbapt } 697262395Sbapt else if (allow_double) { 698262395Sbapt if (p == c) { 699262395Sbapt /* Empty digits sequence, not a number */ 700262395Sbapt *pos = start; 701262395Sbapt return EINVAL; 702262395Sbapt } 703262395Sbapt else if (*p == '.') { 704262395Sbapt if (got_dot) { 705262395Sbapt /* Double dots, not a number */ 706262395Sbapt *pos = start; 707262395Sbapt return EINVAL; 708262395Sbapt } 709262395Sbapt else { 710262395Sbapt got_dot = true; 711262395Sbapt need_double = true; 712262395Sbapt p ++; 713262395Sbapt } 714262395Sbapt } 715262395Sbapt else if (*p == 'e' || *p == 'E') { 716262395Sbapt if (got_exp) { 717262395Sbapt /* Double exp, not a number */ 718262395Sbapt *pos = start; 719262395Sbapt return EINVAL; 720262395Sbapt } 721262395Sbapt else { 722262395Sbapt got_exp = true; 723262395Sbapt need_double = true; 724262395Sbapt p ++; 725262395Sbapt if (p >= end) { 726262395Sbapt *pos = start; 727262395Sbapt return EINVAL; 728262395Sbapt } 729262395Sbapt if (!isdigit (*p) && *p != '+' && *p != '-') { 730262395Sbapt /* Wrong exponent sign */ 731262395Sbapt *pos = start; 732262395Sbapt return EINVAL; 733262395Sbapt } 734262395Sbapt else { 735262395Sbapt p ++; 736262395Sbapt } 737262395Sbapt } 738262395Sbapt } 739262395Sbapt else { 740262395Sbapt /* Got the end of the number, need to check */ 741262395Sbapt break; 742262395Sbapt } 743262395Sbapt } 744262395Sbapt else { 745262395Sbapt break; 746262395Sbapt } 747262395Sbapt } 748262395Sbapt 749262395Sbapt if (!valid_start) { 750262395Sbapt *pos = start; 751262395Sbapt return EINVAL; 752262395Sbapt } 753262395Sbapt 754262395Sbapt errno = 0; 755262395Sbapt if (need_double) { 756262395Sbapt dv = strtod (c, &endptr); 757262395Sbapt } 758262395Sbapt else { 759262395Sbapt if (is_hex) { 760262395Sbapt lv = strtoimax (c, &endptr, 16); 761262395Sbapt } 762262395Sbapt else { 763262395Sbapt lv = strtoimax (c, &endptr, 10); 764262395Sbapt } 765262395Sbapt } 766262395Sbapt if (errno == ERANGE) { 767262395Sbapt *pos = start; 768262395Sbapt return ERANGE; 769262395Sbapt } 770262395Sbapt 771262395Sbapt /* Now check endptr */ 772275223Sbapt if (endptr == NULL || ucl_lex_is_atom_end (*endptr) || *endptr == '\0') { 773262395Sbapt p = endptr; 774262395Sbapt goto set_obj; 775262395Sbapt } 776262395Sbapt 777262395Sbapt if (endptr < end && endptr != start) { 778262395Sbapt p = endptr; 779262395Sbapt switch (*p) { 780262395Sbapt case 'm': 781262395Sbapt case 'M': 782262395Sbapt case 'g': 783262395Sbapt case 'G': 784262395Sbapt case 'k': 785262395Sbapt case 'K': 786262395Sbapt if (end - p >= 2) { 787262395Sbapt if (p[1] == 's' || p[1] == 'S') { 788262395Sbapt /* Milliseconds */ 789262395Sbapt if (!need_double) { 790262395Sbapt need_double = true; 791262395Sbapt dv = lv; 792262395Sbapt } 793263648Sbapt is_time = true; 794262395Sbapt if (p[0] == 'm' || p[0] == 'M') { 795262395Sbapt dv /= 1000.; 796262395Sbapt } 797262395Sbapt else { 798262395Sbapt dv *= ucl_lex_num_multiplier (*p, false); 799262395Sbapt } 800262395Sbapt p += 2; 801262395Sbapt goto set_obj; 802262395Sbapt } 803262395Sbapt else if (number_bytes || (p[1] == 'b' || p[1] == 'B')) { 804262395Sbapt /* Bytes */ 805262395Sbapt if (need_double) { 806262395Sbapt need_double = false; 807262395Sbapt lv = dv; 808262395Sbapt } 809262395Sbapt lv *= ucl_lex_num_multiplier (*p, true); 810262395Sbapt p += 2; 811262395Sbapt goto set_obj; 812262395Sbapt } 813262395Sbapt else if (ucl_lex_is_atom_end (p[1])) { 814262395Sbapt if (need_double) { 815262395Sbapt dv *= ucl_lex_num_multiplier (*p, false); 816262395Sbapt } 817262395Sbapt else { 818262395Sbapt lv *= ucl_lex_num_multiplier (*p, number_bytes); 819262395Sbapt } 820262395Sbapt p ++; 821262395Sbapt goto set_obj; 822262395Sbapt } 823263648Sbapt else if (allow_time && end - p >= 3) { 824262395Sbapt if (tolower (p[0]) == 'm' && 825262395Sbapt tolower (p[1]) == 'i' && 826262395Sbapt tolower (p[2]) == 'n') { 827262395Sbapt /* Minutes */ 828262395Sbapt if (!need_double) { 829262395Sbapt need_double = true; 830262395Sbapt dv = lv; 831262395Sbapt } 832263648Sbapt is_time = true; 833262395Sbapt dv *= 60.; 834262395Sbapt p += 3; 835262395Sbapt goto set_obj; 836262395Sbapt } 837262395Sbapt } 838262395Sbapt } 839262395Sbapt else { 840262395Sbapt if (need_double) { 841262395Sbapt dv *= ucl_lex_num_multiplier (*p, false); 842262395Sbapt } 843262395Sbapt else { 844262395Sbapt lv *= ucl_lex_num_multiplier (*p, number_bytes); 845262395Sbapt } 846262395Sbapt p ++; 847262395Sbapt goto set_obj; 848262395Sbapt } 849262395Sbapt break; 850262395Sbapt case 'S': 851262395Sbapt case 's': 852263648Sbapt if (allow_time && 853263648Sbapt (p == end - 1 || ucl_lex_is_atom_end (p[1]))) { 854262395Sbapt if (!need_double) { 855262395Sbapt need_double = true; 856262395Sbapt dv = lv; 857262395Sbapt } 858262395Sbapt p ++; 859263648Sbapt is_time = true; 860262395Sbapt goto set_obj; 861262395Sbapt } 862262395Sbapt break; 863262395Sbapt case 'h': 864262395Sbapt case 'H': 865262395Sbapt case 'd': 866262395Sbapt case 'D': 867262395Sbapt case 'w': 868262395Sbapt case 'W': 869262395Sbapt case 'Y': 870262395Sbapt case 'y': 871263648Sbapt if (allow_time && 872263648Sbapt (p == end - 1 || ucl_lex_is_atom_end (p[1]))) { 873262395Sbapt if (!need_double) { 874262395Sbapt need_double = true; 875262395Sbapt dv = lv; 876262395Sbapt } 877263648Sbapt is_time = true; 878262395Sbapt dv *= ucl_lex_time_multiplier (*p); 879262395Sbapt p ++; 880262395Sbapt goto set_obj; 881262395Sbapt } 882262395Sbapt break; 883275223Sbapt case '\t': 884275223Sbapt case ' ': 885275223Sbapt while (p < end && ucl_test_character(*p, UCL_CHARACTER_WHITESPACE)) { 886275223Sbapt p++; 887275223Sbapt } 888275223Sbapt if (ucl_lex_is_atom_end(*p)) 889275223Sbapt goto set_obj; 890275223Sbapt break; 891262395Sbapt } 892262395Sbapt } 893275223Sbapt else if (endptr == end) { 894275223Sbapt /* Just a number at the end of chunk */ 895275223Sbapt p = endptr; 896275223Sbapt goto set_obj; 897275223Sbapt } 898262395Sbapt 899262395Sbapt *pos = c; 900262395Sbapt return EINVAL; 901262395Sbapt 902290071Sbaptset_obj: 903290071Sbapt if (obj != NULL) { 904290071Sbapt if (allow_double && (need_double || is_time)) { 905290071Sbapt if (!is_time) { 906290071Sbapt obj->type = UCL_FLOAT; 907290071Sbapt } 908290071Sbapt else { 909290071Sbapt obj->type = UCL_TIME; 910290071Sbapt } 911290071Sbapt obj->value.dv = is_neg ? (-dv) : dv; 912262395Sbapt } 913262395Sbapt else { 914290071Sbapt obj->type = UCL_INT; 915290071Sbapt obj->value.iv = is_neg ? (-lv) : lv; 916262395Sbapt } 917262395Sbapt } 918262395Sbapt *pos = p; 919262395Sbapt return 0; 920262395Sbapt} 921262395Sbapt 922262395Sbapt/** 923262395Sbapt * Parse possible number 924262395Sbapt * @param parser 925262395Sbapt * @param chunk 926290071Sbapt * @param obj 927262395Sbapt * @return true if a number has been parsed 928262395Sbapt */ 929262395Sbaptstatic bool 930262395Sbaptucl_lex_number (struct ucl_parser *parser, 931262395Sbapt struct ucl_chunk *chunk, ucl_object_t *obj) 932262395Sbapt{ 933262395Sbapt const unsigned char *pos; 934262395Sbapt int ret; 935262395Sbapt 936263648Sbapt ret = ucl_maybe_parse_number (obj, chunk->pos, chunk->end, (const char **)&pos, 937263648Sbapt true, false, ((parser->flags & UCL_PARSER_NO_TIME) == 0)); 938262395Sbapt 939262395Sbapt if (ret == 0) { 940262395Sbapt chunk->remain -= pos - chunk->pos; 941262395Sbapt chunk->column += pos - chunk->pos; 942262395Sbapt chunk->pos = pos; 943262395Sbapt return true; 944262395Sbapt } 945262395Sbapt else if (ret == ERANGE) { 946290071Sbapt ucl_set_err (parser, UCL_ESYNTAX, "numeric value out of range", 947290071Sbapt &parser->err); 948262395Sbapt } 949262395Sbapt 950262395Sbapt return false; 951262395Sbapt} 952262395Sbapt 953262395Sbapt/** 954262395Sbapt * Parse quoted string with possible escapes 955262395Sbapt * @param parser 956262395Sbapt * @param chunk 957290071Sbapt * @param need_unescape 958290071Sbapt * @param ucl_escape 959290071Sbapt * @param var_expand 960262395Sbapt * @return true if a string has been parsed 961262395Sbapt */ 962262395Sbaptstatic bool 963262395Sbaptucl_lex_json_string (struct ucl_parser *parser, 964262395Sbapt struct ucl_chunk *chunk, bool *need_unescape, bool *ucl_escape, bool *var_expand) 965262395Sbapt{ 966262395Sbapt const unsigned char *p = chunk->pos; 967262395Sbapt unsigned char c; 968262395Sbapt int i; 969262395Sbapt 970262395Sbapt while (p < chunk->end) { 971262395Sbapt c = *p; 972262395Sbapt if (c < 0x1F) { 973262395Sbapt /* Unmasked control character */ 974262395Sbapt if (c == '\n') { 975275223Sbapt ucl_set_err (parser, UCL_ESYNTAX, "unexpected newline", 976275223Sbapt &parser->err); 977262395Sbapt } 978262395Sbapt else { 979275223Sbapt ucl_set_err (parser, UCL_ESYNTAX, "unexpected control character", 980275223Sbapt &parser->err); 981262395Sbapt } 982262395Sbapt return false; 983262395Sbapt } 984262395Sbapt else if (c == '\\') { 985262395Sbapt ucl_chunk_skipc (chunk, p); 986262395Sbapt c = *p; 987262395Sbapt if (p >= chunk->end) { 988275223Sbapt ucl_set_err (parser, UCL_ESYNTAX, "unfinished escape character", 989275223Sbapt &parser->err); 990262395Sbapt return false; 991262395Sbapt } 992262395Sbapt else if (ucl_test_character (c, UCL_CHARACTER_ESCAPE)) { 993262395Sbapt if (c == 'u') { 994262395Sbapt ucl_chunk_skipc (chunk, p); 995262395Sbapt for (i = 0; i < 4 && p < chunk->end; i ++) { 996262395Sbapt if (!isxdigit (*p)) { 997275223Sbapt ucl_set_err (parser, UCL_ESYNTAX, "invalid utf escape", 998275223Sbapt &parser->err); 999262395Sbapt return false; 1000262395Sbapt } 1001262395Sbapt ucl_chunk_skipc (chunk, p); 1002262395Sbapt } 1003262395Sbapt if (p >= chunk->end) { 1004275223Sbapt ucl_set_err (parser, UCL_ESYNTAX, "unfinished escape character", 1005275223Sbapt &parser->err); 1006262395Sbapt return false; 1007262395Sbapt } 1008262395Sbapt } 1009262395Sbapt else { 1010262395Sbapt ucl_chunk_skipc (chunk, p); 1011262395Sbapt } 1012262395Sbapt } 1013262395Sbapt *need_unescape = true; 1014262395Sbapt *ucl_escape = true; 1015262395Sbapt continue; 1016262395Sbapt } 1017262395Sbapt else if (c == '"') { 1018262395Sbapt ucl_chunk_skipc (chunk, p); 1019262395Sbapt return true; 1020262395Sbapt } 1021262395Sbapt else if (ucl_test_character (c, UCL_CHARACTER_UCL_UNSAFE)) { 1022262395Sbapt *ucl_escape = true; 1023262395Sbapt } 1024262395Sbapt else if (c == '$') { 1025262395Sbapt *var_expand = true; 1026262395Sbapt } 1027262395Sbapt ucl_chunk_skipc (chunk, p); 1028262395Sbapt } 1029262395Sbapt 1030275223Sbapt ucl_set_err (parser, UCL_ESYNTAX, "no quote at the end of json string", 1031275223Sbapt &parser->err); 1032262395Sbapt return false; 1033262395Sbapt} 1034262395Sbapt 1035275223Sbaptstatic void 1036275223Sbaptucl_parser_append_elt (struct ucl_parser *parser, ucl_hash_t *cont, 1037275223Sbapt ucl_object_t *top, 1038275223Sbapt ucl_object_t *elt) 1039275223Sbapt{ 1040275223Sbapt ucl_object_t *nobj; 1041275223Sbapt 1042275223Sbapt if ((parser->flags & UCL_PARSER_NO_IMPLICIT_ARRAYS) == 0) { 1043275223Sbapt /* Implicit array */ 1044275223Sbapt top->flags |= UCL_OBJECT_MULTIVALUE; 1045275223Sbapt DL_APPEND (top, elt); 1046290071Sbapt parser->stack->obj->len ++; 1047275223Sbapt } 1048275223Sbapt else { 1049275223Sbapt if ((top->flags & UCL_OBJECT_MULTIVALUE) != 0) { 1050275223Sbapt /* Just add to the explicit array */ 1051279549Sbapt ucl_array_append (top, elt); 1052275223Sbapt } 1053275223Sbapt else { 1054275223Sbapt /* Convert to an array */ 1055275223Sbapt nobj = ucl_object_typed_new (UCL_ARRAY); 1056275223Sbapt nobj->key = top->key; 1057275223Sbapt nobj->keylen = top->keylen; 1058275223Sbapt nobj->flags |= UCL_OBJECT_MULTIVALUE; 1059279549Sbapt ucl_array_append (nobj, top); 1060279549Sbapt ucl_array_append (nobj, elt); 1061290071Sbapt ucl_hash_replace (cont, top, nobj); 1062275223Sbapt } 1063275223Sbapt } 1064275223Sbapt} 1065275223Sbapt 1066290071Sbaptbool 1067290071Sbaptucl_parser_process_object_element (struct ucl_parser *parser, ucl_object_t *nobj) 1068290071Sbapt{ 1069290071Sbapt ucl_hash_t *container; 1070290071Sbapt ucl_object_t *tobj; 1071298166Sbapt char errmsg[256]; 1072290071Sbapt 1073290071Sbapt container = parser->stack->obj->value.ov; 1074290071Sbapt 1075290071Sbapt tobj = __DECONST (ucl_object_t *, ucl_hash_search_obj (container, nobj)); 1076290071Sbapt if (tobj == NULL) { 1077290071Sbapt container = ucl_hash_insert_object (container, nobj, 1078290071Sbapt parser->flags & UCL_PARSER_KEY_LOWERCASE); 1079290071Sbapt nobj->prev = nobj; 1080290071Sbapt nobj->next = NULL; 1081290071Sbapt parser->stack->obj->len ++; 1082290071Sbapt } 1083290071Sbapt else { 1084290071Sbapt unsigned priold = ucl_object_get_priority (tobj), 1085290071Sbapt prinew = ucl_object_get_priority (nobj); 1086290071Sbapt switch (parser->chunks->strategy) { 1087290071Sbapt 1088290071Sbapt case UCL_DUPLICATE_APPEND: 1089290071Sbapt /* 1090290071Sbapt * The logic here is the following: 1091290071Sbapt * 1092290071Sbapt * - if we have two objects with the same priority, then we form an 1093290071Sbapt * implicit or explicit array 1094290071Sbapt * - if a new object has bigger priority, then we overwrite an old one 1095290071Sbapt * - if a new object has lower priority, then we ignore it 1096290071Sbapt */ 1097290071Sbapt 1098290071Sbapt 1099290071Sbapt /* Special case for inherited objects */ 1100290071Sbapt if (tobj->flags & UCL_OBJECT_INHERITED) { 1101290071Sbapt prinew = priold + 1; 1102290071Sbapt } 1103290071Sbapt 1104290071Sbapt if (priold == prinew) { 1105290071Sbapt ucl_parser_append_elt (parser, container, tobj, nobj); 1106290071Sbapt } 1107290071Sbapt else if (priold > prinew) { 1108290071Sbapt /* 1109290071Sbapt * We add this new object to a list of trash objects just to ensure 1110290071Sbapt * that it won't come to any real object 1111290071Sbapt * XXX: rather inefficient approach 1112290071Sbapt */ 1113290071Sbapt DL_APPEND (parser->trash_objs, nobj); 1114290071Sbapt } 1115290071Sbapt else { 1116290071Sbapt ucl_hash_replace (container, tobj, nobj); 1117290071Sbapt ucl_object_unref (tobj); 1118290071Sbapt } 1119290071Sbapt 1120290071Sbapt break; 1121290071Sbapt 1122290071Sbapt case UCL_DUPLICATE_REWRITE: 1123290071Sbapt /* We just rewrite old values regardless of priority */ 1124290071Sbapt ucl_hash_replace (container, tobj, nobj); 1125290071Sbapt ucl_object_unref (tobj); 1126290071Sbapt 1127290071Sbapt break; 1128290071Sbapt 1129290071Sbapt case UCL_DUPLICATE_ERROR: 1130298166Sbapt snprintf(errmsg, sizeof(errmsg), 1131298166Sbapt "duplicate element for key '%s' found", 1132298166Sbapt nobj->key); 1133298166Sbapt ucl_set_err (parser, UCL_EMERGE, errmsg, &parser->err); 1134290071Sbapt return false; 1135290071Sbapt 1136290071Sbapt case UCL_DUPLICATE_MERGE: 1137290071Sbapt /* 1138290071Sbapt * Here we do have some old object so we just push it on top of objects stack 1139298166Sbapt * Check priority and then perform the merge on the remaining objects 1140290071Sbapt */ 1141290071Sbapt if (tobj->type == UCL_OBJECT || tobj->type == UCL_ARRAY) { 1142290071Sbapt ucl_object_unref (nobj); 1143290071Sbapt nobj = tobj; 1144290071Sbapt } 1145298166Sbapt else if (priold == prinew) { 1146290071Sbapt ucl_parser_append_elt (parser, container, tobj, nobj); 1147290071Sbapt } 1148298166Sbapt else if (priold > prinew) { 1149298166Sbapt /* 1150298166Sbapt * We add this new object to a list of trash objects just to ensure 1151298166Sbapt * that it won't come to any real object 1152298166Sbapt * XXX: rather inefficient approach 1153298166Sbapt */ 1154298166Sbapt DL_APPEND (parser->trash_objs, nobj); 1155298166Sbapt } 1156298166Sbapt else { 1157298166Sbapt ucl_hash_replace (container, tobj, nobj); 1158298166Sbapt ucl_object_unref (tobj); 1159298166Sbapt } 1160290071Sbapt break; 1161290071Sbapt } 1162290071Sbapt } 1163290071Sbapt 1164290071Sbapt parser->stack->obj->value.ov = container; 1165290071Sbapt parser->cur_obj = nobj; 1166298166Sbapt ucl_attach_comment (parser, nobj, false); 1167290071Sbapt 1168290071Sbapt return true; 1169290071Sbapt} 1170290071Sbapt 1171262395Sbapt/** 1172262395Sbapt * Parse a key in an object 1173262395Sbapt * @param parser 1174262395Sbapt * @param chunk 1175290071Sbapt * @param next_key 1176290071Sbapt * @param end_of_object 1177262395Sbapt * @return true if a key has been parsed 1178262395Sbapt */ 1179262395Sbaptstatic bool 1180290071Sbaptucl_parse_key (struct ucl_parser *parser, struct ucl_chunk *chunk, 1181290071Sbapt bool *next_key, bool *end_of_object) 1182262395Sbapt{ 1183262395Sbapt const unsigned char *p, *c = NULL, *end, *t; 1184262395Sbapt const char *key = NULL; 1185262395Sbapt bool got_quote = false, got_eq = false, got_semicolon = false, 1186262395Sbapt need_unescape = false, ucl_escape = false, var_expand = false, 1187262395Sbapt got_content = false, got_sep = false; 1188290071Sbapt ucl_object_t *nobj; 1189262395Sbapt ssize_t keylen; 1190262395Sbapt 1191262395Sbapt p = chunk->pos; 1192262395Sbapt 1193262395Sbapt if (*p == '.') { 1194262395Sbapt /* It is macro actually */ 1195298166Sbapt if (!(parser->flags & UCL_PARSER_DISABLE_MACRO)) { 1196298166Sbapt ucl_chunk_skipc (chunk, p); 1197298166Sbapt } 1198298166Sbapt 1199262395Sbapt parser->prev_state = parser->state; 1200262395Sbapt parser->state = UCL_STATE_MACRO_NAME; 1201279549Sbapt *end_of_object = false; 1202262395Sbapt return true; 1203262395Sbapt } 1204262395Sbapt while (p < chunk->end) { 1205262395Sbapt /* 1206262395Sbapt * A key must start with alpha, number, '/' or '_' and end with space character 1207262395Sbapt */ 1208262395Sbapt if (c == NULL) { 1209262395Sbapt if (chunk->remain >= 2 && ucl_lex_is_comment (p[0], p[1])) { 1210262395Sbapt if (!ucl_skip_comments (parser)) { 1211262395Sbapt return false; 1212262395Sbapt } 1213262395Sbapt p = chunk->pos; 1214262395Sbapt } 1215262395Sbapt else if (ucl_test_character (*p, UCL_CHARACTER_WHITESPACE_UNSAFE)) { 1216262395Sbapt ucl_chunk_skipc (chunk, p); 1217262395Sbapt } 1218262395Sbapt else if (ucl_test_character (*p, UCL_CHARACTER_KEY_START)) { 1219262395Sbapt /* The first symbol */ 1220262395Sbapt c = p; 1221262395Sbapt ucl_chunk_skipc (chunk, p); 1222262395Sbapt got_content = true; 1223262395Sbapt } 1224262395Sbapt else if (*p == '"') { 1225262395Sbapt /* JSON style key */ 1226262395Sbapt c = p + 1; 1227262395Sbapt got_quote = true; 1228262395Sbapt got_content = true; 1229262395Sbapt ucl_chunk_skipc (chunk, p); 1230262395Sbapt } 1231262395Sbapt else if (*p == '}') { 1232262395Sbapt /* We have actually end of an object */ 1233262395Sbapt *end_of_object = true; 1234262395Sbapt return true; 1235262395Sbapt } 1236262395Sbapt else if (*p == '.') { 1237262395Sbapt ucl_chunk_skipc (chunk, p); 1238262395Sbapt parser->prev_state = parser->state; 1239262395Sbapt parser->state = UCL_STATE_MACRO_NAME; 1240262395Sbapt return true; 1241262395Sbapt } 1242262395Sbapt else { 1243262395Sbapt /* Invalid identifier */ 1244275223Sbapt ucl_set_err (parser, UCL_ESYNTAX, "key must begin with a letter", 1245275223Sbapt &parser->err); 1246262395Sbapt return false; 1247262395Sbapt } 1248262395Sbapt } 1249262395Sbapt else { 1250262395Sbapt /* Parse the body of a key */ 1251262395Sbapt if (!got_quote) { 1252262395Sbapt if (ucl_test_character (*p, UCL_CHARACTER_KEY)) { 1253262395Sbapt got_content = true; 1254262395Sbapt ucl_chunk_skipc (chunk, p); 1255262395Sbapt } 1256262395Sbapt else if (ucl_test_character (*p, UCL_CHARACTER_KEY_SEP)) { 1257262395Sbapt end = p; 1258262395Sbapt break; 1259262395Sbapt } 1260262395Sbapt else { 1261275223Sbapt ucl_set_err (parser, UCL_ESYNTAX, "invalid character in a key", 1262275223Sbapt &parser->err); 1263262395Sbapt return false; 1264262395Sbapt } 1265262395Sbapt } 1266262395Sbapt else { 1267262395Sbapt /* We need to parse json like quoted string */ 1268262395Sbapt if (!ucl_lex_json_string (parser, chunk, &need_unescape, &ucl_escape, &var_expand)) { 1269262395Sbapt return false; 1270262395Sbapt } 1271262395Sbapt /* Always escape keys obtained via json */ 1272262395Sbapt end = chunk->pos - 1; 1273262395Sbapt p = chunk->pos; 1274262395Sbapt break; 1275262395Sbapt } 1276262395Sbapt } 1277262395Sbapt } 1278262395Sbapt 1279262395Sbapt if (p >= chunk->end && got_content) { 1280275223Sbapt ucl_set_err (parser, UCL_ESYNTAX, "unfinished key", &parser->err); 1281262395Sbapt return false; 1282262395Sbapt } 1283262395Sbapt else if (!got_content) { 1284262395Sbapt return true; 1285262395Sbapt } 1286262395Sbapt *end_of_object = false; 1287262395Sbapt /* We are now at the end of the key, need to parse the rest */ 1288262395Sbapt while (p < chunk->end) { 1289262395Sbapt if (ucl_test_character (*p, UCL_CHARACTER_WHITESPACE)) { 1290262395Sbapt ucl_chunk_skipc (chunk, p); 1291262395Sbapt } 1292262395Sbapt else if (*p == '=') { 1293262395Sbapt if (!got_eq && !got_semicolon) { 1294262395Sbapt ucl_chunk_skipc (chunk, p); 1295262395Sbapt got_eq = true; 1296262395Sbapt } 1297262395Sbapt else { 1298275223Sbapt ucl_set_err (parser, UCL_ESYNTAX, "unexpected '=' character", 1299275223Sbapt &parser->err); 1300262395Sbapt return false; 1301262395Sbapt } 1302262395Sbapt } 1303262395Sbapt else if (*p == ':') { 1304262395Sbapt if (!got_eq && !got_semicolon) { 1305262395Sbapt ucl_chunk_skipc (chunk, p); 1306262395Sbapt got_semicolon = true; 1307262395Sbapt } 1308262395Sbapt else { 1309275223Sbapt ucl_set_err (parser, UCL_ESYNTAX, "unexpected ':' character", 1310275223Sbapt &parser->err); 1311262395Sbapt return false; 1312262395Sbapt } 1313262395Sbapt } 1314262395Sbapt else if (chunk->remain >= 2 && ucl_lex_is_comment (p[0], p[1])) { 1315262395Sbapt /* Check for comment */ 1316262395Sbapt if (!ucl_skip_comments (parser)) { 1317262395Sbapt return false; 1318262395Sbapt } 1319262395Sbapt p = chunk->pos; 1320262395Sbapt } 1321262395Sbapt else { 1322262395Sbapt /* Start value */ 1323262395Sbapt break; 1324262395Sbapt } 1325262395Sbapt } 1326262395Sbapt 1327262395Sbapt if (p >= chunk->end && got_content) { 1328275223Sbapt ucl_set_err (parser, UCL_ESYNTAX, "unfinished key", &parser->err); 1329262395Sbapt return false; 1330262395Sbapt } 1331262395Sbapt 1332262395Sbapt got_sep = got_semicolon || got_eq; 1333262395Sbapt 1334262395Sbapt if (!got_sep) { 1335262395Sbapt /* 1336262395Sbapt * Maybe we have more keys nested, so search for termination character. 1337262395Sbapt * Possible choices: 1338262395Sbapt * 1) key1 key2 ... keyN [:=] value <- we treat that as error 1339262395Sbapt * 2) key1 ... keyN {} or [] <- we treat that as nested objects 1340262395Sbapt * 3) key1 value[;,\n] <- we treat that as linear object 1341262395Sbapt */ 1342262395Sbapt t = p; 1343262395Sbapt *next_key = false; 1344262395Sbapt while (ucl_test_character (*t, UCL_CHARACTER_WHITESPACE)) { 1345262395Sbapt t ++; 1346262395Sbapt } 1347262395Sbapt /* Check first non-space character after a key */ 1348262395Sbapt if (*t != '{' && *t != '[') { 1349262395Sbapt while (t < chunk->end) { 1350262395Sbapt if (*t == ',' || *t == ';' || *t == '\n' || *t == '\r') { 1351262395Sbapt break; 1352262395Sbapt } 1353262395Sbapt else if (*t == '{' || *t == '[') { 1354262395Sbapt *next_key = true; 1355262395Sbapt break; 1356262395Sbapt } 1357262395Sbapt t ++; 1358262395Sbapt } 1359262395Sbapt } 1360262395Sbapt } 1361262395Sbapt 1362262395Sbapt /* Create a new object */ 1363275223Sbapt nobj = ucl_object_new_full (UCL_NULL, parser->chunks->priority); 1364262395Sbapt keylen = ucl_copy_or_store_ptr (parser, c, &nobj->trash_stack[UCL_TRASH_KEY], 1365262395Sbapt &key, end - c, need_unescape, parser->flags & UCL_PARSER_KEY_LOWERCASE, false); 1366262395Sbapt if (keylen == -1) { 1367264789Sbapt ucl_object_unref (nobj); 1368262395Sbapt return false; 1369262395Sbapt } 1370262395Sbapt else if (keylen == 0) { 1371275223Sbapt ucl_set_err (parser, UCL_ESYNTAX, "empty keys are not allowed", &parser->err); 1372264789Sbapt ucl_object_unref (nobj); 1373262395Sbapt return false; 1374262395Sbapt } 1375262395Sbapt 1376262395Sbapt nobj->key = key; 1377262395Sbapt nobj->keylen = keylen; 1378290071Sbapt 1379290071Sbapt if (!ucl_parser_process_object_element (parser, nobj)) { 1380290071Sbapt return false; 1381262395Sbapt } 1382262395Sbapt 1383262395Sbapt if (ucl_escape) { 1384262395Sbapt nobj->flags |= UCL_OBJECT_NEED_KEY_ESCAPE; 1385262395Sbapt } 1386262395Sbapt 1387262395Sbapt 1388262395Sbapt return true; 1389262395Sbapt} 1390262395Sbapt 1391262395Sbapt/** 1392262395Sbapt * Parse a cl string 1393262395Sbapt * @param parser 1394262395Sbapt * @param chunk 1395290071Sbapt * @param var_expand 1396290071Sbapt * @param need_unescape 1397262395Sbapt * @return true if a key has been parsed 1398262395Sbapt */ 1399262395Sbaptstatic bool 1400262395Sbaptucl_parse_string_value (struct ucl_parser *parser, 1401262395Sbapt struct ucl_chunk *chunk, bool *var_expand, bool *need_unescape) 1402262395Sbapt{ 1403262395Sbapt const unsigned char *p; 1404262395Sbapt enum { 1405262395Sbapt UCL_BRACE_ROUND = 0, 1406262395Sbapt UCL_BRACE_SQUARE, 1407262395Sbapt UCL_BRACE_FIGURE 1408262395Sbapt }; 1409262395Sbapt int braces[3][2] = {{0, 0}, {0, 0}, {0, 0}}; 1410262395Sbapt 1411262395Sbapt p = chunk->pos; 1412262395Sbapt 1413262395Sbapt while (p < chunk->end) { 1414262395Sbapt 1415262395Sbapt /* Skip pairs of figure braces */ 1416262395Sbapt if (*p == '{') { 1417262395Sbapt braces[UCL_BRACE_FIGURE][0] ++; 1418262395Sbapt } 1419262395Sbapt else if (*p == '}') { 1420262395Sbapt braces[UCL_BRACE_FIGURE][1] ++; 1421262395Sbapt if (braces[UCL_BRACE_FIGURE][1] <= braces[UCL_BRACE_FIGURE][0]) { 1422262395Sbapt /* This is not a termination symbol, continue */ 1423262395Sbapt ucl_chunk_skipc (chunk, p); 1424262395Sbapt continue; 1425262395Sbapt } 1426262395Sbapt } 1427262395Sbapt /* Skip pairs of square braces */ 1428262395Sbapt else if (*p == '[') { 1429262395Sbapt braces[UCL_BRACE_SQUARE][0] ++; 1430262395Sbapt } 1431262395Sbapt else if (*p == ']') { 1432262395Sbapt braces[UCL_BRACE_SQUARE][1] ++; 1433262395Sbapt if (braces[UCL_BRACE_SQUARE][1] <= braces[UCL_BRACE_SQUARE][0]) { 1434262395Sbapt /* This is not a termination symbol, continue */ 1435262395Sbapt ucl_chunk_skipc (chunk, p); 1436262395Sbapt continue; 1437262395Sbapt } 1438262395Sbapt } 1439262395Sbapt else if (*p == '$') { 1440262395Sbapt *var_expand = true; 1441262395Sbapt } 1442262395Sbapt else if (*p == '\\') { 1443262395Sbapt *need_unescape = true; 1444262395Sbapt ucl_chunk_skipc (chunk, p); 1445262395Sbapt if (p < chunk->end) { 1446262395Sbapt ucl_chunk_skipc (chunk, p); 1447262395Sbapt } 1448262395Sbapt continue; 1449262395Sbapt } 1450262395Sbapt 1451262395Sbapt if (ucl_lex_is_atom_end (*p) || (chunk->remain >= 2 && ucl_lex_is_comment (p[0], p[1]))) { 1452262395Sbapt break; 1453262395Sbapt } 1454262395Sbapt ucl_chunk_skipc (chunk, p); 1455262395Sbapt } 1456262395Sbapt 1457262395Sbapt return true; 1458262395Sbapt} 1459262395Sbapt 1460262395Sbapt/** 1461262395Sbapt * Parse multiline string ending with \n{term}\n 1462262395Sbapt * @param parser 1463262395Sbapt * @param chunk 1464262395Sbapt * @param term 1465262395Sbapt * @param term_len 1466290071Sbapt * @param beg 1467290071Sbapt * @param var_expand 1468262395Sbapt * @return size of multiline string or 0 in case of error 1469262395Sbapt */ 1470262395Sbaptstatic int 1471262395Sbaptucl_parse_multiline_string (struct ucl_parser *parser, 1472262395Sbapt struct ucl_chunk *chunk, const unsigned char *term, 1473262395Sbapt int term_len, unsigned char const **beg, 1474262395Sbapt bool *var_expand) 1475262395Sbapt{ 1476275223Sbapt const unsigned char *p, *c, *tend; 1477262395Sbapt bool newline = false; 1478262395Sbapt int len = 0; 1479262395Sbapt 1480262395Sbapt p = chunk->pos; 1481262395Sbapt 1482262395Sbapt c = p; 1483262395Sbapt 1484262395Sbapt while (p < chunk->end) { 1485262395Sbapt if (newline) { 1486262395Sbapt if (chunk->end - p < term_len) { 1487262395Sbapt return 0; 1488262395Sbapt } 1489275223Sbapt else if (memcmp (p, term, term_len) == 0) { 1490275223Sbapt tend = p + term_len; 1491275223Sbapt if (*tend != '\n' && *tend != ';' && *tend != ',') { 1492275223Sbapt /* Incomplete terminator */ 1493275223Sbapt ucl_chunk_skipc (chunk, p); 1494275223Sbapt continue; 1495275223Sbapt } 1496262395Sbapt len = p - c; 1497262395Sbapt chunk->remain -= term_len; 1498262395Sbapt chunk->pos = p + term_len; 1499262395Sbapt chunk->column = term_len; 1500262395Sbapt *beg = c; 1501262395Sbapt break; 1502262395Sbapt } 1503262395Sbapt } 1504262395Sbapt if (*p == '\n') { 1505262395Sbapt newline = true; 1506262395Sbapt } 1507262395Sbapt else { 1508262395Sbapt if (*p == '$') { 1509262395Sbapt *var_expand = true; 1510262395Sbapt } 1511262395Sbapt newline = false; 1512262395Sbapt } 1513262395Sbapt ucl_chunk_skipc (chunk, p); 1514262395Sbapt } 1515262395Sbapt 1516262395Sbapt return len; 1517262395Sbapt} 1518262395Sbapt 1519290071Sbaptstatic inline ucl_object_t* 1520290071Sbaptucl_parser_get_container (struct ucl_parser *parser) 1521262975Sbapt{ 1522262975Sbapt ucl_object_t *t, *obj = NULL; 1523262975Sbapt 1524279549Sbapt if (parser == NULL || parser->stack == NULL || parser->stack->obj == NULL) { 1525279549Sbapt return NULL; 1526279549Sbapt } 1527279549Sbapt 1528262975Sbapt if (parser->stack->obj->type == UCL_ARRAY) { 1529262975Sbapt /* Object must be allocated */ 1530275223Sbapt obj = ucl_object_new_full (UCL_NULL, parser->chunks->priority); 1531279549Sbapt t = parser->stack->obj; 1532290071Sbapt 1533290071Sbapt if (!ucl_array_append (t, obj)) { 1534290071Sbapt ucl_object_unref (obj); 1535290071Sbapt return NULL; 1536290071Sbapt } 1537290071Sbapt 1538262975Sbapt parser->cur_obj = obj; 1539298166Sbapt ucl_attach_comment (parser, obj, false); 1540262975Sbapt } 1541262975Sbapt else { 1542262975Sbapt /* Object has been already allocated */ 1543262975Sbapt obj = parser->cur_obj; 1544262975Sbapt } 1545262975Sbapt 1546262975Sbapt return obj; 1547262975Sbapt} 1548262975Sbapt 1549262395Sbapt/** 1550262395Sbapt * Handle value data 1551262395Sbapt * @param parser 1552262395Sbapt * @param chunk 1553262395Sbapt * @return 1554262395Sbapt */ 1555262395Sbaptstatic bool 1556262395Sbaptucl_parse_value (struct ucl_parser *parser, struct ucl_chunk *chunk) 1557262395Sbapt{ 1558262395Sbapt const unsigned char *p, *c; 1559262975Sbapt ucl_object_t *obj = NULL; 1560262395Sbapt unsigned int stripped_spaces; 1561262395Sbapt int str_len; 1562262395Sbapt bool need_unescape = false, ucl_escape = false, var_expand = false; 1563262395Sbapt 1564262395Sbapt p = chunk->pos; 1565262395Sbapt 1566262975Sbapt /* Skip any spaces and comments */ 1567262975Sbapt if (ucl_test_character (*p, UCL_CHARACTER_WHITESPACE_UNSAFE) || 1568262975Sbapt (chunk->remain >= 2 && ucl_lex_is_comment (p[0], p[1]))) { 1569262975Sbapt while (p < chunk->end && ucl_test_character (*p, UCL_CHARACTER_WHITESPACE_UNSAFE)) { 1570262975Sbapt ucl_chunk_skipc (chunk, p); 1571262975Sbapt } 1572262975Sbapt if (!ucl_skip_comments (parser)) { 1573262975Sbapt return false; 1574262975Sbapt } 1575262975Sbapt p = chunk->pos; 1576262975Sbapt } 1577262975Sbapt 1578262395Sbapt while (p < chunk->end) { 1579262395Sbapt c = p; 1580262395Sbapt switch (*p) { 1581262395Sbapt case '"': 1582262395Sbapt ucl_chunk_skipc (chunk, p); 1583290071Sbapt 1584290071Sbapt if (!ucl_lex_json_string (parser, chunk, &need_unescape, &ucl_escape, 1585290071Sbapt &var_expand)) { 1586262395Sbapt return false; 1587262395Sbapt } 1588290071Sbapt 1589290071Sbapt obj = ucl_parser_get_container (parser); 1590298166Sbapt if (!obj) { 1591298166Sbapt return false; 1592298166Sbapt } 1593298166Sbapt 1594262395Sbapt str_len = chunk->pos - c - 2; 1595262395Sbapt obj->type = UCL_STRING; 1596290071Sbapt if ((str_len = ucl_copy_or_store_ptr (parser, c + 1, 1597290071Sbapt &obj->trash_stack[UCL_TRASH_VALUE], 1598290071Sbapt &obj->value.sv, str_len, need_unescape, false, 1599290071Sbapt var_expand)) == -1) { 1600262395Sbapt return false; 1601262395Sbapt } 1602262395Sbapt obj->len = str_len; 1603290071Sbapt 1604262395Sbapt parser->state = UCL_STATE_AFTER_VALUE; 1605262395Sbapt p = chunk->pos; 1606290071Sbapt 1607262395Sbapt return true; 1608262395Sbapt break; 1609262395Sbapt case '{': 1610290071Sbapt obj = ucl_parser_get_container (parser); 1611262395Sbapt /* We have a new object */ 1612290071Sbapt obj = ucl_parser_add_container (obj, parser, false, parser->stack->level); 1613263648Sbapt if (obj == NULL) { 1614263648Sbapt return false; 1615263648Sbapt } 1616262395Sbapt 1617262395Sbapt ucl_chunk_skipc (chunk, p); 1618290071Sbapt 1619262395Sbapt return true; 1620262395Sbapt break; 1621262395Sbapt case '[': 1622290071Sbapt obj = ucl_parser_get_container (parser); 1623262395Sbapt /* We have a new array */ 1624290071Sbapt obj = ucl_parser_add_container (obj, parser, true, parser->stack->level); 1625263648Sbapt if (obj == NULL) { 1626263648Sbapt return false; 1627263648Sbapt } 1628262395Sbapt 1629262395Sbapt ucl_chunk_skipc (chunk, p); 1630290071Sbapt 1631262395Sbapt return true; 1632262395Sbapt break; 1633262975Sbapt case ']': 1634262975Sbapt /* We have the array ending */ 1635262975Sbapt if (parser->stack && parser->stack->obj->type == UCL_ARRAY) { 1636262975Sbapt parser->state = UCL_STATE_AFTER_VALUE; 1637262975Sbapt return true; 1638262975Sbapt } 1639262975Sbapt else { 1640262975Sbapt goto parse_string; 1641262975Sbapt } 1642262975Sbapt break; 1643262395Sbapt case '<': 1644290071Sbapt obj = ucl_parser_get_container (parser); 1645262395Sbapt /* We have something like multiline value, which must be <<[A-Z]+\n */ 1646262395Sbapt if (chunk->end - p > 3) { 1647262395Sbapt if (memcmp (p, "<<", 2) == 0) { 1648262395Sbapt p += 2; 1649262395Sbapt /* We allow only uppercase characters in multiline definitions */ 1650262395Sbapt while (p < chunk->end && *p >= 'A' && *p <= 'Z') { 1651262395Sbapt p ++; 1652262395Sbapt } 1653262395Sbapt if (*p =='\n') { 1654262395Sbapt /* Set chunk positions and start multiline parsing */ 1655262395Sbapt c += 2; 1656262395Sbapt chunk->remain -= p - c; 1657262395Sbapt chunk->pos = p + 1; 1658262395Sbapt chunk->column = 0; 1659262395Sbapt chunk->line ++; 1660262395Sbapt if ((str_len = ucl_parse_multiline_string (parser, chunk, c, 1661262395Sbapt p - c, &c, &var_expand)) == 0) { 1662275223Sbapt ucl_set_err (parser, UCL_ESYNTAX, 1663275223Sbapt "unterminated multiline value", &parser->err); 1664262395Sbapt return false; 1665262395Sbapt } 1666290071Sbapt 1667262395Sbapt obj->type = UCL_STRING; 1668290071Sbapt obj->flags |= UCL_OBJECT_MULTILINE; 1669290071Sbapt if ((str_len = ucl_copy_or_store_ptr (parser, c, 1670290071Sbapt &obj->trash_stack[UCL_TRASH_VALUE], 1671290071Sbapt &obj->value.sv, str_len - 1, false, 1672290071Sbapt false, var_expand)) == -1) { 1673262395Sbapt return false; 1674262395Sbapt } 1675262395Sbapt obj->len = str_len; 1676290071Sbapt 1677262395Sbapt parser->state = UCL_STATE_AFTER_VALUE; 1678290071Sbapt 1679262395Sbapt return true; 1680262395Sbapt } 1681262395Sbapt } 1682262395Sbapt } 1683262395Sbapt /* Fallback to ordinary strings */ 1684262395Sbapt default: 1685262975Sbaptparse_string: 1686262975Sbapt if (obj == NULL) { 1687290071Sbapt obj = ucl_parser_get_container (parser); 1688262395Sbapt } 1689290071Sbapt 1690262395Sbapt /* Parse atom */ 1691262395Sbapt if (ucl_test_character (*p, UCL_CHARACTER_VALUE_DIGIT_START)) { 1692262395Sbapt if (!ucl_lex_number (parser, chunk, obj)) { 1693262395Sbapt if (parser->state == UCL_STATE_ERROR) { 1694262395Sbapt return false; 1695262395Sbapt } 1696262395Sbapt } 1697262395Sbapt else { 1698262395Sbapt parser->state = UCL_STATE_AFTER_VALUE; 1699262395Sbapt return true; 1700262395Sbapt } 1701262395Sbapt /* Fallback to normal string */ 1702262395Sbapt } 1703262395Sbapt 1704290071Sbapt if (!ucl_parse_string_value (parser, chunk, &var_expand, 1705290071Sbapt &need_unescape)) { 1706262395Sbapt return false; 1707262395Sbapt } 1708262395Sbapt /* Cut trailing spaces */ 1709262395Sbapt stripped_spaces = 0; 1710262395Sbapt while (ucl_test_character (*(chunk->pos - 1 - stripped_spaces), 1711262395Sbapt UCL_CHARACTER_WHITESPACE)) { 1712262395Sbapt stripped_spaces ++; 1713262395Sbapt } 1714262395Sbapt str_len = chunk->pos - c - stripped_spaces; 1715262395Sbapt if (str_len <= 0) { 1716290071Sbapt ucl_set_err (parser, UCL_ESYNTAX, "string value must not be empty", 1717275223Sbapt &parser->err); 1718262395Sbapt return false; 1719262395Sbapt } 1720262395Sbapt else if (str_len == 4 && memcmp (c, "null", 4) == 0) { 1721262395Sbapt obj->len = 0; 1722262395Sbapt obj->type = UCL_NULL; 1723262395Sbapt } 1724262395Sbapt else if (!ucl_maybe_parse_boolean (obj, c, str_len)) { 1725262395Sbapt obj->type = UCL_STRING; 1726290071Sbapt if ((str_len = ucl_copy_or_store_ptr (parser, c, 1727290071Sbapt &obj->trash_stack[UCL_TRASH_VALUE], 1728262395Sbapt &obj->value.sv, str_len, need_unescape, 1729262395Sbapt false, var_expand)) == -1) { 1730262395Sbapt return false; 1731262395Sbapt } 1732262395Sbapt obj->len = str_len; 1733262395Sbapt } 1734262395Sbapt parser->state = UCL_STATE_AFTER_VALUE; 1735262395Sbapt p = chunk->pos; 1736262395Sbapt 1737262395Sbapt return true; 1738262395Sbapt break; 1739262395Sbapt } 1740262395Sbapt } 1741262395Sbapt 1742262395Sbapt return true; 1743262395Sbapt} 1744262395Sbapt 1745262395Sbapt/** 1746262395Sbapt * Handle after value data 1747262395Sbapt * @param parser 1748262395Sbapt * @param chunk 1749262395Sbapt * @return 1750262395Sbapt */ 1751262395Sbaptstatic bool 1752262395Sbaptucl_parse_after_value (struct ucl_parser *parser, struct ucl_chunk *chunk) 1753262395Sbapt{ 1754262395Sbapt const unsigned char *p; 1755262395Sbapt bool got_sep = false; 1756262395Sbapt struct ucl_stack *st; 1757262395Sbapt 1758262395Sbapt p = chunk->pos; 1759262395Sbapt 1760262395Sbapt while (p < chunk->end) { 1761262395Sbapt if (ucl_test_character (*p, UCL_CHARACTER_WHITESPACE)) { 1762262395Sbapt /* Skip whitespaces */ 1763262395Sbapt ucl_chunk_skipc (chunk, p); 1764262395Sbapt } 1765262395Sbapt else if (chunk->remain >= 2 && ucl_lex_is_comment (p[0], p[1])) { 1766262395Sbapt /* Skip comment */ 1767262395Sbapt if (!ucl_skip_comments (parser)) { 1768262395Sbapt return false; 1769262395Sbapt } 1770262395Sbapt /* Treat comment as a separator */ 1771262395Sbapt got_sep = true; 1772262395Sbapt p = chunk->pos; 1773262395Sbapt } 1774262395Sbapt else if (ucl_test_character (*p, UCL_CHARACTER_VALUE_END)) { 1775262395Sbapt if (*p == '}' || *p == ']') { 1776262395Sbapt if (parser->stack == NULL) { 1777275223Sbapt ucl_set_err (parser, UCL_ESYNTAX, 1778275223Sbapt "end of array or object detected without corresponding start", 1779275223Sbapt &parser->err); 1780262395Sbapt return false; 1781262395Sbapt } 1782262395Sbapt if ((*p == '}' && parser->stack->obj->type == UCL_OBJECT) || 1783262395Sbapt (*p == ']' && parser->stack->obj->type == UCL_ARRAY)) { 1784262395Sbapt 1785262395Sbapt /* Pop all nested objects from a stack */ 1786262395Sbapt st = parser->stack; 1787262395Sbapt parser->stack = st->next; 1788262395Sbapt UCL_FREE (sizeof (struct ucl_stack), st); 1789262395Sbapt 1790298166Sbapt if (parser->cur_obj) { 1791298166Sbapt ucl_attach_comment (parser, parser->cur_obj, true); 1792298166Sbapt } 1793298166Sbapt 1794262395Sbapt while (parser->stack != NULL) { 1795262395Sbapt st = parser->stack; 1796298166Sbapt 1797262395Sbapt if (st->next == NULL || st->next->level == st->level) { 1798262395Sbapt break; 1799262395Sbapt } 1800298166Sbapt 1801262395Sbapt parser->stack = st->next; 1802298166Sbapt parser->cur_obj = st->obj; 1803262395Sbapt UCL_FREE (sizeof (struct ucl_stack), st); 1804262395Sbapt } 1805262395Sbapt } 1806262395Sbapt else { 1807275223Sbapt ucl_set_err (parser, UCL_ESYNTAX, 1808275223Sbapt "unexpected terminating symbol detected", 1809275223Sbapt &parser->err); 1810262395Sbapt return false; 1811262395Sbapt } 1812262395Sbapt 1813262395Sbapt if (parser->stack == NULL) { 1814262395Sbapt /* Ignore everything after a top object */ 1815262395Sbapt return true; 1816262395Sbapt } 1817262395Sbapt else { 1818262395Sbapt ucl_chunk_skipc (chunk, p); 1819262395Sbapt } 1820262395Sbapt got_sep = true; 1821262395Sbapt } 1822262395Sbapt else { 1823262395Sbapt /* Got a separator */ 1824262395Sbapt got_sep = true; 1825262395Sbapt ucl_chunk_skipc (chunk, p); 1826262395Sbapt } 1827262395Sbapt } 1828262395Sbapt else { 1829262395Sbapt /* Anything else */ 1830262395Sbapt if (!got_sep) { 1831275223Sbapt ucl_set_err (parser, UCL_ESYNTAX, "delimiter is missing", 1832275223Sbapt &parser->err); 1833262395Sbapt return false; 1834262395Sbapt } 1835262395Sbapt return true; 1836262395Sbapt } 1837262395Sbapt } 1838262395Sbapt 1839262395Sbapt return true; 1840262395Sbapt} 1841262395Sbapt 1842298166Sbaptstatic bool 1843298166Sbaptucl_skip_macro_as_comment (struct ucl_parser *parser, 1844298166Sbapt struct ucl_chunk *chunk) 1845298166Sbapt{ 1846298166Sbapt const unsigned char *p, *c; 1847298166Sbapt enum { 1848298166Sbapt macro_skip_start = 0, 1849298166Sbapt macro_has_symbols, 1850298166Sbapt macro_has_obrace, 1851298166Sbapt macro_has_quote, 1852298166Sbapt macro_has_backslash, 1853298166Sbapt macro_has_sqbrace, 1854298166Sbapt macro_save 1855298166Sbapt } state = macro_skip_start, prev_state = macro_skip_start; 1856298166Sbapt 1857298166Sbapt p = chunk->pos; 1858298166Sbapt c = chunk->pos; 1859298166Sbapt 1860298166Sbapt while (p < chunk->end) { 1861298166Sbapt switch (state) { 1862298166Sbapt case macro_skip_start: 1863298166Sbapt if (!ucl_test_character (*p, UCL_CHARACTER_WHITESPACE)) { 1864298166Sbapt state = macro_has_symbols; 1865298166Sbapt } 1866298166Sbapt else if (ucl_test_character (*p, UCL_CHARACTER_WHITESPACE_UNSAFE)) { 1867298166Sbapt state = macro_save; 1868298166Sbapt continue; 1869298166Sbapt } 1870298166Sbapt 1871298166Sbapt ucl_chunk_skipc (chunk, p); 1872298166Sbapt break; 1873298166Sbapt 1874298166Sbapt case macro_has_symbols: 1875298166Sbapt if (*p == '{') { 1876298166Sbapt state = macro_has_sqbrace; 1877298166Sbapt } 1878298166Sbapt else if (*p == '(') { 1879298166Sbapt state = macro_has_obrace; 1880298166Sbapt } 1881298166Sbapt else if (*p == '"') { 1882298166Sbapt state = macro_has_quote; 1883298166Sbapt } 1884298166Sbapt else if (*p == '\n') { 1885298166Sbapt state = macro_save; 1886298166Sbapt continue; 1887298166Sbapt } 1888298166Sbapt 1889298166Sbapt ucl_chunk_skipc (chunk, p); 1890298166Sbapt break; 1891298166Sbapt 1892298166Sbapt case macro_has_obrace: 1893298166Sbapt if (*p == '\\') { 1894298166Sbapt prev_state = state; 1895298166Sbapt state = macro_has_backslash; 1896298166Sbapt } 1897298166Sbapt else if (*p == ')') { 1898298166Sbapt state = macro_has_symbols; 1899298166Sbapt } 1900298166Sbapt 1901298166Sbapt ucl_chunk_skipc (chunk, p); 1902298166Sbapt break; 1903298166Sbapt 1904298166Sbapt case macro_has_sqbrace: 1905298166Sbapt if (*p == '\\') { 1906298166Sbapt prev_state = state; 1907298166Sbapt state = macro_has_backslash; 1908298166Sbapt } 1909298166Sbapt else if (*p == '}') { 1910298166Sbapt state = macro_save; 1911298166Sbapt } 1912298166Sbapt 1913298166Sbapt ucl_chunk_skipc (chunk, p); 1914298166Sbapt break; 1915298166Sbapt 1916298166Sbapt case macro_has_quote: 1917298166Sbapt if (*p == '\\') { 1918298166Sbapt prev_state = state; 1919298166Sbapt state = macro_has_backslash; 1920298166Sbapt } 1921298166Sbapt else if (*p == '"') { 1922298166Sbapt state = macro_save; 1923298166Sbapt } 1924298166Sbapt 1925298166Sbapt ucl_chunk_skipc (chunk, p); 1926298166Sbapt break; 1927298166Sbapt 1928298166Sbapt case macro_has_backslash: 1929298166Sbapt state = prev_state; 1930298166Sbapt ucl_chunk_skipc (chunk, p); 1931298166Sbapt break; 1932298166Sbapt 1933298166Sbapt case macro_save: 1934298166Sbapt if (parser->flags & UCL_PARSER_SAVE_COMMENTS) { 1935298166Sbapt ucl_save_comment (parser, c, p - c); 1936298166Sbapt } 1937298166Sbapt 1938298166Sbapt return true; 1939298166Sbapt } 1940298166Sbapt } 1941298166Sbapt 1942298166Sbapt return false; 1943298166Sbapt} 1944298166Sbapt 1945262395Sbapt/** 1946262395Sbapt * Handle macro data 1947262395Sbapt * @param parser 1948262395Sbapt * @param chunk 1949290071Sbapt * @param marco 1950290071Sbapt * @param macro_start 1951290071Sbapt * @param macro_len 1952262395Sbapt * @return 1953262395Sbapt */ 1954262395Sbaptstatic bool 1955262395Sbaptucl_parse_macro_value (struct ucl_parser *parser, 1956262395Sbapt struct ucl_chunk *chunk, struct ucl_macro *macro, 1957262395Sbapt unsigned char const **macro_start, size_t *macro_len) 1958262395Sbapt{ 1959262395Sbapt const unsigned char *p, *c; 1960262395Sbapt bool need_unescape = false, ucl_escape = false, var_expand = false; 1961262395Sbapt 1962262395Sbapt p = chunk->pos; 1963262395Sbapt 1964262395Sbapt switch (*p) { 1965262395Sbapt case '"': 1966262395Sbapt /* We have macro value encoded in quotes */ 1967262395Sbapt c = p; 1968262395Sbapt ucl_chunk_skipc (chunk, p); 1969262395Sbapt if (!ucl_lex_json_string (parser, chunk, &need_unescape, &ucl_escape, &var_expand)) { 1970262395Sbapt return false; 1971262395Sbapt } 1972262395Sbapt 1973262395Sbapt *macro_start = c + 1; 1974262395Sbapt *macro_len = chunk->pos - c - 2; 1975262395Sbapt p = chunk->pos; 1976262395Sbapt break; 1977262395Sbapt case '{': 1978262395Sbapt /* We got a multiline macro body */ 1979262395Sbapt ucl_chunk_skipc (chunk, p); 1980262395Sbapt /* Skip spaces at the beginning */ 1981262395Sbapt while (p < chunk->end) { 1982262395Sbapt if (ucl_test_character (*p, UCL_CHARACTER_WHITESPACE_UNSAFE)) { 1983262395Sbapt ucl_chunk_skipc (chunk, p); 1984262395Sbapt } 1985262395Sbapt else { 1986262395Sbapt break; 1987262395Sbapt } 1988262395Sbapt } 1989262395Sbapt c = p; 1990262395Sbapt while (p < chunk->end) { 1991262395Sbapt if (*p == '}') { 1992262395Sbapt break; 1993262395Sbapt } 1994262395Sbapt ucl_chunk_skipc (chunk, p); 1995262395Sbapt } 1996262395Sbapt *macro_start = c; 1997262395Sbapt *macro_len = p - c; 1998262395Sbapt ucl_chunk_skipc (chunk, p); 1999262395Sbapt break; 2000262395Sbapt default: 2001262395Sbapt /* Macro is not enclosed in quotes or braces */ 2002262395Sbapt c = p; 2003262395Sbapt while (p < chunk->end) { 2004262395Sbapt if (ucl_lex_is_atom_end (*p)) { 2005262395Sbapt break; 2006262395Sbapt } 2007262395Sbapt ucl_chunk_skipc (chunk, p); 2008262395Sbapt } 2009262395Sbapt *macro_start = c; 2010262395Sbapt *macro_len = p - c; 2011262395Sbapt break; 2012262395Sbapt } 2013262395Sbapt 2014262395Sbapt /* We are at the end of a macro */ 2015262395Sbapt /* Skip ';' and space characters and return to previous state */ 2016262395Sbapt while (p < chunk->end) { 2017262395Sbapt if (!ucl_test_character (*p, UCL_CHARACTER_WHITESPACE_UNSAFE) && *p != ';') { 2018262395Sbapt break; 2019262395Sbapt } 2020262395Sbapt ucl_chunk_skipc (chunk, p); 2021262395Sbapt } 2022262395Sbapt return true; 2023262395Sbapt} 2024262395Sbapt 2025262395Sbapt/** 2026275223Sbapt * Parse macro arguments as UCL object 2027275223Sbapt * @param parser parser structure 2028275223Sbapt * @param chunk the current data chunk 2029275223Sbapt * @return 2030275223Sbapt */ 2031275223Sbaptstatic ucl_object_t * 2032275223Sbaptucl_parse_macro_arguments (struct ucl_parser *parser, 2033275223Sbapt struct ucl_chunk *chunk) 2034275223Sbapt{ 2035275223Sbapt ucl_object_t *res = NULL; 2036275223Sbapt struct ucl_parser *params_parser; 2037275223Sbapt int obraces = 1, ebraces = 0, state = 0; 2038275223Sbapt const unsigned char *p, *c; 2039275223Sbapt size_t args_len = 0; 2040275223Sbapt struct ucl_parser_saved_state saved; 2041275223Sbapt 2042275223Sbapt saved.column = chunk->column; 2043275223Sbapt saved.line = chunk->line; 2044275223Sbapt saved.pos = chunk->pos; 2045275223Sbapt saved.remain = chunk->remain; 2046275223Sbapt p = chunk->pos; 2047275223Sbapt 2048275223Sbapt if (*p != '(' || chunk->remain < 2) { 2049275223Sbapt return NULL; 2050275223Sbapt } 2051275223Sbapt 2052275223Sbapt /* Set begin and start */ 2053275223Sbapt ucl_chunk_skipc (chunk, p); 2054275223Sbapt c = p; 2055275223Sbapt 2056275223Sbapt while ((p) < (chunk)->end) { 2057275223Sbapt switch (state) { 2058275223Sbapt case 0: 2059275223Sbapt /* Parse symbols and check for '(', ')' and '"' */ 2060275223Sbapt if (*p == '(') { 2061275223Sbapt obraces ++; 2062275223Sbapt } 2063275223Sbapt else if (*p == ')') { 2064275223Sbapt ebraces ++; 2065275223Sbapt } 2066275223Sbapt else if (*p == '"') { 2067275223Sbapt state = 1; 2068275223Sbapt } 2069275223Sbapt /* Check pairing */ 2070275223Sbapt if (obraces == ebraces) { 2071275223Sbapt state = 99; 2072275223Sbapt } 2073275223Sbapt else { 2074275223Sbapt args_len ++; 2075275223Sbapt } 2076275223Sbapt /* Check overflow */ 2077275223Sbapt if (chunk->remain == 0) { 2078275223Sbapt goto restore_chunk; 2079275223Sbapt } 2080275223Sbapt ucl_chunk_skipc (chunk, p); 2081275223Sbapt break; 2082275223Sbapt case 1: 2083275223Sbapt /* We have quote character, so skip all but quotes */ 2084275223Sbapt if (*p == '"' && *(p - 1) != '\\') { 2085275223Sbapt state = 0; 2086275223Sbapt } 2087275223Sbapt if (chunk->remain == 0) { 2088275223Sbapt goto restore_chunk; 2089275223Sbapt } 2090290071Sbapt args_len ++; 2091275223Sbapt ucl_chunk_skipc (chunk, p); 2092275223Sbapt break; 2093275223Sbapt case 99: 2094275223Sbapt /* 2095275223Sbapt * We have read the full body of arguments, so we need to parse and set 2096275223Sbapt * object from that 2097275223Sbapt */ 2098275223Sbapt params_parser = ucl_parser_new (parser->flags); 2099275223Sbapt if (!ucl_parser_add_chunk (params_parser, c, args_len)) { 2100275223Sbapt ucl_set_err (parser, UCL_ESYNTAX, "macro arguments parsing error", 2101275223Sbapt &parser->err); 2102275223Sbapt } 2103275223Sbapt else { 2104275223Sbapt res = ucl_parser_get_object (params_parser); 2105275223Sbapt } 2106275223Sbapt ucl_parser_free (params_parser); 2107275223Sbapt 2108275223Sbapt return res; 2109275223Sbapt 2110275223Sbapt break; 2111275223Sbapt } 2112275223Sbapt } 2113275223Sbapt 2114275223Sbapt return res; 2115275223Sbapt 2116275223Sbaptrestore_chunk: 2117275223Sbapt chunk->column = saved.column; 2118275223Sbapt chunk->line = saved.line; 2119275223Sbapt chunk->pos = saved.pos; 2120275223Sbapt chunk->remain = saved.remain; 2121275223Sbapt 2122275223Sbapt return NULL; 2123275223Sbapt} 2124275223Sbapt 2125275223Sbapt#define SKIP_SPACES_COMMENTS(parser, chunk, p) do { \ 2126275223Sbapt while ((p) < (chunk)->end) { \ 2127275223Sbapt if (!ucl_test_character (*(p), UCL_CHARACTER_WHITESPACE_UNSAFE)) { \ 2128275223Sbapt if ((chunk)->remain >= 2 && ucl_lex_is_comment ((p)[0], (p)[1])) { \ 2129275223Sbapt if (!ucl_skip_comments (parser)) { \ 2130275223Sbapt return false; \ 2131275223Sbapt } \ 2132275223Sbapt p = (chunk)->pos; \ 2133275223Sbapt } \ 2134275223Sbapt break; \ 2135275223Sbapt } \ 2136275223Sbapt ucl_chunk_skipc (chunk, p); \ 2137275223Sbapt } \ 2138275223Sbapt} while(0) 2139275223Sbapt 2140275223Sbapt/** 2141262395Sbapt * Handle the main states of rcl parser 2142262395Sbapt * @param parser parser structure 2143262395Sbapt * @return true if chunk has been parsed and false in case of error 2144262395Sbapt */ 2145262395Sbaptstatic bool 2146262395Sbaptucl_state_machine (struct ucl_parser *parser) 2147262395Sbapt{ 2148275223Sbapt ucl_object_t *obj, *macro_args; 2149262395Sbapt struct ucl_chunk *chunk = parser->chunks; 2150262395Sbapt const unsigned char *p, *c = NULL, *macro_start = NULL; 2151262395Sbapt unsigned char *macro_escaped; 2152262395Sbapt size_t macro_len = 0; 2153262395Sbapt struct ucl_macro *macro = NULL; 2154275223Sbapt bool next_key = false, end_of_object = false, ret; 2155262395Sbapt 2156262395Sbapt if (parser->top_obj == NULL) { 2157262395Sbapt parser->state = UCL_STATE_INIT; 2158262395Sbapt } 2159262395Sbapt 2160262395Sbapt p = chunk->pos; 2161262395Sbapt while (chunk->pos < chunk->end) { 2162262395Sbapt switch (parser->state) { 2163262395Sbapt case UCL_STATE_INIT: 2164262395Sbapt /* 2165262395Sbapt * At the init state we can either go to the parse array or object 2166262395Sbapt * if we got [ or { correspondingly or can just treat new data as 2167262395Sbapt * a key of newly created object 2168262395Sbapt */ 2169262395Sbapt if (!ucl_skip_comments (parser)) { 2170262395Sbapt parser->prev_state = parser->state; 2171262395Sbapt parser->state = UCL_STATE_ERROR; 2172262395Sbapt return false; 2173262395Sbapt } 2174262395Sbapt else { 2175268876Sbapt /* Skip any spaces */ 2176268876Sbapt while (p < chunk->end && ucl_test_character (*p, 2177268876Sbapt UCL_CHARACTER_WHITESPACE_UNSAFE)) { 2178268876Sbapt ucl_chunk_skipc (chunk, p); 2179268876Sbapt } 2180290071Sbapt 2181262395Sbapt p = chunk->pos; 2182290071Sbapt 2183262395Sbapt if (*p == '[') { 2184262395Sbapt parser->state = UCL_STATE_VALUE; 2185262395Sbapt ucl_chunk_skipc (chunk, p); 2186262395Sbapt } 2187262395Sbapt else { 2188262395Sbapt parser->state = UCL_STATE_KEY; 2189262395Sbapt if (*p == '{') { 2190262395Sbapt ucl_chunk_skipc (chunk, p); 2191262395Sbapt } 2192262395Sbapt } 2193290071Sbapt 2194290071Sbapt if (parser->top_obj == NULL) { 2195290071Sbapt if (parser->state == UCL_STATE_VALUE) { 2196290071Sbapt obj = ucl_parser_add_container (NULL, parser, true, 0); 2197290071Sbapt } 2198290071Sbapt else { 2199290071Sbapt obj = ucl_parser_add_container (NULL, parser, false, 0); 2200290071Sbapt } 2201290071Sbapt 2202290071Sbapt if (obj == NULL) { 2203290071Sbapt return false; 2204290071Sbapt } 2205290071Sbapt 2206290071Sbapt parser->top_obj = obj; 2207290071Sbapt parser->cur_obj = obj; 2208290071Sbapt } 2209290071Sbapt 2210262395Sbapt } 2211262395Sbapt break; 2212262395Sbapt case UCL_STATE_KEY: 2213262395Sbapt /* Skip any spaces */ 2214262395Sbapt while (p < chunk->end && ucl_test_character (*p, UCL_CHARACTER_WHITESPACE_UNSAFE)) { 2215262395Sbapt ucl_chunk_skipc (chunk, p); 2216262395Sbapt } 2217298166Sbapt if (p == chunk->end || *p == '}') { 2218262395Sbapt /* We have the end of an object */ 2219262395Sbapt parser->state = UCL_STATE_AFTER_VALUE; 2220262395Sbapt continue; 2221262395Sbapt } 2222262395Sbapt if (parser->stack == NULL) { 2223262395Sbapt /* No objects are on stack, but we want to parse a key */ 2224275223Sbapt ucl_set_err (parser, UCL_ESYNTAX, "top object is finished but the parser " 2225262395Sbapt "expects a key", &parser->err); 2226262395Sbapt parser->prev_state = parser->state; 2227262395Sbapt parser->state = UCL_STATE_ERROR; 2228262395Sbapt return false; 2229262395Sbapt } 2230262395Sbapt if (!ucl_parse_key (parser, chunk, &next_key, &end_of_object)) { 2231262395Sbapt parser->prev_state = parser->state; 2232262395Sbapt parser->state = UCL_STATE_ERROR; 2233262395Sbapt return false; 2234262395Sbapt } 2235262395Sbapt if (end_of_object) { 2236262395Sbapt p = chunk->pos; 2237262395Sbapt parser->state = UCL_STATE_AFTER_VALUE; 2238262395Sbapt continue; 2239262395Sbapt } 2240262395Sbapt else if (parser->state != UCL_STATE_MACRO_NAME) { 2241262395Sbapt if (next_key && parser->stack->obj->type == UCL_OBJECT) { 2242262395Sbapt /* Parse more keys and nest objects accordingly */ 2243290071Sbapt obj = ucl_parser_add_container (parser->cur_obj, parser, false, 2244263648Sbapt parser->stack->level + 1); 2245263648Sbapt if (obj == NULL) { 2246263648Sbapt return false; 2247263648Sbapt } 2248262395Sbapt } 2249262395Sbapt else { 2250262395Sbapt parser->state = UCL_STATE_VALUE; 2251262395Sbapt } 2252262395Sbapt } 2253262395Sbapt else { 2254262395Sbapt c = chunk->pos; 2255262395Sbapt } 2256262395Sbapt p = chunk->pos; 2257262395Sbapt break; 2258262395Sbapt case UCL_STATE_VALUE: 2259262395Sbapt /* We need to check what we do have */ 2260298166Sbapt if (!parser->cur_obj || !ucl_parse_value (parser, chunk)) { 2261262395Sbapt parser->prev_state = parser->state; 2262262395Sbapt parser->state = UCL_STATE_ERROR; 2263262395Sbapt return false; 2264262395Sbapt } 2265262395Sbapt /* State is set in ucl_parse_value call */ 2266262395Sbapt p = chunk->pos; 2267262395Sbapt break; 2268262395Sbapt case UCL_STATE_AFTER_VALUE: 2269262395Sbapt if (!ucl_parse_after_value (parser, chunk)) { 2270262395Sbapt parser->prev_state = parser->state; 2271262395Sbapt parser->state = UCL_STATE_ERROR; 2272262395Sbapt return false; 2273262395Sbapt } 2274290071Sbapt 2275262395Sbapt if (parser->stack != NULL) { 2276262395Sbapt if (parser->stack->obj->type == UCL_OBJECT) { 2277262395Sbapt parser->state = UCL_STATE_KEY; 2278262395Sbapt } 2279262395Sbapt else { 2280262395Sbapt /* Array */ 2281262395Sbapt parser->state = UCL_STATE_VALUE; 2282262395Sbapt } 2283262395Sbapt } 2284262395Sbapt else { 2285262395Sbapt /* Skip everything at the end */ 2286262395Sbapt return true; 2287262395Sbapt } 2288298166Sbapt 2289262395Sbapt p = chunk->pos; 2290262395Sbapt break; 2291262395Sbapt case UCL_STATE_MACRO_NAME: 2292298166Sbapt if (parser->flags & UCL_PARSER_DISABLE_MACRO) { 2293298166Sbapt if (!ucl_skip_macro_as_comment (parser, chunk)) { 2294298166Sbapt /* We have invalid macro */ 2295298166Sbapt ucl_create_err (&parser->err, 2296298166Sbapt "error on line %d at column %d: invalid macro", 2297298166Sbapt chunk->line, 2298298166Sbapt chunk->column); 2299298166Sbapt parser->state = UCL_STATE_ERROR; 2300298166Sbapt return false; 2301298166Sbapt } 2302298166Sbapt else { 2303298166Sbapt p = chunk->pos; 2304298166Sbapt parser->state = parser->prev_state; 2305298166Sbapt } 2306262395Sbapt } 2307290071Sbapt else { 2308298166Sbapt if (!ucl_test_character (*p, UCL_CHARACTER_WHITESPACE_UNSAFE) && 2309298166Sbapt *p != '(') { 2310298166Sbapt ucl_chunk_skipc (chunk, p); 2311298166Sbapt } 2312298166Sbapt else { 2313298166Sbapt if (c != NULL && p - c > 0) { 2314298166Sbapt /* We got macro name */ 2315298166Sbapt macro_len = (size_t) (p - c); 2316298166Sbapt HASH_FIND (hh, parser->macroes, c, macro_len, macro); 2317298166Sbapt if (macro == NULL) { 2318298166Sbapt ucl_create_err (&parser->err, 2319298166Sbapt "error on line %d at column %d: " 2320298166Sbapt "unknown macro: '%.*s', character: '%c'", 2321298166Sbapt chunk->line, 2322298166Sbapt chunk->column, 2323298166Sbapt (int) (p - c), 2324298166Sbapt c, 2325298166Sbapt *chunk->pos); 2326298166Sbapt parser->state = UCL_STATE_ERROR; 2327298166Sbapt return false; 2328298166Sbapt } 2329298166Sbapt /* Now we need to skip all spaces */ 2330298166Sbapt SKIP_SPACES_COMMENTS(parser, chunk, p); 2331298166Sbapt parser->state = UCL_STATE_MACRO; 2332298166Sbapt } 2333298166Sbapt else { 2334298166Sbapt /* We have invalid macro name */ 2335290071Sbapt ucl_create_err (&parser->err, 2336298166Sbapt "error on line %d at column %d: invalid macro name", 2337290071Sbapt chunk->line, 2338298166Sbapt chunk->column); 2339290071Sbapt parser->state = UCL_STATE_ERROR; 2340290071Sbapt return false; 2341290071Sbapt } 2342290071Sbapt } 2343262395Sbapt } 2344262395Sbapt break; 2345262395Sbapt case UCL_STATE_MACRO: 2346275223Sbapt if (*chunk->pos == '(') { 2347275223Sbapt macro_args = ucl_parse_macro_arguments (parser, chunk); 2348275223Sbapt p = chunk->pos; 2349275223Sbapt if (macro_args) { 2350275223Sbapt SKIP_SPACES_COMMENTS(parser, chunk, p); 2351275223Sbapt } 2352275223Sbapt } 2353275223Sbapt else { 2354275223Sbapt macro_args = NULL; 2355275223Sbapt } 2356262395Sbapt if (!ucl_parse_macro_value (parser, chunk, macro, 2357262395Sbapt ¯o_start, ¯o_len)) { 2358262395Sbapt parser->prev_state = parser->state; 2359262395Sbapt parser->state = UCL_STATE_ERROR; 2360262395Sbapt return false; 2361262395Sbapt } 2362275223Sbapt macro_len = ucl_expand_variable (parser, ¯o_escaped, 2363275223Sbapt macro_start, macro_len); 2364262395Sbapt parser->state = parser->prev_state; 2365298166Sbapt 2366298166Sbapt if (macro_escaped == NULL && macro != NULL) { 2367290071Sbapt if (macro->is_context) { 2368290071Sbapt ret = macro->h.context_handler (macro_start, macro_len, 2369290071Sbapt macro_args, 2370290071Sbapt parser->top_obj, 2371290071Sbapt macro->ud); 2372290071Sbapt } 2373290071Sbapt else { 2374290071Sbapt ret = macro->h.handler (macro_start, macro_len, macro_args, 2375290071Sbapt macro->ud); 2376290071Sbapt } 2377262395Sbapt } 2378298166Sbapt else if (macro != NULL) { 2379290071Sbapt if (macro->is_context) { 2380290071Sbapt ret = macro->h.context_handler (macro_escaped, macro_len, 2381290071Sbapt macro_args, 2382290071Sbapt parser->top_obj, 2383290071Sbapt macro->ud); 2384290071Sbapt } 2385290071Sbapt else { 2386290071Sbapt ret = macro->h.handler (macro_escaped, macro_len, macro_args, 2387275223Sbapt macro->ud); 2388290071Sbapt } 2389290071Sbapt 2390262395Sbapt UCL_FREE (macro_len + 1, macro_escaped); 2391262395Sbapt } 2392298166Sbapt else { 2393298166Sbapt ret = false; 2394298166Sbapt ucl_set_err (parser, UCL_EINTERNAL, 2395298166Sbapt "internal error: parser has macro undefined", &parser->err); 2396298166Sbapt } 2397290071Sbapt 2398290071Sbapt /* 2399290071Sbapt * Chunk can be modified within macro handler 2400290071Sbapt */ 2401290071Sbapt chunk = parser->chunks; 2402262395Sbapt p = chunk->pos; 2403298166Sbapt 2404275223Sbapt if (macro_args) { 2405275223Sbapt ucl_object_unref (macro_args); 2406275223Sbapt } 2407298166Sbapt 2408275223Sbapt if (!ret) { 2409275223Sbapt return false; 2410275223Sbapt } 2411262395Sbapt break; 2412262395Sbapt default: 2413275223Sbapt ucl_set_err (parser, UCL_EINTERNAL, 2414275223Sbapt "internal error: parser is in an unknown state", &parser->err); 2415262395Sbapt parser->state = UCL_STATE_ERROR; 2416262395Sbapt return false; 2417262395Sbapt } 2418262395Sbapt } 2419262395Sbapt 2420298166Sbapt if (parser->last_comment) { 2421298166Sbapt if (parser->cur_obj) { 2422298166Sbapt ucl_attach_comment (parser, parser->cur_obj, true); 2423298166Sbapt } 2424298166Sbapt else if (parser->stack && parser->stack->obj) { 2425298166Sbapt ucl_attach_comment (parser, parser->stack->obj, true); 2426298166Sbapt } 2427298166Sbapt else if (parser->top_obj) { 2428298166Sbapt ucl_attach_comment (parser, parser->top_obj, true); 2429298166Sbapt } 2430298166Sbapt else { 2431298166Sbapt ucl_object_unref (parser->last_comment); 2432298166Sbapt } 2433298166Sbapt } 2434298166Sbapt 2435262395Sbapt return true; 2436262395Sbapt} 2437262395Sbapt 2438262395Sbaptstruct ucl_parser* 2439262395Sbaptucl_parser_new (int flags) 2440262395Sbapt{ 2441298166Sbapt struct ucl_parser *parser; 2442262395Sbapt 2443298166Sbapt parser = UCL_ALLOC (sizeof (struct ucl_parser)); 2444298166Sbapt if (parser == NULL) { 2445263648Sbapt return NULL; 2446263648Sbapt } 2447290071Sbapt 2448298166Sbapt memset (parser, 0, sizeof (struct ucl_parser)); 2449262395Sbapt 2450298166Sbapt ucl_parser_register_macro (parser, "include", ucl_include_handler, parser); 2451298166Sbapt ucl_parser_register_macro (parser, "try_include", ucl_try_include_handler, parser); 2452298166Sbapt ucl_parser_register_macro (parser, "includes", ucl_includes_handler, parser); 2453298166Sbapt ucl_parser_register_macro (parser, "priority", ucl_priority_handler, parser); 2454298166Sbapt ucl_parser_register_macro (parser, "load", ucl_load_handler, parser); 2455298166Sbapt ucl_parser_register_context_macro (parser, "inherit", ucl_inherit_handler, parser); 2456262395Sbapt 2457298166Sbapt parser->flags = flags; 2458298166Sbapt parser->includepaths = NULL; 2459262395Sbapt 2460298166Sbapt if (flags & UCL_PARSER_SAVE_COMMENTS) { 2461298166Sbapt parser->comments = ucl_object_typed_new (UCL_OBJECT); 2462298166Sbapt } 2463298166Sbapt 2464262395Sbapt /* Initial assumption about filevars */ 2465298166Sbapt ucl_parser_set_filevars (parser, NULL, false); 2466262395Sbapt 2467298166Sbapt return parser; 2468262395Sbapt} 2469262395Sbapt 2470290071Sbaptbool 2471290071Sbaptucl_parser_set_default_priority (struct ucl_parser *parser, unsigned prio) 2472290071Sbapt{ 2473290071Sbapt if (parser == NULL) { 2474290071Sbapt return false; 2475290071Sbapt } 2476262395Sbapt 2477290071Sbapt parser->default_priority = prio; 2478290071Sbapt 2479290071Sbapt return true; 2480290071Sbapt} 2481290071Sbapt 2482262395Sbaptvoid 2483262395Sbaptucl_parser_register_macro (struct ucl_parser *parser, const char *macro, 2484262395Sbapt ucl_macro_handler handler, void* ud) 2485262395Sbapt{ 2486262395Sbapt struct ucl_macro *new; 2487262395Sbapt 2488263648Sbapt if (macro == NULL || handler == NULL) { 2489263648Sbapt return; 2490263648Sbapt } 2491290071Sbapt 2492262395Sbapt new = UCL_ALLOC (sizeof (struct ucl_macro)); 2493263648Sbapt if (new == NULL) { 2494263648Sbapt return; 2495263648Sbapt } 2496290071Sbapt 2497262395Sbapt memset (new, 0, sizeof (struct ucl_macro)); 2498290071Sbapt new->h.handler = handler; 2499262395Sbapt new->name = strdup (macro); 2500262395Sbapt new->ud = ud; 2501262395Sbapt HASH_ADD_KEYPTR (hh, parser->macroes, new->name, strlen (new->name), new); 2502262395Sbapt} 2503262395Sbapt 2504262395Sbaptvoid 2505290071Sbaptucl_parser_register_context_macro (struct ucl_parser *parser, const char *macro, 2506290071Sbapt ucl_context_macro_handler handler, void* ud) 2507290071Sbapt{ 2508290071Sbapt struct ucl_macro *new; 2509290071Sbapt 2510290071Sbapt if (macro == NULL || handler == NULL) { 2511290071Sbapt return; 2512290071Sbapt } 2513290071Sbapt 2514290071Sbapt new = UCL_ALLOC (sizeof (struct ucl_macro)); 2515290071Sbapt if (new == NULL) { 2516290071Sbapt return; 2517290071Sbapt } 2518290071Sbapt 2519290071Sbapt memset (new, 0, sizeof (struct ucl_macro)); 2520290071Sbapt new->h.context_handler = handler; 2521290071Sbapt new->name = strdup (macro); 2522290071Sbapt new->ud = ud; 2523290071Sbapt new->is_context = true; 2524290071Sbapt HASH_ADD_KEYPTR (hh, parser->macroes, new->name, strlen (new->name), new); 2525290071Sbapt} 2526290071Sbapt 2527290071Sbaptvoid 2528262395Sbaptucl_parser_register_variable (struct ucl_parser *parser, const char *var, 2529262395Sbapt const char *value) 2530262395Sbapt{ 2531262395Sbapt struct ucl_variable *new = NULL, *cur; 2532262395Sbapt 2533262395Sbapt if (var == NULL) { 2534262395Sbapt return; 2535262395Sbapt } 2536262395Sbapt 2537262395Sbapt /* Find whether a variable already exists */ 2538262395Sbapt LL_FOREACH (parser->variables, cur) { 2539262395Sbapt if (strcmp (cur->var, var) == 0) { 2540262395Sbapt new = cur; 2541262395Sbapt break; 2542262395Sbapt } 2543262395Sbapt } 2544262395Sbapt 2545262395Sbapt if (value == NULL) { 2546262395Sbapt 2547262395Sbapt if (new != NULL) { 2548262395Sbapt /* Remove variable */ 2549275223Sbapt DL_DELETE (parser->variables, new); 2550262395Sbapt free (new->var); 2551262395Sbapt free (new->value); 2552262395Sbapt UCL_FREE (sizeof (struct ucl_variable), new); 2553262395Sbapt } 2554262395Sbapt else { 2555262395Sbapt /* Do nothing */ 2556262395Sbapt return; 2557262395Sbapt } 2558262395Sbapt } 2559262395Sbapt else { 2560262395Sbapt if (new == NULL) { 2561262395Sbapt new = UCL_ALLOC (sizeof (struct ucl_variable)); 2562263648Sbapt if (new == NULL) { 2563263648Sbapt return; 2564263648Sbapt } 2565262395Sbapt memset (new, 0, sizeof (struct ucl_variable)); 2566262395Sbapt new->var = strdup (var); 2567262395Sbapt new->var_len = strlen (var); 2568262395Sbapt new->value = strdup (value); 2569262395Sbapt new->value_len = strlen (value); 2570262395Sbapt 2571275223Sbapt DL_APPEND (parser->variables, new); 2572262395Sbapt } 2573262395Sbapt else { 2574262395Sbapt free (new->value); 2575262395Sbapt new->value = strdup (value); 2576262395Sbapt new->value_len = strlen (value); 2577262395Sbapt } 2578262395Sbapt } 2579262395Sbapt} 2580262395Sbapt 2581266636Sbaptvoid 2582266636Sbaptucl_parser_set_variables_handler (struct ucl_parser *parser, 2583266636Sbapt ucl_variable_handler handler, void *ud) 2584266636Sbapt{ 2585266636Sbapt parser->var_handler = handler; 2586266636Sbapt parser->var_data = ud; 2587266636Sbapt} 2588266636Sbapt 2589262395Sbaptbool 2590290071Sbaptucl_parser_add_chunk_full (struct ucl_parser *parser, const unsigned char *data, 2591290071Sbapt size_t len, unsigned priority, enum ucl_duplicate_strategy strat, 2592290071Sbapt enum ucl_parse_type parse_type) 2593262395Sbapt{ 2594262395Sbapt struct ucl_chunk *chunk; 2595262395Sbapt 2596290071Sbapt if (parser == NULL) { 2597290071Sbapt return false; 2598290071Sbapt } 2599290071Sbapt 2600301339Sbapt if (data == NULL && len != 0) { 2601263648Sbapt ucl_create_err (&parser->err, "invalid chunk added"); 2602263648Sbapt return false; 2603263648Sbapt } 2604298166Sbapt 2605262395Sbapt if (parser->state != UCL_STATE_ERROR) { 2606262395Sbapt chunk = UCL_ALLOC (sizeof (struct ucl_chunk)); 2607263648Sbapt if (chunk == NULL) { 2608263648Sbapt ucl_create_err (&parser->err, "cannot allocate chunk structure"); 2609263648Sbapt return false; 2610263648Sbapt } 2611301339Sbapt 2612262395Sbapt chunk->begin = data; 2613262395Sbapt chunk->remain = len; 2614262395Sbapt chunk->pos = chunk->begin; 2615262395Sbapt chunk->end = chunk->begin + len; 2616262395Sbapt chunk->line = 1; 2617262395Sbapt chunk->column = 0; 2618275223Sbapt chunk->priority = priority; 2619290071Sbapt chunk->strategy = strat; 2620290071Sbapt chunk->parse_type = parse_type; 2621262395Sbapt LL_PREPEND (parser->chunks, chunk); 2622262395Sbapt parser->recursion ++; 2623290071Sbapt 2624262395Sbapt if (parser->recursion > UCL_MAX_RECURSION) { 2625262395Sbapt ucl_create_err (&parser->err, "maximum include nesting limit is reached: %d", 2626262395Sbapt parser->recursion); 2627262395Sbapt return false; 2628262395Sbapt } 2629290071Sbapt 2630301339Sbapt if (len > 0) { 2631301339Sbapt /* Need to parse something */ 2632301339Sbapt switch (parse_type) { 2633301339Sbapt default: 2634301339Sbapt case UCL_PARSE_UCL: 2635301339Sbapt return ucl_state_machine (parser); 2636301339Sbapt case UCL_PARSE_MSGPACK: 2637301339Sbapt return ucl_parse_msgpack (parser); 2638301339Sbapt } 2639290071Sbapt } 2640301339Sbapt else { 2641301339Sbapt /* Just add empty chunk and go forward */ 2642301339Sbapt if (parser->top_obj == NULL) { 2643301339Sbapt /* 2644301339Sbapt * In case of empty object, create one to indicate that we've 2645301339Sbapt * read something 2646301339Sbapt */ 2647301339Sbapt parser->top_obj = ucl_object_new_full (UCL_OBJECT, priority); 2648301339Sbapt } 2649301339Sbapt 2650301339Sbapt return true; 2651301339Sbapt } 2652262395Sbapt } 2653262395Sbapt 2654262395Sbapt ucl_create_err (&parser->err, "a parser is in an invalid state"); 2655262395Sbapt 2656262395Sbapt return false; 2657262395Sbapt} 2658263648Sbapt 2659263648Sbaptbool 2660290071Sbaptucl_parser_add_chunk_priority (struct ucl_parser *parser, 2661290071Sbapt const unsigned char *data, size_t len, unsigned priority) 2662290071Sbapt{ 2663290071Sbapt /* We dereference parser, so this check is essential */ 2664290071Sbapt if (parser == NULL) { 2665290071Sbapt return false; 2666290071Sbapt } 2667290071Sbapt 2668290071Sbapt return ucl_parser_add_chunk_full (parser, data, len, 2669290071Sbapt priority, UCL_DUPLICATE_APPEND, UCL_PARSE_UCL); 2670290071Sbapt} 2671290071Sbapt 2672290071Sbaptbool 2673275223Sbaptucl_parser_add_chunk (struct ucl_parser *parser, const unsigned char *data, 2674275223Sbapt size_t len) 2675275223Sbapt{ 2676290071Sbapt if (parser == NULL) { 2677290071Sbapt return false; 2678290071Sbapt } 2679290071Sbapt 2680290071Sbapt return ucl_parser_add_chunk_full (parser, data, len, 2681290071Sbapt parser->default_priority, UCL_DUPLICATE_APPEND, UCL_PARSE_UCL); 2682275223Sbapt} 2683275223Sbapt 2684275223Sbaptbool 2685290071Sbaptucl_parser_add_string_priority (struct ucl_parser *parser, const char *data, 2686290071Sbapt size_t len, unsigned priority) 2687263648Sbapt{ 2688263648Sbapt if (data == NULL) { 2689263648Sbapt ucl_create_err (&parser->err, "invalid string added"); 2690263648Sbapt return false; 2691263648Sbapt } 2692263648Sbapt if (len == 0) { 2693263648Sbapt len = strlen (data); 2694263648Sbapt } 2695263648Sbapt 2696290071Sbapt return ucl_parser_add_chunk_priority (parser, 2697290071Sbapt (const unsigned char *)data, len, priority); 2698263648Sbapt} 2699290071Sbapt 2700290071Sbaptbool 2701290071Sbaptucl_parser_add_string (struct ucl_parser *parser, const char *data, 2702290071Sbapt size_t len) 2703290071Sbapt{ 2704290071Sbapt if (parser == NULL) { 2705290071Sbapt return false; 2706290071Sbapt } 2707290071Sbapt 2708290071Sbapt return ucl_parser_add_string_priority (parser, 2709290071Sbapt (const unsigned char *)data, len, parser->default_priority); 2710290071Sbapt} 2711290071Sbapt 2712290071Sbaptbool 2713290071Sbaptucl_set_include_path (struct ucl_parser *parser, ucl_object_t *paths) 2714290071Sbapt{ 2715290071Sbapt if (parser == NULL || paths == NULL) { 2716290071Sbapt return false; 2717290071Sbapt } 2718290071Sbapt 2719290071Sbapt if (parser->includepaths == NULL) { 2720290071Sbapt parser->includepaths = ucl_object_copy (paths); 2721290071Sbapt } 2722290071Sbapt else { 2723290071Sbapt ucl_object_unref (parser->includepaths); 2724290071Sbapt parser->includepaths = ucl_object_copy (paths); 2725290071Sbapt } 2726290071Sbapt 2727290071Sbapt if (parser->includepaths == NULL) { 2728290071Sbapt return false; 2729290071Sbapt } 2730290071Sbapt 2731290071Sbapt return true; 2732290071Sbapt} 2733