Radish alpha
H
rad:z3QDZAW2FAfuLvihrhiyDC9fAD8G9
HardenedBSD Package Manager
Radicle
Git
HardenedBSD-pkg external libucl src ucl_sexp.c
/*
 * Copyright (c) 2015, Vsevolod Stakhov
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *	 * Redistributions of source code must retain the above copyright
 *	   notice, this list of conditions and the following disclaimer.
 *	 * Redistributions in binary form must reproduce the above copyright
 *	   notice, this list of conditions and the following disclaimer in the
 *	   documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY AUTHOR ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <ucl.h>
#include "ucl.h"
#include "ucl_internal.h"
#include "utlist.h"

#define NEXT_STATE                            \
	do {                                      \
		if (p >= end) {                       \
			if (state != read_ebrace) {       \
				ucl_create_err(&parser->err,  \
							   "extra data"); \
				state = parse_err;            \
			}                                 \
		}                                     \
		else {                                \
			switch (*p) {                     \
			case '(':                         \
				state = read_obrace;          \
				break;                        \
			case ')':                         \
				state = read_ebrace;          \
				break;                        \
			default:                          \
				len = 0;                      \
				state = read_length;          \
				break;                        \
			}                                 \
		}                                     \
	} while (0)

bool ucl_parse_csexp(struct ucl_parser *parser)
{
	const unsigned char *p, *end;
	ucl_object_t *obj;
	struct ucl_stack *st;
	uint64_t len = 0;
	unsigned int depth = 0;
	enum {
		start_parse,
		read_obrace,
		read_length,
		read_value,
		read_ebrace,
		parse_err
	} state = start_parse;

	assert(parser != NULL);
	assert(parser->chunks != NULL);
	assert(parser->chunks->begin != NULL);
	assert(parser->chunks->remain != 0);

	p = parser->chunks->begin;
	end = p + parser->chunks->remain;

	while (p < end) {
		switch (state) {
		case start_parse:
			/* At this point we expect open brace */
			if (*p == '(') {
				state = read_obrace;
			}
			else {
				ucl_create_err(&parser->err, "bad starting character for "
											 "sexp block: %x",
							   (int) *p);
				state = parse_err;
			}
			break;

		case read_obrace:
			if (depth >= UCL_MAX_NESTING) {
				ucl_create_err(&parser->err,
							   "csexp nesting too deep (over %d)",
							   UCL_MAX_NESTING);
				state = parse_err;
				continue;
			}

			st = calloc(1, sizeof(*st));

			if (st == NULL) {
				ucl_create_err(&parser->err, "no memory");
				state = parse_err;
				continue;
			}

			st->obj = ucl_object_typed_new(UCL_ARRAY);

			if (st->obj == NULL) {
				ucl_create_err(&parser->err, "no memory");
				state = parse_err;
				free(st);
				continue;
			}

			if (parser->stack == NULL) {
				/* We have no stack */
				parser->stack = st;

				if (parser->top_obj == NULL) {
					parser->top_obj = st->obj;
				}
			}
			else {
				/* Prepend new element to the stack */
				LL_PREPEND(parser->stack, st);
			}

			depth++;
			p++;
			NEXT_STATE;

			break;

		case read_length:
			if (*p == ':') {
				if (len == 0) {
					ucl_create_err(&parser->err, "zero length element");
					state = parse_err;
					continue;
				}

				state = read_value;
			}
			else if (*p >= '0' && *p <= '9') {
				len = len * 10 + (*p - '0');

				if (len > UINT32_MAX) {
					ucl_create_err(&parser->err, "too big length of an "
												 "element");
					state = parse_err;
					continue;
				}
			}
			else {
				ucl_create_err(&parser->err, "bad length character: %x",
							   (int) *p);
				state = parse_err;
				continue;
			}

			p++;
			break;

		case read_value:
			if ((uint64_t) (end - p) < len || len == 0) {
				ucl_create_err(&parser->err, "invalid length: %llu, %ld "
											 "remain",
							   (long long unsigned) len, (long) (end - p));
				state = parse_err;
				continue;
			}
			obj = ucl_object_typed_new(UCL_STRING);

			obj->value.sv = (const char *) p;
			obj->len = len;
			obj->flags |= UCL_OBJECT_BINARY;

			if (!(parser->flags & UCL_PARSER_ZEROCOPY)) {
				ucl_copy_value_trash(obj);
			}

			if (parser->stack == NULL) {
				ucl_object_unref(obj);
				ucl_create_err(&parser->err, "value outside of any list");
				state = parse_err;
				continue;
			}

			ucl_array_append(parser->stack->obj, obj);
			p += len;
			NEXT_STATE;
			break;

		case read_ebrace:
			if (parser->stack == NULL) {
				/* We have an extra end brace */
				ucl_create_err(&parser->err, "invalid length: %llu, %ld "
											 "remain",
							   (long long unsigned) len, (long) (end - p));
				state = parse_err;
				continue;
			}
			/* Pop the container */
			st = parser->stack;
			parser->stack = st->next;

			if (parser->stack != NULL) {
				if (parser->stack->obj->type == UCL_ARRAY) {
					ucl_array_append(parser->stack->obj, st->obj);
				}
				else {
					ucl_create_err(&parser->err, "bad container object, array "
												 "expected");
					state = parse_err;
					continue;
				}
			}

			free(st);
			st = NULL;
			if (depth > 0) {
				depth--;
			}
			p++;
			NEXT_STATE;
			break;

		case parse_err:
		default:
			/* Clean up orphaned stack objects that were never appended
			 * to the tree. The outermost frame's obj == parser->top_obj
			 * and will be freed by ucl_parser_free; all others are
			 * orphaned and must be explicitly released here. */
			while (parser->stack != NULL) {
				struct ucl_stack *st_err = parser->stack;
				parser->stack = st_err->next;
				if (st_err->obj != NULL && st_err->obj != parser->top_obj) {
					ucl_object_unref(st_err->obj);
				}
				free(st_err);
			}
			return false;
		}
	}

	if (state != read_ebrace) {
		ucl_create_err(&parser->err, "invalid finishing state: %d", state);
		while (parser->stack != NULL) {
			struct ucl_stack *st_err = parser->stack;
			parser->stack = st_err->next;
			if (st_err->obj != NULL && st_err->obj != parser->top_obj) {
				ucl_object_unref(st_err->obj);
			}
			free(st_err);
		}
		return false;
	}

	return true;
}