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