diff options
Diffstat (limited to 'contrib/libucl/ucl_sexp.c')
-rw-r--r-- | contrib/libucl/ucl_sexp.c | 226 |
1 files changed, 226 insertions, 0 deletions
diff --git a/contrib/libucl/ucl_sexp.c b/contrib/libucl/ucl_sexp.c new file mode 100644 index 000000000..1ad93d234 --- /dev/null +++ b/contrib/libucl/ucl_sexp.c @@ -0,0 +1,226 @@ +/* + * 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; \ + mult = 1; \ + 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, mult = 1; + 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: + 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); + } + + 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 += (*p - '0') * mult; + mult *= 10; + + 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); + } + + 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->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; + p++; + NEXT_STATE; + break; + + case parse_err: + default: + return false; + } + } + + if (state != read_ebrace) { + ucl_create_err (&parser->err, "invalid finishing state: %d", state); + return false; + } + + return true; +} |