xref: /freebsd/contrib/libucl/src/ucl_sexp.c (revision 1f4bcc459a76b7aa664f3fd557684cd0ba6da352)
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 				continue;
112 			}
113 
114 			if (parser->stack == NULL) {
115 				/* We have no stack */
116 				parser->stack = st;
117 
118 				if (parser->top_obj == NULL) {
119 					parser->top_obj = st->obj;
120 				}
121 			}
122 			else {
123 				/* Prepend new element to the stack */
124 				LL_PREPEND (parser->stack, st);
125 			}
126 
127 			p ++;
128 			NEXT_STATE;
129 
130 			break;
131 
132 		case read_length:
133 			if (*p == ':') {
134 				if (len == 0) {
135 					ucl_create_err (&parser->err, "zero length element");
136 					state = parse_err;
137 					continue;
138 				}
139 
140 				state = read_value;
141 			}
142 			else if (*p >= '0' && *p <= '9') {
143 				len += (*p - '0') * mult;
144 				mult *= 10;
145 
146 				if (len > UINT32_MAX) {
147 					ucl_create_err (&parser->err, "too big length of an "
148 									"element");
149 					state = parse_err;
150 					continue;
151 				}
152 			}
153 			else {
154 				ucl_create_err (&parser->err, "bad length character: %x",
155 						(int)*p);
156 				state = parse_err;
157 				continue;
158 			}
159 
160 			p ++;
161 			break;
162 
163 		case read_value:
164 			if ((uint64_t)(end - p) > len || len == 0) {
165 				ucl_create_err (&parser->err, "invalid length: %llu, %ld "
166 						"remain", (long long unsigned)len, (long)(end - p));
167 				state = parse_err;
168 				continue;
169 			}
170 			obj = ucl_object_typed_new (UCL_STRING);
171 
172 			obj->value.sv = (const char*)p;
173 			obj->len = len;
174 			obj->flags |= UCL_OBJECT_BINARY;
175 
176 			if (!(parser->flags & UCL_PARSER_ZEROCOPY)) {
177 				ucl_copy_value_trash (obj);
178 			}
179 
180 			ucl_array_append (parser->stack->obj, obj);
181 			p += len;
182 			NEXT_STATE;
183 			break;
184 
185 		case read_ebrace:
186 			if (parser->stack == NULL) {
187 				/* We have an extra end brace */
188 				ucl_create_err (&parser->err, "invalid length: %llu, %ld "
189 						"remain", (long long unsigned)len, (long)(end - p));
190 				state = parse_err;
191 				continue;
192 			}
193 			/* Pop the container */
194 			st = parser->stack;
195 			parser->stack = st->next;
196 
197 			if (parser->stack->obj->type == UCL_ARRAY) {
198 				ucl_array_append (parser->stack->obj, st->obj);
199 			}
200 			else {
201 				ucl_create_err (&parser->err, "bad container object, array "
202 						"expected");
203 				state = parse_err;
204 				continue;
205 			}
206 
207 			free (st);
208 			p++;
209 			NEXT_STATE;
210 			break;
211 
212 		case parse_err:
213 		default:
214 			return false;
215 		}
216 	}
217 
218 	if (state != read_ebrace) {
219 		ucl_create_err (&parser->err, "invalid finishing state: %d", state);
220 		return false;
221 	}
222 
223 	return true;
224 }