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