1 /* Copyright (c) 2013, Vsevolod Stakhov
2 * All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are met:
6 * * Redistributions of source code must retain the above copyright
7 * notice, this list of conditions and the following disclaimer.
8 * * Redistributions in binary form must reproduce the above copyright
9 * notice, this list of conditions and the following disclaimer in the
10 * documentation and/or other materials provided with the distribution.
11 *
12 * THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY
13 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
14 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
15 * DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY
16 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
17 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
18 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
19 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
20 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
21 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
22 */
23
24 #include "ucl.h"
25 #include "ucl_internal.h"
26 #include "ucl_chartable.h"
27
28 /**
29 * @file ucl_parser.c
30 * The implementation of ucl parser
31 */
32
33 struct ucl_parser_saved_state {
34 unsigned int line;
35 unsigned int column;
36 size_t remain;
37 const unsigned char *pos;
38 };
39
40 /**
41 * Move up to len characters
42 * @param parser
43 * @param begin
44 * @param len
45 * @return new position in chunk
46 */
47 #define ucl_chunk_skipc(chunk, p) do{ \
48 if (*(p) == '\n') { \
49 (chunk)->line ++; \
50 (chunk)->column = 0; \
51 } \
52 else (chunk)->column ++; \
53 (p++); \
54 (chunk)->pos ++; \
55 (chunk)->remain --; \
56 } while (0)
57
58 static inline void
ucl_set_err(struct ucl_parser * parser,int code,const char * str,UT_string ** err)59 ucl_set_err (struct ucl_parser *parser, int code, const char *str, UT_string **err)
60 {
61 const char *fmt_string, *filename;
62 struct ucl_chunk *chunk = parser->chunks;
63
64 if (parser->cur_file) {
65 filename = parser->cur_file;
66 }
67 else {
68 filename = "<unknown>";
69 }
70
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'";
75 }
76 else {
77 fmt_string = "error while parsing %s: "
78 "line: %d, column: %d - '%s', character: '0x%02x'";
79 }
80 ucl_create_err (err, fmt_string,
81 filename, chunk->line, chunk->column,
82 str, *chunk->pos);
83 }
84 else {
85 ucl_create_err (err, "error while parsing %s: at the end of chunk: %s",
86 filename, str);
87 }
88
89 parser->err_code = code;
90 }
91
92 /**
93 * Skip all comments from the current pos resolving nested and multiline comments
94 * @param parser
95 * @return
96 */
97 static bool
ucl_skip_comments(struct ucl_parser * parser)98 ucl_skip_comments (struct ucl_parser *parser)
99 {
100 struct ucl_chunk *chunk = parser->chunks;
101 const unsigned char *p;
102 int comments_nested = 0;
103 bool quoted = false;
104
105 p = chunk->pos;
106
107 start:
108 if (chunk->remain > 0 && *p == '#') {
109 if (parser->state != UCL_STATE_SCOMMENT &&
110 parser->state != UCL_STATE_MCOMMENT) {
111 while (p < chunk->end) {
112 if (*p == '\n') {
113 ucl_chunk_skipc (chunk, p);
114 goto start;
115 }
116 ucl_chunk_skipc (chunk, p);
117 }
118 }
119 }
120 else if (chunk->remain >= 2 && *p == '/') {
121 if (p[1] == '*') {
122 ucl_chunk_skipc (chunk, p);
123 comments_nested ++;
124 ucl_chunk_skipc (chunk, p);
125
126 while (p < chunk->end) {
127 if (*p == '"' && *(p - 1) != '\\') {
128 quoted = !quoted;
129 }
130
131 if (!quoted) {
132 if (*p == '*') {
133 ucl_chunk_skipc (chunk, p);
134 if (*p == '/') {
135 comments_nested --;
136 if (comments_nested == 0) {
137 ucl_chunk_skipc (chunk, p);
138 goto start;
139 }
140 }
141 ucl_chunk_skipc (chunk, p);
142 }
143 else if (p[0] == '/' && chunk->remain >= 2 && p[1] == '*') {
144 comments_nested ++;
145 ucl_chunk_skipc (chunk, p);
146 ucl_chunk_skipc (chunk, p);
147 continue;
148 }
149 }
150 ucl_chunk_skipc (chunk, p);
151 }
152 if (comments_nested != 0) {
153 ucl_set_err (parser, UCL_ENESTED,
154 "unfinished multiline comment", &parser->err);
155 return false;
156 }
157 }
158 }
159
160 return true;
161 }
162
163 /**
164 * Return multiplier for a character
165 * @param c multiplier character
166 * @param is_bytes if true use 1024 multiplier
167 * @return multiplier
168 */
169 static inline unsigned long
ucl_lex_num_multiplier(const unsigned char c,bool is_bytes)170 ucl_lex_num_multiplier (const unsigned char c, bool is_bytes) {
171 const struct {
172 char c;
173 long mult_normal;
174 long mult_bytes;
175 } multipliers[] = {
176 {'m', 1000 * 1000, 1024 * 1024},
177 {'k', 1000, 1024},
178 {'g', 1000 * 1000 * 1000, 1024 * 1024 * 1024}
179 };
180 int i;
181
182 for (i = 0; i < 3; i ++) {
183 if (tolower (c) == multipliers[i].c) {
184 if (is_bytes) {
185 return multipliers[i].mult_bytes;
186 }
187 return multipliers[i].mult_normal;
188 }
189 }
190
191 return 1;
192 }
193
194
195 /**
196 * Return multiplier for time scaling
197 * @param c
198 * @return
199 */
200 static inline double
ucl_lex_time_multiplier(const unsigned char c)201 ucl_lex_time_multiplier (const unsigned char c) {
202 const struct {
203 char c;
204 double mult;
205 } multipliers[] = {
206 {'m', 60},
207 {'h', 60 * 60},
208 {'d', 60 * 60 * 24},
209 {'w', 60 * 60 * 24 * 7},
210 {'y', 60 * 60 * 24 * 7 * 365}
211 };
212 int i;
213
214 for (i = 0; i < 5; i ++) {
215 if (tolower (c) == multipliers[i].c) {
216 return multipliers[i].mult;
217 }
218 }
219
220 return 1;
221 }
222
223 /**
224 * Return true if a character is a end of an atom
225 * @param c
226 * @return
227 */
228 static inline bool
ucl_lex_is_atom_end(const unsigned char c)229 ucl_lex_is_atom_end (const unsigned char c)
230 {
231 return ucl_test_character (c, UCL_CHARACTER_VALUE_END);
232 }
233
234 static inline bool
ucl_lex_is_comment(const unsigned char c1,const unsigned char c2)235 ucl_lex_is_comment (const unsigned char c1, const unsigned char c2)
236 {
237 if (c1 == '/') {
238 if (c2 == '*') {
239 return true;
240 }
241 }
242 else if (c1 == '#') {
243 return true;
244 }
245 return false;
246 }
247
248 /**
249 * Check variable found
250 * @param parser
251 * @param ptr
252 * @param remain
253 * @param out_len
254 * @param strict
255 * @param found
256 * @return
257 */
258 static inline const char *
ucl_check_variable_safe(struct ucl_parser * parser,const char * ptr,size_t remain,size_t * out_len,bool strict,bool * found)259 ucl_check_variable_safe (struct ucl_parser *parser, const char *ptr, size_t remain,
260 size_t *out_len, bool strict, bool *found)
261 {
262 struct ucl_variable *var;
263 unsigned char *dst;
264 size_t dstlen;
265 bool need_free = false;
266
267 LL_FOREACH (parser->variables, var) {
268 if (strict) {
269 if (remain == var->var_len) {
270 if (memcmp (ptr, var->var, var->var_len) == 0) {
271 *out_len += var->value_len;
272 *found = true;
273 return (ptr + var->var_len);
274 }
275 }
276 }
277 else {
278 if (remain >= var->var_len) {
279 if (memcmp (ptr, var->var, var->var_len) == 0) {
280 *out_len += var->value_len;
281 *found = true;
282 return (ptr + var->var_len);
283 }
284 }
285 }
286 }
287
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,
292 parser->var_data)) {
293 *found = true;
294 if (need_free) {
295 free (dst);
296 }
297 return (ptr + remain);
298 }
299 }
300
301 return ptr;
302 }
303
304 /**
305 * Check for a variable in a given string
306 * @param parser
307 * @param ptr
308 * @param remain
309 * @param out_len
310 * @param vars_found
311 * @return
312 */
313 static const char *
ucl_check_variable(struct ucl_parser * parser,const char * ptr,size_t remain,size_t * out_len,bool * vars_found)314 ucl_check_variable (struct ucl_parser *parser, const char *ptr,
315 size_t remain, size_t *out_len, bool *vars_found)
316 {
317 const char *p, *end, *ret = ptr;
318 bool found = false;
319
320 if (*ptr == '{') {
321 /* We need to match the variable enclosed in braces */
322 p = ptr + 1;
323 end = ptr + remain;
324 while (p < end) {
325 if (*p == '}') {
326 ret = ucl_check_variable_safe (parser, ptr + 1, p - ptr - 1,
327 out_len, true, &found);
328 if (found) {
329 /* {} must be excluded actually */
330 ret ++;
331 if (!*vars_found) {
332 *vars_found = true;
333 }
334 }
335 else {
336 *out_len += 2;
337 }
338 break;
339 }
340 p ++;
341 }
342 }
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) {
347 *vars_found = true;
348 }
349 if (!found) {
350 (*out_len) ++;
351 }
352 }
353 else {
354 ret ++;
355 (*out_len) ++;
356 }
357
358 return ret;
359 }
360
361 /**
362 * Expand a single variable
363 * @param parser
364 * @param ptr
365 * @param remain
366 * @param dest
367 * @return
368 */
369 static const char *
ucl_expand_single_variable(struct ucl_parser * parser,const char * ptr,size_t remain,unsigned char ** dest)370 ucl_expand_single_variable (struct ucl_parser *parser, const char *ptr,
371 size_t remain, unsigned char **dest)
372 {
373 unsigned char *d = *dest, *dst;
374 const char *p = ptr + 1, *ret;
375 struct ucl_variable *var;
376 size_t dstlen;
377 bool need_free = false;
378 bool found = false;
379 bool strict = false;
380
381 ret = ptr + 1;
382 remain --;
383
384 if (*p == '$') {
385 *d++ = *p++;
386 *dest = d;
387 return p;
388 }
389 else if (*p == '{') {
390 p ++;
391 strict = true;
392 ret += 2;
393 remain -= 2;
394 }
395
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);
400 ret += var->var_len;
401 d += var->value_len;
402 found = true;
403 break;
404 }
405 }
406 }
407 if (!found) {
408 if (strict && parser->var_handler != NULL) {
409 if (parser->var_handler (ptr, remain, &dst, &dstlen, &need_free,
410 parser->var_data)) {
411 memcpy (d, dst, dstlen);
412 ret += dstlen;
413 d += remain;
414 found = true;
415 }
416 }
417
418 /* Leave variable as is */
419 if (!found) {
420 if (strict) {
421 /* Copy '${' */
422 memcpy (d, ptr, 2);
423 d += 2;
424 ret --;
425 }
426 else {
427 memcpy (d, ptr, 1);
428 d ++;
429 }
430 }
431 }
432
433 *dest = d;
434 return ret;
435 }
436
437 /**
438 * Expand variables in string
439 * @param parser
440 * @param dst
441 * @param src
442 * @param in_len
443 * @return
444 */
445 static ssize_t
ucl_expand_variable(struct ucl_parser * parser,unsigned char ** dst,const char * src,size_t in_len)446 ucl_expand_variable (struct ucl_parser *parser, unsigned char **dst,
447 const char *src, size_t in_len)
448 {
449 const char *p, *end = src + in_len;
450 unsigned char *d;
451 size_t out_len = 0;
452 bool vars_found = false;
453
454 p = src;
455 while (p != end) {
456 if (*p == '$') {
457 p = ucl_check_variable (parser, p + 1, end - p - 1, &out_len, &vars_found);
458 }
459 else {
460 p ++;
461 out_len ++;
462 }
463 }
464
465 if (!vars_found) {
466 /* Trivial case */
467 *dst = NULL;
468 return in_len;
469 }
470
471 *dst = UCL_ALLOC (out_len + 1);
472 if (*dst == NULL) {
473 return in_len;
474 }
475
476 d = *dst;
477 p = src;
478 while (p != end) {
479 if (*p == '$') {
480 p = ucl_expand_single_variable (parser, p, end - p, &d);
481 }
482 else {
483 *d++ = *p++;
484 }
485 }
486
487 *d = '\0';
488
489 return out_len;
490 }
491
492 /**
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)
503 */
504 static inline ssize_t
ucl_copy_or_store_ptr(struct ucl_parser * parser,const unsigned char * src,unsigned char ** dst,const char ** dst_const,size_t in_len,bool need_unescape,bool need_lowercase,bool need_expand)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)
509 {
510 ssize_t ret = -1, tret;
511 unsigned char *tmp;
512
513 if (need_unescape || need_lowercase ||
514 (need_expand && parser->variables != NULL) ||
515 !(parser->flags & UCL_PARSER_ZEROCOPY)) {
516 /* Copy string */
517 *dst = UCL_ALLOC (in_len + 1);
518 if (*dst == NULL) {
519 ucl_set_err (parser, UCL_EINTERNAL, "cannot allocate memory for a string",
520 &parser->err);
521 return false;
522 }
523 if (need_lowercase) {
524 ret = ucl_strlcpy_tolower (*dst, src, in_len + 1);
525 }
526 else {
527 ret = ucl_strlcpy_unsafe (*dst, src, in_len + 1);
528 }
529
530 if (need_unescape) {
531 ret = ucl_unescape_json_string (*dst, ret);
532 }
533 if (need_expand) {
534 tmp = *dst;
535 tret = ret;
536 ret = ucl_expand_variable (parser, dst, tmp, ret);
537 if (*dst == NULL) {
538 /* Nothing to expand */
539 *dst = tmp;
540 ret = tret;
541 }
542 else {
543 /* Free unexpanded value */
544 UCL_FREE (in_len + 1, tmp);
545 }
546 }
547 *dst_const = *dst;
548 }
549 else {
550 *dst_const = src;
551 ret = in_len;
552 }
553
554 return ret;
555 }
556
557 /**
558 * Create and append an object at the specified level
559 * @param parser
560 * @param is_array
561 * @param level
562 * @return
563 */
564 static inline ucl_object_t *
ucl_parser_add_container(ucl_object_t * obj,struct ucl_parser * parser,bool is_array,int level)565 ucl_parser_add_container (ucl_object_t *obj, struct ucl_parser *parser,
566 bool is_array, int level)
567 {
568 struct ucl_stack *st;
569
570 if (!is_array) {
571 if (obj == NULL) {
572 obj = ucl_object_new_full (UCL_OBJECT, parser->chunks->priority);
573 }
574 else {
575 obj->type = UCL_OBJECT;
576 }
577 if (obj->value.ov == NULL) {
578 obj->value.ov = ucl_hash_create (parser->flags & UCL_PARSER_KEY_LOWERCASE);
579 }
580 parser->state = UCL_STATE_KEY;
581 }
582 else {
583 if (obj == NULL) {
584 obj = ucl_object_new_full (UCL_ARRAY, parser->chunks->priority);
585 }
586 else {
587 obj->type = UCL_ARRAY;
588 }
589 parser->state = UCL_STATE_VALUE;
590 }
591
592 st = UCL_ALLOC (sizeof (struct ucl_stack));
593 if (st == NULL) {
594 ucl_set_err (parser, UCL_EINTERNAL, "cannot allocate memory for an object",
595 &parser->err);
596 ucl_object_unref (obj);
597 return NULL;
598 }
599 st->obj = obj;
600 st->level = level;
601 LL_PREPEND (parser->stack, st);
602 parser->cur_obj = obj;
603
604 return obj;
605 }
606
607 int
ucl_maybe_parse_number(ucl_object_t * obj,const char * start,const char * end,const char ** pos,bool allow_double,bool number_bytes,bool allow_time)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)
611 {
612 const char *p = start, *c = start;
613 char *endptr;
614 bool got_dot = false, got_exp = false, need_double = false,
615 is_time = false, valid_start = false, is_hex = false,
616 is_neg = false;
617 double dv = 0;
618 int64_t lv = 0;
619
620 if (*p == '-') {
621 is_neg = true;
622 c ++;
623 p ++;
624 }
625 while (p < end) {
626 if (is_hex && isxdigit (*p)) {
627 p ++;
628 }
629 else if (isdigit (*p)) {
630 valid_start = true;
631 p ++;
632 }
633 else if (!is_hex && (*p == 'x' || *p == 'X')) {
634 is_hex = true;
635 allow_double = false;
636 c = p + 1;
637 }
638 else if (allow_double) {
639 if (p == c) {
640 /* Empty digits sequence, not a number */
641 *pos = start;
642 return EINVAL;
643 }
644 else if (*p == '.') {
645 if (got_dot) {
646 /* Double dots, not a number */
647 *pos = start;
648 return EINVAL;
649 }
650 else {
651 got_dot = true;
652 need_double = true;
653 p ++;
654 }
655 }
656 else if (*p == 'e' || *p == 'E') {
657 if (got_exp) {
658 /* Double exp, not a number */
659 *pos = start;
660 return EINVAL;
661 }
662 else {
663 got_exp = true;
664 need_double = true;
665 p ++;
666 if (p >= end) {
667 *pos = start;
668 return EINVAL;
669 }
670 if (!isdigit (*p) && *p != '+' && *p != '-') {
671 /* Wrong exponent sign */
672 *pos = start;
673 return EINVAL;
674 }
675 else {
676 p ++;
677 }
678 }
679 }
680 else {
681 /* Got the end of the number, need to check */
682 break;
683 }
684 }
685 else {
686 break;
687 }
688 }
689
690 if (!valid_start) {
691 *pos = start;
692 return EINVAL;
693 }
694
695 errno = 0;
696 if (need_double) {
697 dv = strtod (c, &endptr);
698 }
699 else {
700 if (is_hex) {
701 lv = strtoimax (c, &endptr, 16);
702 }
703 else {
704 lv = strtoimax (c, &endptr, 10);
705 }
706 }
707 if (errno == ERANGE) {
708 *pos = start;
709 return ERANGE;
710 }
711
712 /* Now check endptr */
713 if (endptr == NULL || ucl_lex_is_atom_end (*endptr) || *endptr == '\0') {
714 p = endptr;
715 goto set_obj;
716 }
717
718 if (endptr < end && endptr != start) {
719 p = endptr;
720 switch (*p) {
721 case 'm':
722 case 'M':
723 case 'g':
724 case 'G':
725 case 'k':
726 case 'K':
727 if (end - p >= 2) {
728 if (p[1] == 's' || p[1] == 'S') {
729 /* Milliseconds */
730 if (!need_double) {
731 need_double = true;
732 dv = lv;
733 }
734 is_time = true;
735 if (p[0] == 'm' || p[0] == 'M') {
736 dv /= 1000.;
737 }
738 else {
739 dv *= ucl_lex_num_multiplier (*p, false);
740 }
741 p += 2;
742 goto set_obj;
743 }
744 else if (number_bytes || (p[1] == 'b' || p[1] == 'B')) {
745 /* Bytes */
746 if (need_double) {
747 need_double = false;
748 lv = dv;
749 }
750 lv *= ucl_lex_num_multiplier (*p, true);
751 p += 2;
752 goto set_obj;
753 }
754 else if (ucl_lex_is_atom_end (p[1])) {
755 if (need_double) {
756 dv *= ucl_lex_num_multiplier (*p, false);
757 }
758 else {
759 lv *= ucl_lex_num_multiplier (*p, number_bytes);
760 }
761 p ++;
762 goto set_obj;
763 }
764 else if (allow_time && end - p >= 3) {
765 if (tolower (p[0]) == 'm' &&
766 tolower (p[1]) == 'i' &&
767 tolower (p[2]) == 'n') {
768 /* Minutes */
769 if (!need_double) {
770 need_double = true;
771 dv = lv;
772 }
773 is_time = true;
774 dv *= 60.;
775 p += 3;
776 goto set_obj;
777 }
778 }
779 }
780 else {
781 if (need_double) {
782 dv *= ucl_lex_num_multiplier (*p, false);
783 }
784 else {
785 lv *= ucl_lex_num_multiplier (*p, number_bytes);
786 }
787 p ++;
788 goto set_obj;
789 }
790 break;
791 case 'S':
792 case 's':
793 if (allow_time &&
794 (p == end - 1 || ucl_lex_is_atom_end (p[1]))) {
795 if (!need_double) {
796 need_double = true;
797 dv = lv;
798 }
799 p ++;
800 is_time = true;
801 goto set_obj;
802 }
803 break;
804 case 'h':
805 case 'H':
806 case 'd':
807 case 'D':
808 case 'w':
809 case 'W':
810 case 'Y':
811 case 'y':
812 if (allow_time &&
813 (p == end - 1 || ucl_lex_is_atom_end (p[1]))) {
814 if (!need_double) {
815 need_double = true;
816 dv = lv;
817 }
818 is_time = true;
819 dv *= ucl_lex_time_multiplier (*p);
820 p ++;
821 goto set_obj;
822 }
823 break;
824 case '\t':
825 case ' ':
826 while (p < end && ucl_test_character(*p, UCL_CHARACTER_WHITESPACE)) {
827 p++;
828 }
829 if (ucl_lex_is_atom_end(*p))
830 goto set_obj;
831 break;
832 }
833 }
834 else if (endptr == end) {
835 /* Just a number at the end of chunk */
836 p = endptr;
837 goto set_obj;
838 }
839
840 *pos = c;
841 return EINVAL;
842
843 set_obj:
844 if (obj != NULL) {
845 if (allow_double && (need_double || is_time)) {
846 if (!is_time) {
847 obj->type = UCL_FLOAT;
848 }
849 else {
850 obj->type = UCL_TIME;
851 }
852 obj->value.dv = is_neg ? (-dv) : dv;
853 }
854 else {
855 obj->type = UCL_INT;
856 obj->value.iv = is_neg ? (-lv) : lv;
857 }
858 }
859 *pos = p;
860 return 0;
861 }
862
863 /**
864 * Parse possible number
865 * @param parser
866 * @param chunk
867 * @param obj
868 * @return true if a number has been parsed
869 */
870 static bool
ucl_lex_number(struct ucl_parser * parser,struct ucl_chunk * chunk,ucl_object_t * obj)871 ucl_lex_number (struct ucl_parser *parser,
872 struct ucl_chunk *chunk, ucl_object_t *obj)
873 {
874 const unsigned char *pos;
875 int ret;
876
877 ret = ucl_maybe_parse_number (obj, chunk->pos, chunk->end, (const char **)&pos,
878 true, false, ((parser->flags & UCL_PARSER_NO_TIME) == 0));
879
880 if (ret == 0) {
881 chunk->remain -= pos - chunk->pos;
882 chunk->column += pos - chunk->pos;
883 chunk->pos = pos;
884 return true;
885 }
886 else if (ret == ERANGE) {
887 ucl_set_err (parser, UCL_ESYNTAX, "numeric value out of range",
888 &parser->err);
889 }
890
891 return false;
892 }
893
894 /**
895 * Parse quoted string with possible escapes
896 * @param parser
897 * @param chunk
898 * @param need_unescape
899 * @param ucl_escape
900 * @param var_expand
901 * @return true if a string has been parsed
902 */
903 static bool
ucl_lex_json_string(struct ucl_parser * parser,struct ucl_chunk * chunk,bool * need_unescape,bool * ucl_escape,bool * var_expand)904 ucl_lex_json_string (struct ucl_parser *parser,
905 struct ucl_chunk *chunk, bool *need_unescape, bool *ucl_escape, bool *var_expand)
906 {
907 const unsigned char *p = chunk->pos;
908 unsigned char c;
909 int i;
910
911 while (p < chunk->end) {
912 c = *p;
913 if (c < 0x1F) {
914 /* Unmasked control character */
915 if (c == '\n') {
916 ucl_set_err (parser, UCL_ESYNTAX, "unexpected newline",
917 &parser->err);
918 }
919 else {
920 ucl_set_err (parser, UCL_ESYNTAX, "unexpected control character",
921 &parser->err);
922 }
923 return false;
924 }
925 else if (c == '\\') {
926 ucl_chunk_skipc (chunk, p);
927 c = *p;
928 if (p >= chunk->end) {
929 ucl_set_err (parser, UCL_ESYNTAX, "unfinished escape character",
930 &parser->err);
931 return false;
932 }
933 else if (ucl_test_character (c, UCL_CHARACTER_ESCAPE)) {
934 if (c == 'u') {
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",
939 &parser->err);
940 return false;
941 }
942 ucl_chunk_skipc (chunk, p);
943 }
944 if (p >= chunk->end) {
945 ucl_set_err (parser, UCL_ESYNTAX, "unfinished escape character",
946 &parser->err);
947 return false;
948 }
949 }
950 else {
951 ucl_chunk_skipc (chunk, p);
952 }
953 }
954 *need_unescape = true;
955 *ucl_escape = true;
956 continue;
957 }
958 else if (c == '"') {
959 ucl_chunk_skipc (chunk, p);
960 return true;
961 }
962 else if (ucl_test_character (c, UCL_CHARACTER_UCL_UNSAFE)) {
963 *ucl_escape = true;
964 }
965 else if (c == '$') {
966 *var_expand = true;
967 }
968 ucl_chunk_skipc (chunk, p);
969 }
970
971 ucl_set_err (parser, UCL_ESYNTAX, "no quote at the end of json string",
972 &parser->err);
973 return false;
974 }
975
976 static void
ucl_parser_append_elt(struct ucl_parser * parser,ucl_hash_t * cont,ucl_object_t * top,ucl_object_t * elt)977 ucl_parser_append_elt (struct ucl_parser *parser, ucl_hash_t *cont,
978 ucl_object_t *top,
979 ucl_object_t *elt)
980 {
981 ucl_object_t *nobj;
982
983 if ((parser->flags & UCL_PARSER_NO_IMPLICIT_ARRAYS) == 0) {
984 /* Implicit array */
985 top->flags |= UCL_OBJECT_MULTIVALUE;
986 DL_APPEND (top, elt);
987 parser->stack->obj->len ++;
988 }
989 else {
990 if ((top->flags & UCL_OBJECT_MULTIVALUE) != 0) {
991 /* Just add to the explicit array */
992 ucl_array_append (top, elt);
993 }
994 else {
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);
1003 }
1004 }
1005 }
1006
1007 bool
ucl_parser_process_object_element(struct ucl_parser * parser,ucl_object_t * nobj)1008 ucl_parser_process_object_element (struct ucl_parser *parser, ucl_object_t *nobj)
1009 {
1010 ucl_hash_t *container;
1011 ucl_object_t *tobj;
1012
1013 container = parser->stack->obj->value.ov;
1014
1015 tobj = __DECONST (ucl_object_t *, ucl_hash_search_obj (container, nobj));
1016 if (tobj == NULL) {
1017 container = ucl_hash_insert_object (container, nobj,
1018 parser->flags & UCL_PARSER_KEY_LOWERCASE);
1019 nobj->prev = nobj;
1020 nobj->next = NULL;
1021 parser->stack->obj->len ++;
1022 }
1023 else {
1024 unsigned priold = ucl_object_get_priority (tobj),
1025 prinew = ucl_object_get_priority (nobj);
1026 switch (parser->chunks->strategy) {
1027
1028 case UCL_DUPLICATE_APPEND:
1029 /*
1030 * The logic here is the following:
1031 *
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
1036 */
1037
1038
1039 /* Special case for inherited objects */
1040 if (tobj->flags & UCL_OBJECT_INHERITED) {
1041 prinew = priold + 1;
1042 }
1043
1044 if (priold == prinew) {
1045 ucl_parser_append_elt (parser, container, tobj, nobj);
1046 }
1047 else if (priold > prinew) {
1048 /*
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
1052 */
1053 DL_APPEND (parser->trash_objs, nobj);
1054 }
1055 else {
1056 ucl_hash_replace (container, tobj, nobj);
1057 ucl_object_unref (tobj);
1058 }
1059
1060 break;
1061
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);
1066
1067 break;
1068
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' "
1072 "has been found",
1073 parser->cur_file ? parser->cur_file : "<unknown>",
1074 parser->chunks->line, parser->chunks->column, nobj->key);
1075 return false;
1076
1077 case UCL_DUPLICATE_MERGE:
1078 /*
1079 * Here we do have some old object so we just push it on top of objects stack
1080 */
1081 if (tobj->type == UCL_OBJECT || tobj->type == UCL_ARRAY) {
1082 ucl_object_unref (nobj);
1083 nobj = tobj;
1084 }
1085 else {
1086 /* For other types we create implicit array as usual */
1087 ucl_parser_append_elt (parser, container, tobj, nobj);
1088 }
1089 break;
1090 }
1091 }
1092
1093 parser->stack->obj->value.ov = container;
1094 parser->cur_obj = nobj;
1095
1096 return true;
1097 }
1098
1099 /**
1100 * Parse a key in an object
1101 * @param parser
1102 * @param chunk
1103 * @param next_key
1104 * @param end_of_object
1105 * @return true if a key has been parsed
1106 */
1107 static bool
ucl_parse_key(struct ucl_parser * parser,struct ucl_chunk * chunk,bool * next_key,bool * end_of_object)1108 ucl_parse_key (struct ucl_parser *parser, struct ucl_chunk *chunk,
1109 bool *next_key, bool *end_of_object)
1110 {
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;
1116 ucl_object_t *nobj;
1117 ssize_t keylen;
1118
1119 p = chunk->pos;
1120
1121 if (*p == '.') {
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;
1127 return true;
1128 }
1129 while (p < chunk->end) {
1130 /*
1131 * A key must start with alpha, number, '/' or '_' and end with space character
1132 */
1133 if (c == NULL) {
1134 if (chunk->remain >= 2 && ucl_lex_is_comment (p[0], p[1])) {
1135 if (!ucl_skip_comments (parser)) {
1136 return false;
1137 }
1138 p = chunk->pos;
1139 }
1140 else if (ucl_test_character (*p, UCL_CHARACTER_WHITESPACE_UNSAFE)) {
1141 ucl_chunk_skipc (chunk, p);
1142 }
1143 else if (ucl_test_character (*p, UCL_CHARACTER_KEY_START)) {
1144 /* The first symbol */
1145 c = p;
1146 ucl_chunk_skipc (chunk, p);
1147 got_content = true;
1148 }
1149 else if (*p == '"') {
1150 /* JSON style key */
1151 c = p + 1;
1152 got_quote = true;
1153 got_content = true;
1154 ucl_chunk_skipc (chunk, p);
1155 }
1156 else if (*p == '}') {
1157 /* We have actually end of an object */
1158 *end_of_object = true;
1159 return true;
1160 }
1161 else if (*p == '.') {
1162 ucl_chunk_skipc (chunk, p);
1163 parser->prev_state = parser->state;
1164 parser->state = UCL_STATE_MACRO_NAME;
1165 return true;
1166 }
1167 else {
1168 /* Invalid identifier */
1169 ucl_set_err (parser, UCL_ESYNTAX, "key must begin with a letter",
1170 &parser->err);
1171 return false;
1172 }
1173 }
1174 else {
1175 /* Parse the body of a key */
1176 if (!got_quote) {
1177 if (ucl_test_character (*p, UCL_CHARACTER_KEY)) {
1178 got_content = true;
1179 ucl_chunk_skipc (chunk, p);
1180 }
1181 else if (ucl_test_character (*p, UCL_CHARACTER_KEY_SEP)) {
1182 end = p;
1183 break;
1184 }
1185 else {
1186 ucl_set_err (parser, UCL_ESYNTAX, "invalid character in a key",
1187 &parser->err);
1188 return false;
1189 }
1190 }
1191 else {
1192 /* We need to parse json like quoted string */
1193 if (!ucl_lex_json_string (parser, chunk, &need_unescape, &ucl_escape, &var_expand)) {
1194 return false;
1195 }
1196 /* Always escape keys obtained via json */
1197 end = chunk->pos - 1;
1198 p = chunk->pos;
1199 break;
1200 }
1201 }
1202 }
1203
1204 if (p >= chunk->end && got_content) {
1205 ucl_set_err (parser, UCL_ESYNTAX, "unfinished key", &parser->err);
1206 return false;
1207 }
1208 else if (!got_content) {
1209 return true;
1210 }
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);
1216 }
1217 else if (*p == '=') {
1218 if (!got_eq && !got_semicolon) {
1219 ucl_chunk_skipc (chunk, p);
1220 got_eq = true;
1221 }
1222 else {
1223 ucl_set_err (parser, UCL_ESYNTAX, "unexpected '=' character",
1224 &parser->err);
1225 return false;
1226 }
1227 }
1228 else if (*p == ':') {
1229 if (!got_eq && !got_semicolon) {
1230 ucl_chunk_skipc (chunk, p);
1231 got_semicolon = true;
1232 }
1233 else {
1234 ucl_set_err (parser, UCL_ESYNTAX, "unexpected ':' character",
1235 &parser->err);
1236 return false;
1237 }
1238 }
1239 else if (chunk->remain >= 2 && ucl_lex_is_comment (p[0], p[1])) {
1240 /* Check for comment */
1241 if (!ucl_skip_comments (parser)) {
1242 return false;
1243 }
1244 p = chunk->pos;
1245 }
1246 else {
1247 /* Start value */
1248 break;
1249 }
1250 }
1251
1252 if (p >= chunk->end && got_content) {
1253 ucl_set_err (parser, UCL_ESYNTAX, "unfinished key", &parser->err);
1254 return false;
1255 }
1256
1257 got_sep = got_semicolon || got_eq;
1258
1259 if (!got_sep) {
1260 /*
1261 * Maybe we have more keys nested, so search for termination character.
1262 * Possible choices:
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
1266 */
1267 t = p;
1268 *next_key = false;
1269 while (ucl_test_character (*t, UCL_CHARACTER_WHITESPACE)) {
1270 t ++;
1271 }
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') {
1276 break;
1277 }
1278 else if (*t == '{' || *t == '[') {
1279 *next_key = true;
1280 break;
1281 }
1282 t ++;
1283 }
1284 }
1285 }
1286
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);
1291 if (keylen == -1) {
1292 ucl_object_unref (nobj);
1293 return false;
1294 }
1295 else if (keylen == 0) {
1296 ucl_set_err (parser, UCL_ESYNTAX, "empty keys are not allowed", &parser->err);
1297 ucl_object_unref (nobj);
1298 return false;
1299 }
1300
1301 nobj->key = key;
1302 nobj->keylen = keylen;
1303
1304 if (!ucl_parser_process_object_element (parser, nobj)) {
1305 return false;
1306 }
1307
1308 if (ucl_escape) {
1309 nobj->flags |= UCL_OBJECT_NEED_KEY_ESCAPE;
1310 }
1311
1312
1313 return true;
1314 }
1315
1316 /**
1317 * Parse a cl string
1318 * @param parser
1319 * @param chunk
1320 * @param var_expand
1321 * @param need_unescape
1322 * @return true if a key has been parsed
1323 */
1324 static bool
ucl_parse_string_value(struct ucl_parser * parser,struct ucl_chunk * chunk,bool * var_expand,bool * need_unescape)1325 ucl_parse_string_value (struct ucl_parser *parser,
1326 struct ucl_chunk *chunk, bool *var_expand, bool *need_unescape)
1327 {
1328 const unsigned char *p;
1329 enum {
1330 UCL_BRACE_ROUND = 0,
1331 UCL_BRACE_SQUARE,
1332 UCL_BRACE_FIGURE
1333 };
1334 int braces[3][2] = {{0, 0}, {0, 0}, {0, 0}};
1335
1336 p = chunk->pos;
1337
1338 while (p < chunk->end) {
1339
1340 /* Skip pairs of figure braces */
1341 if (*p == '{') {
1342 braces[UCL_BRACE_FIGURE][0] ++;
1343 }
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);
1349 continue;
1350 }
1351 }
1352 /* Skip pairs of square braces */
1353 else if (*p == '[') {
1354 braces[UCL_BRACE_SQUARE][0] ++;
1355 }
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);
1361 continue;
1362 }
1363 }
1364 else if (*p == '$') {
1365 *var_expand = true;
1366 }
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);
1372 }
1373 continue;
1374 }
1375
1376 if (ucl_lex_is_atom_end (*p) || (chunk->remain >= 2 && ucl_lex_is_comment (p[0], p[1]))) {
1377 break;
1378 }
1379 ucl_chunk_skipc (chunk, p);
1380 }
1381
1382 return true;
1383 }
1384
1385 /**
1386 * Parse multiline string ending with \n{term}\n
1387 * @param parser
1388 * @param chunk
1389 * @param term
1390 * @param term_len
1391 * @param beg
1392 * @param var_expand
1393 * @return size of multiline string or 0 in case of error
1394 */
1395 static int
ucl_parse_multiline_string(struct ucl_parser * parser,struct ucl_chunk * chunk,const unsigned char * term,int term_len,unsigned char const ** beg,bool * var_expand)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,
1399 bool *var_expand)
1400 {
1401 const unsigned char *p, *c, *tend;
1402 bool newline = false;
1403 int len = 0;
1404
1405 p = chunk->pos;
1406
1407 c = p;
1408
1409 while (p < chunk->end) {
1410 if (newline) {
1411 if (chunk->end - p < term_len) {
1412 return 0;
1413 }
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);
1419 continue;
1420 }
1421 len = p - c;
1422 chunk->remain -= term_len;
1423 chunk->pos = p + term_len;
1424 chunk->column = term_len;
1425 *beg = c;
1426 break;
1427 }
1428 }
1429 if (*p == '\n') {
1430 newline = true;
1431 }
1432 else {
1433 if (*p == '$') {
1434 *var_expand = true;
1435 }
1436 newline = false;
1437 }
1438 ucl_chunk_skipc (chunk, p);
1439 }
1440
1441 return len;
1442 }
1443
1444 static inline ucl_object_t*
ucl_parser_get_container(struct ucl_parser * parser)1445 ucl_parser_get_container (struct ucl_parser *parser)
1446 {
1447 ucl_object_t *t, *obj = NULL;
1448
1449 if (parser == NULL || parser->stack == NULL || parser->stack->obj == NULL) {
1450 return NULL;
1451 }
1452
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;
1457
1458 if (!ucl_array_append (t, obj)) {
1459 ucl_object_unref (obj);
1460 return NULL;
1461 }
1462
1463 parser->cur_obj = obj;
1464 }
1465 else {
1466 /* Object has been already allocated */
1467 obj = parser->cur_obj;
1468 }
1469
1470 return obj;
1471 }
1472
1473 /**
1474 * Handle value data
1475 * @param parser
1476 * @param chunk
1477 * @return
1478 */
1479 static bool
ucl_parse_value(struct ucl_parser * parser,struct ucl_chunk * chunk)1480 ucl_parse_value (struct ucl_parser *parser, struct ucl_chunk *chunk)
1481 {
1482 const unsigned char *p, *c;
1483 ucl_object_t *obj = NULL;
1484 unsigned int stripped_spaces;
1485 int str_len;
1486 bool need_unescape = false, ucl_escape = false, var_expand = false;
1487
1488 p = chunk->pos;
1489
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);
1495 }
1496 if (!ucl_skip_comments (parser)) {
1497 return false;
1498 }
1499 p = chunk->pos;
1500 }
1501
1502 while (p < chunk->end) {
1503 c = p;
1504 switch (*p) {
1505 case '"':
1506 ucl_chunk_skipc (chunk, p);
1507
1508 if (!ucl_lex_json_string (parser, chunk, &need_unescape, &ucl_escape,
1509 &var_expand)) {
1510 return false;
1511 }
1512
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) {
1520 return false;
1521 }
1522 obj->len = str_len;
1523
1524 parser->state = UCL_STATE_AFTER_VALUE;
1525 p = chunk->pos;
1526
1527 return true;
1528 break;
1529 case '{':
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);
1533 if (obj == NULL) {
1534 return false;
1535 }
1536
1537 ucl_chunk_skipc (chunk, p);
1538
1539 return true;
1540 break;
1541 case '[':
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);
1545 if (obj == NULL) {
1546 return false;
1547 }
1548
1549 ucl_chunk_skipc (chunk, p);
1550
1551 return true;
1552 break;
1553 case ']':
1554 /* We have the array ending */
1555 if (parser->stack && parser->stack->obj->type == UCL_ARRAY) {
1556 parser->state = UCL_STATE_AFTER_VALUE;
1557 return true;
1558 }
1559 else {
1560 goto parse_string;
1561 }
1562 break;
1563 case '<':
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) {
1568 p += 2;
1569 /* We allow only uppercase characters in multiline definitions */
1570 while (p < chunk->end && *p >= 'A' && *p <= 'Z') {
1571 p ++;
1572 }
1573 if (*p =='\n') {
1574 /* Set chunk positions and start multiline parsing */
1575 c += 2;
1576 chunk->remain -= p - c;
1577 chunk->pos = p + 1;
1578 chunk->column = 0;
1579 chunk->line ++;
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);
1584 return false;
1585 }
1586
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) {
1593 return false;
1594 }
1595 obj->len = str_len;
1596
1597 parser->state = UCL_STATE_AFTER_VALUE;
1598
1599 return true;
1600 }
1601 }
1602 }
1603 /* Fallback to ordinary strings */
1604 default:
1605 parse_string:
1606 if (obj == NULL) {
1607 obj = ucl_parser_get_container (parser);
1608 }
1609
1610 /* Parse atom */
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) {
1614 return false;
1615 }
1616 }
1617 else {
1618 parser->state = UCL_STATE_AFTER_VALUE;
1619 return true;
1620 }
1621 /* Fallback to normal string */
1622 }
1623
1624 if (!ucl_parse_string_value (parser, chunk, &var_expand,
1625 &need_unescape)) {
1626 return false;
1627 }
1628 /* Cut trailing spaces */
1629 stripped_spaces = 0;
1630 while (ucl_test_character (*(chunk->pos - 1 - stripped_spaces),
1631 UCL_CHARACTER_WHITESPACE)) {
1632 stripped_spaces ++;
1633 }
1634 str_len = chunk->pos - c - stripped_spaces;
1635 if (str_len <= 0) {
1636 ucl_set_err (parser, UCL_ESYNTAX, "string value must not be empty",
1637 &parser->err);
1638 return false;
1639 }
1640 else if (str_len == 4 && memcmp (c, "null", 4) == 0) {
1641 obj->len = 0;
1642 obj->type = UCL_NULL;
1643 }
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) {
1650 return false;
1651 }
1652 obj->len = str_len;
1653 }
1654 parser->state = UCL_STATE_AFTER_VALUE;
1655 p = chunk->pos;
1656
1657 return true;
1658 break;
1659 }
1660 }
1661
1662 return true;
1663 }
1664
1665 /**
1666 * Handle after value data
1667 * @param parser
1668 * @param chunk
1669 * @return
1670 */
1671 static bool
ucl_parse_after_value(struct ucl_parser * parser,struct ucl_chunk * chunk)1672 ucl_parse_after_value (struct ucl_parser *parser, struct ucl_chunk *chunk)
1673 {
1674 const unsigned char *p;
1675 bool got_sep = false;
1676 struct ucl_stack *st;
1677
1678 p = chunk->pos;
1679
1680 while (p < chunk->end) {
1681 if (ucl_test_character (*p, UCL_CHARACTER_WHITESPACE)) {
1682 /* Skip whitespaces */
1683 ucl_chunk_skipc (chunk, p);
1684 }
1685 else if (chunk->remain >= 2 && ucl_lex_is_comment (p[0], p[1])) {
1686 /* Skip comment */
1687 if (!ucl_skip_comments (parser)) {
1688 return false;
1689 }
1690 /* Treat comment as a separator */
1691 got_sep = true;
1692 p = chunk->pos;
1693 }
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",
1699 &parser->err);
1700 return false;
1701 }
1702 if ((*p == '}' && parser->stack->obj->type == UCL_OBJECT) ||
1703 (*p == ']' && parser->stack->obj->type == UCL_ARRAY)) {
1704
1705 /* Pop all nested objects from a stack */
1706 st = parser->stack;
1707 parser->stack = st->next;
1708 UCL_FREE (sizeof (struct ucl_stack), st);
1709
1710 while (parser->stack != NULL) {
1711 st = parser->stack;
1712 if (st->next == NULL || st->next->level == st->level) {
1713 break;
1714 }
1715 parser->stack = st->next;
1716 UCL_FREE (sizeof (struct ucl_stack), st);
1717 }
1718 }
1719 else {
1720 ucl_set_err (parser, UCL_ESYNTAX,
1721 "unexpected terminating symbol detected",
1722 &parser->err);
1723 return false;
1724 }
1725
1726 if (parser->stack == NULL) {
1727 /* Ignore everything after a top object */
1728 return true;
1729 }
1730 else {
1731 ucl_chunk_skipc (chunk, p);
1732 }
1733 got_sep = true;
1734 }
1735 else {
1736 /* Got a separator */
1737 got_sep = true;
1738 ucl_chunk_skipc (chunk, p);
1739 }
1740 }
1741 else {
1742 /* Anything else */
1743 if (!got_sep) {
1744 ucl_set_err (parser, UCL_ESYNTAX, "delimiter is missing",
1745 &parser->err);
1746 return false;
1747 }
1748 return true;
1749 }
1750 }
1751
1752 return true;
1753 }
1754
1755 /**
1756 * Handle macro data
1757 * @param parser
1758 * @param chunk
1759 * @param marco
1760 * @param macro_start
1761 * @param macro_len
1762 * @return
1763 */
1764 static bool
ucl_parse_macro_value(struct ucl_parser * parser,struct ucl_chunk * chunk,struct ucl_macro * macro,unsigned char const ** macro_start,size_t * macro_len)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)
1768 {
1769 const unsigned char *p, *c;
1770 bool need_unescape = false, ucl_escape = false, var_expand = false;
1771
1772 p = chunk->pos;
1773
1774 switch (*p) {
1775 case '"':
1776 /* We have macro value encoded in quotes */
1777 c = p;
1778 ucl_chunk_skipc (chunk, p);
1779 if (!ucl_lex_json_string (parser, chunk, &need_unescape, &ucl_escape, &var_expand)) {
1780 return false;
1781 }
1782
1783 *macro_start = c + 1;
1784 *macro_len = chunk->pos - c - 2;
1785 p = chunk->pos;
1786 break;
1787 case '{':
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);
1794 }
1795 else {
1796 break;
1797 }
1798 }
1799 c = p;
1800 while (p < chunk->end) {
1801 if (*p == '}') {
1802 break;
1803 }
1804 ucl_chunk_skipc (chunk, p);
1805 }
1806 *macro_start = c;
1807 *macro_len = p - c;
1808 ucl_chunk_skipc (chunk, p);
1809 break;
1810 default:
1811 /* Macro is not enclosed in quotes or braces */
1812 c = p;
1813 while (p < chunk->end) {
1814 if (ucl_lex_is_atom_end (*p)) {
1815 break;
1816 }
1817 ucl_chunk_skipc (chunk, p);
1818 }
1819 *macro_start = c;
1820 *macro_len = p - c;
1821 break;
1822 }
1823
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 != ';') {
1828 break;
1829 }
1830 ucl_chunk_skipc (chunk, p);
1831 }
1832 return true;
1833 }
1834
1835 /**
1836 * Parse macro arguments as UCL object
1837 * @param parser parser structure
1838 * @param chunk the current data chunk
1839 * @return
1840 */
1841 static ucl_object_t *
ucl_parse_macro_arguments(struct ucl_parser * parser,struct ucl_chunk * chunk)1842 ucl_parse_macro_arguments (struct ucl_parser *parser,
1843 struct ucl_chunk *chunk)
1844 {
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;
1851
1852 saved.column = chunk->column;
1853 saved.line = chunk->line;
1854 saved.pos = chunk->pos;
1855 saved.remain = chunk->remain;
1856 p = chunk->pos;
1857
1858 if (*p != '(' || chunk->remain < 2) {
1859 return NULL;
1860 }
1861
1862 /* Set begin and start */
1863 ucl_chunk_skipc (chunk, p);
1864 c = p;
1865
1866 while ((p) < (chunk)->end) {
1867 switch (state) {
1868 case 0:
1869 /* Parse symbols and check for '(', ')' and '"' */
1870 if (*p == '(') {
1871 obraces ++;
1872 }
1873 else if (*p == ')') {
1874 ebraces ++;
1875 }
1876 else if (*p == '"') {
1877 state = 1;
1878 }
1879 /* Check pairing */
1880 if (obraces == ebraces) {
1881 state = 99;
1882 }
1883 else {
1884 args_len ++;
1885 }
1886 /* Check overflow */
1887 if (chunk->remain == 0) {
1888 goto restore_chunk;
1889 }
1890 ucl_chunk_skipc (chunk, p);
1891 break;
1892 case 1:
1893 /* We have quote character, so skip all but quotes */
1894 if (*p == '"' && *(p - 1) != '\\') {
1895 state = 0;
1896 }
1897 if (chunk->remain == 0) {
1898 goto restore_chunk;
1899 }
1900 args_len ++;
1901 ucl_chunk_skipc (chunk, p);
1902 break;
1903 case 99:
1904 /*
1905 * We have read the full body of arguments, so we need to parse and set
1906 * object from that
1907 */
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",
1911 &parser->err);
1912 }
1913 else {
1914 res = ucl_parser_get_object (params_parser);
1915 }
1916 ucl_parser_free (params_parser);
1917
1918 return res;
1919
1920 break;
1921 }
1922 }
1923
1924 return res;
1925
1926 restore_chunk:
1927 chunk->column = saved.column;
1928 chunk->line = saved.line;
1929 chunk->pos = saved.pos;
1930 chunk->remain = saved.remain;
1931
1932 return NULL;
1933 }
1934
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)) { \
1940 return false; \
1941 } \
1942 p = (chunk)->pos; \
1943 } \
1944 break; \
1945 } \
1946 ucl_chunk_skipc (chunk, p); \
1947 } \
1948 } while(0)
1949
1950 /**
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
1954 */
1955 static bool
ucl_state_machine(struct ucl_parser * parser)1956 ucl_state_machine (struct ucl_parser *parser)
1957 {
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;
1965
1966 if (parser->top_obj == NULL) {
1967 parser->state = UCL_STATE_INIT;
1968 }
1969
1970 p = chunk->pos;
1971 while (chunk->pos < chunk->end) {
1972 switch (parser->state) {
1973 case UCL_STATE_INIT:
1974 /*
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
1978 */
1979 if (!ucl_skip_comments (parser)) {
1980 parser->prev_state = parser->state;
1981 parser->state = UCL_STATE_ERROR;
1982 return false;
1983 }
1984 else {
1985 /* Skip any spaces */
1986 while (p < chunk->end && ucl_test_character (*p,
1987 UCL_CHARACTER_WHITESPACE_UNSAFE)) {
1988 ucl_chunk_skipc (chunk, p);
1989 }
1990
1991 p = chunk->pos;
1992
1993 if (*p == '[') {
1994 parser->state = UCL_STATE_VALUE;
1995 ucl_chunk_skipc (chunk, p);
1996 }
1997 else {
1998 parser->state = UCL_STATE_KEY;
1999 if (*p == '{') {
2000 ucl_chunk_skipc (chunk, p);
2001 }
2002 }
2003
2004 if (parser->top_obj == NULL) {
2005 if (parser->state == UCL_STATE_VALUE) {
2006 obj = ucl_parser_add_container (NULL, parser, true, 0);
2007 }
2008 else {
2009 obj = ucl_parser_add_container (NULL, parser, false, 0);
2010 }
2011
2012 if (obj == NULL) {
2013 return false;
2014 }
2015
2016 parser->top_obj = obj;
2017 parser->cur_obj = obj;
2018 }
2019
2020 }
2021 break;
2022 case UCL_STATE_KEY:
2023 /* Skip any spaces */
2024 while (p < chunk->end && ucl_test_character (*p, UCL_CHARACTER_WHITESPACE_UNSAFE)) {
2025 ucl_chunk_skipc (chunk, p);
2026 }
2027 if (*p == '}') {
2028 /* We have the end of an object */
2029 parser->state = UCL_STATE_AFTER_VALUE;
2030 continue;
2031 }
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;
2038 return false;
2039 }
2040 if (!ucl_parse_key (parser, chunk, &next_key, &end_of_object)) {
2041 parser->prev_state = parser->state;
2042 parser->state = UCL_STATE_ERROR;
2043 return false;
2044 }
2045 if (end_of_object) {
2046 p = chunk->pos;
2047 parser->state = UCL_STATE_AFTER_VALUE;
2048 continue;
2049 }
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);
2055 if (obj == NULL) {
2056 return false;
2057 }
2058 }
2059 else {
2060 parser->state = UCL_STATE_VALUE;
2061 }
2062 }
2063 else {
2064 c = chunk->pos;
2065 }
2066 p = chunk->pos;
2067 break;
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;
2073 return false;
2074 }
2075 /* State is set in ucl_parse_value call */
2076 p = chunk->pos;
2077 break;
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;
2082 return false;
2083 }
2084
2085 if (parser->stack != NULL) {
2086 if (parser->stack->obj->type == UCL_OBJECT) {
2087 parser->state = UCL_STATE_KEY;
2088 }
2089 else {
2090 /* Array */
2091 parser->state = UCL_STATE_VALUE;
2092 }
2093 }
2094 else {
2095 /* Skip everything at the end */
2096 return true;
2097 }
2098 p = chunk->pos;
2099 break;
2100 case UCL_STATE_MACRO_NAME:
2101 if (!ucl_test_character (*p, UCL_CHARACTER_WHITESPACE_UNSAFE) &&
2102 *p != '(') {
2103 ucl_chunk_skipc (chunk, p);
2104 }
2105 else {
2106 if (p - c > 0) {
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'",
2114 chunk->line,
2115 chunk->column,
2116 (int) (p - c),
2117 c,
2118 *chunk->pos);
2119 parser->state = UCL_STATE_ERROR;
2120 return false;
2121 }
2122 /* Now we need to skip all spaces */
2123 SKIP_SPACES_COMMENTS(parser, chunk, p);
2124 parser->state = UCL_STATE_MACRO;
2125 }
2126 else {
2127 /* We have invalid macro name */
2128 ucl_create_err (&parser->err,
2129 "error on line %d at column %d: invalid macro name",
2130 chunk->line,
2131 chunk->column);
2132 parser->state = UCL_STATE_ERROR;
2133 return false;
2134 }
2135 }
2136 break;
2137 case UCL_STATE_MACRO:
2138 if (*chunk->pos == '(') {
2139 macro_args = ucl_parse_macro_arguments (parser, chunk);
2140 p = chunk->pos;
2141 if (macro_args) {
2142 SKIP_SPACES_COMMENTS(parser, chunk, p);
2143 }
2144 }
2145 else {
2146 macro_args = NULL;
2147 }
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;
2152 return false;
2153 }
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,
2160 macro_args,
2161 parser->top_obj,
2162 macro->ud);
2163 }
2164 else {
2165 ret = macro->h.handler (macro_start, macro_len, macro_args,
2166 macro->ud);
2167 }
2168 }
2169 else {
2170 if (macro->is_context) {
2171 ret = macro->h.context_handler (macro_escaped, macro_len,
2172 macro_args,
2173 parser->top_obj,
2174 macro->ud);
2175 }
2176 else {
2177 ret = macro->h.handler (macro_escaped, macro_len, macro_args,
2178 macro->ud);
2179 }
2180
2181 UCL_FREE (macro_len + 1, macro_escaped);
2182 }
2183
2184 /*
2185 * Chunk can be modified within macro handler
2186 */
2187 chunk = parser->chunks;
2188 p = chunk->pos;
2189 if (macro_args) {
2190 ucl_object_unref (macro_args);
2191 }
2192 if (!ret) {
2193 return false;
2194 }
2195 break;
2196 default:
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;
2201 return false;
2202 }
2203 }
2204
2205 return true;
2206 }
2207
2208 struct ucl_parser*
ucl_parser_new(int flags)2209 ucl_parser_new (int flags)
2210 {
2211 struct ucl_parser *new;
2212
2213 new = UCL_ALLOC (sizeof (struct ucl_parser));
2214 if (new == NULL) {
2215 return NULL;
2216 }
2217
2218 memset (new, 0, sizeof (struct ucl_parser));
2219
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);
2226
2227 new->flags = flags;
2228 new->includepaths = NULL;
2229
2230 /* Initial assumption about filevars */
2231 ucl_parser_set_filevars (new, NULL, false);
2232
2233 return new;
2234 }
2235
2236 bool
ucl_parser_set_default_priority(struct ucl_parser * parser,unsigned prio)2237 ucl_parser_set_default_priority (struct ucl_parser *parser, unsigned prio)
2238 {
2239 if (parser == NULL) {
2240 return false;
2241 }
2242
2243 parser->default_priority = prio;
2244
2245 return true;
2246 }
2247
2248 void
ucl_parser_register_macro(struct ucl_parser * parser,const char * macro,ucl_macro_handler handler,void * ud)2249 ucl_parser_register_macro (struct ucl_parser *parser, const char *macro,
2250 ucl_macro_handler handler, void* ud)
2251 {
2252 struct ucl_macro *new;
2253
2254 if (macro == NULL || handler == NULL) {
2255 return;
2256 }
2257
2258 new = UCL_ALLOC (sizeof (struct ucl_macro));
2259 if (new == NULL) {
2260 return;
2261 }
2262
2263 memset (new, 0, sizeof (struct ucl_macro));
2264 new->h.handler = handler;
2265 new->name = strdup (macro);
2266 new->ud = ud;
2267 HASH_ADD_KEYPTR (hh, parser->macroes, new->name, strlen (new->name), new);
2268 }
2269
2270 void
ucl_parser_register_context_macro(struct ucl_parser * parser,const char * macro,ucl_context_macro_handler handler,void * ud)2271 ucl_parser_register_context_macro (struct ucl_parser *parser, const char *macro,
2272 ucl_context_macro_handler handler, void* ud)
2273 {
2274 struct ucl_macro *new;
2275
2276 if (macro == NULL || handler == NULL) {
2277 return;
2278 }
2279
2280 new = UCL_ALLOC (sizeof (struct ucl_macro));
2281 if (new == NULL) {
2282 return;
2283 }
2284
2285 memset (new, 0, sizeof (struct ucl_macro));
2286 new->h.context_handler = handler;
2287 new->name = strdup (macro);
2288 new->ud = ud;
2289 new->is_context = true;
2290 HASH_ADD_KEYPTR (hh, parser->macroes, new->name, strlen (new->name), new);
2291 }
2292
2293 void
ucl_parser_register_variable(struct ucl_parser * parser,const char * var,const char * value)2294 ucl_parser_register_variable (struct ucl_parser *parser, const char *var,
2295 const char *value)
2296 {
2297 struct ucl_variable *new = NULL, *cur;
2298
2299 if (var == NULL) {
2300 return;
2301 }
2302
2303 /* Find whether a variable already exists */
2304 LL_FOREACH (parser->variables, cur) {
2305 if (strcmp (cur->var, var) == 0) {
2306 new = cur;
2307 break;
2308 }
2309 }
2310
2311 if (value == NULL) {
2312
2313 if (new != NULL) {
2314 /* Remove variable */
2315 DL_DELETE (parser->variables, new);
2316 free (new->var);
2317 free (new->value);
2318 UCL_FREE (sizeof (struct ucl_variable), new);
2319 }
2320 else {
2321 /* Do nothing */
2322 return;
2323 }
2324 }
2325 else {
2326 if (new == NULL) {
2327 new = UCL_ALLOC (sizeof (struct ucl_variable));
2328 if (new == NULL) {
2329 return;
2330 }
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);
2336
2337 DL_APPEND (parser->variables, new);
2338 }
2339 else {
2340 free (new->value);
2341 new->value = strdup (value);
2342 new->value_len = strlen (value);
2343 }
2344 }
2345 }
2346
2347 void
ucl_parser_set_variables_handler(struct ucl_parser * parser,ucl_variable_handler handler,void * ud)2348 ucl_parser_set_variables_handler (struct ucl_parser *parser,
2349 ucl_variable_handler handler, void *ud)
2350 {
2351 parser->var_handler = handler;
2352 parser->var_data = ud;
2353 }
2354
2355 bool
ucl_parser_add_chunk_full(struct ucl_parser * parser,const unsigned char * data,size_t len,unsigned priority,enum ucl_duplicate_strategy strat,enum ucl_parse_type parse_type)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)
2359 {
2360 struct ucl_chunk *chunk;
2361
2362 if (parser == NULL) {
2363 return false;
2364 }
2365
2366 if (data == NULL) {
2367 ucl_create_err (&parser->err, "invalid chunk added");
2368 return false;
2369 }
2370 if (len == 0) {
2371 parser->top_obj = ucl_object_new_full (UCL_OBJECT, priority);
2372 return true;
2373 }
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");
2378 return false;
2379 }
2380 chunk->begin = data;
2381 chunk->remain = len;
2382 chunk->pos = chunk->begin;
2383 chunk->end = chunk->begin + len;
2384 chunk->line = 1;
2385 chunk->column = 0;
2386 chunk->priority = priority;
2387 chunk->strategy = strat;
2388 chunk->parse_type = parse_type;
2389 LL_PREPEND (parser->chunks, chunk);
2390 parser->recursion ++;
2391
2392 if (parser->recursion > UCL_MAX_RECURSION) {
2393 ucl_create_err (&parser->err, "maximum include nesting limit is reached: %d",
2394 parser->recursion);
2395 return false;
2396 }
2397
2398 switch (parse_type) {
2399 default:
2400 case UCL_PARSE_UCL:
2401 return ucl_state_machine (parser);
2402 case UCL_PARSE_MSGPACK:
2403 return ucl_parse_msgpack (parser);
2404 }
2405 }
2406
2407 ucl_create_err (&parser->err, "a parser is in an invalid state");
2408
2409 return false;
2410 }
2411
2412 bool
ucl_parser_add_chunk_priority(struct ucl_parser * parser,const unsigned char * data,size_t len,unsigned priority)2413 ucl_parser_add_chunk_priority (struct ucl_parser *parser,
2414 const unsigned char *data, size_t len, unsigned priority)
2415 {
2416 /* We dereference parser, so this check is essential */
2417 if (parser == NULL) {
2418 return false;
2419 }
2420
2421 return ucl_parser_add_chunk_full (parser, data, len,
2422 priority, UCL_DUPLICATE_APPEND, UCL_PARSE_UCL);
2423 }
2424
2425 bool
ucl_parser_add_chunk(struct ucl_parser * parser,const unsigned char * data,size_t len)2426 ucl_parser_add_chunk (struct ucl_parser *parser, const unsigned char *data,
2427 size_t len)
2428 {
2429 if (parser == NULL) {
2430 return false;
2431 }
2432
2433 return ucl_parser_add_chunk_full (parser, data, len,
2434 parser->default_priority, UCL_DUPLICATE_APPEND, UCL_PARSE_UCL);
2435 }
2436
2437 bool
ucl_parser_add_string_priority(struct ucl_parser * parser,const char * data,size_t len,unsigned priority)2438 ucl_parser_add_string_priority (struct ucl_parser *parser, const char *data,
2439 size_t len, unsigned priority)
2440 {
2441 if (data == NULL) {
2442 ucl_create_err (&parser->err, "invalid string added");
2443 return false;
2444 }
2445 if (len == 0) {
2446 len = strlen (data);
2447 }
2448
2449 return ucl_parser_add_chunk_priority (parser,
2450 (const unsigned char *)data, len, priority);
2451 }
2452
2453 bool
ucl_parser_add_string(struct ucl_parser * parser,const char * data,size_t len)2454 ucl_parser_add_string (struct ucl_parser *parser, const char *data,
2455 size_t len)
2456 {
2457 if (parser == NULL) {
2458 return false;
2459 }
2460
2461 return ucl_parser_add_string_priority (parser,
2462 (const unsigned char *)data, len, parser->default_priority);
2463 }
2464
2465 bool
ucl_set_include_path(struct ucl_parser * parser,ucl_object_t * paths)2466 ucl_set_include_path (struct ucl_parser *parser, ucl_object_t *paths)
2467 {
2468 if (parser == NULL || paths == NULL) {
2469 return false;
2470 }
2471
2472 if (parser->includepaths == NULL) {
2473 parser->includepaths = ucl_object_copy (paths);
2474 }
2475 else {
2476 ucl_object_unref (parser->includepaths);
2477 parser->includepaths = ucl_object_copy (paths);
2478 }
2479
2480 if (parser->includepaths == NULL) {
2481 return false;
2482 }
2483
2484 return true;
2485 }
2486