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 {            \
35if (p >= end) {                    \
36    if (state != read_ebrace) {    \
37      ucl_create_err (&parser->err,\
38                     "extra data");\
39      state = parse_err;           \
40    }                              \
41}                                  \
42else {                             \
43switch (*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
59bool
60ucl_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