xref: /freebsd/contrib/libucl/src/ucl_sexp.c (revision cddbc3b40812213ff00041f79174cac0be360a2a)
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 }
227