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 ucl_save_comment (struct ucl_parser *parser, const char *begin, size_t len)
97 if (len > 0 && begin != NULL) {
98 nobj = ucl_object_fromstring_common (begin, len, 0);
100 if (parser->last_comment) {
101 /* We need to append data to an existing object */
102 DL_APPEND (parser->last_comment, nobj);
105 parser->last_comment = nobj;
111 ucl_attach_comment (struct ucl_parser *parser, ucl_object_t *obj, bool before)
113 if (parser->last_comment) {
114 ucl_object_insert_key (parser->comments, parser->last_comment,
115 (const char *)&obj, sizeof (void *), true);
118 parser->last_comment->flags |= UCL_OBJECT_INHERITED;
121 parser->last_comment = NULL;
126 * Skip all comments from the current pos resolving nested and multiline comments
131 ucl_skip_comments (struct ucl_parser *parser)
133 struct ucl_chunk *chunk = parser->chunks;
134 const unsigned char *p, *beg = NULL;
135 int comments_nested = 0;
141 if (chunk->remain > 0 && *p == '#') {
142 if (parser->state != UCL_STATE_SCOMMENT &&
143 parser->state != UCL_STATE_MCOMMENT) {
146 while (p < chunk->end) {
148 if (parser->flags & UCL_PARSER_SAVE_COMMENTS) {
149 ucl_save_comment (parser, beg, p - beg);
153 ucl_chunk_skipc (chunk, p);
157 ucl_chunk_skipc (chunk, p);
161 else if (chunk->remain >= 2 && *p == '/') {
164 ucl_chunk_skipc (chunk, p);
166 ucl_chunk_skipc (chunk, p);
168 while (p < chunk->end) {
169 if (*p == '"' && *(p - 1) != '\\') {
175 ucl_chunk_skipc (chunk, p);
178 if (comments_nested == 0) {
179 if (parser->flags & UCL_PARSER_SAVE_COMMENTS) {
180 ucl_save_comment (parser, beg, p - beg + 1);
184 ucl_chunk_skipc (chunk, p);
188 ucl_chunk_skipc (chunk, p);
190 else if (p[0] == '/' && chunk->remain >= 2 && p[1] == '*') {
192 ucl_chunk_skipc (chunk, p);
193 ucl_chunk_skipc (chunk, p);
198 ucl_chunk_skipc (chunk, p);
200 if (comments_nested != 0) {
201 ucl_set_err (parser, UCL_ENESTED,
202 "unfinished multiline comment", &parser->err);
208 if (beg && p > beg && (parser->flags & UCL_PARSER_SAVE_COMMENTS)) {
209 ucl_save_comment (parser, beg, p - beg);
216 * Return multiplier for a character
217 * @param c multiplier character
218 * @param is_bytes if true use 1024 multiplier
221 static inline unsigned long
222 ucl_lex_num_multiplier (const unsigned char c, bool is_bytes) {
228 {'m', 1000 * 1000, 1024 * 1024},
230 {'g', 1000 * 1000 * 1000, 1024 * 1024 * 1024}
234 for (i = 0; i < 3; i ++) {
235 if (tolower (c) == multipliers[i].c) {
237 return multipliers[i].mult_bytes;
239 return multipliers[i].mult_normal;
248 * Return multiplier for time scaling
253 ucl_lex_time_multiplier (const unsigned char c) {
261 {'w', 60 * 60 * 24 * 7},
262 {'y', 60 * 60 * 24 * 365}
266 for (i = 0; i < 5; i ++) {
267 if (tolower (c) == multipliers[i].c) {
268 return multipliers[i].mult;
276 * Return true if a character is a end of an atom
281 ucl_lex_is_atom_end (const unsigned char c)
283 return ucl_test_character (c, UCL_CHARACTER_VALUE_END);
287 ucl_lex_is_comment (const unsigned char c1, const unsigned char c2)
294 else if (c1 == '#') {
301 * Check variable found
310 static inline const char *
311 ucl_check_variable_safe (struct ucl_parser *parser, const char *ptr, size_t remain,
312 size_t *out_len, bool strict, bool *found)
314 struct ucl_variable *var;
317 bool need_free = false;
319 LL_FOREACH (parser->variables, var) {
321 if (remain == var->var_len) {
322 if (memcmp (ptr, var->var, var->var_len) == 0) {
323 *out_len += var->value_len;
325 return (ptr + var->var_len);
330 if (remain >= var->var_len) {
331 if (memcmp (ptr, var->var, var->var_len) == 0) {
332 *out_len += var->value_len;
334 return (ptr + var->var_len);
340 /* XXX: can only handle ${VAR} */
341 if (!(*found) && parser->var_handler != NULL && strict) {
342 /* Call generic handler */
343 if (parser->var_handler (ptr, remain, &dst, &dstlen, &need_free,
350 return (ptr + remain);
358 * Check for a variable in a given string
367 ucl_check_variable (struct ucl_parser *parser, const char *ptr,
368 size_t remain, size_t *out_len, bool *vars_found)
370 const char *p, *end, *ret = ptr;
374 /* We need to match the variable enclosed in braces */
379 ret = ucl_check_variable_safe (parser, ptr + 1, p - ptr - 1,
380 out_len, true, &found);
382 /* {} must be excluded actually */
396 else if (*ptr != '$') {
397 /* Not count escaped dollar sign */
398 ret = ucl_check_variable_safe (parser, ptr, remain, out_len, false, &found);
399 if (found && !*vars_found) {
415 * Expand a single variable
423 ucl_expand_single_variable (struct ucl_parser *parser, const char *ptr,
424 size_t remain, unsigned char **dest)
426 unsigned char *d = *dest, *dst;
427 const char *p = ptr + 1, *ret;
428 struct ucl_variable *var;
430 bool need_free = false;
442 else if (*p == '{') {
449 LL_FOREACH (parser->variables, var) {
450 if (remain >= var->var_len) {
451 if (memcmp (p, var->var, var->var_len) == 0) {
452 memcpy (d, var->value, var->value_len);
461 if (strict && parser->var_handler != NULL) {
463 while (var_len < remain && p[var_len] != '}')
466 if (parser->var_handler (p, var_len, &dst, &dstlen, &need_free,
468 memcpy (d, dst, dstlen);
478 /* Leave variable as is */
498 * Expand variables in string
506 ucl_expand_variable (struct ucl_parser *parser, unsigned char **dst,
507 const char *src, size_t in_len)
509 const char *p, *end = src + in_len;
512 bool vars_found = false;
514 if (parser->flags & UCL_PARSER_DISABLE_MACRO) {
522 p = ucl_check_variable (parser, p + 1, end - p - 1, &out_len, &vars_found);
536 *dst = UCL_ALLOC (out_len + 1);
545 p = ucl_expand_single_variable (parser, p, end - p, &d);
558 * Store or copy pointer to the trash stack
559 * @param parser parser object
560 * @param src src string
561 * @param dst destination buffer (trash stack pointer)
562 * @param dst_const const destination pointer (e.g. value of object)
563 * @param in_len input length
564 * @param need_unescape need to unescape source (and copy it)
565 * @param need_lowercase need to lowercase value (and copy)
566 * @param need_expand need to expand variables (and copy as well)
567 * @return output length (excluding \0 symbol)
569 static inline ssize_t
570 ucl_copy_or_store_ptr (struct ucl_parser *parser,
571 const unsigned char *src, unsigned char **dst,
572 const char **dst_const, size_t in_len,
573 bool need_unescape, bool need_lowercase, bool need_expand)
575 ssize_t ret = -1, tret;
578 if (need_unescape || need_lowercase ||
579 (need_expand && parser->variables != NULL) ||
580 !(parser->flags & UCL_PARSER_ZEROCOPY)) {
582 *dst = UCL_ALLOC (in_len + 1);
584 ucl_set_err (parser, UCL_EINTERNAL, "cannot allocate memory for a string",
588 if (need_lowercase) {
589 ret = ucl_strlcpy_tolower (*dst, src, in_len + 1);
592 ret = ucl_strlcpy_unsafe (*dst, src, in_len + 1);
596 ret = ucl_unescape_json_string (*dst, ret);
601 ret = ucl_expand_variable (parser, dst, tmp, ret);
603 /* Nothing to expand */
608 /* Free unexpanded value */
609 UCL_FREE (in_len + 1, tmp);
623 * Create and append an object at the specified level
629 static inline ucl_object_t *
630 ucl_parser_add_container (ucl_object_t *obj, struct ucl_parser *parser,
631 bool is_array, int level)
633 struct ucl_stack *st;
637 obj = ucl_object_new_full (UCL_OBJECT, parser->chunks->priority);
640 obj->type = UCL_OBJECT;
642 if (obj->value.ov == NULL) {
643 obj->value.ov = ucl_hash_create (parser->flags & UCL_PARSER_KEY_LOWERCASE);
645 parser->state = UCL_STATE_KEY;
649 obj = ucl_object_new_full (UCL_ARRAY, parser->chunks->priority);
652 obj->type = UCL_ARRAY;
654 parser->state = UCL_STATE_VALUE;
657 st = UCL_ALLOC (sizeof (struct ucl_stack));
660 ucl_set_err (parser, UCL_EINTERNAL, "cannot allocate memory for an object",
662 ucl_object_unref (obj);
668 LL_PREPEND (parser->stack, st);
669 parser->cur_obj = obj;
675 ucl_maybe_parse_number (ucl_object_t *obj,
676 const char *start, const char *end, const char **pos,
677 bool allow_double, bool number_bytes, bool allow_time)
679 const char *p = start, *c = start;
681 bool got_dot = false, got_exp = false, need_double = false,
682 is_time = false, valid_start = false, is_hex = false,
693 if (is_hex && isxdigit (*p)) {
696 else if (isdigit (*p)) {
700 else if (!is_hex && (*p == 'x' || *p == 'X')) {
702 allow_double = false;
705 else if (allow_double) {
707 /* Empty digits sequence, not a number */
711 else if (*p == '.') {
713 /* Double dots, not a number */
723 else if (*p == 'e' || *p == 'E') {
725 /* Double exp, not a number */
737 if (!isdigit (*p) && *p != '+' && *p != '-') {
738 /* Wrong exponent sign */
748 /* Got the end of the number, need to check */
764 dv = strtod (c, &endptr);
768 lv = strtoimax (c, &endptr, 16);
771 lv = strtoimax (c, &endptr, 10);
774 if (errno == ERANGE) {
779 /* Now check endptr */
780 if (endptr == NULL || ucl_lex_is_atom_end (*endptr) || *endptr == '\0') {
785 if (endptr < end && endptr != start) {
795 if (p[1] == 's' || p[1] == 'S') {
802 if (p[0] == 'm' || p[0] == 'M') {
806 dv *= ucl_lex_num_multiplier (*p, false);
811 else if (number_bytes || (p[1] == 'b' || p[1] == 'B')) {
817 lv *= ucl_lex_num_multiplier (*p, true);
821 else if (ucl_lex_is_atom_end (p[1])) {
823 dv *= ucl_lex_num_multiplier (*p, false);
826 lv *= ucl_lex_num_multiplier (*p, number_bytes);
831 else if (allow_time && end - p >= 3) {
832 if (tolower (p[0]) == 'm' &&
833 tolower (p[1]) == 'i' &&
834 tolower (p[2]) == 'n') {
849 dv *= ucl_lex_num_multiplier (*p, false);
852 lv *= ucl_lex_num_multiplier (*p, number_bytes);
861 (p == end - 1 || ucl_lex_is_atom_end (p[1]))) {
880 (p == end - 1 || ucl_lex_is_atom_end (p[1]))) {
886 dv *= ucl_lex_time_multiplier (*p);
893 while (p < end && ucl_test_character(*p, UCL_CHARACTER_WHITESPACE)) {
896 if (ucl_lex_is_atom_end(*p))
901 else if (endptr == end) {
902 /* Just a number at the end of chunk */
912 if (allow_double && (need_double || is_time)) {
914 obj->type = UCL_FLOAT;
917 obj->type = UCL_TIME;
919 obj->value.dv = is_neg ? (-dv) : dv;
923 obj->value.iv = is_neg ? (-lv) : lv;
931 * Parse possible number
935 * @return true if a number has been parsed
938 ucl_lex_number (struct ucl_parser *parser,
939 struct ucl_chunk *chunk, ucl_object_t *obj)
941 const unsigned char *pos;
944 ret = ucl_maybe_parse_number (obj, chunk->pos, chunk->end, (const char **)&pos,
945 true, false, ((parser->flags & UCL_PARSER_NO_TIME) == 0));
948 chunk->remain -= pos - chunk->pos;
949 chunk->column += pos - chunk->pos;
953 else if (ret == ERANGE) {
954 ucl_set_err (parser, UCL_ESYNTAX, "numeric value out of range",
962 * Parse quoted string with possible escapes
965 * @param need_unescape
968 * @return true if a string has been parsed
971 ucl_lex_json_string (struct ucl_parser *parser,
972 struct ucl_chunk *chunk, bool *need_unescape, bool *ucl_escape, bool *var_expand)
974 const unsigned char *p = chunk->pos;
978 while (p < chunk->end) {
981 /* Unmasked control character */
983 ucl_set_err (parser, UCL_ESYNTAX, "unexpected newline",
987 ucl_set_err (parser, UCL_ESYNTAX, "unexpected control character",
992 else if (c == '\\') {
993 ucl_chunk_skipc (chunk, p);
995 if (p >= chunk->end) {
996 ucl_set_err (parser, UCL_ESYNTAX, "unfinished escape character",
1000 else if (ucl_test_character (c, UCL_CHARACTER_ESCAPE)) {
1002 ucl_chunk_skipc (chunk, p);
1003 for (i = 0; i < 4 && p < chunk->end; i ++) {
1004 if (!isxdigit (*p)) {
1005 ucl_set_err (parser, UCL_ESYNTAX, "invalid utf escape",
1009 ucl_chunk_skipc (chunk, p);
1011 if (p >= chunk->end) {
1012 ucl_set_err (parser, UCL_ESYNTAX, "unfinished escape character",
1018 ucl_chunk_skipc (chunk, p);
1021 *need_unescape = true;
1025 else if (c == '"') {
1026 ucl_chunk_skipc (chunk, p);
1029 else if (ucl_test_character (c, UCL_CHARACTER_UCL_UNSAFE)) {
1032 else if (c == '$') {
1035 ucl_chunk_skipc (chunk, p);
1038 ucl_set_err (parser, UCL_ESYNTAX, "no quote at the end of json string",
1044 ucl_parser_append_elt (struct ucl_parser *parser, ucl_hash_t *cont,
1050 if ((parser->flags & UCL_PARSER_NO_IMPLICIT_ARRAYS) == 0) {
1051 /* Implicit array */
1052 top->flags |= UCL_OBJECT_MULTIVALUE;
1053 DL_APPEND (top, elt);
1054 parser->stack->obj->len ++;
1057 if ((top->flags & UCL_OBJECT_MULTIVALUE) != 0) {
1058 /* Just add to the explicit array */
1059 ucl_array_append (top, elt);
1062 /* Convert to an array */
1063 nobj = ucl_object_typed_new (UCL_ARRAY);
1064 nobj->key = top->key;
1065 nobj->keylen = top->keylen;
1066 nobj->flags |= UCL_OBJECT_MULTIVALUE;
1067 ucl_array_append (nobj, top);
1068 ucl_array_append (nobj, elt);
1069 ucl_hash_replace (cont, top, nobj);
1075 ucl_parser_process_object_element (struct ucl_parser *parser, ucl_object_t *nobj)
1077 ucl_hash_t *container;
1081 container = parser->stack->obj->value.ov;
1083 tobj = __DECONST (ucl_object_t *, ucl_hash_search_obj (container, nobj));
1085 container = ucl_hash_insert_object (container, nobj,
1086 parser->flags & UCL_PARSER_KEY_LOWERCASE);
1089 parser->stack->obj->len ++;
1092 unsigned priold = ucl_object_get_priority (tobj),
1093 prinew = ucl_object_get_priority (nobj);
1094 switch (parser->chunks->strategy) {
1096 case UCL_DUPLICATE_APPEND:
1098 * The logic here is the following:
1100 * - if we have two objects with the same priority, then we form an
1101 * implicit or explicit array
1102 * - if a new object has bigger priority, then we overwrite an old one
1103 * - if a new object has lower priority, then we ignore it
1107 /* Special case for inherited objects */
1108 if (tobj->flags & UCL_OBJECT_INHERITED) {
1109 prinew = priold + 1;
1112 if (priold == prinew) {
1113 ucl_parser_append_elt (parser, container, tobj, nobj);
1115 else if (priold > prinew) {
1117 * We add this new object to a list of trash objects just to ensure
1118 * that it won't come to any real object
1119 * XXX: rather inefficient approach
1121 DL_APPEND (parser->trash_objs, nobj);
1124 ucl_hash_replace (container, tobj, nobj);
1125 ucl_object_unref (tobj);
1130 case UCL_DUPLICATE_REWRITE:
1131 /* We just rewrite old values regardless of priority */
1132 ucl_hash_replace (container, tobj, nobj);
1133 ucl_object_unref (tobj);
1137 case UCL_DUPLICATE_ERROR:
1138 snprintf(errmsg, sizeof(errmsg),
1139 "duplicate element for key '%s' found",
1141 ucl_set_err (parser, UCL_EMERGE, errmsg, &parser->err);
1144 case UCL_DUPLICATE_MERGE:
1146 * Here we do have some old object so we just push it on top of objects stack
1147 * Check priority and then perform the merge on the remaining objects
1149 if (tobj->type == UCL_OBJECT || tobj->type == UCL_ARRAY) {
1150 ucl_object_unref (nobj);
1153 else if (priold == prinew) {
1154 ucl_parser_append_elt (parser, container, tobj, nobj);
1156 else if (priold > prinew) {
1158 * We add this new object to a list of trash objects just to ensure
1159 * that it won't come to any real object
1160 * XXX: rather inefficient approach
1162 DL_APPEND (parser->trash_objs, nobj);
1165 ucl_hash_replace (container, tobj, nobj);
1166 ucl_object_unref (tobj);
1172 parser->stack->obj->value.ov = container;
1173 parser->cur_obj = nobj;
1174 ucl_attach_comment (parser, nobj, false);
1180 * Parse a key in an object
1184 * @param end_of_object
1185 * @return true if a key has been parsed
1188 ucl_parse_key (struct ucl_parser *parser, struct ucl_chunk *chunk,
1189 bool *next_key, bool *end_of_object)
1191 const unsigned char *p, *c = NULL, *end, *t;
1192 const char *key = NULL;
1193 bool got_quote = false, got_eq = false, got_semicolon = false,
1194 need_unescape = false, ucl_escape = false, var_expand = false,
1195 got_content = false, got_sep = false;
1202 /* It is macro actually */
1203 if (!(parser->flags & UCL_PARSER_DISABLE_MACRO)) {
1204 ucl_chunk_skipc (chunk, p);
1207 parser->prev_state = parser->state;
1208 parser->state = UCL_STATE_MACRO_NAME;
1209 *end_of_object = false;
1212 while (p < chunk->end) {
1214 * A key must start with alpha, number, '/' or '_' and end with space character
1217 if (chunk->remain >= 2 && ucl_lex_is_comment (p[0], p[1])) {
1218 if (!ucl_skip_comments (parser)) {
1223 else if (ucl_test_character (*p, UCL_CHARACTER_WHITESPACE_UNSAFE)) {
1224 ucl_chunk_skipc (chunk, p);
1226 else if (ucl_test_character (*p, UCL_CHARACTER_KEY_START)) {
1227 /* The first symbol */
1229 ucl_chunk_skipc (chunk, p);
1232 else if (*p == '"') {
1233 /* JSON style key */
1237 ucl_chunk_skipc (chunk, p);
1239 else if (*p == '}') {
1240 /* We have actually end of an object */
1241 *end_of_object = true;
1244 else if (*p == '.') {
1245 ucl_chunk_skipc (chunk, p);
1246 parser->prev_state = parser->state;
1247 parser->state = UCL_STATE_MACRO_NAME;
1251 /* Invalid identifier */
1252 ucl_set_err (parser, UCL_ESYNTAX, "key must begin with a letter",
1258 /* Parse the body of a key */
1260 if (ucl_test_character (*p, UCL_CHARACTER_KEY)) {
1262 ucl_chunk_skipc (chunk, p);
1264 else if (ucl_test_character (*p, UCL_CHARACTER_KEY_SEP)) {
1269 ucl_set_err (parser, UCL_ESYNTAX, "invalid character in a key",
1275 /* We need to parse json like quoted string */
1276 if (!ucl_lex_json_string (parser, chunk, &need_unescape, &ucl_escape, &var_expand)) {
1279 /* Always escape keys obtained via json */
1280 end = chunk->pos - 1;
1287 if (p >= chunk->end && got_content) {
1288 ucl_set_err (parser, UCL_ESYNTAX, "unfinished key", &parser->err);
1291 else if (!got_content) {
1294 *end_of_object = false;
1295 /* We are now at the end of the key, need to parse the rest */
1296 while (p < chunk->end) {
1297 if (ucl_test_character (*p, UCL_CHARACTER_WHITESPACE)) {
1298 ucl_chunk_skipc (chunk, p);
1300 else if (*p == '=') {
1301 if (!got_eq && !got_semicolon) {
1302 ucl_chunk_skipc (chunk, p);
1306 ucl_set_err (parser, UCL_ESYNTAX, "unexpected '=' character",
1311 else if (*p == ':') {
1312 if (!got_eq && !got_semicolon) {
1313 ucl_chunk_skipc (chunk, p);
1314 got_semicolon = true;
1317 ucl_set_err (parser, UCL_ESYNTAX, "unexpected ':' character",
1322 else if (chunk->remain >= 2 && ucl_lex_is_comment (p[0], p[1])) {
1323 /* Check for comment */
1324 if (!ucl_skip_comments (parser)) {
1335 if (p >= chunk->end && got_content) {
1336 ucl_set_err (parser, UCL_ESYNTAX, "unfinished key", &parser->err);
1340 got_sep = got_semicolon || got_eq;
1344 * Maybe we have more keys nested, so search for termination character.
1346 * 1) key1 key2 ... keyN [:=] value <- we treat that as error
1347 * 2) key1 ... keyN {} or [] <- we treat that as nested objects
1348 * 3) key1 value[;,\n] <- we treat that as linear object
1352 while (ucl_test_character (*t, UCL_CHARACTER_WHITESPACE)) {
1355 /* Check first non-space character after a key */
1356 if (*t != '{' && *t != '[') {
1357 while (t < chunk->end) {
1358 if (*t == ',' || *t == ';' || *t == '\n' || *t == '\r') {
1361 else if (*t == '{' || *t == '[') {
1370 /* Create a new object */
1371 nobj = ucl_object_new_full (UCL_NULL, parser->chunks->priority);
1372 keylen = ucl_copy_or_store_ptr (parser, c, &nobj->trash_stack[UCL_TRASH_KEY],
1373 &key, end - c, need_unescape, parser->flags & UCL_PARSER_KEY_LOWERCASE, false);
1375 ucl_object_unref (nobj);
1378 else if (keylen == 0) {
1379 ucl_set_err (parser, UCL_ESYNTAX, "empty keys are not allowed", &parser->err);
1380 ucl_object_unref (nobj);
1385 nobj->keylen = keylen;
1387 if (!ucl_parser_process_object_element (parser, nobj)) {
1392 nobj->flags |= UCL_OBJECT_NEED_KEY_ESCAPE;
1404 * @param need_unescape
1405 * @return true if a key has been parsed
1408 ucl_parse_string_value (struct ucl_parser *parser,
1409 struct ucl_chunk *chunk, bool *var_expand, bool *need_unescape)
1411 const unsigned char *p;
1413 UCL_BRACE_ROUND = 0,
1417 int braces[3][2] = {{0, 0}, {0, 0}, {0, 0}};
1421 while (p < chunk->end) {
1423 /* Skip pairs of figure braces */
1425 braces[UCL_BRACE_FIGURE][0] ++;
1427 else if (*p == '}') {
1428 braces[UCL_BRACE_FIGURE][1] ++;
1429 if (braces[UCL_BRACE_FIGURE][1] <= braces[UCL_BRACE_FIGURE][0]) {
1430 /* This is not a termination symbol, continue */
1431 ucl_chunk_skipc (chunk, p);
1435 /* Skip pairs of square braces */
1436 else if (*p == '[') {
1437 braces[UCL_BRACE_SQUARE][0] ++;
1439 else if (*p == ']') {
1440 braces[UCL_BRACE_SQUARE][1] ++;
1441 if (braces[UCL_BRACE_SQUARE][1] <= braces[UCL_BRACE_SQUARE][0]) {
1442 /* This is not a termination symbol, continue */
1443 ucl_chunk_skipc (chunk, p);
1447 else if (*p == '$') {
1450 else if (*p == '\\') {
1451 *need_unescape = true;
1452 ucl_chunk_skipc (chunk, p);
1453 if (p < chunk->end) {
1454 ucl_chunk_skipc (chunk, p);
1459 if (ucl_lex_is_atom_end (*p) || (chunk->remain >= 2 && ucl_lex_is_comment (p[0], p[1]))) {
1462 ucl_chunk_skipc (chunk, p);
1469 * Parse multiline string ending with \n{term}\n
1476 * @return size of multiline string or 0 in case of error
1479 ucl_parse_multiline_string (struct ucl_parser *parser,
1480 struct ucl_chunk *chunk, const unsigned char *term,
1481 int term_len, unsigned char const **beg,
1484 const unsigned char *p, *c, *tend;
1485 bool newline = false;
1492 while (p < chunk->end) {
1494 if (chunk->end - p < term_len) {
1497 else if (memcmp (p, term, term_len) == 0) {
1498 tend = p + term_len;
1499 if (*tend != '\n' && *tend != ';' && *tend != ',') {
1500 /* Incomplete terminator */
1501 ucl_chunk_skipc (chunk, p);
1505 chunk->remain -= term_len;
1506 chunk->pos = p + term_len;
1507 chunk->column = term_len;
1521 ucl_chunk_skipc (chunk, p);
1527 static inline ucl_object_t*
1528 ucl_parser_get_container (struct ucl_parser *parser)
1530 ucl_object_t *t, *obj = NULL;
1532 if (parser == NULL || parser->stack == NULL || parser->stack->obj == NULL) {
1536 if (parser->stack->obj->type == UCL_ARRAY) {
1537 /* Object must be allocated */
1538 obj = ucl_object_new_full (UCL_NULL, parser->chunks->priority);
1539 t = parser->stack->obj;
1541 if (!ucl_array_append (t, obj)) {
1542 ucl_object_unref (obj);
1546 parser->cur_obj = obj;
1547 ucl_attach_comment (parser, obj, false);
1550 /* Object has been already allocated */
1551 obj = parser->cur_obj;
1564 ucl_parse_value (struct ucl_parser *parser, struct ucl_chunk *chunk)
1566 const unsigned char *p, *c;
1567 ucl_object_t *obj = NULL;
1568 unsigned int stripped_spaces;
1570 bool need_unescape = false, ucl_escape = false, var_expand = false;
1574 /* Skip any spaces and comments */
1575 if (ucl_test_character (*p, UCL_CHARACTER_WHITESPACE_UNSAFE) ||
1576 (chunk->remain >= 2 && ucl_lex_is_comment (p[0], p[1]))) {
1577 while (p < chunk->end && ucl_test_character (*p, UCL_CHARACTER_WHITESPACE_UNSAFE)) {
1578 ucl_chunk_skipc (chunk, p);
1580 if (!ucl_skip_comments (parser)) {
1586 while (p < chunk->end) {
1590 ucl_chunk_skipc (chunk, p);
1592 if (!ucl_lex_json_string (parser, chunk, &need_unescape, &ucl_escape,
1597 obj = ucl_parser_get_container (parser);
1602 str_len = chunk->pos - c - 2;
1603 obj->type = UCL_STRING;
1604 if ((str_len = ucl_copy_or_store_ptr (parser, c + 1,
1605 &obj->trash_stack[UCL_TRASH_VALUE],
1606 &obj->value.sv, str_len, need_unescape, false,
1607 var_expand)) == -1) {
1612 parser->state = UCL_STATE_AFTER_VALUE;
1618 obj = ucl_parser_get_container (parser);
1619 /* We have a new object */
1620 obj = ucl_parser_add_container (obj, parser, false, parser->stack->level);
1625 ucl_chunk_skipc (chunk, p);
1630 obj = ucl_parser_get_container (parser);
1631 /* We have a new array */
1632 obj = ucl_parser_add_container (obj, parser, true, parser->stack->level);
1637 ucl_chunk_skipc (chunk, p);
1642 /* We have the array ending */
1643 if (parser->stack && parser->stack->obj->type == UCL_ARRAY) {
1644 parser->state = UCL_STATE_AFTER_VALUE;
1652 obj = ucl_parser_get_container (parser);
1653 /* We have something like multiline value, which must be <<[A-Z]+\n */
1654 if (chunk->end - p > 3) {
1655 if (memcmp (p, "<<", 2) == 0) {
1657 /* We allow only uppercase characters in multiline definitions */
1658 while (p < chunk->end && *p >= 'A' && *p <= 'Z') {
1662 /* Set chunk positions and start multiline parsing */
1664 chunk->remain -= p - c;
1668 if ((str_len = ucl_parse_multiline_string (parser, chunk, c,
1669 p - c, &c, &var_expand)) == 0) {
1670 ucl_set_err (parser, UCL_ESYNTAX,
1671 "unterminated multiline value", &parser->err);
1675 obj->type = UCL_STRING;
1676 obj->flags |= UCL_OBJECT_MULTILINE;
1677 if ((str_len = ucl_copy_or_store_ptr (parser, c,
1678 &obj->trash_stack[UCL_TRASH_VALUE],
1679 &obj->value.sv, str_len - 1, false,
1680 false, var_expand)) == -1) {
1685 parser->state = UCL_STATE_AFTER_VALUE;
1691 /* Fallback to ordinary strings */
1695 obj = ucl_parser_get_container (parser);
1699 if (ucl_test_character (*p, UCL_CHARACTER_VALUE_DIGIT_START)) {
1700 if (!ucl_lex_number (parser, chunk, obj)) {
1701 if (parser->state == UCL_STATE_ERROR) {
1706 parser->state = UCL_STATE_AFTER_VALUE;
1709 /* Fallback to normal string */
1712 if (!ucl_parse_string_value (parser, chunk, &var_expand,
1716 /* Cut trailing spaces */
1717 stripped_spaces = 0;
1718 while (ucl_test_character (*(chunk->pos - 1 - stripped_spaces),
1719 UCL_CHARACTER_WHITESPACE)) {
1722 str_len = chunk->pos - c - stripped_spaces;
1724 ucl_set_err (parser, UCL_ESYNTAX, "string value must not be empty",
1728 else if (str_len == 4 && memcmp (c, "null", 4) == 0) {
1730 obj->type = UCL_NULL;
1732 else if (!ucl_maybe_parse_boolean (obj, c, str_len)) {
1733 obj->type = UCL_STRING;
1734 if ((str_len = ucl_copy_or_store_ptr (parser, c,
1735 &obj->trash_stack[UCL_TRASH_VALUE],
1736 &obj->value.sv, str_len, need_unescape,
1737 false, var_expand)) == -1) {
1742 parser->state = UCL_STATE_AFTER_VALUE;
1754 * Handle after value data
1760 ucl_parse_after_value (struct ucl_parser *parser, struct ucl_chunk *chunk)
1762 const unsigned char *p;
1763 bool got_sep = false;
1764 struct ucl_stack *st;
1768 while (p < chunk->end) {
1769 if (ucl_test_character (*p, UCL_CHARACTER_WHITESPACE)) {
1770 /* Skip whitespaces */
1771 ucl_chunk_skipc (chunk, p);
1773 else if (chunk->remain >= 2 && ucl_lex_is_comment (p[0], p[1])) {
1775 if (!ucl_skip_comments (parser)) {
1778 /* Treat comment as a separator */
1782 else if (ucl_test_character (*p, UCL_CHARACTER_VALUE_END)) {
1783 if (*p == '}' || *p == ']') {
1784 if (parser->stack == NULL) {
1785 ucl_set_err (parser, UCL_ESYNTAX,
1786 "end of array or object detected without corresponding start",
1790 if ((*p == '}' && parser->stack->obj->type == UCL_OBJECT) ||
1791 (*p == ']' && parser->stack->obj->type == UCL_ARRAY)) {
1793 /* Pop all nested objects from a stack */
1795 parser->stack = st->next;
1796 UCL_FREE (sizeof (struct ucl_stack), st);
1798 if (parser->cur_obj) {
1799 ucl_attach_comment (parser, parser->cur_obj, true);
1802 while (parser->stack != NULL) {
1805 if (st->next == NULL || st->next->level == st->level) {
1809 parser->stack = st->next;
1810 parser->cur_obj = st->obj;
1811 UCL_FREE (sizeof (struct ucl_stack), st);
1815 ucl_set_err (parser, UCL_ESYNTAX,
1816 "unexpected terminating symbol detected",
1821 if (parser->stack == NULL) {
1822 /* Ignore everything after a top object */
1826 ucl_chunk_skipc (chunk, p);
1831 /* Got a separator */
1833 ucl_chunk_skipc (chunk, p);
1839 ucl_set_err (parser, UCL_ESYNTAX, "delimiter is missing",
1851 ucl_skip_macro_as_comment (struct ucl_parser *parser,
1852 struct ucl_chunk *chunk)
1854 const unsigned char *p, *c;
1856 macro_skip_start = 0,
1860 macro_has_backslash,
1863 } state = macro_skip_start, prev_state = macro_skip_start;
1868 while (p < chunk->end) {
1870 case macro_skip_start:
1871 if (!ucl_test_character (*p, UCL_CHARACTER_WHITESPACE)) {
1872 state = macro_has_symbols;
1874 else if (ucl_test_character (*p, UCL_CHARACTER_WHITESPACE_UNSAFE)) {
1879 ucl_chunk_skipc (chunk, p);
1882 case macro_has_symbols:
1884 state = macro_has_sqbrace;
1886 else if (*p == '(') {
1887 state = macro_has_obrace;
1889 else if (*p == '"') {
1890 state = macro_has_quote;
1892 else if (*p == '\n') {
1897 ucl_chunk_skipc (chunk, p);
1900 case macro_has_obrace:
1903 state = macro_has_backslash;
1905 else if (*p == ')') {
1906 state = macro_has_symbols;
1909 ucl_chunk_skipc (chunk, p);
1912 case macro_has_sqbrace:
1915 state = macro_has_backslash;
1917 else if (*p == '}') {
1921 ucl_chunk_skipc (chunk, p);
1924 case macro_has_quote:
1927 state = macro_has_backslash;
1929 else if (*p == '"') {
1933 ucl_chunk_skipc (chunk, p);
1936 case macro_has_backslash:
1938 ucl_chunk_skipc (chunk, p);
1942 if (parser->flags & UCL_PARSER_SAVE_COMMENTS) {
1943 ucl_save_comment (parser, c, p - c);
1958 * @param macro_start
1963 ucl_parse_macro_value (struct ucl_parser *parser,
1964 struct ucl_chunk *chunk, struct ucl_macro *macro,
1965 unsigned char const **macro_start, size_t *macro_len)
1967 const unsigned char *p, *c;
1968 bool need_unescape = false, ucl_escape = false, var_expand = false;
1974 /* We have macro value encoded in quotes */
1976 ucl_chunk_skipc (chunk, p);
1977 if (!ucl_lex_json_string (parser, chunk, &need_unescape, &ucl_escape, &var_expand)) {
1981 *macro_start = c + 1;
1982 *macro_len = chunk->pos - c - 2;
1986 /* We got a multiline macro body */
1987 ucl_chunk_skipc (chunk, p);
1988 /* Skip spaces at the beginning */
1989 while (p < chunk->end) {
1990 if (ucl_test_character (*p, UCL_CHARACTER_WHITESPACE_UNSAFE)) {
1991 ucl_chunk_skipc (chunk, p);
1998 while (p < chunk->end) {
2002 ucl_chunk_skipc (chunk, p);
2006 ucl_chunk_skipc (chunk, p);
2009 /* Macro is not enclosed in quotes or braces */
2011 while (p < chunk->end) {
2012 if (ucl_lex_is_atom_end (*p)) {
2015 ucl_chunk_skipc (chunk, p);
2022 /* We are at the end of a macro */
2023 /* Skip ';' and space characters and return to previous state */
2024 while (p < chunk->end) {
2025 if (!ucl_test_character (*p, UCL_CHARACTER_WHITESPACE_UNSAFE) && *p != ';') {
2028 ucl_chunk_skipc (chunk, p);
2034 * Parse macro arguments as UCL object
2035 * @param parser parser structure
2036 * @param chunk the current data chunk
2039 static ucl_object_t *
2040 ucl_parse_macro_arguments (struct ucl_parser *parser,
2041 struct ucl_chunk *chunk)
2043 ucl_object_t *res = NULL;
2044 struct ucl_parser *params_parser;
2045 int obraces = 1, ebraces = 0, state = 0;
2046 const unsigned char *p, *c;
2047 size_t args_len = 0;
2048 struct ucl_parser_saved_state saved;
2050 saved.column = chunk->column;
2051 saved.line = chunk->line;
2052 saved.pos = chunk->pos;
2053 saved.remain = chunk->remain;
2056 if (*p != '(' || chunk->remain < 2) {
2060 /* Set begin and start */
2061 ucl_chunk_skipc (chunk, p);
2064 while ((p) < (chunk)->end) {
2067 /* Parse symbols and check for '(', ')' and '"' */
2071 else if (*p == ')') {
2074 else if (*p == '"') {
2078 if (obraces == ebraces) {
2084 /* Check overflow */
2085 if (chunk->remain == 0) {
2088 ucl_chunk_skipc (chunk, p);
2091 /* We have quote character, so skip all but quotes */
2092 if (*p == '"' && *(p - 1) != '\\') {
2095 if (chunk->remain == 0) {
2099 ucl_chunk_skipc (chunk, p);
2103 * We have read the full body of arguments, so we need to parse and set
2106 params_parser = ucl_parser_new (parser->flags);
2107 if (!ucl_parser_add_chunk (params_parser, c, args_len)) {
2108 ucl_set_err (parser, UCL_ESYNTAX, "macro arguments parsing error",
2112 res = ucl_parser_get_object (params_parser);
2114 ucl_parser_free (params_parser);
2125 chunk->column = saved.column;
2126 chunk->line = saved.line;
2127 chunk->pos = saved.pos;
2128 chunk->remain = saved.remain;
2133 #define SKIP_SPACES_COMMENTS(parser, chunk, p) do { \
2134 while ((p) < (chunk)->end) { \
2135 if (!ucl_test_character (*(p), UCL_CHARACTER_WHITESPACE_UNSAFE)) { \
2136 if ((chunk)->remain >= 2 && ucl_lex_is_comment ((p)[0], (p)[1])) { \
2137 if (!ucl_skip_comments (parser)) { \
2144 ucl_chunk_skipc (chunk, p); \
2149 * Handle the main states of rcl parser
2150 * @param parser parser structure
2151 * @return true if chunk has been parsed and false in case of error
2154 ucl_state_machine (struct ucl_parser *parser)
2156 ucl_object_t *obj, *macro_args;
2157 struct ucl_chunk *chunk = parser->chunks;
2158 const unsigned char *p, *c = NULL, *macro_start = NULL;
2159 unsigned char *macro_escaped;
2160 size_t macro_len = 0;
2161 struct ucl_macro *macro = NULL;
2162 bool next_key = false, end_of_object = false, ret;
2164 if (parser->top_obj == NULL) {
2165 parser->state = UCL_STATE_INIT;
2169 while (chunk->pos < chunk->end) {
2170 switch (parser->state) {
2171 case UCL_STATE_INIT:
2173 * At the init state we can either go to the parse array or object
2174 * if we got [ or { correspondingly or can just treat new data as
2175 * a key of newly created object
2177 if (!ucl_skip_comments (parser)) {
2178 parser->prev_state = parser->state;
2179 parser->state = UCL_STATE_ERROR;
2183 /* Skip any spaces */
2184 while (p < chunk->end && ucl_test_character (*p,
2185 UCL_CHARACTER_WHITESPACE_UNSAFE)) {
2186 ucl_chunk_skipc (chunk, p);
2192 parser->state = UCL_STATE_VALUE;
2193 ucl_chunk_skipc (chunk, p);
2196 parser->state = UCL_STATE_KEY;
2198 ucl_chunk_skipc (chunk, p);
2202 if (parser->top_obj == NULL) {
2203 if (parser->state == UCL_STATE_VALUE) {
2204 obj = ucl_parser_add_container (NULL, parser, true, 0);
2207 obj = ucl_parser_add_container (NULL, parser, false, 0);
2214 parser->top_obj = obj;
2215 parser->cur_obj = obj;
2221 /* Skip any spaces */
2222 while (p < chunk->end && ucl_test_character (*p, UCL_CHARACTER_WHITESPACE_UNSAFE)) {
2223 ucl_chunk_skipc (chunk, p);
2225 if (p == chunk->end || *p == '}') {
2226 /* We have the end of an object */
2227 parser->state = UCL_STATE_AFTER_VALUE;
2230 if (parser->stack == NULL) {
2231 /* No objects are on stack, but we want to parse a key */
2232 ucl_set_err (parser, UCL_ESYNTAX, "top object is finished but the parser "
2233 "expects a key", &parser->err);
2234 parser->prev_state = parser->state;
2235 parser->state = UCL_STATE_ERROR;
2238 if (!ucl_parse_key (parser, chunk, &next_key, &end_of_object)) {
2239 parser->prev_state = parser->state;
2240 parser->state = UCL_STATE_ERROR;
2243 if (end_of_object) {
2245 parser->state = UCL_STATE_AFTER_VALUE;
2248 else if (parser->state != UCL_STATE_MACRO_NAME) {
2249 if (next_key && parser->stack->obj->type == UCL_OBJECT) {
2250 /* Parse more keys and nest objects accordingly */
2251 obj = ucl_parser_add_container (parser->cur_obj, parser, false,
2252 parser->stack->level + 1);
2258 parser->state = UCL_STATE_VALUE;
2266 case UCL_STATE_VALUE:
2267 /* We need to check what we do have */
2268 if (!parser->cur_obj || !ucl_parse_value (parser, chunk)) {
2269 parser->prev_state = parser->state;
2270 parser->state = UCL_STATE_ERROR;
2273 /* State is set in ucl_parse_value call */
2276 case UCL_STATE_AFTER_VALUE:
2277 if (!ucl_parse_after_value (parser, chunk)) {
2278 parser->prev_state = parser->state;
2279 parser->state = UCL_STATE_ERROR;
2283 if (parser->stack != NULL) {
2284 if (parser->stack->obj->type == UCL_OBJECT) {
2285 parser->state = UCL_STATE_KEY;
2289 parser->state = UCL_STATE_VALUE;
2293 /* Skip everything at the end */
2299 case UCL_STATE_MACRO_NAME:
2300 if (parser->flags & UCL_PARSER_DISABLE_MACRO) {
2301 if (!ucl_skip_macro_as_comment (parser, chunk)) {
2302 /* We have invalid macro */
2303 ucl_create_err (&parser->err,
2304 "error on line %d at column %d: invalid macro",
2307 parser->state = UCL_STATE_ERROR;
2312 parser->state = parser->prev_state;
2316 if (!ucl_test_character (*p, UCL_CHARACTER_WHITESPACE_UNSAFE) &&
2318 ucl_chunk_skipc (chunk, p);
2321 if (c != NULL && p - c > 0) {
2322 /* We got macro name */
2323 macro_len = (size_t) (p - c);
2324 HASH_FIND (hh, parser->macroes, c, macro_len, macro);
2325 if (macro == NULL) {
2326 ucl_create_err (&parser->err,
2327 "error on line %d at column %d: "
2328 "unknown macro: '%.*s', character: '%c'",
2334 parser->state = UCL_STATE_ERROR;
2337 /* Now we need to skip all spaces */
2338 SKIP_SPACES_COMMENTS(parser, chunk, p);
2339 parser->state = UCL_STATE_MACRO;
2342 /* We have invalid macro name */
2343 ucl_create_err (&parser->err,
2344 "error on line %d at column %d: invalid macro name",
2347 parser->state = UCL_STATE_ERROR;
2353 case UCL_STATE_MACRO:
2354 if (*chunk->pos == '(') {
2355 macro_args = ucl_parse_macro_arguments (parser, chunk);
2358 SKIP_SPACES_COMMENTS(parser, chunk, p);
2364 if (!ucl_parse_macro_value (parser, chunk, macro,
2365 ¯o_start, ¯o_len)) {
2366 parser->prev_state = parser->state;
2367 parser->state = UCL_STATE_ERROR;
2370 macro_len = ucl_expand_variable (parser, ¯o_escaped,
2371 macro_start, macro_len);
2372 parser->state = parser->prev_state;
2374 if (macro_escaped == NULL && macro != NULL) {
2375 if (macro->is_context) {
2376 ret = macro->h.context_handler (macro_start, macro_len,
2382 ret = macro->h.handler (macro_start, macro_len, macro_args,
2386 else if (macro != NULL) {
2387 if (macro->is_context) {
2388 ret = macro->h.context_handler (macro_escaped, macro_len,
2394 ret = macro->h.handler (macro_escaped, macro_len, macro_args,
2398 UCL_FREE (macro_len + 1, macro_escaped);
2402 ucl_set_err (parser, UCL_EINTERNAL,
2403 "internal error: parser has macro undefined", &parser->err);
2407 * Chunk can be modified within macro handler
2409 chunk = parser->chunks;
2413 ucl_object_unref (macro_args);
2421 ucl_set_err (parser, UCL_EINTERNAL,
2422 "internal error: parser is in an unknown state", &parser->err);
2423 parser->state = UCL_STATE_ERROR;
2428 if (parser->last_comment) {
2429 if (parser->cur_obj) {
2430 ucl_attach_comment (parser, parser->cur_obj, true);
2432 else if (parser->stack && parser->stack->obj) {
2433 ucl_attach_comment (parser, parser->stack->obj, true);
2435 else if (parser->top_obj) {
2436 ucl_attach_comment (parser, parser->top_obj, true);
2439 ucl_object_unref (parser->last_comment);
2447 ucl_parser_new (int flags)
2449 struct ucl_parser *parser;
2451 parser = UCL_ALLOC (sizeof (struct ucl_parser));
2452 if (parser == NULL) {
2456 memset (parser, 0, sizeof (struct ucl_parser));
2458 ucl_parser_register_macro (parser, "include", ucl_include_handler, parser);
2459 ucl_parser_register_macro (parser, "try_include", ucl_try_include_handler, parser);
2460 ucl_parser_register_macro (parser, "includes", ucl_includes_handler, parser);
2461 ucl_parser_register_macro (parser, "priority", ucl_priority_handler, parser);
2462 ucl_parser_register_macro (parser, "load", ucl_load_handler, parser);
2463 ucl_parser_register_context_macro (parser, "inherit", ucl_inherit_handler, parser);
2465 parser->flags = flags;
2466 parser->includepaths = NULL;
2468 if (flags & UCL_PARSER_SAVE_COMMENTS) {
2469 parser->comments = ucl_object_typed_new (UCL_OBJECT);
2472 if (!(flags & UCL_PARSER_NO_FILEVARS)) {
2473 /* Initial assumption about filevars */
2474 ucl_parser_set_filevars (parser, NULL, false);
2481 ucl_parser_set_default_priority (struct ucl_parser *parser, unsigned prio)
2483 if (parser == NULL) {
2487 parser->default_priority = prio;
2493 ucl_parser_register_macro (struct ucl_parser *parser, const char *macro,
2494 ucl_macro_handler handler, void* ud)
2496 struct ucl_macro *new;
2498 if (macro == NULL || handler == NULL) {
2502 new = UCL_ALLOC (sizeof (struct ucl_macro));
2507 memset (new, 0, sizeof (struct ucl_macro));
2508 new->h.handler = handler;
2509 new->name = strdup (macro);
2511 HASH_ADD_KEYPTR (hh, parser->macroes, new->name, strlen (new->name), new);
2515 ucl_parser_register_context_macro (struct ucl_parser *parser, const char *macro,
2516 ucl_context_macro_handler handler, void* ud)
2518 struct ucl_macro *new;
2520 if (macro == NULL || handler == NULL) {
2524 new = UCL_ALLOC (sizeof (struct ucl_macro));
2529 memset (new, 0, sizeof (struct ucl_macro));
2530 new->h.context_handler = handler;
2531 new->name = strdup (macro);
2533 new->is_context = true;
2534 HASH_ADD_KEYPTR (hh, parser->macroes, new->name, strlen (new->name), new);
2538 ucl_parser_register_variable (struct ucl_parser *parser, const char *var,
2541 struct ucl_variable *new = NULL, *cur;
2547 /* Find whether a variable already exists */
2548 LL_FOREACH (parser->variables, cur) {
2549 if (strcmp (cur->var, var) == 0) {
2555 if (value == NULL) {
2558 /* Remove variable */
2559 DL_DELETE (parser->variables, new);
2562 UCL_FREE (sizeof (struct ucl_variable), new);
2571 new = UCL_ALLOC (sizeof (struct ucl_variable));
2575 memset (new, 0, sizeof (struct ucl_variable));
2576 new->var = strdup (var);
2577 new->var_len = strlen (var);
2578 new->value = strdup (value);
2579 new->value_len = strlen (value);
2581 DL_APPEND (parser->variables, new);
2585 new->value = strdup (value);
2586 new->value_len = strlen (value);
2592 ucl_parser_set_variables_handler (struct ucl_parser *parser,
2593 ucl_variable_handler handler, void *ud)
2595 parser->var_handler = handler;
2596 parser->var_data = ud;
2600 ucl_parser_add_chunk_full (struct ucl_parser *parser, const unsigned char *data,
2601 size_t len, unsigned priority, enum ucl_duplicate_strategy strat,
2602 enum ucl_parse_type parse_type)
2604 struct ucl_chunk *chunk;
2606 if (parser == NULL) {
2610 if (data == NULL && len != 0) {
2611 ucl_create_err (&parser->err, "invalid chunk added");
2615 if (parser->state != UCL_STATE_ERROR) {
2616 chunk = UCL_ALLOC (sizeof (struct ucl_chunk));
2617 if (chunk == NULL) {
2618 ucl_create_err (&parser->err, "cannot allocate chunk structure");
2622 if (parse_type == UCL_PARSE_AUTO && len > 0) {
2623 /* We need to detect parse type by the first symbol */
2624 if ((*data & 0x80) == 0x80 && (*data >= 0xdc && *data <= 0xdf)) {
2625 parse_type = UCL_PARSE_MSGPACK;
2627 else if (*data == '(') {
2628 parse_type = UCL_PARSE_CSEXP;
2631 parse_type = UCL_PARSE_UCL;
2635 chunk->begin = data;
2636 chunk->remain = len;
2637 chunk->pos = chunk->begin;
2638 chunk->end = chunk->begin + len;
2641 chunk->priority = priority;
2642 chunk->strategy = strat;
2643 chunk->parse_type = parse_type;
2644 LL_PREPEND (parser->chunks, chunk);
2645 parser->recursion ++;
2647 if (parser->recursion > UCL_MAX_RECURSION) {
2648 ucl_create_err (&parser->err, "maximum include nesting limit is reached: %d",
2654 /* Need to parse something */
2655 switch (parse_type) {
2658 return ucl_state_machine (parser);
2659 case UCL_PARSE_MSGPACK:
2660 return ucl_parse_msgpack (parser);
2661 case UCL_PARSE_CSEXP:
2662 return ucl_parse_csexp (parser);
2666 /* Just add empty chunk and go forward */
2667 if (parser->top_obj == NULL) {
2669 * In case of empty object, create one to indicate that we've
2672 parser->top_obj = ucl_object_new_full (UCL_OBJECT, priority);
2679 ucl_create_err (&parser->err, "a parser is in an invalid state");
2685 ucl_parser_add_chunk_priority (struct ucl_parser *parser,
2686 const unsigned char *data, size_t len, unsigned priority)
2688 /* We dereference parser, so this check is essential */
2689 if (parser == NULL) {
2693 return ucl_parser_add_chunk_full (parser, data, len,
2694 priority, UCL_DUPLICATE_APPEND, UCL_PARSE_UCL);
2698 ucl_parser_add_chunk (struct ucl_parser *parser, const unsigned char *data,
2701 if (parser == NULL) {
2705 return ucl_parser_add_chunk_full (parser, data, len,
2706 parser->default_priority, UCL_DUPLICATE_APPEND, UCL_PARSE_UCL);
2710 ucl_parser_add_string_priority (struct ucl_parser *parser, const char *data,
2711 size_t len, unsigned priority)
2714 ucl_create_err (&parser->err, "invalid string added");
2718 len = strlen (data);
2721 return ucl_parser_add_chunk_priority (parser,
2722 (const unsigned char *)data, len, priority);
2726 ucl_parser_add_string (struct ucl_parser *parser, const char *data,
2729 if (parser == NULL) {
2733 return ucl_parser_add_string_priority (parser,
2734 (const unsigned char *)data, len, parser->default_priority);
2738 ucl_set_include_path (struct ucl_parser *parser, ucl_object_t *paths)
2740 if (parser == NULL || paths == NULL) {
2744 if (parser->includepaths == NULL) {
2745 parser->includepaths = ucl_object_copy (paths);
2748 ucl_object_unref (parser->includepaths);
2749 parser->includepaths = ucl_object_copy (paths);
2752 if (parser->includepaths == NULL) {