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