]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/libucl/src/ucl_sexp.c
MFV r302003,r302037,r302038,r302056:
[FreeBSD/FreeBSD.git] / contrib / libucl / src / ucl_sexp.c
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 do {            \
35 if (p >= end) {                    \
36     if (state != read_ebrace) {    \
37       ucl_create_err (&parser->err,\
38                      "extra data");\
39       state = parse_err;           \
40     }                              \
41 }                                  \
42 else {                             \
43 switch (*p) {                      \
44     case '(':                      \
45         state = read_obrace;       \
46         break;                     \
47     case ')':                      \
48         state = read_ebrace;       \
49         break;                     \
50     default:                       \
51         len = 0;                   \
52         mult = 1;                  \
53         state = read_length;       \
54         break;                     \
55     }                              \
56 }                                  \
57 } while(0)
58
59 bool
60 ucl_parse_csexp (struct ucl_parser *parser)
61 {
62         const unsigned char *p, *end;
63         ucl_object_t *obj;
64         struct ucl_stack *st;
65         uint64_t len = 0, mult = 1;
66         enum {
67                 start_parse,
68                 read_obrace,
69                 read_length,
70                 read_value,
71                 read_ebrace,
72                 parse_err
73         } state = start_parse;
74
75         assert (parser != NULL);
76         assert (parser->chunks != NULL);
77         assert (parser->chunks->begin != NULL);
78         assert (parser->chunks->remain != 0);
79
80         p = parser->chunks->begin;
81         end = p + parser->chunks->remain;
82
83         while (p < end) {
84                 switch (state) {
85                 case start_parse:
86                         /* At this point we expect open brace */
87                         if (*p == '(') {
88                                 state = read_obrace;
89                         }
90                         else {
91                                 ucl_create_err (&parser->err, "bad starting character for "
92                                                 "sexp block: %x", (int)*p);
93                                 state = parse_err;
94                         }
95                         break;
96
97                 case read_obrace:
98                         st = calloc (1, sizeof (*st));
99
100                         if (st == NULL) {
101                                 ucl_create_err (&parser->err, "no memory");
102                                 state = parse_err;
103                                 continue;
104                         }
105
106                         st->obj = ucl_object_typed_new (UCL_ARRAY);
107
108                         if (st->obj == NULL) {
109                                 ucl_create_err (&parser->err, "no memory");
110                                 state = parse_err;
111                                 free (st);
112                                 continue;
113                         }
114
115                         if (parser->stack == NULL) {
116                                 /* We have no stack */
117                                 parser->stack = st;
118
119                                 if (parser->top_obj == NULL) {
120                                         parser->top_obj = st->obj;
121                                 }
122                         }
123                         else {
124                                 /* Prepend new element to the stack */
125                                 LL_PREPEND (parser->stack, st);
126                         }
127
128                         p ++;
129                         NEXT_STATE;
130
131                         break;
132
133                 case read_length:
134                         if (*p == ':') {
135                                 if (len == 0) {
136                                         ucl_create_err (&parser->err, "zero length element");
137                                         state = parse_err;
138                                         continue;
139                                 }
140
141                                 state = read_value;
142                         }
143                         else if (*p >= '0' && *p <= '9') {
144                                 len += (*p - '0') * mult;
145                                 mult *= 10;
146
147                                 if (len > UINT32_MAX) {
148                                         ucl_create_err (&parser->err, "too big length of an "
149                                                                         "element");
150                                         state = parse_err;
151                                         continue;
152                                 }
153                         }
154                         else {
155                                 ucl_create_err (&parser->err, "bad length character: %x",
156                                                 (int)*p);
157                                 state = parse_err;
158                                 continue;
159                         }
160
161                         p ++;
162                         break;
163
164                 case read_value:
165                         if ((uint64_t)(end - p) > len || len == 0) {
166                                 ucl_create_err (&parser->err, "invalid length: %llu, %ld "
167                                                 "remain", (long long unsigned)len, (long)(end - p));
168                                 state = parse_err;
169                                 continue;
170                         }
171                         obj = ucl_object_typed_new (UCL_STRING);
172
173                         obj->value.sv = (const char*)p;
174                         obj->len = len;
175                         obj->flags |= UCL_OBJECT_BINARY;
176
177                         if (!(parser->flags & UCL_PARSER_ZEROCOPY)) {
178                                 ucl_copy_value_trash (obj);
179                         }
180
181                         ucl_array_append (parser->stack->obj, obj);
182                         p += len;
183                         NEXT_STATE;
184                         break;
185
186                 case read_ebrace:
187                         if (parser->stack == NULL) {
188                                 /* We have an extra end brace */
189                                 ucl_create_err (&parser->err, "invalid length: %llu, %ld "
190                                                 "remain", (long long unsigned)len, (long)(end - p));
191                                 state = parse_err;
192                                 continue;
193                         }
194                         /* Pop the container */
195                         st = parser->stack;
196                         parser->stack = st->next;
197
198                         if (parser->stack->obj->type == UCL_ARRAY) {
199                                 ucl_array_append (parser->stack->obj, st->obj);
200                         }
201                         else {
202                                 ucl_create_err (&parser->err, "bad container object, array "
203                                                 "expected");
204                                 state = parse_err;
205                                 continue;
206                         }
207
208                         free (st);
209                         st = NULL;
210                         p++;
211                         NEXT_STATE;
212                         break;
213
214                 case parse_err:
215                 default:
216                         return false;
217                 }
218         }
219
220         if (state != read_ebrace) {
221                 ucl_create_err (&parser->err, "invalid finishing state: %d", state);
222                 return false;
223         }
224
225         return true;
226 }