1 /* Copyright (c) 2013, Vsevolod Stakhov
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are met:
6 * * Redistributions of source code must retain the above copyright
7 * notice, this list of conditions and the following disclaimer.
8 * * Redistributions in binary form must reproduce the above copyright
9 * notice, this list of conditions and the following disclaimer in the
10 * documentation and/or other materials provided with the distribution.
12 * THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY
13 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
14 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
15 * DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY
16 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
17 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
18 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
19 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
20 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
21 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 #include "ucl_internal.h"
26 #include "ucl_chartable.h"
30 * The implementation of ucl parser
33 struct ucl_parser_saved_state {
37 const unsigned char *pos;
41 * Move up to len characters
45 * @return new position in chunk
47 #define ucl_chunk_skipc(chunk, p) do{ \
50 (chunk)->column = 0; \
52 else (chunk)->column ++; \
59 ucl_set_err (struct ucl_parser *parser, int code, const char *str, UT_string **err)
61 const char *fmt_string, *filename;
62 struct ucl_chunk *chunk = parser->chunks;
64 if (parser->cur_file) {
65 filename = parser->cur_file;
68 filename = "<unknown>";
71 if (chunk->pos < chunk->end) {
72 if (isgraph (*chunk->pos)) {
73 fmt_string = "error while parsing %s: "
74 "line: %d, column: %d - '%s', character: '%c'";
77 fmt_string = "error while parsing %s: "
78 "line: %d, column: %d - '%s', character: '0x%02x'";
80 ucl_create_err (err, fmt_string,
81 filename, chunk->line, chunk->column,
85 ucl_create_err (err, "error while parsing %s: at the end of chunk: %s",
89 parser->err_code = code;
93 * Skip all comments from the current pos resolving nested and multiline comments
98 ucl_skip_comments (struct ucl_parser *parser)
100 struct ucl_chunk *chunk = parser->chunks;
101 const unsigned char *p;
102 int comments_nested = 0;
108 if (chunk->remain > 0 && *p == '#') {
109 if (parser->state != UCL_STATE_SCOMMENT &&
110 parser->state != UCL_STATE_MCOMMENT) {
111 while (p < chunk->end) {
113 ucl_chunk_skipc (chunk, p);
116 ucl_chunk_skipc (chunk, p);
120 else if (chunk->remain >= 2 && *p == '/') {
122 ucl_chunk_skipc (chunk, p);
124 ucl_chunk_skipc (chunk, p);
126 while (p < chunk->end) {
127 if (*p == '"' && *(p - 1) != '\\') {
133 ucl_chunk_skipc (chunk, p);
136 if (comments_nested == 0) {
137 ucl_chunk_skipc (chunk, p);
141 ucl_chunk_skipc (chunk, p);
143 else if (p[0] == '/' && chunk->remain >= 2 && p[1] == '*') {
145 ucl_chunk_skipc (chunk, p);
146 ucl_chunk_skipc (chunk, p);
150 ucl_chunk_skipc (chunk, p);
152 if (comments_nested != 0) {
153 ucl_set_err (parser, UCL_ENESTED,
154 "unfinished multiline comment", &parser->err);
164 * Return multiplier for a character
165 * @param c multiplier character
166 * @param is_bytes if true use 1024 multiplier
169 static inline unsigned long
170 ucl_lex_num_multiplier (const unsigned char c, bool is_bytes) {
176 {'m', 1000 * 1000, 1024 * 1024},
178 {'g', 1000 * 1000 * 1000, 1024 * 1024 * 1024}
182 for (i = 0; i < 3; i ++) {
183 if (tolower (c) == multipliers[i].c) {
185 return multipliers[i].mult_bytes;
187 return multipliers[i].mult_normal;
196 * Return multiplier for time scaling
201 ucl_lex_time_multiplier (const unsigned char c) {
209 {'w', 60 * 60 * 24 * 7},
210 {'y', 60 * 60 * 24 * 7 * 365}
214 for (i = 0; i < 5; i ++) {
215 if (tolower (c) == multipliers[i].c) {
216 return multipliers[i].mult;
224 * Return true if a character is a end of an atom
229 ucl_lex_is_atom_end (const unsigned char c)
231 return ucl_test_character (c, UCL_CHARACTER_VALUE_END);
235 ucl_lex_is_comment (const unsigned char c1, const unsigned char c2)
242 else if (c1 == '#') {
249 * Check variable found
258 static inline const char *
259 ucl_check_variable_safe (struct ucl_parser *parser, const char *ptr, size_t remain,
260 size_t *out_len, bool strict, bool *found)
262 struct ucl_variable *var;
265 bool need_free = false;
267 LL_FOREACH (parser->variables, var) {
269 if (remain == var->var_len) {
270 if (memcmp (ptr, var->var, var->var_len) == 0) {
271 *out_len += var->value_len;
273 return (ptr + var->var_len);
278 if (remain >= var->var_len) {
279 if (memcmp (ptr, var->var, var->var_len) == 0) {
280 *out_len += var->value_len;
282 return (ptr + var->var_len);
288 /* XXX: can only handle ${VAR} */
289 if (!(*found) && parser->var_handler != NULL && strict) {
290 /* Call generic handler */
291 if (parser->var_handler (ptr, remain, &dst, &dstlen, &need_free,
297 return (ptr + remain);
305 * Check for a variable in a given string
314 ucl_check_variable (struct ucl_parser *parser, const char *ptr,
315 size_t remain, size_t *out_len, bool *vars_found)
317 const char *p, *end, *ret = ptr;
321 /* We need to match the variable enclosed in braces */
326 ret = ucl_check_variable_safe (parser, ptr + 1, p - ptr - 1,
327 out_len, true, &found);
329 /* {} must be excluded actually */
343 else if (*ptr != '$') {
344 /* Not count escaped dollar sign */
345 ret = ucl_check_variable_safe (parser, ptr, remain, out_len, false, &found);
346 if (found && !*vars_found) {
362 * Expand a single variable
370 ucl_expand_single_variable (struct ucl_parser *parser, const char *ptr,
371 size_t remain, unsigned char **dest)
373 unsigned char *d = *dest, *dst;
374 const char *p = ptr + 1, *ret;
375 struct ucl_variable *var;
377 bool need_free = false;
389 else if (*p == '{') {
396 LL_FOREACH (parser->variables, var) {
397 if (remain >= var->var_len) {
398 if (memcmp (p, var->var, var->var_len) == 0) {
399 memcpy (d, var->value, var->value_len);
408 if (strict && parser->var_handler != NULL) {
409 if (parser->var_handler (ptr, remain, &dst, &dstlen, &need_free,
411 memcpy (d, dst, dstlen);
418 /* Leave variable as is */
438 * Expand variables in string
446 ucl_expand_variable (struct ucl_parser *parser, unsigned char **dst,
447 const char *src, size_t in_len)
449 const char *p, *end = src + in_len;
452 bool vars_found = false;
457 p = ucl_check_variable (parser, p + 1, end - p - 1, &out_len, &vars_found);
471 *dst = UCL_ALLOC (out_len + 1);
480 p = ucl_expand_single_variable (parser, p, end - p, &d);
493 * Store or copy pointer to the trash stack
494 * @param parser parser object
495 * @param src src string
496 * @param dst destination buffer (trash stack pointer)
497 * @param dst_const const destination pointer (e.g. value of object)
498 * @param in_len input length
499 * @param need_unescape need to unescape source (and copy it)
500 * @param need_lowercase need to lowercase value (and copy)
501 * @param need_expand need to expand variables (and copy as well)
502 * @return output length (excluding \0 symbol)
504 static inline ssize_t
505 ucl_copy_or_store_ptr (struct ucl_parser *parser,
506 const unsigned char *src, unsigned char **dst,
507 const char **dst_const, size_t in_len,
508 bool need_unescape, bool need_lowercase, bool need_expand)
510 ssize_t ret = -1, tret;
513 if (need_unescape || need_lowercase ||
514 (need_expand && parser->variables != NULL) ||
515 !(parser->flags & UCL_PARSER_ZEROCOPY)) {
517 *dst = UCL_ALLOC (in_len + 1);
519 ucl_set_err (parser, UCL_EINTERNAL, "cannot allocate memory for a string",
523 if (need_lowercase) {
524 ret = ucl_strlcpy_tolower (*dst, src, in_len + 1);
527 ret = ucl_strlcpy_unsafe (*dst, src, in_len + 1);
531 ret = ucl_unescape_json_string (*dst, ret);
536 ret = ucl_expand_variable (parser, dst, tmp, ret);
538 /* Nothing to expand */
543 /* Free unexpanded value */
544 UCL_FREE (in_len + 1, tmp);
558 * Create and append an object at the specified level
564 static inline ucl_object_t *
565 ucl_parser_add_container (ucl_object_t *obj, struct ucl_parser *parser,
566 bool is_array, int level)
568 struct ucl_stack *st;
572 obj = ucl_object_new_full (UCL_OBJECT, parser->chunks->priority);
575 obj->type = UCL_OBJECT;
577 if (obj->value.ov == NULL) {
578 obj->value.ov = ucl_hash_create (parser->flags & UCL_PARSER_KEY_LOWERCASE);
580 parser->state = UCL_STATE_KEY;
584 obj = ucl_object_new_full (UCL_ARRAY, parser->chunks->priority);
587 obj->type = UCL_ARRAY;
589 parser->state = UCL_STATE_VALUE;
592 st = UCL_ALLOC (sizeof (struct ucl_stack));
594 ucl_set_err (parser, UCL_EINTERNAL, "cannot allocate memory for an object",
596 ucl_object_unref (obj);
601 LL_PREPEND (parser->stack, st);
602 parser->cur_obj = obj;
608 ucl_maybe_parse_number (ucl_object_t *obj,
609 const char *start, const char *end, const char **pos,
610 bool allow_double, bool number_bytes, bool allow_time)
612 const char *p = start, *c = start;
614 bool got_dot = false, got_exp = false, need_double = false,
615 is_time = false, valid_start = false, is_hex = false,
626 if (is_hex && isxdigit (*p)) {
629 else if (isdigit (*p)) {
633 else if (!is_hex && (*p == 'x' || *p == 'X')) {
635 allow_double = false;
638 else if (allow_double) {
640 /* Empty digits sequence, not a number */
644 else if (*p == '.') {
646 /* Double dots, not a number */
656 else if (*p == 'e' || *p == 'E') {
658 /* Double exp, not a number */
670 if (!isdigit (*p) && *p != '+' && *p != '-') {
671 /* Wrong exponent sign */
681 /* Got the end of the number, need to check */
697 dv = strtod (c, &endptr);
701 lv = strtoimax (c, &endptr, 16);
704 lv = strtoimax (c, &endptr, 10);
707 if (errno == ERANGE) {
712 /* Now check endptr */
713 if (endptr == NULL || ucl_lex_is_atom_end (*endptr) || *endptr == '\0') {
718 if (endptr < end && endptr != start) {
728 if (p[1] == 's' || p[1] == 'S') {
735 if (p[0] == 'm' || p[0] == 'M') {
739 dv *= ucl_lex_num_multiplier (*p, false);
744 else if (number_bytes || (p[1] == 'b' || p[1] == 'B')) {
750 lv *= ucl_lex_num_multiplier (*p, true);
754 else if (ucl_lex_is_atom_end (p[1])) {
756 dv *= ucl_lex_num_multiplier (*p, false);
759 lv *= ucl_lex_num_multiplier (*p, number_bytes);
764 else if (allow_time && end - p >= 3) {
765 if (tolower (p[0]) == 'm' &&
766 tolower (p[1]) == 'i' &&
767 tolower (p[2]) == 'n') {
782 dv *= ucl_lex_num_multiplier (*p, false);
785 lv *= ucl_lex_num_multiplier (*p, number_bytes);
794 (p == end - 1 || ucl_lex_is_atom_end (p[1]))) {
813 (p == end - 1 || ucl_lex_is_atom_end (p[1]))) {
819 dv *= ucl_lex_time_multiplier (*p);
826 while (p < end && ucl_test_character(*p, UCL_CHARACTER_WHITESPACE)) {
829 if (ucl_lex_is_atom_end(*p))
834 else if (endptr == end) {
835 /* Just a number at the end of chunk */
845 if (allow_double && (need_double || is_time)) {
847 obj->type = UCL_FLOAT;
850 obj->type = UCL_TIME;
852 obj->value.dv = is_neg ? (-dv) : dv;
856 obj->value.iv = is_neg ? (-lv) : lv;
864 * Parse possible number
868 * @return true if a number has been parsed
871 ucl_lex_number (struct ucl_parser *parser,
872 struct ucl_chunk *chunk, ucl_object_t *obj)
874 const unsigned char *pos;
877 ret = ucl_maybe_parse_number (obj, chunk->pos, chunk->end, (const char **)&pos,
878 true, false, ((parser->flags & UCL_PARSER_NO_TIME) == 0));
881 chunk->remain -= pos - chunk->pos;
882 chunk->column += pos - chunk->pos;
886 else if (ret == ERANGE) {
887 ucl_set_err (parser, UCL_ESYNTAX, "numeric value out of range",
895 * Parse quoted string with possible escapes
898 * @param need_unescape
901 * @return true if a string has been parsed
904 ucl_lex_json_string (struct ucl_parser *parser,
905 struct ucl_chunk *chunk, bool *need_unescape, bool *ucl_escape, bool *var_expand)
907 const unsigned char *p = chunk->pos;
911 while (p < chunk->end) {
914 /* Unmasked control character */
916 ucl_set_err (parser, UCL_ESYNTAX, "unexpected newline",
920 ucl_set_err (parser, UCL_ESYNTAX, "unexpected control character",
925 else if (c == '\\') {
926 ucl_chunk_skipc (chunk, p);
928 if (p >= chunk->end) {
929 ucl_set_err (parser, UCL_ESYNTAX, "unfinished escape character",
933 else if (ucl_test_character (c, UCL_CHARACTER_ESCAPE)) {
935 ucl_chunk_skipc (chunk, p);
936 for (i = 0; i < 4 && p < chunk->end; i ++) {
937 if (!isxdigit (*p)) {
938 ucl_set_err (parser, UCL_ESYNTAX, "invalid utf escape",
942 ucl_chunk_skipc (chunk, p);
944 if (p >= chunk->end) {
945 ucl_set_err (parser, UCL_ESYNTAX, "unfinished escape character",
951 ucl_chunk_skipc (chunk, p);
954 *need_unescape = true;
959 ucl_chunk_skipc (chunk, p);
962 else if (ucl_test_character (c, UCL_CHARACTER_UCL_UNSAFE)) {
968 ucl_chunk_skipc (chunk, p);
971 ucl_set_err (parser, UCL_ESYNTAX, "no quote at the end of json string",
977 ucl_parser_append_elt (struct ucl_parser *parser, ucl_hash_t *cont,
983 if ((parser->flags & UCL_PARSER_NO_IMPLICIT_ARRAYS) == 0) {
985 top->flags |= UCL_OBJECT_MULTIVALUE;
986 DL_APPEND (top, elt);
987 parser->stack->obj->len ++;
990 if ((top->flags & UCL_OBJECT_MULTIVALUE) != 0) {
991 /* Just add to the explicit array */
992 ucl_array_append (top, elt);
995 /* Convert to an array */
996 nobj = ucl_object_typed_new (UCL_ARRAY);
997 nobj->key = top->key;
998 nobj->keylen = top->keylen;
999 nobj->flags |= UCL_OBJECT_MULTIVALUE;
1000 ucl_array_append (nobj, top);
1001 ucl_array_append (nobj, elt);
1002 ucl_hash_replace (cont, top, nobj);
1008 ucl_parser_process_object_element (struct ucl_parser *parser, ucl_object_t *nobj)
1010 ucl_hash_t *container;
1013 container = parser->stack->obj->value.ov;
1015 tobj = __DECONST (ucl_object_t *, ucl_hash_search_obj (container, nobj));
1017 container = ucl_hash_insert_object (container, nobj,
1018 parser->flags & UCL_PARSER_KEY_LOWERCASE);
1021 parser->stack->obj->len ++;
1024 unsigned priold = ucl_object_get_priority (tobj),
1025 prinew = ucl_object_get_priority (nobj);
1026 switch (parser->chunks->strategy) {
1028 case UCL_DUPLICATE_APPEND:
1030 * The logic here is the following:
1032 * - if we have two objects with the same priority, then we form an
1033 * implicit or explicit array
1034 * - if a new object has bigger priority, then we overwrite an old one
1035 * - if a new object has lower priority, then we ignore it
1039 /* Special case for inherited objects */
1040 if (tobj->flags & UCL_OBJECT_INHERITED) {
1041 prinew = priold + 1;
1044 if (priold == prinew) {
1045 ucl_parser_append_elt (parser, container, tobj, nobj);
1047 else if (priold > prinew) {
1049 * We add this new object to a list of trash objects just to ensure
1050 * that it won't come to any real object
1051 * XXX: rather inefficient approach
1053 DL_APPEND (parser->trash_objs, nobj);
1056 ucl_hash_replace (container, tobj, nobj);
1057 ucl_object_unref (tobj);
1062 case UCL_DUPLICATE_REWRITE:
1063 /* We just rewrite old values regardless of priority */
1064 ucl_hash_replace (container, tobj, nobj);
1065 ucl_object_unref (tobj);
1069 case UCL_DUPLICATE_ERROR:
1070 ucl_create_err (&parser->err, "error while parsing %s: "
1071 "line: %d, column: %d: duplicate element for key '%s' "
1073 parser->cur_file ? parser->cur_file : "<unknown>",
1074 parser->chunks->line, parser->chunks->column, nobj->key);
1077 case UCL_DUPLICATE_MERGE:
1079 * Here we do have some old object so we just push it on top of objects stack
1081 if (tobj->type == UCL_OBJECT || tobj->type == UCL_ARRAY) {
1082 ucl_object_unref (nobj);
1086 /* For other types we create implicit array as usual */
1087 ucl_parser_append_elt (parser, container, tobj, nobj);
1093 parser->stack->obj->value.ov = container;
1094 parser->cur_obj = nobj;
1100 * Parse a key in an object
1104 * @param end_of_object
1105 * @return true if a key has been parsed
1108 ucl_parse_key (struct ucl_parser *parser, struct ucl_chunk *chunk,
1109 bool *next_key, bool *end_of_object)
1111 const unsigned char *p, *c = NULL, *end, *t;
1112 const char *key = NULL;
1113 bool got_quote = false, got_eq = false, got_semicolon = false,
1114 need_unescape = false, ucl_escape = false, var_expand = false,
1115 got_content = false, got_sep = false;
1122 /* It is macro actually */
1123 ucl_chunk_skipc (chunk, p);
1124 parser->prev_state = parser->state;
1125 parser->state = UCL_STATE_MACRO_NAME;
1126 *end_of_object = false;
1129 while (p < chunk->end) {
1131 * A key must start with alpha, number, '/' or '_' and end with space character
1134 if (chunk->remain >= 2 && ucl_lex_is_comment (p[0], p[1])) {
1135 if (!ucl_skip_comments (parser)) {
1140 else if (ucl_test_character (*p, UCL_CHARACTER_WHITESPACE_UNSAFE)) {
1141 ucl_chunk_skipc (chunk, p);
1143 else if (ucl_test_character (*p, UCL_CHARACTER_KEY_START)) {
1144 /* The first symbol */
1146 ucl_chunk_skipc (chunk, p);
1149 else if (*p == '"') {
1150 /* JSON style key */
1154 ucl_chunk_skipc (chunk, p);
1156 else if (*p == '}') {
1157 /* We have actually end of an object */
1158 *end_of_object = true;
1161 else if (*p == '.') {
1162 ucl_chunk_skipc (chunk, p);
1163 parser->prev_state = parser->state;
1164 parser->state = UCL_STATE_MACRO_NAME;
1168 /* Invalid identifier */
1169 ucl_set_err (parser, UCL_ESYNTAX, "key must begin with a letter",
1175 /* Parse the body of a key */
1177 if (ucl_test_character (*p, UCL_CHARACTER_KEY)) {
1179 ucl_chunk_skipc (chunk, p);
1181 else if (ucl_test_character (*p, UCL_CHARACTER_KEY_SEP)) {
1186 ucl_set_err (parser, UCL_ESYNTAX, "invalid character in a key",
1192 /* We need to parse json like quoted string */
1193 if (!ucl_lex_json_string (parser, chunk, &need_unescape, &ucl_escape, &var_expand)) {
1196 /* Always escape keys obtained via json */
1197 end = chunk->pos - 1;
1204 if (p >= chunk->end && got_content) {
1205 ucl_set_err (parser, UCL_ESYNTAX, "unfinished key", &parser->err);
1208 else if (!got_content) {
1211 *end_of_object = false;
1212 /* We are now at the end of the key, need to parse the rest */
1213 while (p < chunk->end) {
1214 if (ucl_test_character (*p, UCL_CHARACTER_WHITESPACE)) {
1215 ucl_chunk_skipc (chunk, p);
1217 else if (*p == '=') {
1218 if (!got_eq && !got_semicolon) {
1219 ucl_chunk_skipc (chunk, p);
1223 ucl_set_err (parser, UCL_ESYNTAX, "unexpected '=' character",
1228 else if (*p == ':') {
1229 if (!got_eq && !got_semicolon) {
1230 ucl_chunk_skipc (chunk, p);
1231 got_semicolon = true;
1234 ucl_set_err (parser, UCL_ESYNTAX, "unexpected ':' character",
1239 else if (chunk->remain >= 2 && ucl_lex_is_comment (p[0], p[1])) {
1240 /* Check for comment */
1241 if (!ucl_skip_comments (parser)) {
1252 if (p >= chunk->end && got_content) {
1253 ucl_set_err (parser, UCL_ESYNTAX, "unfinished key", &parser->err);
1257 got_sep = got_semicolon || got_eq;
1261 * Maybe we have more keys nested, so search for termination character.
1263 * 1) key1 key2 ... keyN [:=] value <- we treat that as error
1264 * 2) key1 ... keyN {} or [] <- we treat that as nested objects
1265 * 3) key1 value[;,\n] <- we treat that as linear object
1269 while (ucl_test_character (*t, UCL_CHARACTER_WHITESPACE)) {
1272 /* Check first non-space character after a key */
1273 if (*t != '{' && *t != '[') {
1274 while (t < chunk->end) {
1275 if (*t == ',' || *t == ';' || *t == '\n' || *t == '\r') {
1278 else if (*t == '{' || *t == '[') {
1287 /* Create a new object */
1288 nobj = ucl_object_new_full (UCL_NULL, parser->chunks->priority);
1289 keylen = ucl_copy_or_store_ptr (parser, c, &nobj->trash_stack[UCL_TRASH_KEY],
1290 &key, end - c, need_unescape, parser->flags & UCL_PARSER_KEY_LOWERCASE, false);
1292 ucl_object_unref (nobj);
1295 else if (keylen == 0) {
1296 ucl_set_err (parser, UCL_ESYNTAX, "empty keys are not allowed", &parser->err);
1297 ucl_object_unref (nobj);
1302 nobj->keylen = keylen;
1304 if (!ucl_parser_process_object_element (parser, nobj)) {
1309 nobj->flags |= UCL_OBJECT_NEED_KEY_ESCAPE;
1321 * @param need_unescape
1322 * @return true if a key has been parsed
1325 ucl_parse_string_value (struct ucl_parser *parser,
1326 struct ucl_chunk *chunk, bool *var_expand, bool *need_unescape)
1328 const unsigned char *p;
1330 UCL_BRACE_ROUND = 0,
1334 int braces[3][2] = {{0, 0}, {0, 0}, {0, 0}};
1338 while (p < chunk->end) {
1340 /* Skip pairs of figure braces */
1342 braces[UCL_BRACE_FIGURE][0] ++;
1344 else if (*p == '}') {
1345 braces[UCL_BRACE_FIGURE][1] ++;
1346 if (braces[UCL_BRACE_FIGURE][1] <= braces[UCL_BRACE_FIGURE][0]) {
1347 /* This is not a termination symbol, continue */
1348 ucl_chunk_skipc (chunk, p);
1352 /* Skip pairs of square braces */
1353 else if (*p == '[') {
1354 braces[UCL_BRACE_SQUARE][0] ++;
1356 else if (*p == ']') {
1357 braces[UCL_BRACE_SQUARE][1] ++;
1358 if (braces[UCL_BRACE_SQUARE][1] <= braces[UCL_BRACE_SQUARE][0]) {
1359 /* This is not a termination symbol, continue */
1360 ucl_chunk_skipc (chunk, p);
1364 else if (*p == '$') {
1367 else if (*p == '\\') {
1368 *need_unescape = true;
1369 ucl_chunk_skipc (chunk, p);
1370 if (p < chunk->end) {
1371 ucl_chunk_skipc (chunk, p);
1376 if (ucl_lex_is_atom_end (*p) || (chunk->remain >= 2 && ucl_lex_is_comment (p[0], p[1]))) {
1379 ucl_chunk_skipc (chunk, p);
1386 * Parse multiline string ending with \n{term}\n
1393 * @return size of multiline string or 0 in case of error
1396 ucl_parse_multiline_string (struct ucl_parser *parser,
1397 struct ucl_chunk *chunk, const unsigned char *term,
1398 int term_len, unsigned char const **beg,
1401 const unsigned char *p, *c, *tend;
1402 bool newline = false;
1409 while (p < chunk->end) {
1411 if (chunk->end - p < term_len) {
1414 else if (memcmp (p, term, term_len) == 0) {
1415 tend = p + term_len;
1416 if (*tend != '\n' && *tend != ';' && *tend != ',') {
1417 /* Incomplete terminator */
1418 ucl_chunk_skipc (chunk, p);
1422 chunk->remain -= term_len;
1423 chunk->pos = p + term_len;
1424 chunk->column = term_len;
1438 ucl_chunk_skipc (chunk, p);
1444 static inline ucl_object_t*
1445 ucl_parser_get_container (struct ucl_parser *parser)
1447 ucl_object_t *t, *obj = NULL;
1449 if (parser == NULL || parser->stack == NULL || parser->stack->obj == NULL) {
1453 if (parser->stack->obj->type == UCL_ARRAY) {
1454 /* Object must be allocated */
1455 obj = ucl_object_new_full (UCL_NULL, parser->chunks->priority);
1456 t = parser->stack->obj;
1458 if (!ucl_array_append (t, obj)) {
1459 ucl_object_unref (obj);
1463 parser->cur_obj = obj;
1466 /* Object has been already allocated */
1467 obj = parser->cur_obj;
1480 ucl_parse_value (struct ucl_parser *parser, struct ucl_chunk *chunk)
1482 const unsigned char *p, *c;
1483 ucl_object_t *obj = NULL;
1484 unsigned int stripped_spaces;
1486 bool need_unescape = false, ucl_escape = false, var_expand = false;
1490 /* Skip any spaces and comments */
1491 if (ucl_test_character (*p, UCL_CHARACTER_WHITESPACE_UNSAFE) ||
1492 (chunk->remain >= 2 && ucl_lex_is_comment (p[0], p[1]))) {
1493 while (p < chunk->end && ucl_test_character (*p, UCL_CHARACTER_WHITESPACE_UNSAFE)) {
1494 ucl_chunk_skipc (chunk, p);
1496 if (!ucl_skip_comments (parser)) {
1502 while (p < chunk->end) {
1506 ucl_chunk_skipc (chunk, p);
1508 if (!ucl_lex_json_string (parser, chunk, &need_unescape, &ucl_escape,
1513 obj = ucl_parser_get_container (parser);
1514 str_len = chunk->pos - c - 2;
1515 obj->type = UCL_STRING;
1516 if ((str_len = ucl_copy_or_store_ptr (parser, c + 1,
1517 &obj->trash_stack[UCL_TRASH_VALUE],
1518 &obj->value.sv, str_len, need_unescape, false,
1519 var_expand)) == -1) {
1524 parser->state = UCL_STATE_AFTER_VALUE;
1530 obj = ucl_parser_get_container (parser);
1531 /* We have a new object */
1532 obj = ucl_parser_add_container (obj, parser, false, parser->stack->level);
1537 ucl_chunk_skipc (chunk, p);
1542 obj = ucl_parser_get_container (parser);
1543 /* We have a new array */
1544 obj = ucl_parser_add_container (obj, parser, true, parser->stack->level);
1549 ucl_chunk_skipc (chunk, p);
1554 /* We have the array ending */
1555 if (parser->stack && parser->stack->obj->type == UCL_ARRAY) {
1556 parser->state = UCL_STATE_AFTER_VALUE;
1564 obj = ucl_parser_get_container (parser);
1565 /* We have something like multiline value, which must be <<[A-Z]+\n */
1566 if (chunk->end - p > 3) {
1567 if (memcmp (p, "<<", 2) == 0) {
1569 /* We allow only uppercase characters in multiline definitions */
1570 while (p < chunk->end && *p >= 'A' && *p <= 'Z') {
1574 /* Set chunk positions and start multiline parsing */
1576 chunk->remain -= p - c;
1580 if ((str_len = ucl_parse_multiline_string (parser, chunk, c,
1581 p - c, &c, &var_expand)) == 0) {
1582 ucl_set_err (parser, UCL_ESYNTAX,
1583 "unterminated multiline value", &parser->err);
1587 obj->type = UCL_STRING;
1588 obj->flags |= UCL_OBJECT_MULTILINE;
1589 if ((str_len = ucl_copy_or_store_ptr (parser, c,
1590 &obj->trash_stack[UCL_TRASH_VALUE],
1591 &obj->value.sv, str_len - 1, false,
1592 false, var_expand)) == -1) {
1597 parser->state = UCL_STATE_AFTER_VALUE;
1603 /* Fallback to ordinary strings */
1607 obj = ucl_parser_get_container (parser);
1611 if (ucl_test_character (*p, UCL_CHARACTER_VALUE_DIGIT_START)) {
1612 if (!ucl_lex_number (parser, chunk, obj)) {
1613 if (parser->state == UCL_STATE_ERROR) {
1618 parser->state = UCL_STATE_AFTER_VALUE;
1621 /* Fallback to normal string */
1624 if (!ucl_parse_string_value (parser, chunk, &var_expand,
1628 /* Cut trailing spaces */
1629 stripped_spaces = 0;
1630 while (ucl_test_character (*(chunk->pos - 1 - stripped_spaces),
1631 UCL_CHARACTER_WHITESPACE)) {
1634 str_len = chunk->pos - c - stripped_spaces;
1636 ucl_set_err (parser, UCL_ESYNTAX, "string value must not be empty",
1640 else if (str_len == 4 && memcmp (c, "null", 4) == 0) {
1642 obj->type = UCL_NULL;
1644 else if (!ucl_maybe_parse_boolean (obj, c, str_len)) {
1645 obj->type = UCL_STRING;
1646 if ((str_len = ucl_copy_or_store_ptr (parser, c,
1647 &obj->trash_stack[UCL_TRASH_VALUE],
1648 &obj->value.sv, str_len, need_unescape,
1649 false, var_expand)) == -1) {
1654 parser->state = UCL_STATE_AFTER_VALUE;
1666 * Handle after value data
1672 ucl_parse_after_value (struct ucl_parser *parser, struct ucl_chunk *chunk)
1674 const unsigned char *p;
1675 bool got_sep = false;
1676 struct ucl_stack *st;
1680 while (p < chunk->end) {
1681 if (ucl_test_character (*p, UCL_CHARACTER_WHITESPACE)) {
1682 /* Skip whitespaces */
1683 ucl_chunk_skipc (chunk, p);
1685 else if (chunk->remain >= 2 && ucl_lex_is_comment (p[0], p[1])) {
1687 if (!ucl_skip_comments (parser)) {
1690 /* Treat comment as a separator */
1694 else if (ucl_test_character (*p, UCL_CHARACTER_VALUE_END)) {
1695 if (*p == '}' || *p == ']') {
1696 if (parser->stack == NULL) {
1697 ucl_set_err (parser, UCL_ESYNTAX,
1698 "end of array or object detected without corresponding start",
1702 if ((*p == '}' && parser->stack->obj->type == UCL_OBJECT) ||
1703 (*p == ']' && parser->stack->obj->type == UCL_ARRAY)) {
1705 /* Pop all nested objects from a stack */
1707 parser->stack = st->next;
1708 UCL_FREE (sizeof (struct ucl_stack), st);
1710 while (parser->stack != NULL) {
1712 if (st->next == NULL || st->next->level == st->level) {
1715 parser->stack = st->next;
1716 UCL_FREE (sizeof (struct ucl_stack), st);
1720 ucl_set_err (parser, UCL_ESYNTAX,
1721 "unexpected terminating symbol detected",
1726 if (parser->stack == NULL) {
1727 /* Ignore everything after a top object */
1731 ucl_chunk_skipc (chunk, p);
1736 /* Got a separator */
1738 ucl_chunk_skipc (chunk, p);
1744 ucl_set_err (parser, UCL_ESYNTAX, "delimiter is missing",
1760 * @param macro_start
1765 ucl_parse_macro_value (struct ucl_parser *parser,
1766 struct ucl_chunk *chunk, struct ucl_macro *macro,
1767 unsigned char const **macro_start, size_t *macro_len)
1769 const unsigned char *p, *c;
1770 bool need_unescape = false, ucl_escape = false, var_expand = false;
1776 /* We have macro value encoded in quotes */
1778 ucl_chunk_skipc (chunk, p);
1779 if (!ucl_lex_json_string (parser, chunk, &need_unescape, &ucl_escape, &var_expand)) {
1783 *macro_start = c + 1;
1784 *macro_len = chunk->pos - c - 2;
1788 /* We got a multiline macro body */
1789 ucl_chunk_skipc (chunk, p);
1790 /* Skip spaces at the beginning */
1791 while (p < chunk->end) {
1792 if (ucl_test_character (*p, UCL_CHARACTER_WHITESPACE_UNSAFE)) {
1793 ucl_chunk_skipc (chunk, p);
1800 while (p < chunk->end) {
1804 ucl_chunk_skipc (chunk, p);
1808 ucl_chunk_skipc (chunk, p);
1811 /* Macro is not enclosed in quotes or braces */
1813 while (p < chunk->end) {
1814 if (ucl_lex_is_atom_end (*p)) {
1817 ucl_chunk_skipc (chunk, p);
1824 /* We are at the end of a macro */
1825 /* Skip ';' and space characters and return to previous state */
1826 while (p < chunk->end) {
1827 if (!ucl_test_character (*p, UCL_CHARACTER_WHITESPACE_UNSAFE) && *p != ';') {
1830 ucl_chunk_skipc (chunk, p);
1836 * Parse macro arguments as UCL object
1837 * @param parser parser structure
1838 * @param chunk the current data chunk
1841 static ucl_object_t *
1842 ucl_parse_macro_arguments (struct ucl_parser *parser,
1843 struct ucl_chunk *chunk)
1845 ucl_object_t *res = NULL;
1846 struct ucl_parser *params_parser;
1847 int obraces = 1, ebraces = 0, state = 0;
1848 const unsigned char *p, *c;
1849 size_t args_len = 0;
1850 struct ucl_parser_saved_state saved;
1852 saved.column = chunk->column;
1853 saved.line = chunk->line;
1854 saved.pos = chunk->pos;
1855 saved.remain = chunk->remain;
1858 if (*p != '(' || chunk->remain < 2) {
1862 /* Set begin and start */
1863 ucl_chunk_skipc (chunk, p);
1866 while ((p) < (chunk)->end) {
1869 /* Parse symbols and check for '(', ')' and '"' */
1873 else if (*p == ')') {
1876 else if (*p == '"') {
1880 if (obraces == ebraces) {
1886 /* Check overflow */
1887 if (chunk->remain == 0) {
1890 ucl_chunk_skipc (chunk, p);
1893 /* We have quote character, so skip all but quotes */
1894 if (*p == '"' && *(p - 1) != '\\') {
1897 if (chunk->remain == 0) {
1901 ucl_chunk_skipc (chunk, p);
1905 * We have read the full body of arguments, so we need to parse and set
1908 params_parser = ucl_parser_new (parser->flags);
1909 if (!ucl_parser_add_chunk (params_parser, c, args_len)) {
1910 ucl_set_err (parser, UCL_ESYNTAX, "macro arguments parsing error",
1914 res = ucl_parser_get_object (params_parser);
1916 ucl_parser_free (params_parser);
1927 chunk->column = saved.column;
1928 chunk->line = saved.line;
1929 chunk->pos = saved.pos;
1930 chunk->remain = saved.remain;
1935 #define SKIP_SPACES_COMMENTS(parser, chunk, p) do { \
1936 while ((p) < (chunk)->end) { \
1937 if (!ucl_test_character (*(p), UCL_CHARACTER_WHITESPACE_UNSAFE)) { \
1938 if ((chunk)->remain >= 2 && ucl_lex_is_comment ((p)[0], (p)[1])) { \
1939 if (!ucl_skip_comments (parser)) { \
1946 ucl_chunk_skipc (chunk, p); \
1951 * Handle the main states of rcl parser
1952 * @param parser parser structure
1953 * @return true if chunk has been parsed and false in case of error
1956 ucl_state_machine (struct ucl_parser *parser)
1958 ucl_object_t *obj, *macro_args;
1959 struct ucl_chunk *chunk = parser->chunks;
1960 const unsigned char *p, *c = NULL, *macro_start = NULL;
1961 unsigned char *macro_escaped;
1962 size_t macro_len = 0;
1963 struct ucl_macro *macro = NULL;
1964 bool next_key = false, end_of_object = false, ret;
1966 if (parser->top_obj == NULL) {
1967 parser->state = UCL_STATE_INIT;
1971 while (chunk->pos < chunk->end) {
1972 switch (parser->state) {
1973 case UCL_STATE_INIT:
1975 * At the init state we can either go to the parse array or object
1976 * if we got [ or { correspondingly or can just treat new data as
1977 * a key of newly created object
1979 if (!ucl_skip_comments (parser)) {
1980 parser->prev_state = parser->state;
1981 parser->state = UCL_STATE_ERROR;
1985 /* Skip any spaces */
1986 while (p < chunk->end && ucl_test_character (*p,
1987 UCL_CHARACTER_WHITESPACE_UNSAFE)) {
1988 ucl_chunk_skipc (chunk, p);
1994 parser->state = UCL_STATE_VALUE;
1995 ucl_chunk_skipc (chunk, p);
1998 parser->state = UCL_STATE_KEY;
2000 ucl_chunk_skipc (chunk, p);
2004 if (parser->top_obj == NULL) {
2005 if (parser->state == UCL_STATE_VALUE) {
2006 obj = ucl_parser_add_container (NULL, parser, true, 0);
2009 obj = ucl_parser_add_container (NULL, parser, false, 0);
2016 parser->top_obj = obj;
2017 parser->cur_obj = obj;
2023 /* Skip any spaces */
2024 while (p < chunk->end && ucl_test_character (*p, UCL_CHARACTER_WHITESPACE_UNSAFE)) {
2025 ucl_chunk_skipc (chunk, p);
2028 /* We have the end of an object */
2029 parser->state = UCL_STATE_AFTER_VALUE;
2032 if (parser->stack == NULL) {
2033 /* No objects are on stack, but we want to parse a key */
2034 ucl_set_err (parser, UCL_ESYNTAX, "top object is finished but the parser "
2035 "expects a key", &parser->err);
2036 parser->prev_state = parser->state;
2037 parser->state = UCL_STATE_ERROR;
2040 if (!ucl_parse_key (parser, chunk, &next_key, &end_of_object)) {
2041 parser->prev_state = parser->state;
2042 parser->state = UCL_STATE_ERROR;
2045 if (end_of_object) {
2047 parser->state = UCL_STATE_AFTER_VALUE;
2050 else if (parser->state != UCL_STATE_MACRO_NAME) {
2051 if (next_key && parser->stack->obj->type == UCL_OBJECT) {
2052 /* Parse more keys and nest objects accordingly */
2053 obj = ucl_parser_add_container (parser->cur_obj, parser, false,
2054 parser->stack->level + 1);
2060 parser->state = UCL_STATE_VALUE;
2068 case UCL_STATE_VALUE:
2069 /* We need to check what we do have */
2070 if (!ucl_parse_value (parser, chunk)) {
2071 parser->prev_state = parser->state;
2072 parser->state = UCL_STATE_ERROR;
2075 /* State is set in ucl_parse_value call */
2078 case UCL_STATE_AFTER_VALUE:
2079 if (!ucl_parse_after_value (parser, chunk)) {
2080 parser->prev_state = parser->state;
2081 parser->state = UCL_STATE_ERROR;
2085 if (parser->stack != NULL) {
2086 if (parser->stack->obj->type == UCL_OBJECT) {
2087 parser->state = UCL_STATE_KEY;
2091 parser->state = UCL_STATE_VALUE;
2095 /* Skip everything at the end */
2100 case UCL_STATE_MACRO_NAME:
2101 if (!ucl_test_character (*p, UCL_CHARACTER_WHITESPACE_UNSAFE) &&
2103 ucl_chunk_skipc (chunk, p);
2107 /* We got macro name */
2108 macro_len = (size_t) (p - c);
2109 HASH_FIND (hh, parser->macroes, c, macro_len, macro);
2110 if (macro == NULL) {
2111 ucl_create_err (&parser->err,
2112 "error on line %d at column %d: "
2113 "unknown macro: '%.*s', character: '%c'",
2119 parser->state = UCL_STATE_ERROR;
2122 /* Now we need to skip all spaces */
2123 SKIP_SPACES_COMMENTS(parser, chunk, p);
2124 parser->state = UCL_STATE_MACRO;
2127 /* We have invalid macro name */
2128 ucl_create_err (&parser->err,
2129 "error on line %d at column %d: invalid macro name",
2132 parser->state = UCL_STATE_ERROR;
2137 case UCL_STATE_MACRO:
2138 if (*chunk->pos == '(') {
2139 macro_args = ucl_parse_macro_arguments (parser, chunk);
2142 SKIP_SPACES_COMMENTS(parser, chunk, p);
2148 if (!ucl_parse_macro_value (parser, chunk, macro,
2149 ¯o_start, ¯o_len)) {
2150 parser->prev_state = parser->state;
2151 parser->state = UCL_STATE_ERROR;
2154 macro_len = ucl_expand_variable (parser, ¯o_escaped,
2155 macro_start, macro_len);
2156 parser->state = parser->prev_state;
2157 if (macro_escaped == NULL) {
2158 if (macro->is_context) {
2159 ret = macro->h.context_handler (macro_start, macro_len,
2165 ret = macro->h.handler (macro_start, macro_len, macro_args,
2170 if (macro->is_context) {
2171 ret = macro->h.context_handler (macro_escaped, macro_len,
2177 ret = macro->h.handler (macro_escaped, macro_len, macro_args,
2181 UCL_FREE (macro_len + 1, macro_escaped);
2185 * Chunk can be modified within macro handler
2187 chunk = parser->chunks;
2190 ucl_object_unref (macro_args);
2197 /* TODO: add all states */
2198 ucl_set_err (parser, UCL_EINTERNAL,
2199 "internal error: parser is in an unknown state", &parser->err);
2200 parser->state = UCL_STATE_ERROR;
2209 ucl_parser_new (int flags)
2211 struct ucl_parser *new;
2213 new = UCL_ALLOC (sizeof (struct ucl_parser));
2218 memset (new, 0, sizeof (struct ucl_parser));
2220 ucl_parser_register_macro (new, "include", ucl_include_handler, new);
2221 ucl_parser_register_macro (new, "try_include", ucl_try_include_handler, new);
2222 ucl_parser_register_macro (new, "includes", ucl_includes_handler, new);
2223 ucl_parser_register_macro (new, "priority", ucl_priority_handler, new);
2224 ucl_parser_register_macro (new, "load", ucl_load_handler, new);
2225 ucl_parser_register_context_macro (new, "inherit", ucl_inherit_handler, new);
2228 new->includepaths = NULL;
2230 /* Initial assumption about filevars */
2231 ucl_parser_set_filevars (new, NULL, false);
2237 ucl_parser_set_default_priority (struct ucl_parser *parser, unsigned prio)
2239 if (parser == NULL) {
2243 parser->default_priority = prio;
2249 ucl_parser_register_macro (struct ucl_parser *parser, const char *macro,
2250 ucl_macro_handler handler, void* ud)
2252 struct ucl_macro *new;
2254 if (macro == NULL || handler == NULL) {
2258 new = UCL_ALLOC (sizeof (struct ucl_macro));
2263 memset (new, 0, sizeof (struct ucl_macro));
2264 new->h.handler = handler;
2265 new->name = strdup (macro);
2267 HASH_ADD_KEYPTR (hh, parser->macroes, new->name, strlen (new->name), new);
2271 ucl_parser_register_context_macro (struct ucl_parser *parser, const char *macro,
2272 ucl_context_macro_handler handler, void* ud)
2274 struct ucl_macro *new;
2276 if (macro == NULL || handler == NULL) {
2280 new = UCL_ALLOC (sizeof (struct ucl_macro));
2285 memset (new, 0, sizeof (struct ucl_macro));
2286 new->h.context_handler = handler;
2287 new->name = strdup (macro);
2289 new->is_context = true;
2290 HASH_ADD_KEYPTR (hh, parser->macroes, new->name, strlen (new->name), new);
2294 ucl_parser_register_variable (struct ucl_parser *parser, const char *var,
2297 struct ucl_variable *new = NULL, *cur;
2303 /* Find whether a variable already exists */
2304 LL_FOREACH (parser->variables, cur) {
2305 if (strcmp (cur->var, var) == 0) {
2311 if (value == NULL) {
2314 /* Remove variable */
2315 DL_DELETE (parser->variables, new);
2318 UCL_FREE (sizeof (struct ucl_variable), new);
2327 new = UCL_ALLOC (sizeof (struct ucl_variable));
2331 memset (new, 0, sizeof (struct ucl_variable));
2332 new->var = strdup (var);
2333 new->var_len = strlen (var);
2334 new->value = strdup (value);
2335 new->value_len = strlen (value);
2337 DL_APPEND (parser->variables, new);
2341 new->value = strdup (value);
2342 new->value_len = strlen (value);
2348 ucl_parser_set_variables_handler (struct ucl_parser *parser,
2349 ucl_variable_handler handler, void *ud)
2351 parser->var_handler = handler;
2352 parser->var_data = ud;
2356 ucl_parser_add_chunk_full (struct ucl_parser *parser, const unsigned char *data,
2357 size_t len, unsigned priority, enum ucl_duplicate_strategy strat,
2358 enum ucl_parse_type parse_type)
2360 struct ucl_chunk *chunk;
2362 if (parser == NULL) {
2367 ucl_create_err (&parser->err, "invalid chunk added");
2371 parser->top_obj = ucl_object_new_full (UCL_OBJECT, priority);
2374 if (parser->state != UCL_STATE_ERROR) {
2375 chunk = UCL_ALLOC (sizeof (struct ucl_chunk));
2376 if (chunk == NULL) {
2377 ucl_create_err (&parser->err, "cannot allocate chunk structure");
2380 chunk->begin = data;
2381 chunk->remain = len;
2382 chunk->pos = chunk->begin;
2383 chunk->end = chunk->begin + len;
2386 chunk->priority = priority;
2387 chunk->strategy = strat;
2388 chunk->parse_type = parse_type;
2389 LL_PREPEND (parser->chunks, chunk);
2390 parser->recursion ++;
2392 if (parser->recursion > UCL_MAX_RECURSION) {
2393 ucl_create_err (&parser->err, "maximum include nesting limit is reached: %d",
2398 switch (parse_type) {
2401 return ucl_state_machine (parser);
2402 case UCL_PARSE_MSGPACK:
2403 return ucl_parse_msgpack (parser);
2407 ucl_create_err (&parser->err, "a parser is in an invalid state");
2413 ucl_parser_add_chunk_priority (struct ucl_parser *parser,
2414 const unsigned char *data, size_t len, unsigned priority)
2416 /* We dereference parser, so this check is essential */
2417 if (parser == NULL) {
2421 return ucl_parser_add_chunk_full (parser, data, len,
2422 priority, UCL_DUPLICATE_APPEND, UCL_PARSE_UCL);
2426 ucl_parser_add_chunk (struct ucl_parser *parser, const unsigned char *data,
2429 if (parser == NULL) {
2433 return ucl_parser_add_chunk_full (parser, data, len,
2434 parser->default_priority, UCL_DUPLICATE_APPEND, UCL_PARSE_UCL);
2438 ucl_parser_add_string_priority (struct ucl_parser *parser, const char *data,
2439 size_t len, unsigned priority)
2442 ucl_create_err (&parser->err, "invalid string added");
2446 len = strlen (data);
2449 return ucl_parser_add_chunk_priority (parser,
2450 (const unsigned char *)data, len, priority);
2454 ucl_parser_add_string (struct ucl_parser *parser, const char *data,
2457 if (parser == NULL) {
2461 return ucl_parser_add_string_priority (parser,
2462 (const unsigned char *)data, len, parser->default_priority);
2466 ucl_set_include_path (struct ucl_parser *parser, ucl_object_t *paths)
2468 if (parser == NULL || paths == NULL) {
2472 if (parser->includepaths == NULL) {
2473 parser->includepaths = ucl_object_copy (paths);
2476 ucl_object_unref (parser->includepaths);
2477 parser->includepaths = ucl_object_copy (paths);
2480 if (parser->includepaths == NULL) {