/src/libucl/src/ucl_sexp.c
Line | Count | Source |
1 | | /* |
2 | | * Copyright (c) 2015, Vsevolod Stakhov |
3 | | * All rights reserved. |
4 | | * |
5 | | * Redistribution and use in source and binary forms, with or without |
6 | | * modification, are permitted provided that the following conditions are met: |
7 | | * * Redistributions of source code must retain the above copyright |
8 | | * notice, this list of conditions and the following disclaimer. |
9 | | * * Redistributions in binary form must reproduce the above copyright |
10 | | * notice, this list of conditions and the following disclaimer in the |
11 | | * documentation and/or other materials provided with the distribution. |
12 | | * |
13 | | * THIS SOFTWARE IS PROVIDED BY AUTHOR ''AS IS'' AND ANY |
14 | | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
15 | | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
16 | | * DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY |
17 | | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
18 | | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
19 | | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
20 | | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
21 | | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
22 | | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
23 | | */ |
24 | | |
25 | | #ifdef HAVE_CONFIG_H |
26 | | #include "config.h" |
27 | | #endif |
28 | | |
29 | | #include <ucl.h> |
30 | | #include "ucl.h" |
31 | | #include "ucl_internal.h" |
32 | | #include "utlist.h" |
33 | | |
34 | | #define NEXT_STATE \ |
35 | 0 | do { \ |
36 | 0 | if (p >= end) { \ |
37 | 0 | if (state != read_ebrace) { \ |
38 | 0 | ucl_create_err(&parser->err, \ |
39 | 0 | "extra data"); \ |
40 | 0 | state = parse_err; \ |
41 | 0 | } \ |
42 | 0 | } \ |
43 | 0 | else { \ |
44 | 0 | switch (*p) { \ |
45 | 0 | case '(': \ |
46 | 0 | state = read_obrace; \ |
47 | 0 | break; \ |
48 | 0 | case ')': \ |
49 | 0 | state = read_ebrace; \ |
50 | 0 | break; \ |
51 | 0 | default: \ |
52 | 0 | len = 0; \ |
53 | 0 | mult = 1; \ |
54 | 0 | state = read_length; \ |
55 | 0 | break; \ |
56 | 0 | } \ |
57 | 0 | } \ |
58 | 0 | } while (0) |
59 | | |
60 | | bool ucl_parse_csexp(struct ucl_parser *parser) |
61 | 0 | { |
62 | 0 | const unsigned char *p, *end; |
63 | 0 | ucl_object_t *obj; |
64 | 0 | struct ucl_stack *st; |
65 | 0 | uint64_t len = 0, mult = 1; |
66 | 0 | enum { |
67 | 0 | start_parse, |
68 | 0 | read_obrace, |
69 | 0 | read_length, |
70 | 0 | read_value, |
71 | 0 | read_ebrace, |
72 | 0 | parse_err |
73 | 0 | } state = start_parse; |
74 | |
|
75 | 0 | assert(parser != NULL); |
76 | 0 | assert(parser->chunks != NULL); |
77 | 0 | assert(parser->chunks->begin != NULL); |
78 | 0 | assert(parser->chunks->remain != 0); |
79 | |
|
80 | 0 | p = parser->chunks->begin; |
81 | 0 | end = p + parser->chunks->remain; |
82 | |
|
83 | 0 | while (p < end) { |
84 | 0 | switch (state) { |
85 | 0 | case start_parse: |
86 | | /* At this point we expect open brace */ |
87 | 0 | if (*p == '(') { |
88 | 0 | state = read_obrace; |
89 | 0 | } |
90 | 0 | else { |
91 | 0 | ucl_create_err(&parser->err, "bad starting character for " |
92 | 0 | "sexp block: %x", |
93 | 0 | (int) *p); |
94 | 0 | state = parse_err; |
95 | 0 | } |
96 | 0 | break; |
97 | | |
98 | 0 | case read_obrace: |
99 | 0 | st = calloc(1, sizeof(*st)); |
100 | |
|
101 | 0 | if (st == NULL) { |
102 | 0 | ucl_create_err(&parser->err, "no memory"); |
103 | 0 | state = parse_err; |
104 | 0 | continue; |
105 | 0 | } |
106 | | |
107 | 0 | st->obj = ucl_object_typed_new(UCL_ARRAY); |
108 | |
|
109 | 0 | if (st->obj == NULL) { |
110 | 0 | ucl_create_err(&parser->err, "no memory"); |
111 | 0 | state = parse_err; |
112 | 0 | free(st); |
113 | 0 | continue; |
114 | 0 | } |
115 | | |
116 | 0 | if (parser->stack == NULL) { |
117 | | /* We have no stack */ |
118 | 0 | parser->stack = st; |
119 | |
|
120 | 0 | if (parser->top_obj == NULL) { |
121 | 0 | parser->top_obj = st->obj; |
122 | 0 | } |
123 | 0 | } |
124 | 0 | else { |
125 | | /* Prepend new element to the stack */ |
126 | 0 | LL_PREPEND(parser->stack, st); |
127 | 0 | } |
128 | |
|
129 | 0 | p++; |
130 | 0 | NEXT_STATE; |
131 | | |
132 | 0 | break; |
133 | | |
134 | 0 | case read_length: |
135 | 0 | if (*p == ':') { |
136 | 0 | if (len == 0) { |
137 | 0 | ucl_create_err(&parser->err, "zero length element"); |
138 | 0 | state = parse_err; |
139 | 0 | continue; |
140 | 0 | } |
141 | | |
142 | 0 | state = read_value; |
143 | 0 | } |
144 | 0 | else if (*p >= '0' && *p <= '9') { |
145 | 0 | len += (*p - '0') * mult; |
146 | 0 | mult *= 10; |
147 | |
|
148 | 0 | if (len > UINT32_MAX) { |
149 | 0 | ucl_create_err(&parser->err, "too big length of an " |
150 | 0 | "element"); |
151 | 0 | state = parse_err; |
152 | 0 | continue; |
153 | 0 | } |
154 | 0 | } |
155 | 0 | else { |
156 | 0 | ucl_create_err(&parser->err, "bad length character: %x", |
157 | 0 | (int) *p); |
158 | 0 | state = parse_err; |
159 | 0 | continue; |
160 | 0 | } |
161 | | |
162 | 0 | p++; |
163 | 0 | break; |
164 | | |
165 | 0 | case read_value: |
166 | 0 | if ((uint64_t) (end - p) > len || len == 0) { |
167 | 0 | ucl_create_err(&parser->err, "invalid length: %llu, %ld " |
168 | 0 | "remain", |
169 | 0 | (long long unsigned) len, (long) (end - p)); |
170 | 0 | state = parse_err; |
171 | 0 | continue; |
172 | 0 | } |
173 | 0 | obj = ucl_object_typed_new(UCL_STRING); |
174 | |
|
175 | 0 | obj->value.sv = (const char *) p; |
176 | 0 | obj->len = len; |
177 | 0 | obj->flags |= UCL_OBJECT_BINARY; |
178 | |
|
179 | 0 | if (!(parser->flags & UCL_PARSER_ZEROCOPY)) { |
180 | 0 | ucl_copy_value_trash(obj); |
181 | 0 | } |
182 | |
|
183 | 0 | ucl_array_append(parser->stack->obj, obj); |
184 | 0 | p += len; |
185 | 0 | NEXT_STATE; |
186 | 0 | break; |
187 | | |
188 | 0 | case read_ebrace: |
189 | 0 | if (parser->stack == NULL) { |
190 | | /* We have an extra end brace */ |
191 | 0 | ucl_create_err(&parser->err, "invalid length: %llu, %ld " |
192 | 0 | "remain", |
193 | 0 | (long long unsigned) len, (long) (end - p)); |
194 | 0 | state = parse_err; |
195 | 0 | continue; |
196 | 0 | } |
197 | | /* Pop the container */ |
198 | 0 | st = parser->stack; |
199 | 0 | parser->stack = st->next; |
200 | |
|
201 | 0 | if (parser->stack->obj->type == UCL_ARRAY) { |
202 | 0 | ucl_array_append(parser->stack->obj, st->obj); |
203 | 0 | } |
204 | 0 | else { |
205 | 0 | ucl_create_err(&parser->err, "bad container object, array " |
206 | 0 | "expected"); |
207 | 0 | state = parse_err; |
208 | 0 | continue; |
209 | 0 | } |
210 | | |
211 | 0 | free(st); |
212 | 0 | st = NULL; |
213 | 0 | p++; |
214 | 0 | NEXT_STATE; |
215 | 0 | break; |
216 | | |
217 | 0 | case parse_err: |
218 | 0 | default: |
219 | 0 | return false; |
220 | 0 | } |
221 | 0 | } |
222 | | |
223 | 0 | if (state != read_ebrace) { |
224 | 0 | ucl_create_err(&parser->err, "invalid finishing state: %d", state); |
225 | 0 | return false; |
226 | 0 | } |
227 | | |
228 | 0 | return true; |
229 | 0 | } |