diff options
Diffstat (limited to 'contrib/libucl/src')
-rw-r--r-- | contrib/libucl/src/Makefile.am | 27 | ||||
-rw-r--r-- | contrib/libucl/src/tree.h | 212 | ||||
-rw-r--r-- | contrib/libucl/src/ucl_chartable.h | 267 | ||||
-rw-r--r-- | contrib/libucl/src/ucl_emitter.c | 506 | ||||
-rw-r--r-- | contrib/libucl/src/ucl_emitter_streamline.c | 165 | ||||
-rw-r--r-- | contrib/libucl/src/ucl_emitter_utils.c | 486 | ||||
-rw-r--r-- | contrib/libucl/src/ucl_hash.c | 141 | ||||
-rw-r--r-- | contrib/libucl/src/ucl_hash.h | 99 | ||||
-rw-r--r-- | contrib/libucl/src/ucl_internal.h | 396 | ||||
-rw-r--r-- | contrib/libucl/src/ucl_parser.c | 2219 | ||||
-rw-r--r-- | contrib/libucl/src/ucl_schema.c | 1014 | ||||
-rw-r--r-- | contrib/libucl/src/ucl_util.c | 2407 | ||||
-rw-r--r-- | contrib/libucl/src/xxhash.c | 475 | ||||
-rw-r--r-- | contrib/libucl/src/xxhash.h | 164 |
14 files changed, 8578 insertions, 0 deletions
diff --git a/contrib/libucl/src/Makefile.am b/contrib/libucl/src/Makefile.am new file mode 100644 index 0000000..417d34e --- /dev/null +++ b/contrib/libucl/src/Makefile.am @@ -0,0 +1,27 @@ +libucl_common_cflags= -I$(top_srcdir)/src \ + -I$(top_srcdir)/include \ + -I$(top_srcdir)/uthash \ + -Wall -W -Wno-unused-parameter -Wno-pointer-sign +lib_LTLIBRARIES= libucl.la +libucl_la_SOURCES= ucl_emitter.c \ + ucl_emitter_streamline.c \ + ucl_emitter_utils.c \ + ucl_hash.c \ + ucl_parser.c \ + ucl_schema.c \ + ucl_util.c \ + xxhash.c +libucl_la_CFLAGS= $(libucl_common_cflags) \ + @CURL_CFLAGS@ +libucl_la_LDFLAGS = -version-info @SO_VERSION@ +libucl_la_LIBADD= @LIBFETCH_LIBS@ \ + @LIBCRYPTO_LIB@ \ + @LIBREGEX_LIB@ \ + @CURL_LIBS@ + +include_HEADERS= $(top_srcdir)/include/ucl.h +noinst_HEADERS= ucl_internal.h \ + xxhash.h \ + ucl_hash.h \ + ucl_chartable.h \ + tree.h diff --git a/contrib/libucl/src/tree.h b/contrib/libucl/src/tree.h new file mode 100644 index 0000000..cee9373 --- /dev/null +++ b/contrib/libucl/src/tree.h @@ -0,0 +1,212 @@ +/* tree.h -- AVL trees (in the spirit of BSD's 'queue.h') -*- C -*- */ + +/* Copyright (c) 2005 Ian Piumarta + * + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the 'Software'), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, and/or sell copies of the + * Software, and to permit persons to whom the Software is furnished to do so, + * provided that the above copyright notice(s) and this permission notice appear + * in all copies of the Software and that both the above copyright notice(s) and + * this permission notice appear in supporting documentation. + * + * THE SOFTWARE IS PROVIDED 'AS IS'. USE ENTIRELY AT YOUR OWN RISK. + */ + +/* This file defines an AVL balanced binary tree [Georgii M. Adelson-Velskii and + * Evgenii M. Landis, 'An algorithm for the organization of information', + * Doklady Akademii Nauk SSSR, 146:263-266, 1962 (Russian). Also in Myron + * J. Ricci (trans.), Soviet Math, 3:1259-1263, 1962 (English)]. + * + * An AVL tree is headed by pointers to the root node and to a function defining + * the ordering relation between nodes. Each node contains an arbitrary payload + * plus three fields per tree entry: the depth of the subtree for which it forms + * the root and two pointers to child nodes (singly-linked for minimum space, at + * the expense of direct access to the parent node given a pointer to one of the + * children). The tree is rebalanced after every insertion or removal. The + * tree may be traversed in two directions: forward (in-order left-to-right) and + * reverse (in-order, right-to-left). + * + * Because of the recursive nature of many of the operations on trees it is + * necessary to define a number of helper functions for each type of tree node. + * The macro TREE_DEFINE(node_tag, entry_name) defines these functions with + * unique names according to the node_tag. This macro should be invoked, + * thereby defining the necessary functions, once per node tag in the program. + * + * For details on the use of these macros, see the tree(3) manual page. + */ + +#ifndef __tree_h +#define __tree_h + + +#define TREE_DELTA_MAX 1 + +#define TREE_ENTRY(type) \ + struct { \ + struct type *avl_left; \ + struct type *avl_right; \ + int avl_height; \ + } + +#define TREE_HEAD(name, type) \ + struct name { \ + struct type *th_root; \ + int (*th_cmp)(struct type *lhs, struct type *rhs); \ + } + +#define TREE_INITIALIZER(cmp) { 0, cmp } + +#define TREE_DELTA(self, field) \ + (( (((self)->field.avl_left) ? (self)->field.avl_left->field.avl_height : 0)) \ + - (((self)->field.avl_right) ? (self)->field.avl_right->field.avl_height : 0)) + +/* Recursion prevents the following from being defined as macros. */ + +#define TREE_DEFINE(node, field) \ + \ + struct node *TREE_BALANCE_##node##_##field(struct node *); \ + \ + struct node *TREE_ROTL_##node##_##field(struct node *self) \ + { \ + struct node *r= self->field.avl_right; \ + self->field.avl_right= r->field.avl_left; \ + r->field.avl_left= TREE_BALANCE_##node##_##field(self); \ + return TREE_BALANCE_##node##_##field(r); \ + } \ + \ + struct node *TREE_ROTR_##node##_##field(struct node *self) \ + { \ + struct node *l= self->field.avl_left; \ + self->field.avl_left= l->field.avl_right; \ + l->field.avl_right= TREE_BALANCE_##node##_##field(self); \ + return TREE_BALANCE_##node##_##field(l); \ + } \ + \ + struct node *TREE_BALANCE_##node##_##field(struct node *self) \ + { \ + int delta= TREE_DELTA(self, field); \ + \ + if (delta < -TREE_DELTA_MAX) \ + { \ + if (TREE_DELTA(self->field.avl_right, field) > 0) \ + self->field.avl_right= TREE_ROTR_##node##_##field(self->field.avl_right); \ + return TREE_ROTL_##node##_##field(self); \ + } \ + else if (delta > TREE_DELTA_MAX) \ + { \ + if (TREE_DELTA(self->field.avl_left, field) < 0) \ + self->field.avl_left= TREE_ROTL_##node##_##field(self->field.avl_left); \ + return TREE_ROTR_##node##_##field(self); \ + } \ + self->field.avl_height= 0; \ + if (self->field.avl_left && (self->field.avl_left->field.avl_height > self->field.avl_height)) \ + self->field.avl_height= self->field.avl_left->field.avl_height; \ + if (self->field.avl_right && (self->field.avl_right->field.avl_height > self->field.avl_height)) \ + self->field.avl_height= self->field.avl_right->field.avl_height; \ + self->field.avl_height += 1; \ + return self; \ + } \ + \ + struct node *TREE_INSERT_##node##_##field \ + (struct node *self, struct node *elm, int (*compare)(struct node *lhs, struct node *rhs)) \ + { \ + if (!self) \ + return elm; \ + if (compare(elm, self) < 0) \ + self->field.avl_left= TREE_INSERT_##node##_##field(self->field.avl_left, elm, compare); \ + else \ + self->field.avl_right= TREE_INSERT_##node##_##field(self->field.avl_right, elm, compare); \ + return TREE_BALANCE_##node##_##field(self); \ + } \ + \ + struct node *TREE_FIND_##node##_##field \ + (struct node *self, struct node *elm, int (*compare)(struct node *lhs, struct node *rhs)) \ + { \ + if (!self) \ + return 0; \ + if (compare(elm, self) == 0) \ + return self; \ + if (compare(elm, self) < 0) \ + return TREE_FIND_##node##_##field(self->field.avl_left, elm, compare); \ + else \ + return TREE_FIND_##node##_##field(self->field.avl_right, elm, compare); \ + } \ + \ + struct node *TREE_MOVE_RIGHT(struct node *self, struct node *rhs) \ + { \ + if (!self) \ + return rhs; \ + self->field.avl_right= TREE_MOVE_RIGHT(self->field.avl_right, rhs); \ + return TREE_BALANCE_##node##_##field(self); \ + } \ + \ + struct node *TREE_REMOVE_##node##_##field \ + (struct node *self, struct node *elm, int (*compare)(struct node *lhs, struct node *rhs)) \ + { \ + if (!self) return 0; \ + \ + if (compare(elm, self) == 0) \ + { \ + struct node *tmp= TREE_MOVE_RIGHT(self->field.avl_left, self->field.avl_right); \ + self->field.avl_left= 0; \ + self->field.avl_right= 0; \ + return tmp; \ + } \ + if (compare(elm, self) < 0) \ + self->field.avl_left= TREE_REMOVE_##node##_##field(self->field.avl_left, elm, compare); \ + else \ + self->field.avl_right= TREE_REMOVE_##node##_##field(self->field.avl_right, elm, compare); \ + return TREE_BALANCE_##node##_##field(self); \ + } \ + \ + void TREE_FORWARD_APPLY_ALL_##node##_##field \ + (struct node *self, void (*function)(struct node *node, void *data), void *data) \ + { \ + if (self) \ + { \ + TREE_FORWARD_APPLY_ALL_##node##_##field(self->field.avl_left, function, data); \ + function(self, data); \ + TREE_FORWARD_APPLY_ALL_##node##_##field(self->field.avl_right, function, data); \ + } \ + } \ + \ + void TREE_REVERSE_APPLY_ALL_##node##_##field \ + (struct node *self, void (*function)(struct node *node, void *data), void *data) \ + { \ + if (self) \ + { \ + TREE_REVERSE_APPLY_ALL_##node##_##field(self->field.avl_right, function, data); \ + function(self, data); \ + TREE_REVERSE_APPLY_ALL_##node##_##field(self->field.avl_left, function, data); \ + } \ + } + +#define TREE_INSERT(head, node, field, elm) \ + ((head)->th_root= TREE_INSERT_##node##_##field((head)->th_root, (elm), (head)->th_cmp)) + +#define TREE_FIND(head, node, field, elm) \ + (TREE_FIND_##node##_##field((head)->th_root, (elm), (head)->th_cmp)) + +#define TREE_REMOVE(head, node, field, elm) \ + ((head)->th_root= TREE_REMOVE_##node##_##field((head)->th_root, (elm), (head)->th_cmp)) + +#define TREE_DEPTH(head, field) \ + ((head)->th_root->field.avl_height) + +#define TREE_FORWARD_APPLY(head, node, field, function, data) \ + TREE_FORWARD_APPLY_ALL_##node##_##field((head)->th_root, function, data) + +#define TREE_REVERSE_APPLY(head, node, field, function, data) \ + TREE_REVERSE_APPLY_ALL_##node##_##field((head)->th_root, function, data) + +#define TREE_INIT(head, cmp) do { \ + (head)->th_root= 0; \ + (head)->th_cmp= (cmp); \ + } while (0) + + +#endif /* __tree_h */ diff --git a/contrib/libucl/src/ucl_chartable.h b/contrib/libucl/src/ucl_chartable.h new file mode 100644 index 0000000..5248e11 --- /dev/null +++ b/contrib/libucl/src/ucl_chartable.h @@ -0,0 +1,267 @@ +/* Copyright (c) 2013, 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 ''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. + */ + +#ifndef UCL_CHARTABLE_H_ +#define UCL_CHARTABLE_H_ + +#include "ucl_internal.h" + +static const unsigned int ucl_chartable[255] = { +UCL_CHARACTER_VALUE_END, UCL_CHARACTER_DENIED, UCL_CHARACTER_DENIED, +UCL_CHARACTER_DENIED, UCL_CHARACTER_DENIED, UCL_CHARACTER_DENIED, +UCL_CHARACTER_DENIED, UCL_CHARACTER_DENIED, +UCL_CHARACTER_JSON_UNSAFE|UCL_CHARACTER_UCL_UNSAFE, +UCL_CHARACTER_WHITESPACE|UCL_CHARACTER_WHITESPACE_UNSAFE|UCL_CHARACTER_KEY_SEP|UCL_CHARACTER_JSON_UNSAFE|UCL_CHARACTER_UCL_UNSAFE, +UCL_CHARACTER_WHITESPACE_UNSAFE|UCL_CHARACTER_VALUE_END|UCL_CHARACTER_JSON_UNSAFE|UCL_CHARACTER_UCL_UNSAFE, +UCL_CHARACTER_WHITESPACE_UNSAFE, +UCL_CHARACTER_WHITESPACE_UNSAFE|UCL_CHARACTER_JSON_UNSAFE|UCL_CHARACTER_UCL_UNSAFE, +UCL_CHARACTER_WHITESPACE_UNSAFE|UCL_CHARACTER_VALUE_END|UCL_CHARACTER_JSON_UNSAFE|UCL_CHARACTER_UCL_UNSAFE, +UCL_CHARACTER_DENIED, UCL_CHARACTER_DENIED, UCL_CHARACTER_DENIED, +UCL_CHARACTER_DENIED, UCL_CHARACTER_DENIED, UCL_CHARACTER_DENIED, +UCL_CHARACTER_DENIED, UCL_CHARACTER_DENIED, UCL_CHARACTER_DENIED, +UCL_CHARACTER_DENIED, UCL_CHARACTER_DENIED, UCL_CHARACTER_DENIED, +UCL_CHARACTER_DENIED, UCL_CHARACTER_DENIED, UCL_CHARACTER_DENIED, +UCL_CHARACTER_DENIED, UCL_CHARACTER_DENIED, UCL_CHARACTER_DENIED, +UCL_CHARACTER_WHITESPACE|UCL_CHARACTER_WHITESPACE_UNSAFE|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_KEY_SEP|UCL_CHARACTER_UCL_UNSAFE /* */, +UCL_CHARACTER_VALUE_STR /* ! */, +UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_ESCAPE|UCL_CHARACTER_JSON_UNSAFE|UCL_CHARACTER_UCL_UNSAFE /* " */, +UCL_CHARACTER_VALUE_END /* # */, UCL_CHARACTER_VALUE_STR /* $ */, +UCL_CHARACTER_VALUE_STR /* % */, UCL_CHARACTER_VALUE_STR /* & */, +UCL_CHARACTER_VALUE_STR /* ' */, UCL_CHARACTER_VALUE_STR /* ( */, +UCL_CHARACTER_VALUE_STR /* ) */, UCL_CHARACTER_VALUE_STR /* * */, +UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* + */, +UCL_CHARACTER_VALUE_END /* , */, +UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT_START|UCL_CHARACTER_VALUE_DIGIT /* - */, +UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* . */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_ESCAPE /* / */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT_START|UCL_CHARACTER_VALUE_DIGIT /* 0 */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT_START|UCL_CHARACTER_VALUE_DIGIT /* 1 */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT_START|UCL_CHARACTER_VALUE_DIGIT /* 2 */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT_START|UCL_CHARACTER_VALUE_DIGIT /* 3 */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT_START|UCL_CHARACTER_VALUE_DIGIT /* 4 */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT_START|UCL_CHARACTER_VALUE_DIGIT /* 5 */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT_START|UCL_CHARACTER_VALUE_DIGIT /* 6 */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT_START|UCL_CHARACTER_VALUE_DIGIT /* 7 */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT_START|UCL_CHARACTER_VALUE_DIGIT /* 8 */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT_START|UCL_CHARACTER_VALUE_DIGIT /* 9 */, +UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_KEY_SEP|UCL_CHARACTER_UCL_UNSAFE /* : */, +UCL_CHARACTER_VALUE_END /* ; */, UCL_CHARACTER_VALUE_STR /* < */, +UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_KEY_SEP|UCL_CHARACTER_UCL_UNSAFE /* = */, +UCL_CHARACTER_VALUE_STR /* > */, UCL_CHARACTER_VALUE_STR /* ? */, +UCL_CHARACTER_VALUE_STR /* @ */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* A */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* B */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* C */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* D */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* E */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* F */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* G */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* H */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* I */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* J */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* K */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* L */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* M */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* N */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* O */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* P */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* Q */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* R */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* S */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* T */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* U */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* V */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* W */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* X */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* Y */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* Z */, +UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_UCL_UNSAFE /* [ */, +UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_ESCAPE|UCL_CHARACTER_JSON_UNSAFE|UCL_CHARACTER_UCL_UNSAFE /* \ */, +UCL_CHARACTER_VALUE_END /* ] */, UCL_CHARACTER_VALUE_STR /* ^ */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR /* _ */, +UCL_CHARACTER_VALUE_STR /* ` */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* a */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT|UCL_CHARACTER_ESCAPE /* b */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* c */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* d */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* e */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT|UCL_CHARACTER_ESCAPE /* f */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* g */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* h */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* i */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* j */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* k */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* l */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* m */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT|UCL_CHARACTER_ESCAPE /* n */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* o */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* p */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* q */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT|UCL_CHARACTER_ESCAPE /* r */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* s */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT|UCL_CHARACTER_ESCAPE /* t */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT|UCL_CHARACTER_ESCAPE /* u */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* v */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* w */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* x */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* y */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* z */, +UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_UCL_UNSAFE /* { */, +UCL_CHARACTER_VALUE_STR /* | */, UCL_CHARACTER_VALUE_END /* } */, +UCL_CHARACTER_VALUE_STR /* ~ */, UCL_CHARACTER_DENIED, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR +}; + +static inline bool +ucl_test_character (unsigned char c, int type_flags) +{ + return (ucl_chartable[c] & type_flags) != 0; +} + +#endif /* UCL_CHARTABLE_H_ */ diff --git a/contrib/libucl/src/ucl_emitter.c b/contrib/libucl/src/ucl_emitter.c new file mode 100644 index 0000000..8134d09 --- /dev/null +++ b/contrib/libucl/src/ucl_emitter.c @@ -0,0 +1,506 @@ +/* Copyright (c) 2013, 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 ''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_internal.h" +#include "ucl_chartable.h" +#ifdef HAVE_FLOAT_H +#include <float.h> +#endif +#ifdef HAVE_MATH_H +#include <math.h> +#endif + +/** + * @file ucl_emitter.c + * Serialise UCL object to various of output formats + */ + +static void ucl_emitter_common_elt (struct ucl_emitter_context *ctx, + const ucl_object_t *obj, bool first, bool print_key, bool compact); + +#define UCL_EMIT_TYPE_OPS(type) \ + static void ucl_emit_ ## type ## _elt (struct ucl_emitter_context *ctx, \ + const ucl_object_t *obj, bool first, bool print_key); \ + static void ucl_emit_ ## type ## _start_obj (struct ucl_emitter_context *ctx, \ + const ucl_object_t *obj, bool print_key); \ + static void ucl_emit_ ## type## _start_array (struct ucl_emitter_context *ctx, \ + const ucl_object_t *obj, bool print_key); \ + static void ucl_emit_ ##type## _end_object (struct ucl_emitter_context *ctx, \ + const ucl_object_t *obj); \ + static void ucl_emit_ ##type## _end_array (struct ucl_emitter_context *ctx, \ + const ucl_object_t *obj) + +/* + * JSON format operations + */ +UCL_EMIT_TYPE_OPS(json); +UCL_EMIT_TYPE_OPS(json_compact); +UCL_EMIT_TYPE_OPS(config); +UCL_EMIT_TYPE_OPS(yaml); + +#define UCL_EMIT_TYPE_CONTENT(type) { \ + .ucl_emitter_write_elt = ucl_emit_ ## type ## _elt, \ + .ucl_emitter_start_object = ucl_emit_ ## type ##_start_obj, \ + .ucl_emitter_start_array = ucl_emit_ ## type ##_start_array, \ + .ucl_emitter_end_object = ucl_emit_ ## type ##_end_object, \ + .ucl_emitter_end_array = ucl_emit_ ## type ##_end_array \ +} + + +const struct ucl_emitter_operations ucl_standartd_emitter_ops[] = { + [UCL_EMIT_JSON] = UCL_EMIT_TYPE_CONTENT(json), + [UCL_EMIT_JSON_COMPACT] = UCL_EMIT_TYPE_CONTENT(json_compact), + [UCL_EMIT_CONFIG] = UCL_EMIT_TYPE_CONTENT(config), + [UCL_EMIT_YAML] = UCL_EMIT_TYPE_CONTENT(yaml) +}; + +/* + * Utility to check whether we need a top object + */ +#define UCL_EMIT_IDENT_TOP_OBJ(ctx, obj) ((ctx)->top != (obj) || \ + ((ctx)->id == UCL_EMIT_JSON_COMPACT || (ctx)->id == UCL_EMIT_JSON)) + + +/** + * Add tabulation to the output buffer + * @param buf target buffer + * @param tabs number of tabs to add + */ +static inline void +ucl_add_tabs (const struct ucl_emitter_functions *func, unsigned int tabs, + bool compact) +{ + if (!compact && tabs > 0) { + func->ucl_emitter_append_character (' ', tabs * 4, func->ud); + } +} + +/** + * Print key for the element + * @param ctx + * @param obj + */ +static void +ucl_emitter_print_key (bool print_key, struct ucl_emitter_context *ctx, + const ucl_object_t *obj, bool compact) +{ + const struct ucl_emitter_functions *func = ctx->func; + + if (!print_key) { + return; + } + + if (ctx->id == UCL_EMIT_CONFIG) { + if (obj->flags & UCL_OBJECT_NEED_KEY_ESCAPE) { + ucl_elt_string_write_json (obj->key, obj->keylen, ctx); + } + else { + func->ucl_emitter_append_len (obj->key, obj->keylen, func->ud); + } + + if (obj->type != UCL_OBJECT && obj->type != UCL_ARRAY) { + func->ucl_emitter_append_len (" = ", 3, func->ud); + } + else { + func->ucl_emitter_append_character (' ', 1, func->ud); + } + } + else if (ctx->id == UCL_EMIT_YAML) { + if (obj->keylen > 0 && (obj->flags & UCL_OBJECT_NEED_KEY_ESCAPE)) { + ucl_elt_string_write_json (obj->key, obj->keylen, ctx); + } + else if (obj->keylen > 0) { + func->ucl_emitter_append_len (obj->key, obj->keylen, func->ud); + } + else { + func->ucl_emitter_append_len ("null", 4, func->ud); + } + + func->ucl_emitter_append_len (": ", 2, func->ud); + } + else { + if (obj->keylen > 0) { + ucl_elt_string_write_json (obj->key, obj->keylen, ctx); + } + else { + func->ucl_emitter_append_len ("null", 4, func->ud); + } + + if (compact) { + func->ucl_emitter_append_character (':', 1, func->ud); + } + else { + func->ucl_emitter_append_len (": ", 2, func->ud); + } + } +} + +static void +ucl_emitter_finish_object (struct ucl_emitter_context *ctx, + const ucl_object_t *obj, bool compact, bool is_array) +{ + const struct ucl_emitter_functions *func = ctx->func; + + if (ctx->id == UCL_EMIT_CONFIG && obj != ctx->top) { + if (obj->type != UCL_OBJECT && obj->type != UCL_ARRAY) { + if (!is_array) { + /* Objects are split by ';' */ + func->ucl_emitter_append_len (";\n", 2, func->ud); + } + else { + /* Use commas for arrays */ + func->ucl_emitter_append_len (",\n", 2, func->ud); + } + } + else { + func->ucl_emitter_append_character ('\n', 1, func->ud); + } + } +} + +/** + * End standard ucl object + * @param ctx emitter context + * @param compact compact flag + */ +static void +ucl_emitter_common_end_object (struct ucl_emitter_context *ctx, + const ucl_object_t *obj, bool compact) +{ + const struct ucl_emitter_functions *func = ctx->func; + + if (UCL_EMIT_IDENT_TOP_OBJ(ctx, obj)) { + ctx->indent --; + if (compact) { + func->ucl_emitter_append_character ('}', 1, func->ud); + } + else { + if (ctx->id != UCL_EMIT_CONFIG) { + /* newline is already added for this format */ + func->ucl_emitter_append_character ('\n', 1, func->ud); + } + ucl_add_tabs (func, ctx->indent, compact); + func->ucl_emitter_append_character ('}', 1, func->ud); + } + } + + ucl_emitter_finish_object (ctx, obj, compact, false); +} + +/** + * End standard ucl array + * @param ctx emitter context + * @param compact compact flag + */ +static void +ucl_emitter_common_end_array (struct ucl_emitter_context *ctx, + const ucl_object_t *obj, bool compact) +{ + const struct ucl_emitter_functions *func = ctx->func; + + ctx->indent --; + if (compact) { + func->ucl_emitter_append_character (']', 1, func->ud); + } + else { + if (ctx->id != UCL_EMIT_CONFIG) { + /* newline is already added for this format */ + func->ucl_emitter_append_character ('\n', 1, func->ud); + } + ucl_add_tabs (func, ctx->indent, compact); + func->ucl_emitter_append_character (']', 1, func->ud); + } + + ucl_emitter_finish_object (ctx, obj, compact, true); +} + +/** + * Start emit standard UCL array + * @param ctx emitter context + * @param obj object to write + * @param compact compact flag + */ +static void +ucl_emitter_common_start_array (struct ucl_emitter_context *ctx, + const ucl_object_t *obj, bool print_key, bool compact) +{ + const ucl_object_t *cur; + const struct ucl_emitter_functions *func = ctx->func; + bool first = true; + + ucl_emitter_print_key (print_key, ctx, obj, compact); + + if (compact) { + func->ucl_emitter_append_character ('[', 1, func->ud); + } + else { + func->ucl_emitter_append_len ("[\n", 2, func->ud); + } + + ctx->indent ++; + + if (obj->type == UCL_ARRAY) { + /* explicit array */ + cur = obj->value.av; + } + else { + /* implicit array */ + cur = obj; + } + + while (cur) { + ucl_emitter_common_elt (ctx, cur, first, false, compact); + first = false; + cur = cur->next; + } +} + +/** + * Start emit standard UCL object + * @param ctx emitter context + * @param obj object to write + * @param compact compact flag + */ +static void +ucl_emitter_common_start_object (struct ucl_emitter_context *ctx, + const ucl_object_t *obj, bool print_key, bool compact) +{ + ucl_hash_iter_t it = NULL; + const ucl_object_t *cur, *elt; + const struct ucl_emitter_functions *func = ctx->func; + bool first = true; + + ucl_emitter_print_key (print_key, ctx, obj, compact); + /* + * Print <ident_level>{ + * <ident_level + 1><object content> + */ + if (UCL_EMIT_IDENT_TOP_OBJ(ctx, obj)) { + if (compact) { + func->ucl_emitter_append_character ('{', 1, func->ud); + } + else { + func->ucl_emitter_append_len ("{\n", 2, func->ud); + } + ctx->indent ++; + } + + while ((cur = ucl_hash_iterate (obj->value.ov, &it))) { + + if (ctx->id == UCL_EMIT_CONFIG) { + LL_FOREACH (cur, elt) { + ucl_emitter_common_elt (ctx, elt, first, true, compact); + } + } + else { + /* Expand implicit arrays */ + if (cur->next != NULL) { + if (!first) { + if (compact) { + func->ucl_emitter_append_character (',', 1, func->ud); + } + else { + func->ucl_emitter_append_len (",\n", 2, func->ud); + } + } + ucl_add_tabs (func, ctx->indent, compact); + ucl_emitter_common_start_array (ctx, cur, true, compact); + ucl_emitter_common_end_array (ctx, cur, compact); + } + else { + ucl_emitter_common_elt (ctx, cur, first, true, compact); + } + } + + first = false; + } +} + +/** + * Common choice of object emitting + * @param ctx emitter context + * @param obj object to print + * @param first flag to mark the first element + * @param print_key print key of an object + * @param compact compact output + */ +static void +ucl_emitter_common_elt (struct ucl_emitter_context *ctx, + const ucl_object_t *obj, bool first, bool print_key, bool compact) +{ + const struct ucl_emitter_functions *func = ctx->func; + bool flag; + struct ucl_object_userdata *ud; + const char *ud_out = ""; + + if (ctx->id != UCL_EMIT_CONFIG && !first) { + if (compact) { + func->ucl_emitter_append_character (',', 1, func->ud); + } + else { + if (ctx->id == UCL_EMIT_YAML && ctx->indent == 0) { + func->ucl_emitter_append_len ("\n", 1, func->ud); + } else { + func->ucl_emitter_append_len (",\n", 2, func->ud); + } + } + } + + ucl_add_tabs (func, ctx->indent, compact); + + switch (obj->type) { + case UCL_INT: + ucl_emitter_print_key (print_key, ctx, obj, compact); + func->ucl_emitter_append_int (ucl_object_toint (obj), func->ud); + ucl_emitter_finish_object (ctx, obj, compact, !print_key); + break; + case UCL_FLOAT: + case UCL_TIME: + ucl_emitter_print_key (print_key, ctx, obj, compact); + func->ucl_emitter_append_double (ucl_object_todouble (obj), func->ud); + ucl_emitter_finish_object (ctx, obj, compact, !print_key); + break; + case UCL_BOOLEAN: + ucl_emitter_print_key (print_key, ctx, obj, compact); + flag = ucl_object_toboolean (obj); + if (flag) { + func->ucl_emitter_append_len ("true", 4, func->ud); + } + else { + func->ucl_emitter_append_len ("false", 5, func->ud); + } + ucl_emitter_finish_object (ctx, obj, compact, !print_key); + break; + case UCL_STRING: + ucl_emitter_print_key (print_key, ctx, obj, compact); + if (ctx->id == UCL_EMIT_CONFIG && ucl_maybe_long_string (obj)) { + ucl_elt_string_write_multiline (obj->value.sv, obj->len, ctx); + } + else { + ucl_elt_string_write_json (obj->value.sv, obj->len, ctx); + } + ucl_emitter_finish_object (ctx, obj, compact, !print_key); + break; + case UCL_NULL: + ucl_emitter_print_key (print_key, ctx, obj, compact); + func->ucl_emitter_append_len ("null", 4, func->ud); + ucl_emitter_finish_object (ctx, obj, compact, !print_key); + break; + case UCL_OBJECT: + ucl_emitter_common_start_object (ctx, obj, print_key, compact); + ucl_emitter_common_end_object (ctx, obj, compact); + break; + case UCL_ARRAY: + ucl_emitter_common_start_array (ctx, obj, print_key, compact); + ucl_emitter_common_end_array (ctx, obj, compact); + break; + case UCL_USERDATA: + ud = (struct ucl_object_userdata *)obj; + ucl_emitter_print_key (print_key, ctx, obj, compact); + if (ud->emitter) { + ud_out = ud->emitter (obj->value.ud); + if (ud_out == NULL) { + ud_out = "null"; + } + } + ucl_elt_string_write_json (ud_out, strlen (ud_out), ctx); + ucl_emitter_finish_object (ctx, obj, compact, !print_key); + break; + } +} + +/* + * Specific standard implementations of the emitter functions + */ +#define UCL_EMIT_TYPE_IMPL(type, compact) \ + static void ucl_emit_ ## type ## _elt (struct ucl_emitter_context *ctx, \ + const ucl_object_t *obj, bool first, bool print_key) { \ + ucl_emitter_common_elt (ctx, obj, first, print_key, (compact)); \ + } \ + static void ucl_emit_ ## type ## _start_obj (struct ucl_emitter_context *ctx, \ + const ucl_object_t *obj, bool print_key) { \ + ucl_emitter_common_start_object (ctx, obj, print_key, (compact)); \ + } \ + static void ucl_emit_ ## type## _start_array (struct ucl_emitter_context *ctx, \ + const ucl_object_t *obj, bool print_key) { \ + ucl_emitter_common_start_array (ctx, obj, print_key, (compact)); \ + } \ + static void ucl_emit_ ##type## _end_object (struct ucl_emitter_context *ctx, \ + const ucl_object_t *obj) { \ + ucl_emitter_common_end_object (ctx, obj, (compact)); \ + } \ + static void ucl_emit_ ##type## _end_array (struct ucl_emitter_context *ctx, \ + const ucl_object_t *obj) { \ + ucl_emitter_common_end_array (ctx, obj, (compact)); \ + } + +UCL_EMIT_TYPE_IMPL(json, false) +UCL_EMIT_TYPE_IMPL(json_compact, true) +UCL_EMIT_TYPE_IMPL(config, false) +UCL_EMIT_TYPE_IMPL(yaml, false) + +unsigned char * +ucl_object_emit (const ucl_object_t *obj, enum ucl_emitter emit_type) +{ + unsigned char *res = NULL; + struct ucl_emitter_functions *func; + if (obj == NULL) { + return NULL; + } + + func = ucl_object_emit_memory_funcs ((void **)&res); + + if (func != NULL) { + ucl_object_emit_full (obj, emit_type, func); + ucl_object_emit_funcs_free (func); + } + + return res; +} + +bool +ucl_object_emit_full (const ucl_object_t *obj, enum ucl_emitter emit_type, + struct ucl_emitter_functions *emitter) +{ + const struct ucl_emitter_context *ctx; + struct ucl_emitter_context my_ctx; + bool res = false; + + ctx = ucl_emit_get_standard_context (emit_type); + if (ctx != NULL) { + memcpy (&my_ctx, ctx, sizeof (my_ctx)); + my_ctx.func = emitter; + my_ctx.indent = 0; + my_ctx.top = obj; + + my_ctx.ops->ucl_emitter_write_elt (&my_ctx, obj, true, false); + res = true; + } + + return res; +} diff --git a/contrib/libucl/src/ucl_emitter_streamline.c b/contrib/libucl/src/ucl_emitter_streamline.c new file mode 100644 index 0000000..ff27c88 --- /dev/null +++ b/contrib/libucl/src/ucl_emitter_streamline.c @@ -0,0 +1,165 @@ +/* Copyright (c) 2014, 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 ''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_internal.h" +#include "ucl_chartable.h" + +struct ucl_emitter_streamline_stack { + bool is_array; + bool empty; + const ucl_object_t *obj; + struct ucl_emitter_streamline_stack *next; +}; + +struct ucl_emitter_context_streamline { + /* Inherited from the main context */ + const char *name; + int id; + const struct ucl_emitter_functions *func; + const struct ucl_emitter_operations *ops; + unsigned int ident; + const ucl_object_t *top; + + /* Streamline specific fields */ + struct ucl_emitter_streamline_stack *containers; +}; + +#define TO_STREAMLINE(ctx) (struct ucl_emitter_context_streamline *)(ctx) + +struct ucl_emitter_context* +ucl_object_emit_streamline_new (const ucl_object_t *obj, + enum ucl_emitter emit_type, + struct ucl_emitter_functions *emitter) +{ + const struct ucl_emitter_context *ctx; + struct ucl_emitter_context_streamline *sctx; + + ctx = ucl_emit_get_standard_context (emit_type); + if (ctx == NULL) { + return NULL; + } + + sctx = calloc (1, sizeof (*sctx)); + if (sctx == NULL) { + return NULL; + } + + memcpy (sctx, ctx, sizeof (*ctx)); + sctx->func = emitter; + sctx->top = obj; + + ucl_object_emit_streamline_start_container ((struct ucl_emitter_context *)sctx, + obj); + + return (struct ucl_emitter_context *)sctx; +} + +void +ucl_object_emit_streamline_start_container (struct ucl_emitter_context *ctx, + const ucl_object_t *obj) +{ + struct ucl_emitter_context_streamline *sctx = TO_STREAMLINE(ctx); + struct ucl_emitter_streamline_stack *st, *top; + bool print_key = false; + + /* Check top object presence */ + if (sctx->top == NULL) { + sctx->top = obj; + } + + top = sctx->containers; + st = malloc (sizeof (*st)); + if (st != NULL) { + if (top != NULL && !top->is_array) { + print_key = true; + } + st->empty = true; + st->obj = obj; + if (obj != NULL && obj->type == UCL_ARRAY) { + st->is_array = true; + sctx->ops->ucl_emitter_start_array (ctx, obj, print_key); + } + else { + st->is_array = false; + sctx->ops->ucl_emitter_start_object (ctx, obj, print_key); + } + LL_PREPEND (sctx->containers, st); + } +} + +void +ucl_object_emit_streamline_add_object ( + struct ucl_emitter_context *ctx, const ucl_object_t *obj) +{ + struct ucl_emitter_context_streamline *sctx = TO_STREAMLINE(ctx); + bool is_array = false, is_first = false; + + if (sctx->containers != NULL) { + if (sctx->containers->is_array) { + is_array = true; + } + if (sctx->containers->empty) { + is_first = true; + sctx->containers->empty = false; + } + } + + sctx->ops->ucl_emitter_write_elt (ctx, obj, is_first, !is_array); +} + +void +ucl_object_emit_streamline_end_container (struct ucl_emitter_context *ctx) +{ + struct ucl_emitter_context_streamline *sctx = TO_STREAMLINE(ctx); + struct ucl_emitter_streamline_stack *st; + + if (sctx->containers != NULL) { + st = sctx->containers; + + if (st->is_array) { + sctx->ops->ucl_emitter_end_array (ctx, st->obj); + } + else { + sctx->ops->ucl_emitter_end_object (ctx, st->obj); + } + sctx->containers = st->next; + free (st); + } +} + +void +ucl_object_emit_streamline_finish (struct ucl_emitter_context *ctx) +{ + struct ucl_emitter_context_streamline *sctx = TO_STREAMLINE(ctx); + + while (sctx->containers != NULL) { + ucl_object_emit_streamline_end_container (ctx); + } + + free (sctx); +} diff --git a/contrib/libucl/src/ucl_emitter_utils.c b/contrib/libucl/src/ucl_emitter_utils.c new file mode 100644 index 0000000..da41209 --- /dev/null +++ b/contrib/libucl/src/ucl_emitter_utils.c @@ -0,0 +1,486 @@ +/* Copyright (c) 2014, 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 ''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_internal.h" +#include "ucl_chartable.h" + +#ifdef HAVE_FLOAT_H +#include <float.h> +#endif +#ifdef HAVE_MATH_H +#include <math.h> +#endif + +extern const struct ucl_emitter_operations ucl_standartd_emitter_ops[]; + +static const struct ucl_emitter_context ucl_standard_emitters[] = { + [UCL_EMIT_JSON] = { + .name = "json", + .id = UCL_EMIT_JSON, + .func = NULL, + .ops = &ucl_standartd_emitter_ops[UCL_EMIT_JSON] + }, + [UCL_EMIT_JSON_COMPACT] = { + .name = "json_compact", + .id = UCL_EMIT_JSON_COMPACT, + .func = NULL, + .ops = &ucl_standartd_emitter_ops[UCL_EMIT_JSON_COMPACT] + }, + [UCL_EMIT_CONFIG] = { + .name = "config", + .id = UCL_EMIT_CONFIG, + .func = NULL, + .ops = &ucl_standartd_emitter_ops[UCL_EMIT_CONFIG] + }, + [UCL_EMIT_YAML] = { + .name = "yaml", + .id = UCL_EMIT_YAML, + .func = NULL, + .ops = &ucl_standartd_emitter_ops[UCL_EMIT_YAML] + } +}; + +/** + * Get standard emitter context for a specified emit_type + * @param emit_type type of emitter + * @return context or NULL if input is invalid + */ +const struct ucl_emitter_context * +ucl_emit_get_standard_context (enum ucl_emitter emit_type) +{ + if (emit_type >= UCL_EMIT_JSON && emit_type <= UCL_EMIT_YAML) { + return &ucl_standard_emitters[emit_type]; + } + + return NULL; +} + +/** + * Serialise string + * @param str string to emit + * @param buf target buffer + */ +void +ucl_elt_string_write_json (const char *str, size_t size, + struct ucl_emitter_context *ctx) +{ + const char *p = str, *c = str; + size_t len = 0; + const struct ucl_emitter_functions *func = ctx->func; + + func->ucl_emitter_append_character ('"', 1, func->ud); + + while (size) { + if (ucl_test_character (*p, UCL_CHARACTER_JSON_UNSAFE)) { + if (len > 0) { + func->ucl_emitter_append_len (c, len, func->ud); + } + switch (*p) { + case '\n': + func->ucl_emitter_append_len ("\\n", 2, func->ud); + break; + case '\r': + func->ucl_emitter_append_len ("\\r", 2, func->ud); + break; + case '\b': + func->ucl_emitter_append_len ("\\b", 2, func->ud); + break; + case '\t': + func->ucl_emitter_append_len ("\\t", 2, func->ud); + break; + case '\f': + func->ucl_emitter_append_len ("\\f", 2, func->ud); + break; + case '\\': + func->ucl_emitter_append_len ("\\\\", 2, func->ud); + break; + case '"': + func->ucl_emitter_append_len ("\\\"", 2, func->ud); + break; + } + len = 0; + c = ++p; + } + else { + p ++; + len ++; + } + size --; + } + if (len > 0) { + func->ucl_emitter_append_len (c, len, func->ud); + } + func->ucl_emitter_append_character ('"', 1, func->ud); +} + +void +ucl_elt_string_write_multiline (const char *str, size_t size, + struct ucl_emitter_context *ctx) +{ + const struct ucl_emitter_functions *func = ctx->func; + + func->ucl_emitter_append_len ("<<EOD\n", sizeof ("<<EOD\n") - 1, func->ud); + func->ucl_emitter_append_len (str, size, func->ud); + func->ucl_emitter_append_len ("\nEOD", sizeof ("\nEOD") - 1, func->ud); +} + +/* + * Generic utstring output + */ +static int +ucl_utstring_append_character (unsigned char c, size_t len, void *ud) +{ + UT_string *buf = ud; + + if (len == 1) { + utstring_append_c (buf, c); + } + else { + utstring_reserve (buf, len + 1); + memset (&buf->d[buf->i], c, len); + buf->i += len; + buf->d[buf->i] = '\0'; + } + + return 0; +} + +static int +ucl_utstring_append_len (const unsigned char *str, size_t len, void *ud) +{ + UT_string *buf = ud; + + utstring_append_len (buf, str, len); + + return 0; +} + +static int +ucl_utstring_append_int (int64_t val, void *ud) +{ + UT_string *buf = ud; + + utstring_printf (buf, "%jd", (intmax_t)val); + return 0; +} + +static int +ucl_utstring_append_double (double val, void *ud) +{ + UT_string *buf = ud; + const double delta = 0.0000001; + + if (val == (double)(int)val) { + utstring_printf (buf, "%.1lf", val); + } + else if (fabs (val - (double)(int)val) < delta) { + /* Write at maximum precision */ + utstring_printf (buf, "%.*lg", DBL_DIG, val); + } + else { + utstring_printf (buf, "%lf", val); + } + + return 0; +} + +/* + * Generic file output + */ +static int +ucl_file_append_character (unsigned char c, size_t len, void *ud) +{ + FILE *fp = ud; + + while (len --) { + fputc (c, fp); + } + + return 0; +} + +static int +ucl_file_append_len (const unsigned char *str, size_t len, void *ud) +{ + FILE *fp = ud; + + fwrite (str, len, 1, fp); + + return 0; +} + +static int +ucl_file_append_int (int64_t val, void *ud) +{ + FILE *fp = ud; + + fprintf (fp, "%jd", (intmax_t)val); + + return 0; +} + +static int +ucl_file_append_double (double val, void *ud) +{ + FILE *fp = ud; + const double delta = 0.0000001; + + if (val == (double)(int)val) { + fprintf (fp, "%.1lf", val); + } + else if (fabs (val - (double)(int)val) < delta) { + /* Write at maximum precision */ + fprintf (fp, "%.*lg", DBL_DIG, val); + } + else { + fprintf (fp, "%lf", val); + } + + return 0; +} + +/* + * Generic file descriptor writing functions + */ +static int +ucl_fd_append_character (unsigned char c, size_t len, void *ud) +{ + int fd = *(int *)ud; + unsigned char *buf; + + if (len == 1) { + return write (fd, &c, 1); + } + else { + buf = malloc (len); + if (buf == NULL) { + /* Fallback */ + while (len --) { + if (write (fd, &c, 1) == -1) { + return -1; + } + } + } + else { + memset (buf, c, len); + if (write (fd, buf, len) == -1) { + return -1; + } + free (buf); + } + } + + return 0; +} + +static int +ucl_fd_append_len (const unsigned char *str, size_t len, void *ud) +{ + int fd = *(int *)ud; + + return write (fd, str, len); +} + +static int +ucl_fd_append_int (int64_t val, void *ud) +{ + int fd = *(int *)ud; + char intbuf[64]; + + snprintf (intbuf, sizeof (intbuf), "%jd", (intmax_t)val); + return write (fd, intbuf, strlen (intbuf)); +} + +static int +ucl_fd_append_double (double val, void *ud) +{ + int fd = *(int *)ud; + const double delta = 0.0000001; + char nbuf[64]; + + if (val == (double)(int)val) { + snprintf (nbuf, sizeof (nbuf), "%.1lf", val); + } + else if (fabs (val - (double)(int)val) < delta) { + /* Write at maximum precision */ + snprintf (nbuf, sizeof (nbuf), "%.*lg", DBL_DIG, val); + } + else { + snprintf (nbuf, sizeof (nbuf), "%lf", val); + } + + return write (fd, nbuf, strlen (nbuf)); +} + +struct ucl_emitter_functions* +ucl_object_emit_memory_funcs (void **pmem) +{ + struct ucl_emitter_functions *f; + UT_string *s; + + f = calloc (1, sizeof (*f)); + + if (f != NULL) { + f->ucl_emitter_append_character = ucl_utstring_append_character; + f->ucl_emitter_append_double = ucl_utstring_append_double; + f->ucl_emitter_append_int = ucl_utstring_append_int; + f->ucl_emitter_append_len = ucl_utstring_append_len; + f->ucl_emitter_free_func = free; + utstring_new (s); + f->ud = s; + *pmem = s->d; + s->pd = pmem; + } + + return f; +} + +struct ucl_emitter_functions* +ucl_object_emit_file_funcs (FILE *fp) +{ + struct ucl_emitter_functions *f; + + f = calloc (1, sizeof (*f)); + + if (f != NULL) { + f->ucl_emitter_append_character = ucl_file_append_character; + f->ucl_emitter_append_double = ucl_file_append_double; + f->ucl_emitter_append_int = ucl_file_append_int; + f->ucl_emitter_append_len = ucl_file_append_len; + f->ucl_emitter_free_func = NULL; + f->ud = fp; + } + + return f; +} + +struct ucl_emitter_functions* +ucl_object_emit_fd_funcs (int fd) +{ + struct ucl_emitter_functions *f; + int *ip; + + f = calloc (1, sizeof (*f)); + + if (f != NULL) { + ip = malloc (sizeof (fd)); + if (ip == NULL) { + free (f); + return NULL; + } + + memcpy (ip, &fd, sizeof (fd)); + f->ucl_emitter_append_character = ucl_fd_append_character; + f->ucl_emitter_append_double = ucl_fd_append_double; + f->ucl_emitter_append_int = ucl_fd_append_int; + f->ucl_emitter_append_len = ucl_fd_append_len; + f->ucl_emitter_free_func = free; + f->ud = ip; + } + + return f; +} + +void +ucl_object_emit_funcs_free (struct ucl_emitter_functions *f) +{ + if (f != NULL) { + if (f->ucl_emitter_free_func != NULL) { + f->ucl_emitter_free_func (f->ud); + } + free (f); + } +} + + +unsigned char * +ucl_object_emit_single_json (const ucl_object_t *obj) +{ + UT_string *buf = NULL; + unsigned char *res = NULL; + + if (obj == NULL) { + return NULL; + } + + utstring_new (buf); + + if (buf != NULL) { + switch (obj->type) { + case UCL_OBJECT: + ucl_utstring_append_len ("object", 6, buf); + break; + case UCL_ARRAY: + ucl_utstring_append_len ("array", 5, buf); + break; + case UCL_INT: + ucl_utstring_append_int (obj->value.iv, buf); + break; + case UCL_FLOAT: + case UCL_TIME: + ucl_utstring_append_double (obj->value.dv, buf); + break; + case UCL_NULL: + ucl_utstring_append_len ("null", 4, buf); + break; + case UCL_BOOLEAN: + if (obj->value.iv) { + ucl_utstring_append_len ("true", 4, buf); + } + else { + ucl_utstring_append_len ("false", 5, buf); + } + break; + case UCL_STRING: + ucl_utstring_append_len (obj->value.sv, obj->len, buf); + break; + case UCL_USERDATA: + ucl_utstring_append_len ("userdata", 8, buf); + break; + } + res = utstring_body (buf); + free (buf); + } + + return res; +} + +#define LONG_STRING_LIMIT 80 + +bool +ucl_maybe_long_string (const ucl_object_t *obj) +{ + if (obj->len > LONG_STRING_LIMIT || (obj->flags & UCL_OBJECT_MULTILINE)) { + /* String is long enough, so search for newline characters in it */ + if (memchr (obj->value.sv, '\n', obj->len) != NULL) { + return true; + } + } + + return false; +} diff --git a/contrib/libucl/src/ucl_hash.c b/contrib/libucl/src/ucl_hash.c new file mode 100644 index 0000000..ea55491 --- /dev/null +++ b/contrib/libucl/src/ucl_hash.c @@ -0,0 +1,141 @@ +/* Copyright (c) 2013, 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 ''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. + */ + +#include "ucl_internal.h" +#include "ucl_hash.h" +#include "utlist.h" + +ucl_hash_t* +ucl_hash_create (void) +{ + ucl_hash_t *new; + + new = UCL_ALLOC (sizeof (ucl_hash_t)); + if (new != NULL) { + new->buckets = NULL; + } + return new; +} + +void ucl_hash_destroy (ucl_hash_t* hashlin, ucl_hash_free_func *func) +{ + ucl_hash_node_t *elt, *tmp; + const ucl_object_t *cur, *otmp; + + HASH_ITER (hh, hashlin->buckets, elt, tmp) { + HASH_DELETE (hh, hashlin->buckets, elt); + if (func) { + DL_FOREACH_SAFE (elt->data, cur, otmp) { + /* Need to deconst here */ + func (__DECONST (ucl_object_t *, cur)); + } + } + UCL_FREE (sizeof (ucl_hash_node_t), elt); + } + UCL_FREE (sizeof (ucl_hash_t), hashlin); +} + +void +ucl_hash_insert (ucl_hash_t* hashlin, const ucl_object_t *obj, + const char *key, unsigned keylen) +{ + ucl_hash_node_t *node; + + node = UCL_ALLOC (sizeof (ucl_hash_node_t)); + node->data = obj; + HASH_ADD_KEYPTR (hh, hashlin->buckets, key, keylen, node); +} + +void ucl_hash_replace (ucl_hash_t* hashlin, const ucl_object_t *old, + const ucl_object_t *new) +{ + ucl_hash_node_t *node; + + HASH_FIND (hh, hashlin->buckets, old->key, old->keylen, node); + if (node != NULL) { + /* Direct replacement */ + node->data = new; + node->hh.key = new->key; + node->hh.keylen = new->keylen; + } +} + +const void* +ucl_hash_iterate (ucl_hash_t *hashlin, ucl_hash_iter_t *iter) +{ + ucl_hash_node_t *elt = *iter; + + if (elt == NULL) { + if (hashlin == NULL || hashlin->buckets == NULL) { + return NULL; + } + elt = hashlin->buckets; + if (elt == NULL) { + return NULL; + } + } + else if (elt == hashlin->buckets) { + return NULL; + } + + *iter = elt->hh.next ? elt->hh.next : hashlin->buckets; + return elt->data; +} + +bool +ucl_hash_iter_has_next (ucl_hash_iter_t iter) +{ + ucl_hash_node_t *elt = iter; + + return (elt == NULL || elt->hh.prev != NULL); +} + + +const ucl_object_t* +ucl_hash_search (ucl_hash_t* hashlin, const char *key, unsigned keylen) +{ + ucl_hash_node_t *found; + + if (hashlin == NULL) { + return NULL; + } + HASH_FIND (hh, hashlin->buckets, key, keylen, found); + + if (found) { + return found->data; + } + return NULL; +} + +void +ucl_hash_delete (ucl_hash_t* hashlin, const ucl_object_t *obj) +{ + ucl_hash_node_t *found; + + HASH_FIND (hh, hashlin->buckets, obj->key, obj->keylen, found); + + if (found) { + HASH_DELETE (hh, hashlin->buckets, found); + UCL_FREE (sizeof (ucl_hash_node_t), found); + } +} diff --git a/contrib/libucl/src/ucl_hash.h b/contrib/libucl/src/ucl_hash.h new file mode 100644 index 0000000..ddbfdba --- /dev/null +++ b/contrib/libucl/src/ucl_hash.h @@ -0,0 +1,99 @@ +/* Copyright (c) 2013, 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 ''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. + */ + +#ifndef __UCL_HASH_H +#define __UCL_HASH_H + +#include "ucl.h" +#include "uthash.h" + +/******************************************************************************/ + +typedef struct ucl_hash_node_s +{ + const ucl_object_t *data; + UT_hash_handle hh; +} ucl_hash_node_t; + +typedef int ucl_hash_cmp_func (const void* void_a, const void* void_b); +typedef void ucl_hash_free_func (void *ptr); +typedef void* ucl_hash_iter_t; + + +/** + * Linear chained hashtable. + */ +typedef struct ucl_hash_struct +{ + ucl_hash_node_t *buckets; /**< array of hash buckets. One list for each hash modulus. */ +} ucl_hash_t; + + +/** + * Initializes the hashtable. + */ +ucl_hash_t* ucl_hash_create (void); + +/** + * Deinitializes the hashtable. + */ +void ucl_hash_destroy (ucl_hash_t* hashlin, ucl_hash_free_func *func); + +/** + * Inserts an element in the the hashtable. + */ +void ucl_hash_insert (ucl_hash_t* hashlin, const ucl_object_t *obj, const char *key, + unsigned keylen); + +/** + * Replace element in the hash + */ +void ucl_hash_replace (ucl_hash_t* hashlin, const ucl_object_t *old, + const ucl_object_t *new); + +/** + * Delete an element from the the hashtable. + */ +void ucl_hash_delete (ucl_hash_t* hashlin, const ucl_object_t *obj); + +/** + * Searches an element in the hashtable. + */ +const ucl_object_t* ucl_hash_search (ucl_hash_t* hashlin, const char *key, + unsigned keylen); + + +/** + * Iterate over hash table + * @param hashlin hash + * @param iter iterator (must be NULL on first iteration) + * @return the next object + */ +const void* ucl_hash_iterate (ucl_hash_t *hashlin, ucl_hash_iter_t *iter); + +/** + * Check whether an iterator has next element + */ +bool ucl_hash_iter_has_next (ucl_hash_iter_t iter); + +#endif diff --git a/contrib/libucl/src/ucl_internal.h b/contrib/libucl/src/ucl_internal.h new file mode 100644 index 0000000..2f75872 --- /dev/null +++ b/contrib/libucl/src/ucl_internal.h @@ -0,0 +1,396 @@ +/* Copyright (c) 2013, 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 ''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. + */ + +#ifndef UCL_INTERNAL_H_ +#define UCL_INTERNAL_H_ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#else +/* Help embedded builds */ +#define HAVE_SYS_TYPES_H +#define HAVE_SYS_MMAN_H +#define HAVE_SYS_STAT_H +#define HAVE_SYS_PARAM_H +#define HAVE_LIMITS_H +#define HAVE_FCNTL_H +#define HAVE_ERRNO_H +#define HAVE_UNISTD_H +#define HAVE_CTYPE_H +#define HAVE_STDIO_H +#define HAVE_STRING_H +#define HAVE_FLOAT_H +#define HAVE_LIBGEN_H +#define HAVE_MATH_H +#define HAVE_STDBOOL_H +#define HAVE_STDINT_H +#define HAVE_STDARG_H +#ifndef _WIN32 +# define HAVE_REGEX_H +#endif +#endif + +#ifdef HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif + +#ifdef HAVE_SYS_MMAN_H +# ifndef _WIN32 +# include <sys/mman.h> +# endif +#endif +#ifdef HAVE_SYS_STAT_H +#include <sys/stat.h> +#endif +#ifdef HAVE_SYS_PARAM_H +#include <sys/param.h> +#endif + +#ifdef HAVE_LIMITS_H +#include <limits.h> +#endif +#ifdef HAVE_FCNTL_H +#include <fcntl.h> +#endif +#ifdef HAVE_ERRNO_H +#include <errno.h> +#endif +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#ifdef HAVE_CTYPE_H +#include <ctype.h> +#endif +#ifdef HAVE_STDIO_H +#include <stdio.h> +#endif +#ifdef HAVE_STRING_H +#include <string.h> +#endif + +#include "utlist.h" +#include "utstring.h" +#include "uthash.h" +#include "ucl.h" +#include "ucl_hash.h" +#include "xxhash.h" + +#ifdef HAVE_OPENSSL +#include <openssl/evp.h> +#endif + +#ifndef __DECONST +#define __DECONST(type, var) ((type)(uintptr_t)(const void *)(var)) +#endif + +/** + * @file rcl_internal.h + * Internal structures and functions of UCL library + */ + +#define UCL_MAX_RECURSION 16 +#define UCL_TRASH_KEY 0 +#define UCL_TRASH_VALUE 1 + +enum ucl_parser_state { + UCL_STATE_INIT = 0, + UCL_STATE_OBJECT, + UCL_STATE_ARRAY, + UCL_STATE_KEY, + UCL_STATE_VALUE, + UCL_STATE_AFTER_VALUE, + UCL_STATE_ARRAY_VALUE, + UCL_STATE_SCOMMENT, + UCL_STATE_MCOMMENT, + UCL_STATE_MACRO_NAME, + UCL_STATE_MACRO, + UCL_STATE_ERROR +}; + +enum ucl_character_type { + UCL_CHARACTER_DENIED = 0, + UCL_CHARACTER_KEY = 1, + UCL_CHARACTER_KEY_START = 1 << 1, + UCL_CHARACTER_WHITESPACE = 1 << 2, + UCL_CHARACTER_WHITESPACE_UNSAFE = 1 << 3, + UCL_CHARACTER_VALUE_END = 1 << 4, + UCL_CHARACTER_VALUE_STR = 1 << 5, + UCL_CHARACTER_VALUE_DIGIT = 1 << 6, + UCL_CHARACTER_VALUE_DIGIT_START = 1 << 7, + UCL_CHARACTER_ESCAPE = 1 << 8, + UCL_CHARACTER_KEY_SEP = 1 << 9, + UCL_CHARACTER_JSON_UNSAFE = 1 << 10, + UCL_CHARACTER_UCL_UNSAFE = 1 << 11 +}; + +struct ucl_macro { + char *name; + ucl_macro_handler handler; + void* ud; + UT_hash_handle hh; +}; + +struct ucl_stack { + ucl_object_t *obj; + struct ucl_stack *next; + int level; +}; + +struct ucl_chunk { + const unsigned char *begin; + const unsigned char *end; + const unsigned char *pos; + size_t remain; + unsigned int line; + unsigned int column; + unsigned priority; + struct ucl_chunk *next; +}; + +#ifdef HAVE_OPENSSL +struct ucl_pubkey { + EVP_PKEY *key; + struct ucl_pubkey *next; +}; +#else +struct ucl_pubkey { + struct ucl_pubkey *next; +}; +#endif + +struct ucl_variable { + char *var; + char *value; + size_t var_len; + size_t value_len; + struct ucl_variable *prev, *next; +}; + +struct ucl_parser { + enum ucl_parser_state state; + enum ucl_parser_state prev_state; + unsigned int recursion; + int flags; + ucl_object_t *top_obj; + ucl_object_t *cur_obj; + char *cur_file; + struct ucl_macro *macroes; + struct ucl_stack *stack; + struct ucl_chunk *chunks; + struct ucl_pubkey *keys; + struct ucl_variable *variables; + ucl_variable_handler var_handler; + void *var_data; + UT_string *err; +}; + +struct ucl_object_userdata { + ucl_object_t obj; + ucl_userdata_dtor dtor; + ucl_userdata_emitter emitter; +}; + +/** + * Unescape json string inplace + * @param str + */ +size_t ucl_unescape_json_string (char *str, size_t len); + +/** + * Handle include macro + * @param data include data + * @param len length of data + * @param ud user data + * @param err error ptr + * @return + */ +bool ucl_include_handler (const unsigned char *data, size_t len, + const ucl_object_t *args, void* ud); + +bool ucl_try_include_handler (const unsigned char *data, size_t len, + const ucl_object_t *args, void* ud); + +/** + * Handle includes macro + * @param data include data + * @param len length of data + * @param ud user data + * @param err error ptr + * @return + */ +bool ucl_includes_handler (const unsigned char *data, size_t len, + const ucl_object_t *args, void* ud); + +size_t ucl_strlcpy (char *dst, const char *src, size_t siz); +size_t ucl_strlcpy_unsafe (char *dst, const char *src, size_t siz); +size_t ucl_strlcpy_tolower (char *dst, const char *src, size_t siz); + + +#ifdef __GNUC__ +static inline void +ucl_create_err (UT_string **err, const char *fmt, ...) +__attribute__ (( format( printf, 2, 3) )); +#endif + +static inline void +ucl_create_err (UT_string **err, const char *fmt, ...) + +{ + if (*err == NULL) { + utstring_new (*err); + va_list ap; + va_start (ap, fmt); + utstring_printf_va (*err, fmt, ap); + va_end (ap); + } +} + +/** + * Check whether a given string contains a boolean value + * @param obj object to set + * @param start start of a string + * @param len length of a string + * @return true if a string is a boolean value + */ +static inline bool +ucl_maybe_parse_boolean (ucl_object_t *obj, const unsigned char *start, size_t len) +{ + const char *p = (const char *)start; + bool ret = false, val = false; + + if (len == 5) { + if ((p[0] == 'f' || p[0] == 'F') && strncasecmp (p, "false", 5) == 0) { + ret = true; + val = false; + } + } + else if (len == 4) { + if ((p[0] == 't' || p[0] == 'T') && strncasecmp (p, "true", 4) == 0) { + ret = true; + val = true; + } + } + else if (len == 3) { + if ((p[0] == 'y' || p[0] == 'Y') && strncasecmp (p, "yes", 3) == 0) { + ret = true; + val = true; + } + else if ((p[0] == 'o' || p[0] == 'O') && strncasecmp (p, "off", 3) == 0) { + ret = true; + val = false; + } + } + else if (len == 2) { + if ((p[0] == 'n' || p[0] == 'N') && strncasecmp (p, "no", 2) == 0) { + ret = true; + val = false; + } + else if ((p[0] == 'o' || p[0] == 'O') && strncasecmp (p, "on", 2) == 0) { + ret = true; + val = true; + } + } + + if (ret) { + obj->type = UCL_BOOLEAN; + obj->value.iv = val; + } + + return ret; +} + +/** + * Check numeric string + * @param obj object to set if a string is numeric + * @param start start of string + * @param end end of string + * @param pos position where parsing has stopped + * @param allow_double allow parsing of floating point values + * @return 0 if string is numeric and error code (EINVAL or ERANGE) in case of conversion error + */ +int ucl_maybe_parse_number (ucl_object_t *obj, + const char *start, const char *end, const char **pos, + bool allow_double, bool number_bytes, bool allow_time); + + +static inline const ucl_object_t * +ucl_hash_search_obj (ucl_hash_t* hashlin, ucl_object_t *obj) +{ + return (const ucl_object_t *)ucl_hash_search (hashlin, obj->key, obj->keylen); +} + +static inline ucl_hash_t * +ucl_hash_insert_object (ucl_hash_t *hashlin, const ucl_object_t *obj) UCL_WARN_UNUSED_RESULT; + +static inline ucl_hash_t * +ucl_hash_insert_object (ucl_hash_t *hashlin, const ucl_object_t *obj) +{ + if (hashlin == NULL) { + hashlin = ucl_hash_create (); + } + ucl_hash_insert (hashlin, obj, obj->key, obj->keylen); + + return hashlin; +} + +/** + * Get standard emitter context for a specified emit_type + * @param emit_type type of emitter + * @return context or NULL if input is invalid + */ +const struct ucl_emitter_context * +ucl_emit_get_standard_context (enum ucl_emitter emit_type); + +/** + * Serialize string as JSON string + * @param str string to emit + * @param buf target buffer + */ +void ucl_elt_string_write_json (const char *str, size_t size, + struct ucl_emitter_context *ctx); + +/** + * Write multiline string using `EOD` as string terminator + * @param str + * @param size + * @param ctx + */ +void ucl_elt_string_write_multiline (const char *str, size_t size, + struct ucl_emitter_context *ctx); + +/** + * Emit a single object to string + * @param obj + * @return + */ +unsigned char * ucl_object_emit_single_json (const ucl_object_t *obj); + +/** + * Check whether a specified string is long and should be likely printed in + * multiline mode + * @param obj + * @return + */ +bool ucl_maybe_long_string (const ucl_object_t *obj); + +#endif /* UCL_INTERNAL_H_ */ diff --git a/contrib/libucl/src/ucl_parser.c b/contrib/libucl/src/ucl_parser.c new file mode 100644 index 0000000..0d118d8 --- /dev/null +++ b/contrib/libucl/src/ucl_parser.c @@ -0,0 +1,2219 @@ +/* Copyright (c) 2013, 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 ''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. + */ + +#include "ucl.h" +#include "ucl_internal.h" +#include "ucl_chartable.h" + +/** + * @file ucl_parser.c + * The implementation of ucl parser + */ + +struct ucl_parser_saved_state { + unsigned int line; + unsigned int column; + size_t remain; + const unsigned char *pos; +}; + +/** + * Move up to len characters + * @param parser + * @param begin + * @param len + * @return new position in chunk + */ +#define ucl_chunk_skipc(chunk, p) do{ \ + if (*(p) == '\n') { \ + (chunk)->line ++; \ + (chunk)->column = 0; \ + } \ + else (chunk)->column ++; \ + (p++); \ + (chunk)->pos ++; \ + (chunk)->remain --; \ + } while (0) + +static inline void +ucl_set_err (struct ucl_parser *parser, int code, const char *str, UT_string **err) +{ + const char *fmt_string, *filename; + struct ucl_chunk *chunk = parser->chunks; + + if (parser->cur_file) { + filename = parser->cur_file; + } + else { + filename = "<unknown>"; + } + if (chunk->pos < chunk->end) { + if (isgraph (*chunk->pos)) { + fmt_string = "error while parsing %s: " + "line: %d, column: %d - '%s', character: '%c'"; + } + else { + fmt_string = "error while parsing %s: " + "line: %d, column: %d - '%s', character: '0x%02x'"; + } + ucl_create_err (err, fmt_string, + filename, chunk->line, chunk->column, + str, *chunk->pos); + } + else { + ucl_create_err (err, "error while parsing %s: at the end of chunk: %s", + filename, str); + } +} + +/** + * Skip all comments from the current pos resolving nested and multiline comments + * @param parser + * @return + */ +static bool +ucl_skip_comments (struct ucl_parser *parser) +{ + struct ucl_chunk *chunk = parser->chunks; + const unsigned char *p; + int comments_nested = 0; + bool quoted = false; + + p = chunk->pos; + +start: + if (chunk->remain > 0 && *p == '#') { + if (parser->state != UCL_STATE_SCOMMENT && + parser->state != UCL_STATE_MCOMMENT) { + while (p < chunk->end) { + if (*p == '\n') { + ucl_chunk_skipc (chunk, p); + goto start; + } + ucl_chunk_skipc (chunk, p); + } + } + } + else if (chunk->remain >= 2 && *p == '/') { + if (p[1] == '*') { + ucl_chunk_skipc (chunk, p); + comments_nested ++; + ucl_chunk_skipc (chunk, p); + + while (p < chunk->end) { + if (*p == '"' && *(p - 1) != '\\') { + quoted = !quoted; + } + + if (!quoted) { + if (*p == '*') { + ucl_chunk_skipc (chunk, p); + if (*p == '/') { + comments_nested --; + if (comments_nested == 0) { + ucl_chunk_skipc (chunk, p); + goto start; + } + } + ucl_chunk_skipc (chunk, p); + } + else if (p[0] == '/' && chunk->remain >= 2 && p[1] == '*') { + comments_nested ++; + ucl_chunk_skipc (chunk, p); + ucl_chunk_skipc (chunk, p); + continue; + } + } + ucl_chunk_skipc (chunk, p); + } + if (comments_nested != 0) { + ucl_set_err (parser, UCL_ENESTED, + "unfinished multiline comment", &parser->err); + return false; + } + } + } + + return true; +} + +/** + * Return multiplier for a character + * @param c multiplier character + * @param is_bytes if true use 1024 multiplier + * @return multiplier + */ +static inline unsigned long +ucl_lex_num_multiplier (const unsigned char c, bool is_bytes) { + const struct { + char c; + long mult_normal; + long mult_bytes; + } multipliers[] = { + {'m', 1000 * 1000, 1024 * 1024}, + {'k', 1000, 1024}, + {'g', 1000 * 1000 * 1000, 1024 * 1024 * 1024} + }; + int i; + + for (i = 0; i < 3; i ++) { + if (tolower (c) == multipliers[i].c) { + if (is_bytes) { + return multipliers[i].mult_bytes; + } + return multipliers[i].mult_normal; + } + } + + return 1; +} + + +/** + * Return multiplier for time scaling + * @param c + * @return + */ +static inline double +ucl_lex_time_multiplier (const unsigned char c) { + const struct { + char c; + double mult; + } multipliers[] = { + {'m', 60}, + {'h', 60 * 60}, + {'d', 60 * 60 * 24}, + {'w', 60 * 60 * 24 * 7}, + {'y', 60 * 60 * 24 * 7 * 365} + }; + int i; + + for (i = 0; i < 5; i ++) { + if (tolower (c) == multipliers[i].c) { + return multipliers[i].mult; + } + } + + return 1; +} + +/** + * Return true if a character is a end of an atom + * @param c + * @return + */ +static inline bool +ucl_lex_is_atom_end (const unsigned char c) +{ + return ucl_test_character (c, UCL_CHARACTER_VALUE_END); +} + +static inline bool +ucl_lex_is_comment (const unsigned char c1, const unsigned char c2) +{ + if (c1 == '/') { + if (c2 == '*') { + return true; + } + } + else if (c1 == '#') { + return true; + } + return false; +} + +/** + * Check variable found + * @param parser + * @param ptr + * @param remain + * @param out_len + * @param strict + * @param found + * @return + */ +static inline const char * +ucl_check_variable_safe (struct ucl_parser *parser, const char *ptr, size_t remain, + size_t *out_len, bool strict, bool *found) +{ + struct ucl_variable *var; + unsigned char *dst; + size_t dstlen; + bool need_free = false; + + LL_FOREACH (parser->variables, var) { + if (strict) { + if (remain == var->var_len) { + if (memcmp (ptr, var->var, var->var_len) == 0) { + *out_len += var->value_len; + *found = true; + return (ptr + var->var_len); + } + } + } + else { + if (remain >= var->var_len) { + if (memcmp (ptr, var->var, var->var_len) == 0) { + *out_len += var->value_len; + *found = true; + return (ptr + var->var_len); + } + } + } + } + + /* XXX: can only handle ${VAR} */ + if (!(*found) && parser->var_handler != NULL && strict) { + /* Call generic handler */ + if (parser->var_handler (ptr, remain, &dst, &dstlen, &need_free, + parser->var_data)) { + *found = true; + if (need_free) { + free (dst); + } + return (ptr + remain); + } + } + + return ptr; +} + +/** + * Check for a variable in a given string + * @param parser + * @param ptr + * @param remain + * @param out_len + * @param vars_found + * @return + */ +static const char * +ucl_check_variable (struct ucl_parser *parser, const char *ptr, + size_t remain, size_t *out_len, bool *vars_found) +{ + const char *p, *end, *ret = ptr; + bool found = false; + + if (*ptr == '{') { + /* We need to match the variable enclosed in braces */ + p = ptr + 1; + end = ptr + remain; + while (p < end) { + if (*p == '}') { + ret = ucl_check_variable_safe (parser, ptr + 1, p - ptr - 1, + out_len, true, &found); + if (found) { + /* {} must be excluded actually */ + ret ++; + if (!*vars_found) { + *vars_found = true; + } + } + else { + *out_len += 2; + } + break; + } + p ++; + } + } + else if (*ptr != '$') { + /* Not count escaped dollar sign */ + ret = ucl_check_variable_safe (parser, ptr, remain, out_len, false, &found); + if (found && !*vars_found) { + *vars_found = true; + } + if (!found) { + (*out_len) ++; + } + } + else { + ret ++; + (*out_len) ++; + } + + return ret; +} + +/** + * Expand a single variable + * @param parser + * @param ptr + * @param remain + * @param dest + * @return + */ +static const char * +ucl_expand_single_variable (struct ucl_parser *parser, const char *ptr, + size_t remain, unsigned char **dest) +{ + unsigned char *d = *dest, *dst; + const char *p = ptr + 1, *ret; + struct ucl_variable *var; + size_t dstlen; + bool need_free = false; + bool found = false; + bool strict = false; + + ret = ptr + 1; + remain --; + + if (*p == '$') { + *d++ = *p++; + *dest = d; + return p; + } + else if (*p == '{') { + p ++; + strict = true; + ret += 2; + remain -= 2; + } + + LL_FOREACH (parser->variables, var) { + if (remain >= var->var_len) { + if (memcmp (p, var->var, var->var_len) == 0) { + memcpy (d, var->value, var->value_len); + ret += var->var_len; + d += var->value_len; + found = true; + break; + } + } + } + if (!found) { + if (strict && parser->var_handler != NULL) { + if (parser->var_handler (ptr, remain, &dst, &dstlen, &need_free, + parser->var_data)) { + memcpy (d, dst, dstlen); + ret += dstlen; + d += remain; + found = true; + } + } + + /* Leave variable as is */ + if (!found) { + if (strict) { + /* Copy '${' */ + memcpy (d, ptr, 2); + d += 2; + ret --; + } + else { + memcpy (d, ptr, 1); + d ++; + } + } + } + + *dest = d; + return ret; +} + +/** + * Expand variables in string + * @param parser + * @param dst + * @param src + * @param in_len + * @return + */ +static ssize_t +ucl_expand_variable (struct ucl_parser *parser, unsigned char **dst, + const char *src, size_t in_len) +{ + const char *p, *end = src + in_len; + unsigned char *d; + size_t out_len = 0; + bool vars_found = false; + + p = src; + while (p != end) { + if (*p == '$') { + p = ucl_check_variable (parser, p + 1, end - p - 1, &out_len, &vars_found); + } + else { + p ++; + out_len ++; + } + } + + if (!vars_found) { + /* Trivial case */ + *dst = NULL; + return in_len; + } + + *dst = UCL_ALLOC (out_len + 1); + if (*dst == NULL) { + return in_len; + } + + d = *dst; + p = src; + while (p != end) { + if (*p == '$') { + p = ucl_expand_single_variable (parser, p, end - p, &d); + } + else { + *d++ = *p++; + } + } + + *d = '\0'; + + return out_len; +} + +/** + * Store or copy pointer to the trash stack + * @param parser parser object + * @param src src string + * @param dst destination buffer (trash stack pointer) + * @param dst_const const destination pointer (e.g. value of object) + * @param in_len input length + * @param need_unescape need to unescape source (and copy it) + * @param need_lowercase need to lowercase value (and copy) + * @param need_expand need to expand variables (and copy as well) + * @return output length (excluding \0 symbol) + */ +static inline ssize_t +ucl_copy_or_store_ptr (struct ucl_parser *parser, + const unsigned char *src, unsigned char **dst, + const char **dst_const, size_t in_len, + bool need_unescape, bool need_lowercase, bool need_expand) +{ + ssize_t ret = -1, tret; + unsigned char *tmp; + + if (need_unescape || need_lowercase || + (need_expand && parser->variables != NULL) || + !(parser->flags & UCL_PARSER_ZEROCOPY)) { + /* Copy string */ + *dst = UCL_ALLOC (in_len + 1); + if (*dst == NULL) { + ucl_set_err (parser, 0, "cannot allocate memory for a string", + &parser->err); + return false; + } + if (need_lowercase) { + ret = ucl_strlcpy_tolower (*dst, src, in_len + 1); + } + else { + ret = ucl_strlcpy_unsafe (*dst, src, in_len + 1); + } + + if (need_unescape) { + ret = ucl_unescape_json_string (*dst, ret); + } + if (need_expand) { + tmp = *dst; + tret = ret; + ret = ucl_expand_variable (parser, dst, tmp, ret); + if (*dst == NULL) { + /* Nothing to expand */ + *dst = tmp; + ret = tret; + } + else { + /* Free unexpanded value */ + UCL_FREE (in_len + 1, tmp); + } + } + *dst_const = *dst; + } + else { + *dst_const = src; + ret = in_len; + } + + return ret; +} + +/** + * Create and append an object at the specified level + * @param parser + * @param is_array + * @param level + * @return + */ +static inline ucl_object_t * +ucl_add_parser_stack (ucl_object_t *obj, struct ucl_parser *parser, bool is_array, int level) +{ + struct ucl_stack *st; + + if (!is_array) { + if (obj == NULL) { + obj = ucl_object_new_full (UCL_OBJECT, parser->chunks->priority); + } + else { + obj->type = UCL_OBJECT; + } + obj->value.ov = ucl_hash_create (); + parser->state = UCL_STATE_KEY; + } + else { + if (obj == NULL) { + obj = ucl_object_new_full (UCL_ARRAY, parser->chunks->priority); + } + else { + obj->type = UCL_ARRAY; + } + parser->state = UCL_STATE_VALUE; + } + + st = UCL_ALLOC (sizeof (struct ucl_stack)); + if (st == NULL) { + ucl_set_err (parser, 0, "cannot allocate memory for an object", + &parser->err); + ucl_object_unref (obj); + return NULL; + } + st->obj = obj; + st->level = level; + LL_PREPEND (parser->stack, st); + parser->cur_obj = obj; + + return obj; +} + +int +ucl_maybe_parse_number (ucl_object_t *obj, + const char *start, const char *end, const char **pos, + bool allow_double, bool number_bytes, bool allow_time) +{ + const char *p = start, *c = start; + char *endptr; + bool got_dot = false, got_exp = false, need_double = false, + is_time = false, valid_start = false, is_hex = false, + is_neg = false; + double dv = 0; + int64_t lv = 0; + + if (*p == '-') { + is_neg = true; + c ++; + p ++; + } + while (p < end) { + if (is_hex && isxdigit (*p)) { + p ++; + } + else if (isdigit (*p)) { + valid_start = true; + p ++; + } + else if (!is_hex && (*p == 'x' || *p == 'X')) { + is_hex = true; + allow_double = false; + c = p + 1; + } + else if (allow_double) { + if (p == c) { + /* Empty digits sequence, not a number */ + *pos = start; + return EINVAL; + } + else if (*p == '.') { + if (got_dot) { + /* Double dots, not a number */ + *pos = start; + return EINVAL; + } + else { + got_dot = true; + need_double = true; + p ++; + } + } + else if (*p == 'e' || *p == 'E') { + if (got_exp) { + /* Double exp, not a number */ + *pos = start; + return EINVAL; + } + else { + got_exp = true; + need_double = true; + p ++; + if (p >= end) { + *pos = start; + return EINVAL; + } + if (!isdigit (*p) && *p != '+' && *p != '-') { + /* Wrong exponent sign */ + *pos = start; + return EINVAL; + } + else { + p ++; + } + } + } + else { + /* Got the end of the number, need to check */ + break; + } + } + else { + break; + } + } + + if (!valid_start) { + *pos = start; + return EINVAL; + } + + errno = 0; + if (need_double) { + dv = strtod (c, &endptr); + } + else { + if (is_hex) { + lv = strtoimax (c, &endptr, 16); + } + else { + lv = strtoimax (c, &endptr, 10); + } + } + if (errno == ERANGE) { + *pos = start; + return ERANGE; + } + + /* Now check endptr */ + if (endptr == NULL || ucl_lex_is_atom_end (*endptr) || *endptr == '\0') { + p = endptr; + goto set_obj; + } + + if (endptr < end && endptr != start) { + p = endptr; + switch (*p) { + case 'm': + case 'M': + case 'g': + case 'G': + case 'k': + case 'K': + if (end - p >= 2) { + if (p[1] == 's' || p[1] == 'S') { + /* Milliseconds */ + if (!need_double) { + need_double = true; + dv = lv; + } + is_time = true; + if (p[0] == 'm' || p[0] == 'M') { + dv /= 1000.; + } + else { + dv *= ucl_lex_num_multiplier (*p, false); + } + p += 2; + goto set_obj; + } + else if (number_bytes || (p[1] == 'b' || p[1] == 'B')) { + /* Bytes */ + if (need_double) { + need_double = false; + lv = dv; + } + lv *= ucl_lex_num_multiplier (*p, true); + p += 2; + goto set_obj; + } + else if (ucl_lex_is_atom_end (p[1])) { + if (need_double) { + dv *= ucl_lex_num_multiplier (*p, false); + } + else { + lv *= ucl_lex_num_multiplier (*p, number_bytes); + } + p ++; + goto set_obj; + } + else if (allow_time && end - p >= 3) { + if (tolower (p[0]) == 'm' && + tolower (p[1]) == 'i' && + tolower (p[2]) == 'n') { + /* Minutes */ + if (!need_double) { + need_double = true; + dv = lv; + } + is_time = true; + dv *= 60.; + p += 3; + goto set_obj; + } + } + } + else { + if (need_double) { + dv *= ucl_lex_num_multiplier (*p, false); + } + else { + lv *= ucl_lex_num_multiplier (*p, number_bytes); + } + p ++; + goto set_obj; + } + break; + case 'S': + case 's': + if (allow_time && + (p == end - 1 || ucl_lex_is_atom_end (p[1]))) { + if (!need_double) { + need_double = true; + dv = lv; + } + p ++; + is_time = true; + goto set_obj; + } + break; + case 'h': + case 'H': + case 'd': + case 'D': + case 'w': + case 'W': + case 'Y': + case 'y': + if (allow_time && + (p == end - 1 || ucl_lex_is_atom_end (p[1]))) { + if (!need_double) { + need_double = true; + dv = lv; + } + is_time = true; + dv *= ucl_lex_time_multiplier (*p); + p ++; + goto set_obj; + } + break; + case '\t': + case ' ': + while (p < end && ucl_test_character(*p, UCL_CHARACTER_WHITESPACE)) { + p++; + } + if (ucl_lex_is_atom_end(*p)) + goto set_obj; + break; + } + } + else if (endptr == end) { + /* Just a number at the end of chunk */ + p = endptr; + goto set_obj; + } + + *pos = c; + return EINVAL; + + set_obj: + if (allow_double && (need_double || is_time)) { + if (!is_time) { + obj->type = UCL_FLOAT; + } + else { + obj->type = UCL_TIME; + } + obj->value.dv = is_neg ? (-dv) : dv; + } + else { + obj->type = UCL_INT; + obj->value.iv = is_neg ? (-lv) : lv; + } + *pos = p; + return 0; +} + +/** + * Parse possible number + * @param parser + * @param chunk + * @return true if a number has been parsed + */ +static bool +ucl_lex_number (struct ucl_parser *parser, + struct ucl_chunk *chunk, ucl_object_t *obj) +{ + const unsigned char *pos; + int ret; + + ret = ucl_maybe_parse_number (obj, chunk->pos, chunk->end, (const char **)&pos, + true, false, ((parser->flags & UCL_PARSER_NO_TIME) == 0)); + + if (ret == 0) { + chunk->remain -= pos - chunk->pos; + chunk->column += pos - chunk->pos; + chunk->pos = pos; + return true; + } + else if (ret == ERANGE) { + ucl_set_err (parser, ERANGE, "numeric value out of range", &parser->err); + } + + return false; +} + +/** + * Parse quoted string with possible escapes + * @param parser + * @param chunk + * @return true if a string has been parsed + */ +static bool +ucl_lex_json_string (struct ucl_parser *parser, + struct ucl_chunk *chunk, bool *need_unescape, bool *ucl_escape, bool *var_expand) +{ + const unsigned char *p = chunk->pos; + unsigned char c; + int i; + + while (p < chunk->end) { + c = *p; + if (c < 0x1F) { + /* Unmasked control character */ + if (c == '\n') { + ucl_set_err (parser, UCL_ESYNTAX, "unexpected newline", + &parser->err); + } + else { + ucl_set_err (parser, UCL_ESYNTAX, "unexpected control character", + &parser->err); + } + return false; + } + else if (c == '\\') { + ucl_chunk_skipc (chunk, p); + c = *p; + if (p >= chunk->end) { + ucl_set_err (parser, UCL_ESYNTAX, "unfinished escape character", + &parser->err); + return false; + } + else if (ucl_test_character (c, UCL_CHARACTER_ESCAPE)) { + if (c == 'u') { + ucl_chunk_skipc (chunk, p); + for (i = 0; i < 4 && p < chunk->end; i ++) { + if (!isxdigit (*p)) { + ucl_set_err (parser, UCL_ESYNTAX, "invalid utf escape", + &parser->err); + return false; + } + ucl_chunk_skipc (chunk, p); + } + if (p >= chunk->end) { + ucl_set_err (parser, UCL_ESYNTAX, "unfinished escape character", + &parser->err); + return false; + } + } + else { + ucl_chunk_skipc (chunk, p); + } + } + *need_unescape = true; + *ucl_escape = true; + continue; + } + else if (c == '"') { + ucl_chunk_skipc (chunk, p); + return true; + } + else if (ucl_test_character (c, UCL_CHARACTER_UCL_UNSAFE)) { + *ucl_escape = true; + } + else if (c == '$') { + *var_expand = true; + } + ucl_chunk_skipc (chunk, p); + } + + ucl_set_err (parser, UCL_ESYNTAX, "no quote at the end of json string", + &parser->err); + return false; +} + +static void +ucl_parser_append_elt (struct ucl_parser *parser, ucl_hash_t *cont, + ucl_object_t *top, + ucl_object_t *elt) +{ + ucl_object_t *nobj; + + if ((parser->flags & UCL_PARSER_NO_IMPLICIT_ARRAYS) == 0) { + /* Implicit array */ + top->flags |= UCL_OBJECT_MULTIVALUE; + DL_APPEND (top, elt); + } + else { + if ((top->flags & UCL_OBJECT_MULTIVALUE) != 0) { + /* Just add to the explicit array */ + DL_APPEND (top->value.av, elt); + } + else { + /* Convert to an array */ + ucl_hash_delete (cont, top); + nobj = ucl_object_typed_new (UCL_ARRAY); + nobj->key = top->key; + nobj->keylen = top->keylen; + nobj->flags |= UCL_OBJECT_MULTIVALUE; + DL_APPEND (nobj->value.av, top); + DL_APPEND (nobj->value.av, elt); + ucl_hash_insert (cont, nobj, nobj->key, nobj->keylen); + } + } +} + +/** + * Parse a key in an object + * @param parser + * @param chunk + * @return true if a key has been parsed + */ +static bool +ucl_parse_key (struct ucl_parser *parser, struct ucl_chunk *chunk, bool *next_key, bool *end_of_object) +{ + const unsigned char *p, *c = NULL, *end, *t; + const char *key = NULL; + bool got_quote = false, got_eq = false, got_semicolon = false, + need_unescape = false, ucl_escape = false, var_expand = false, + got_content = false, got_sep = false; + ucl_object_t *nobj, *tobj; + ucl_hash_t *container; + ssize_t keylen; + + p = chunk->pos; + + if (*p == '.') { + /* It is macro actually */ + ucl_chunk_skipc (chunk, p); + parser->prev_state = parser->state; + parser->state = UCL_STATE_MACRO_NAME; + return true; + } + while (p < chunk->end) { + /* + * A key must start with alpha, number, '/' or '_' and end with space character + */ + if (c == NULL) { + if (chunk->remain >= 2 && ucl_lex_is_comment (p[0], p[1])) { + if (!ucl_skip_comments (parser)) { + return false; + } + p = chunk->pos; + } + else if (ucl_test_character (*p, UCL_CHARACTER_WHITESPACE_UNSAFE)) { + ucl_chunk_skipc (chunk, p); + } + else if (ucl_test_character (*p, UCL_CHARACTER_KEY_START)) { + /* The first symbol */ + c = p; + ucl_chunk_skipc (chunk, p); + got_content = true; + } + else if (*p == '"') { + /* JSON style key */ + c = p + 1; + got_quote = true; + got_content = true; + ucl_chunk_skipc (chunk, p); + } + else if (*p == '}') { + /* We have actually end of an object */ + *end_of_object = true; + return true; + } + else if (*p == '.') { + ucl_chunk_skipc (chunk, p); + parser->prev_state = parser->state; + parser->state = UCL_STATE_MACRO_NAME; + return true; + } + else { + /* Invalid identifier */ + ucl_set_err (parser, UCL_ESYNTAX, "key must begin with a letter", + &parser->err); + return false; + } + } + else { + /* Parse the body of a key */ + if (!got_quote) { + if (ucl_test_character (*p, UCL_CHARACTER_KEY)) { + got_content = true; + ucl_chunk_skipc (chunk, p); + } + else if (ucl_test_character (*p, UCL_CHARACTER_KEY_SEP)) { + end = p; + break; + } + else { + ucl_set_err (parser, UCL_ESYNTAX, "invalid character in a key", + &parser->err); + return false; + } + } + else { + /* We need to parse json like quoted string */ + if (!ucl_lex_json_string (parser, chunk, &need_unescape, &ucl_escape, &var_expand)) { + return false; + } + /* Always escape keys obtained via json */ + end = chunk->pos - 1; + p = chunk->pos; + break; + } + } + } + + if (p >= chunk->end && got_content) { + ucl_set_err (parser, UCL_ESYNTAX, "unfinished key", &parser->err); + return false; + } + else if (!got_content) { + return true; + } + *end_of_object = false; + /* We are now at the end of the key, need to parse the rest */ + while (p < chunk->end) { + if (ucl_test_character (*p, UCL_CHARACTER_WHITESPACE)) { + ucl_chunk_skipc (chunk, p); + } + else if (*p == '=') { + if (!got_eq && !got_semicolon) { + ucl_chunk_skipc (chunk, p); + got_eq = true; + } + else { + ucl_set_err (parser, UCL_ESYNTAX, "unexpected '=' character", + &parser->err); + return false; + } + } + else if (*p == ':') { + if (!got_eq && !got_semicolon) { + ucl_chunk_skipc (chunk, p); + got_semicolon = true; + } + else { + ucl_set_err (parser, UCL_ESYNTAX, "unexpected ':' character", + &parser->err); + return false; + } + } + else if (chunk->remain >= 2 && ucl_lex_is_comment (p[0], p[1])) { + /* Check for comment */ + if (!ucl_skip_comments (parser)) { + return false; + } + p = chunk->pos; + } + else { + /* Start value */ + break; + } + } + + if (p >= chunk->end && got_content) { + ucl_set_err (parser, UCL_ESYNTAX, "unfinished key", &parser->err); + return false; + } + + got_sep = got_semicolon || got_eq; + + if (!got_sep) { + /* + * Maybe we have more keys nested, so search for termination character. + * Possible choices: + * 1) key1 key2 ... keyN [:=] value <- we treat that as error + * 2) key1 ... keyN {} or [] <- we treat that as nested objects + * 3) key1 value[;,\n] <- we treat that as linear object + */ + t = p; + *next_key = false; + while (ucl_test_character (*t, UCL_CHARACTER_WHITESPACE)) { + t ++; + } + /* Check first non-space character after a key */ + if (*t != '{' && *t != '[') { + while (t < chunk->end) { + if (*t == ',' || *t == ';' || *t == '\n' || *t == '\r') { + break; + } + else if (*t == '{' || *t == '[') { + *next_key = true; + break; + } + t ++; + } + } + } + + /* Create a new object */ + nobj = ucl_object_new_full (UCL_NULL, parser->chunks->priority); + keylen = ucl_copy_or_store_ptr (parser, c, &nobj->trash_stack[UCL_TRASH_KEY], + &key, end - c, need_unescape, parser->flags & UCL_PARSER_KEY_LOWERCASE, false); + if (keylen == -1) { + ucl_object_unref (nobj); + return false; + } + else if (keylen == 0) { + ucl_set_err (parser, UCL_ESYNTAX, "empty keys are not allowed", &parser->err); + ucl_object_unref (nobj); + return false; + } + + container = parser->stack->obj->value.ov; + nobj->key = key; + nobj->keylen = keylen; + tobj = __DECONST (ucl_object_t *, ucl_hash_search_obj (container, nobj)); + if (tobj == NULL) { + container = ucl_hash_insert_object (container, nobj); + nobj->prev = nobj; + nobj->next = NULL; + parser->stack->obj->len ++; + } + else { + /* + * The logic here is the following: + * + * - if we have two objects with the same priority, then we form an + * implicit or explicit array + * - if a new object has bigger priority, then we overwrite an old one + * - if a new object has lower priority, then we ignore it + */ + unsigned priold = ucl_object_get_priority (tobj), + prinew = ucl_object_get_priority (nobj); + if (priold == prinew) { + ucl_parser_append_elt (parser, container, tobj, nobj); + } + else if (priold > prinew) { + ucl_object_unref (nobj); + return true; + } + else { + ucl_hash_replace (container, tobj, nobj); + ucl_object_unref (tobj); + } + } + + if (ucl_escape) { + nobj->flags |= UCL_OBJECT_NEED_KEY_ESCAPE; + } + parser->stack->obj->value.ov = container; + + parser->cur_obj = nobj; + + return true; +} + +/** + * Parse a cl string + * @param parser + * @param chunk + * @return true if a key has been parsed + */ +static bool +ucl_parse_string_value (struct ucl_parser *parser, + struct ucl_chunk *chunk, bool *var_expand, bool *need_unescape) +{ + const unsigned char *p; + enum { + UCL_BRACE_ROUND = 0, + UCL_BRACE_SQUARE, + UCL_BRACE_FIGURE + }; + int braces[3][2] = {{0, 0}, {0, 0}, {0, 0}}; + + p = chunk->pos; + + while (p < chunk->end) { + + /* Skip pairs of figure braces */ + if (*p == '{') { + braces[UCL_BRACE_FIGURE][0] ++; + } + else if (*p == '}') { + braces[UCL_BRACE_FIGURE][1] ++; + if (braces[UCL_BRACE_FIGURE][1] <= braces[UCL_BRACE_FIGURE][0]) { + /* This is not a termination symbol, continue */ + ucl_chunk_skipc (chunk, p); + continue; + } + } + /* Skip pairs of square braces */ + else if (*p == '[') { + braces[UCL_BRACE_SQUARE][0] ++; + } + else if (*p == ']') { + braces[UCL_BRACE_SQUARE][1] ++; + if (braces[UCL_BRACE_SQUARE][1] <= braces[UCL_BRACE_SQUARE][0]) { + /* This is not a termination symbol, continue */ + ucl_chunk_skipc (chunk, p); + continue; + } + } + else if (*p == '$') { + *var_expand = true; + } + else if (*p == '\\') { + *need_unescape = true; + ucl_chunk_skipc (chunk, p); + if (p < chunk->end) { + ucl_chunk_skipc (chunk, p); + } + continue; + } + + if (ucl_lex_is_atom_end (*p) || (chunk->remain >= 2 && ucl_lex_is_comment (p[0], p[1]))) { + break; + } + ucl_chunk_skipc (chunk, p); + } + + return true; +} + +/** + * Parse multiline string ending with \n{term}\n + * @param parser + * @param chunk + * @param term + * @param term_len + * @return size of multiline string or 0 in case of error + */ +static int +ucl_parse_multiline_string (struct ucl_parser *parser, + struct ucl_chunk *chunk, const unsigned char *term, + int term_len, unsigned char const **beg, + bool *var_expand) +{ + const unsigned char *p, *c, *tend; + bool newline = false; + int len = 0; + + p = chunk->pos; + + c = p; + + while (p < chunk->end) { + if (newline) { + if (chunk->end - p < term_len) { + return 0; + } + else if (memcmp (p, term, term_len) == 0) { + tend = p + term_len; + if (*tend != '\n' && *tend != ';' && *tend != ',') { + /* Incomplete terminator */ + ucl_chunk_skipc (chunk, p); + continue; + } + len = p - c; + chunk->remain -= term_len; + chunk->pos = p + term_len; + chunk->column = term_len; + *beg = c; + break; + } + } + if (*p == '\n') { + newline = true; + } + else { + if (*p == '$') { + *var_expand = true; + } + newline = false; + } + ucl_chunk_skipc (chunk, p); + } + + return len; +} + +static ucl_object_t* +ucl_get_value_object (struct ucl_parser *parser) +{ + ucl_object_t *t, *obj = NULL; + + if (parser->stack->obj->type == UCL_ARRAY) { + /* Object must be allocated */ + obj = ucl_object_new_full (UCL_NULL, parser->chunks->priority); + t = parser->stack->obj->value.av; + DL_APPEND (t, obj); + parser->cur_obj = obj; + parser->stack->obj->value.av = t; + parser->stack->obj->len ++; + } + else { + /* Object has been already allocated */ + obj = parser->cur_obj; + } + + return obj; +} + +/** + * Handle value data + * @param parser + * @param chunk + * @return + */ +static bool +ucl_parse_value (struct ucl_parser *parser, struct ucl_chunk *chunk) +{ + const unsigned char *p, *c; + ucl_object_t *obj = NULL; + unsigned int stripped_spaces; + int str_len; + bool need_unescape = false, ucl_escape = false, var_expand = false; + + p = chunk->pos; + + /* Skip any spaces and comments */ + if (ucl_test_character (*p, UCL_CHARACTER_WHITESPACE_UNSAFE) || + (chunk->remain >= 2 && ucl_lex_is_comment (p[0], p[1]))) { + while (p < chunk->end && ucl_test_character (*p, UCL_CHARACTER_WHITESPACE_UNSAFE)) { + ucl_chunk_skipc (chunk, p); + } + if (!ucl_skip_comments (parser)) { + return false; + } + p = chunk->pos; + } + + while (p < chunk->end) { + c = p; + switch (*p) { + case '"': + obj = ucl_get_value_object (parser); + ucl_chunk_skipc (chunk, p); + if (!ucl_lex_json_string (parser, chunk, &need_unescape, &ucl_escape, &var_expand)) { + return false; + } + str_len = chunk->pos - c - 2; + obj->type = UCL_STRING; + if ((str_len = ucl_copy_or_store_ptr (parser, c + 1, &obj->trash_stack[UCL_TRASH_VALUE], + &obj->value.sv, str_len, need_unescape, false, var_expand)) == -1) { + return false; + } + obj->len = str_len; + parser->state = UCL_STATE_AFTER_VALUE; + p = chunk->pos; + return true; + break; + case '{': + obj = ucl_get_value_object (parser); + /* We have a new object */ + obj = ucl_add_parser_stack (obj, parser, false, parser->stack->level); + if (obj == NULL) { + return false; + } + + ucl_chunk_skipc (chunk, p); + return true; + break; + case '[': + obj = ucl_get_value_object (parser); + /* We have a new array */ + obj = ucl_add_parser_stack (obj, parser, true, parser->stack->level); + if (obj == NULL) { + return false; + } + + ucl_chunk_skipc (chunk, p); + return true; + break; + case ']': + /* We have the array ending */ + if (parser->stack && parser->stack->obj->type == UCL_ARRAY) { + parser->state = UCL_STATE_AFTER_VALUE; + return true; + } + else { + goto parse_string; + } + break; + case '<': + obj = ucl_get_value_object (parser); + /* We have something like multiline value, which must be <<[A-Z]+\n */ + if (chunk->end - p > 3) { + if (memcmp (p, "<<", 2) == 0) { + p += 2; + /* We allow only uppercase characters in multiline definitions */ + while (p < chunk->end && *p >= 'A' && *p <= 'Z') { + p ++; + } + if (*p =='\n') { + /* Set chunk positions and start multiline parsing */ + c += 2; + chunk->remain -= p - c; + chunk->pos = p + 1; + chunk->column = 0; + chunk->line ++; + if ((str_len = ucl_parse_multiline_string (parser, chunk, c, + p - c, &c, &var_expand)) == 0) { + ucl_set_err (parser, UCL_ESYNTAX, + "unterminated multiline value", &parser->err); + return false; + } + obj->type = UCL_STRING; + if ((str_len = ucl_copy_or_store_ptr (parser, c, &obj->trash_stack[UCL_TRASH_VALUE], + &obj->value.sv, str_len - 1, false, false, var_expand)) == -1) { + return false; + } + obj->len = str_len; + parser->state = UCL_STATE_AFTER_VALUE; + return true; + } + } + } + /* Fallback to ordinary strings */ + default: +parse_string: + if (obj == NULL) { + obj = ucl_get_value_object (parser); + } + /* Parse atom */ + if (ucl_test_character (*p, UCL_CHARACTER_VALUE_DIGIT_START)) { + if (!ucl_lex_number (parser, chunk, obj)) { + if (parser->state == UCL_STATE_ERROR) { + return false; + } + } + else { + parser->state = UCL_STATE_AFTER_VALUE; + return true; + } + /* Fallback to normal string */ + } + + if (!ucl_parse_string_value (parser, chunk, &var_expand, &need_unescape)) { + return false; + } + /* Cut trailing spaces */ + stripped_spaces = 0; + while (ucl_test_character (*(chunk->pos - 1 - stripped_spaces), + UCL_CHARACTER_WHITESPACE)) { + stripped_spaces ++; + } + str_len = chunk->pos - c - stripped_spaces; + if (str_len <= 0) { + ucl_set_err (parser, 0, "string value must not be empty", + &parser->err); + return false; + } + else if (str_len == 4 && memcmp (c, "null", 4) == 0) { + obj->len = 0; + obj->type = UCL_NULL; + } + else if (!ucl_maybe_parse_boolean (obj, c, str_len)) { + obj->type = UCL_STRING; + if ((str_len = ucl_copy_or_store_ptr (parser, c, &obj->trash_stack[UCL_TRASH_VALUE], + &obj->value.sv, str_len, need_unescape, + false, var_expand)) == -1) { + return false; + } + obj->len = str_len; + } + parser->state = UCL_STATE_AFTER_VALUE; + p = chunk->pos; + + return true; + break; + } + } + + return true; +} + +/** + * Handle after value data + * @param parser + * @param chunk + * @return + */ +static bool +ucl_parse_after_value (struct ucl_parser *parser, struct ucl_chunk *chunk) +{ + const unsigned char *p; + bool got_sep = false; + struct ucl_stack *st; + + p = chunk->pos; + + while (p < chunk->end) { + if (ucl_test_character (*p, UCL_CHARACTER_WHITESPACE)) { + /* Skip whitespaces */ + ucl_chunk_skipc (chunk, p); + } + else if (chunk->remain >= 2 && ucl_lex_is_comment (p[0], p[1])) { + /* Skip comment */ + if (!ucl_skip_comments (parser)) { + return false; + } + /* Treat comment as a separator */ + got_sep = true; + p = chunk->pos; + } + else if (ucl_test_character (*p, UCL_CHARACTER_VALUE_END)) { + if (*p == '}' || *p == ']') { + if (parser->stack == NULL) { + ucl_set_err (parser, UCL_ESYNTAX, + "end of array or object detected without corresponding start", + &parser->err); + return false; + } + if ((*p == '}' && parser->stack->obj->type == UCL_OBJECT) || + (*p == ']' && parser->stack->obj->type == UCL_ARRAY)) { + + /* Pop all nested objects from a stack */ + st = parser->stack; + parser->stack = st->next; + UCL_FREE (sizeof (struct ucl_stack), st); + + while (parser->stack != NULL) { + st = parser->stack; + if (st->next == NULL || st->next->level == st->level) { + break; + } + parser->stack = st->next; + UCL_FREE (sizeof (struct ucl_stack), st); + } + } + else { + ucl_set_err (parser, UCL_ESYNTAX, + "unexpected terminating symbol detected", + &parser->err); + return false; + } + + if (parser->stack == NULL) { + /* Ignore everything after a top object */ + return true; + } + else { + ucl_chunk_skipc (chunk, p); + } + got_sep = true; + } + else { + /* Got a separator */ + got_sep = true; + ucl_chunk_skipc (chunk, p); + } + } + else { + /* Anything else */ + if (!got_sep) { + ucl_set_err (parser, UCL_ESYNTAX, "delimiter is missing", + &parser->err); + return false; + } + return true; + } + } + + return true; +} + +/** + * Handle macro data + * @param parser + * @param chunk + * @return + */ +static bool +ucl_parse_macro_value (struct ucl_parser *parser, + struct ucl_chunk *chunk, struct ucl_macro *macro, + unsigned char const **macro_start, size_t *macro_len) +{ + const unsigned char *p, *c; + bool need_unescape = false, ucl_escape = false, var_expand = false; + + p = chunk->pos; + + switch (*p) { + case '"': + /* We have macro value encoded in quotes */ + c = p; + ucl_chunk_skipc (chunk, p); + if (!ucl_lex_json_string (parser, chunk, &need_unescape, &ucl_escape, &var_expand)) { + return false; + } + + *macro_start = c + 1; + *macro_len = chunk->pos - c - 2; + p = chunk->pos; + break; + case '{': + /* We got a multiline macro body */ + ucl_chunk_skipc (chunk, p); + /* Skip spaces at the beginning */ + while (p < chunk->end) { + if (ucl_test_character (*p, UCL_CHARACTER_WHITESPACE_UNSAFE)) { + ucl_chunk_skipc (chunk, p); + } + else { + break; + } + } + c = p; + while (p < chunk->end) { + if (*p == '}') { + break; + } + ucl_chunk_skipc (chunk, p); + } + *macro_start = c; + *macro_len = p - c; + ucl_chunk_skipc (chunk, p); + break; + default: + /* Macro is not enclosed in quotes or braces */ + c = p; + while (p < chunk->end) { + if (ucl_lex_is_atom_end (*p)) { + break; + } + ucl_chunk_skipc (chunk, p); + } + *macro_start = c; + *macro_len = p - c; + break; + } + + /* We are at the end of a macro */ + /* Skip ';' and space characters and return to previous state */ + while (p < chunk->end) { + if (!ucl_test_character (*p, UCL_CHARACTER_WHITESPACE_UNSAFE) && *p != ';') { + break; + } + ucl_chunk_skipc (chunk, p); + } + return true; +} + +/** + * Parse macro arguments as UCL object + * @param parser parser structure + * @param chunk the current data chunk + * @return + */ +static ucl_object_t * +ucl_parse_macro_arguments (struct ucl_parser *parser, + struct ucl_chunk *chunk) +{ + ucl_object_t *res = NULL; + struct ucl_parser *params_parser; + int obraces = 1, ebraces = 0, state = 0; + const unsigned char *p, *c; + size_t args_len = 0; + struct ucl_parser_saved_state saved; + + saved.column = chunk->column; + saved.line = chunk->line; + saved.pos = chunk->pos; + saved.remain = chunk->remain; + p = chunk->pos; + + if (*p != '(' || chunk->remain < 2) { + return NULL; + } + + /* Set begin and start */ + ucl_chunk_skipc (chunk, p); + c = p; + + while ((p) < (chunk)->end) { + switch (state) { + case 0: + /* Parse symbols and check for '(', ')' and '"' */ + if (*p == '(') { + obraces ++; + } + else if (*p == ')') { + ebraces ++; + } + else if (*p == '"') { + state = 1; + } + /* Check pairing */ + if (obraces == ebraces) { + state = 99; + } + else { + args_len ++; + } + /* Check overflow */ + if (chunk->remain == 0) { + goto restore_chunk; + } + ucl_chunk_skipc (chunk, p); + break; + case 1: + /* We have quote character, so skip all but quotes */ + if (*p == '"' && *(p - 1) != '\\') { + state = 0; + } + if (chunk->remain == 0) { + goto restore_chunk; + } + ucl_chunk_skipc (chunk, p); + break; + case 99: + /* + * We have read the full body of arguments, so we need to parse and set + * object from that + */ + params_parser = ucl_parser_new (parser->flags); + if (!ucl_parser_add_chunk (params_parser, c, args_len)) { + ucl_set_err (parser, UCL_ESYNTAX, "macro arguments parsing error", + &parser->err); + } + else { + res = ucl_parser_get_object (params_parser); + } + ucl_parser_free (params_parser); + + return res; + + break; + } + } + + return res; + +restore_chunk: + chunk->column = saved.column; + chunk->line = saved.line; + chunk->pos = saved.pos; + chunk->remain = saved.remain; + + return NULL; +} + +#define SKIP_SPACES_COMMENTS(parser, chunk, p) do { \ + while ((p) < (chunk)->end) { \ + if (!ucl_test_character (*(p), UCL_CHARACTER_WHITESPACE_UNSAFE)) { \ + if ((chunk)->remain >= 2 && ucl_lex_is_comment ((p)[0], (p)[1])) { \ + if (!ucl_skip_comments (parser)) { \ + return false; \ + } \ + p = (chunk)->pos; \ + } \ + break; \ + } \ + ucl_chunk_skipc (chunk, p); \ + } \ +} while(0) + +/** + * Handle the main states of rcl parser + * @param parser parser structure + * @param data the pointer to the beginning of a chunk + * @param len the length of a chunk + * @return true if chunk has been parsed and false in case of error + */ +static bool +ucl_state_machine (struct ucl_parser *parser) +{ + ucl_object_t *obj, *macro_args; + struct ucl_chunk *chunk = parser->chunks; + const unsigned char *p, *c = NULL, *macro_start = NULL; + unsigned char *macro_escaped; + size_t macro_len = 0; + struct ucl_macro *macro = NULL; + bool next_key = false, end_of_object = false, ret; + + if (parser->top_obj == NULL) { + if (*chunk->pos == '[') { + obj = ucl_add_parser_stack (NULL, parser, true, 0); + } + else { + obj = ucl_add_parser_stack (NULL, parser, false, 0); + } + if (obj == NULL) { + return false; + } + parser->top_obj = obj; + parser->cur_obj = obj; + parser->state = UCL_STATE_INIT; + } + + p = chunk->pos; + while (chunk->pos < chunk->end) { + switch (parser->state) { + case UCL_STATE_INIT: + /* + * At the init state we can either go to the parse array or object + * if we got [ or { correspondingly or can just treat new data as + * a key of newly created object + */ + if (!ucl_skip_comments (parser)) { + parser->prev_state = parser->state; + parser->state = UCL_STATE_ERROR; + return false; + } + else { + /* Skip any spaces */ + while (p < chunk->end && ucl_test_character (*p, + UCL_CHARACTER_WHITESPACE_UNSAFE)) { + ucl_chunk_skipc (chunk, p); + } + p = chunk->pos; + if (*p == '[') { + parser->state = UCL_STATE_VALUE; + ucl_chunk_skipc (chunk, p); + } + else { + parser->state = UCL_STATE_KEY; + if (*p == '{') { + ucl_chunk_skipc (chunk, p); + } + } + } + break; + case UCL_STATE_KEY: + /* Skip any spaces */ + while (p < chunk->end && ucl_test_character (*p, UCL_CHARACTER_WHITESPACE_UNSAFE)) { + ucl_chunk_skipc (chunk, p); + } + if (*p == '}') { + /* We have the end of an object */ + parser->state = UCL_STATE_AFTER_VALUE; + continue; + } + if (parser->stack == NULL) { + /* No objects are on stack, but we want to parse a key */ + ucl_set_err (parser, UCL_ESYNTAX, "top object is finished but the parser " + "expects a key", &parser->err); + parser->prev_state = parser->state; + parser->state = UCL_STATE_ERROR; + return false; + } + if (!ucl_parse_key (parser, chunk, &next_key, &end_of_object)) { + parser->prev_state = parser->state; + parser->state = UCL_STATE_ERROR; + return false; + } + if (end_of_object) { + p = chunk->pos; + parser->state = UCL_STATE_AFTER_VALUE; + continue; + } + else if (parser->state != UCL_STATE_MACRO_NAME) { + if (next_key && parser->stack->obj->type == UCL_OBJECT) { + /* Parse more keys and nest objects accordingly */ + obj = ucl_add_parser_stack (parser->cur_obj, parser, false, + parser->stack->level + 1); + if (obj == NULL) { + return false; + } + } + else { + parser->state = UCL_STATE_VALUE; + } + } + else { + c = chunk->pos; + } + p = chunk->pos; + break; + case UCL_STATE_VALUE: + /* We need to check what we do have */ + if (!ucl_parse_value (parser, chunk)) { + parser->prev_state = parser->state; + parser->state = UCL_STATE_ERROR; + return false; + } + /* State is set in ucl_parse_value call */ + p = chunk->pos; + break; + case UCL_STATE_AFTER_VALUE: + if (!ucl_parse_after_value (parser, chunk)) { + parser->prev_state = parser->state; + parser->state = UCL_STATE_ERROR; + return false; + } + if (parser->stack != NULL) { + if (parser->stack->obj->type == UCL_OBJECT) { + parser->state = UCL_STATE_KEY; + } + else { + /* Array */ + parser->state = UCL_STATE_VALUE; + } + } + else { + /* Skip everything at the end */ + return true; + } + p = chunk->pos; + break; + case UCL_STATE_MACRO_NAME: + if (!ucl_test_character (*p, UCL_CHARACTER_WHITESPACE_UNSAFE) && + *p != '(') { + ucl_chunk_skipc (chunk, p); + } + else if (p - c > 0) { + /* We got macro name */ + macro_len = (size_t)(p - c); + HASH_FIND (hh, parser->macroes, c, macro_len, macro); + if (macro == NULL) { + ucl_create_err (&parser->err, "error on line %d at column %d: " + "unknown macro: '%.*s', character: '%c'", + chunk->line, chunk->column, (int)(p - c), c, *chunk->pos); + parser->state = UCL_STATE_ERROR; + return false; + } + /* Now we need to skip all spaces */ + SKIP_SPACES_COMMENTS(parser, chunk, p); + parser->state = UCL_STATE_MACRO; + } + break; + case UCL_STATE_MACRO: + if (*chunk->pos == '(') { + macro_args = ucl_parse_macro_arguments (parser, chunk); + p = chunk->pos; + if (macro_args) { + SKIP_SPACES_COMMENTS(parser, chunk, p); + } + } + else { + macro_args = NULL; + } + if (!ucl_parse_macro_value (parser, chunk, macro, + ¯o_start, ¯o_len)) { + parser->prev_state = parser->state; + parser->state = UCL_STATE_ERROR; + return false; + } + macro_len = ucl_expand_variable (parser, ¯o_escaped, + macro_start, macro_len); + parser->state = parser->prev_state; + if (macro_escaped == NULL) { + ret = macro->handler (macro_start, macro_len, macro_args, + macro->ud); + } + else { + ret = macro->handler (macro_escaped, macro_len, macro_args, + macro->ud); + UCL_FREE (macro_len + 1, macro_escaped); + } + p = chunk->pos; + if (macro_args) { + ucl_object_unref (macro_args); + } + if (!ret) { + return false; + } + break; + default: + /* TODO: add all states */ + ucl_set_err (parser, UCL_EINTERNAL, + "internal error: parser is in an unknown state", &parser->err); + parser->state = UCL_STATE_ERROR; + return false; + } + } + + return true; +} + +struct ucl_parser* +ucl_parser_new (int flags) +{ + struct ucl_parser *new; + + new = UCL_ALLOC (sizeof (struct ucl_parser)); + if (new == NULL) { + return NULL; + } + memset (new, 0, sizeof (struct ucl_parser)); + + ucl_parser_register_macro (new, "include", ucl_include_handler, new); + ucl_parser_register_macro (new, "try_include", ucl_try_include_handler, new); + ucl_parser_register_macro (new, "includes", ucl_includes_handler, new); + + new->flags = flags; + + /* Initial assumption about filevars */ + ucl_parser_set_filevars (new, NULL, false); + + return new; +} + + +void +ucl_parser_register_macro (struct ucl_parser *parser, const char *macro, + ucl_macro_handler handler, void* ud) +{ + struct ucl_macro *new; + + if (macro == NULL || handler == NULL) { + return; + } + new = UCL_ALLOC (sizeof (struct ucl_macro)); + if (new == NULL) { + return; + } + memset (new, 0, sizeof (struct ucl_macro)); + new->handler = handler; + new->name = strdup (macro); + new->ud = ud; + HASH_ADD_KEYPTR (hh, parser->macroes, new->name, strlen (new->name), new); +} + +void +ucl_parser_register_variable (struct ucl_parser *parser, const char *var, + const char *value) +{ + struct ucl_variable *new = NULL, *cur; + + if (var == NULL) { + return; + } + + /* Find whether a variable already exists */ + LL_FOREACH (parser->variables, cur) { + if (strcmp (cur->var, var) == 0) { + new = cur; + break; + } + } + + if (value == NULL) { + + if (new != NULL) { + /* Remove variable */ + DL_DELETE (parser->variables, new); + free (new->var); + free (new->value); + UCL_FREE (sizeof (struct ucl_variable), new); + } + else { + /* Do nothing */ + return; + } + } + else { + if (new == NULL) { + new = UCL_ALLOC (sizeof (struct ucl_variable)); + if (new == NULL) { + return; + } + memset (new, 0, sizeof (struct ucl_variable)); + new->var = strdup (var); + new->var_len = strlen (var); + new->value = strdup (value); + new->value_len = strlen (value); + + DL_APPEND (parser->variables, new); + } + else { + free (new->value); + new->value = strdup (value); + new->value_len = strlen (value); + } + } +} + +void +ucl_parser_set_variables_handler (struct ucl_parser *parser, + ucl_variable_handler handler, void *ud) +{ + parser->var_handler = handler; + parser->var_data = ud; +} + +bool +ucl_parser_add_chunk_priority (struct ucl_parser *parser, const unsigned char *data, + size_t len, unsigned priority) +{ + struct ucl_chunk *chunk; + + if (data == NULL) { + ucl_create_err (&parser->err, "invalid chunk added"); + return false; + } + if (len == 0) { + parser->top_obj = ucl_object_new_full (UCL_OBJECT, priority); + return true; + } + if (parser->state != UCL_STATE_ERROR) { + chunk = UCL_ALLOC (sizeof (struct ucl_chunk)); + if (chunk == NULL) { + ucl_create_err (&parser->err, "cannot allocate chunk structure"); + return false; + } + chunk->begin = data; + chunk->remain = len; + chunk->pos = chunk->begin; + chunk->end = chunk->begin + len; + chunk->line = 1; + chunk->column = 0; + chunk->priority = priority; + LL_PREPEND (parser->chunks, chunk); + parser->recursion ++; + if (parser->recursion > UCL_MAX_RECURSION) { + ucl_create_err (&parser->err, "maximum include nesting limit is reached: %d", + parser->recursion); + return false; + } + return ucl_state_machine (parser); + } + + ucl_create_err (&parser->err, "a parser is in an invalid state"); + + return false; +} + +bool +ucl_parser_add_chunk (struct ucl_parser *parser, const unsigned char *data, + size_t len) +{ + return ucl_parser_add_chunk_priority (parser, data, len, 0); +} + +bool +ucl_parser_add_string (struct ucl_parser *parser, const char *data, + size_t len) +{ + if (data == NULL) { + ucl_create_err (&parser->err, "invalid string added"); + return false; + } + if (len == 0) { + len = strlen (data); + } + + return ucl_parser_add_chunk (parser, (const unsigned char *)data, len); +} diff --git a/contrib/libucl/src/ucl_schema.c b/contrib/libucl/src/ucl_schema.c new file mode 100644 index 0000000..faffe86 --- /dev/null +++ b/contrib/libucl/src/ucl_schema.c @@ -0,0 +1,1014 @@ +/* + * Copyright (c) 2014, 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. + */ + +#include "ucl.h" +#include "ucl_internal.h" +#include "tree.h" +#include "utlist.h" +#ifdef HAVE_STDARG_H +#include <stdarg.h> +#endif +#ifdef HAVE_STDIO_H +#include <stdio.h> +#endif +#ifdef HAVE_REGEX_H +#include <regex.h> +#endif +#ifdef HAVE_MATH_H +#include <math.h> +#endif + +static bool ucl_schema_validate (const ucl_object_t *schema, + const ucl_object_t *obj, bool try_array, + struct ucl_schema_error *err, + const ucl_object_t *root); + +static bool +ucl_string_to_type (const char *input, ucl_type_t *res) +{ + if (strcasecmp (input, "object") == 0) { + *res = UCL_OBJECT; + } + else if (strcasecmp (input, "array") == 0) { + *res = UCL_ARRAY; + } + else if (strcasecmp (input, "integer") == 0) { + *res = UCL_INT; + } + else if (strcasecmp (input, "number") == 0) { + *res = UCL_FLOAT; + } + else if (strcasecmp (input, "string") == 0) { + *res = UCL_STRING; + } + else if (strcasecmp (input, "boolean") == 0) { + *res = UCL_BOOLEAN; + } + else if (strcasecmp (input, "null") == 0) { + *res = UCL_NULL; + } + else { + return false; + } + + return true; +} + +static const char * +ucl_object_type_to_string (ucl_type_t type) +{ + const char *res = "unknown"; + + switch (type) { + case UCL_OBJECT: + res = "object"; + break; + case UCL_ARRAY: + res = "array"; + break; + case UCL_INT: + res = "integer"; + break; + case UCL_FLOAT: + case UCL_TIME: + res = "number"; + break; + case UCL_STRING: + res = "string"; + break; + case UCL_BOOLEAN: + res = "boolean"; + break; + case UCL_NULL: + case UCL_USERDATA: + res = "null"; + break; + } + + return res; +} + +/* + * Create validation error + */ +static void +ucl_schema_create_error (struct ucl_schema_error *err, + enum ucl_schema_error_code code, const ucl_object_t *obj, + const char *fmt, ...) +{ + va_list va; + + if (err != NULL) { + err->code = code; + err->obj = obj; + va_start (va, fmt); + vsnprintf (err->msg, sizeof (err->msg), fmt, va); + va_end (va); + } +} + +/* + * Check whether we have a pattern specified + */ +static const ucl_object_t * +ucl_schema_test_pattern (const ucl_object_t *obj, const char *pattern) +{ + const ucl_object_t *res = NULL; +#ifdef HAVE_REGEX_H + regex_t reg; + const ucl_object_t *elt; + ucl_object_iter_t iter = NULL; + + if (regcomp (®, pattern, REG_EXTENDED | REG_NOSUB) == 0) { + while ((elt = ucl_iterate_object (obj, &iter, true)) != NULL) { + if (regexec (®, ucl_object_key (elt), 0, NULL, 0) == 0) { + res = elt; + break; + } + } + regfree (®); + } +#endif + return res; +} + +/* + * Check dependencies for an object + */ +static bool +ucl_schema_validate_dependencies (const ucl_object_t *deps, + const ucl_object_t *obj, struct ucl_schema_error *err, + const ucl_object_t *root) +{ + const ucl_object_t *elt, *cur, *cur_dep; + ucl_object_iter_t iter = NULL, piter; + bool ret = true; + + while (ret && (cur = ucl_iterate_object (deps, &iter, true)) != NULL) { + elt = ucl_object_find_key (obj, ucl_object_key (cur)); + if (elt != NULL) { + /* Need to check dependencies */ + if (cur->type == UCL_ARRAY) { + piter = NULL; + while (ret && (cur_dep = ucl_iterate_object (cur, &piter, true)) != NULL) { + if (ucl_object_find_key (obj, ucl_object_tostring (cur_dep)) == NULL) { + ucl_schema_create_error (err, UCL_SCHEMA_MISSING_DEPENDENCY, elt, + "dependency %s is missing for key %s", + ucl_object_tostring (cur_dep), ucl_object_key (cur)); + ret = false; + break; + } + } + } + else if (cur->type == UCL_OBJECT) { + ret = ucl_schema_validate (cur, obj, true, err, root); + } + } + } + + return ret; +} + +/* + * Validate object + */ +static bool +ucl_schema_validate_object (const ucl_object_t *schema, + const ucl_object_t *obj, struct ucl_schema_error *err, + const ucl_object_t *root) +{ + const ucl_object_t *elt, *prop, *found, *additional_schema = NULL, + *required = NULL, *pat, *pelt; + ucl_object_iter_t iter = NULL, piter = NULL; + bool ret = true, allow_additional = true; + int64_t minmax; + + while (ret && (elt = ucl_iterate_object (schema, &iter, true)) != NULL) { + if (elt->type == UCL_OBJECT && + strcmp (ucl_object_key (elt), "properties") == 0) { + piter = NULL; + while (ret && (prop = ucl_iterate_object (elt, &piter, true)) != NULL) { + found = ucl_object_find_key (obj, ucl_object_key (prop)); + if (found) { + ret = ucl_schema_validate (prop, found, true, err, root); + } + } + } + else if (strcmp (ucl_object_key (elt), "additionalProperties") == 0) { + if (elt->type == UCL_BOOLEAN) { + if (!ucl_object_toboolean (elt)) { + /* Deny additional fields completely */ + allow_additional = false; + } + } + else if (elt->type == UCL_OBJECT) { + /* Define validator for additional fields */ + additional_schema = elt; + } + else { + ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, elt, + "additionalProperties attribute is invalid in schema"); + ret = false; + break; + } + } + else if (strcmp (ucl_object_key (elt), "required") == 0) { + if (elt->type == UCL_ARRAY) { + required = elt; + } + else { + ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, elt, + "required attribute is invalid in schema"); + ret = false; + break; + } + } + else if (strcmp (ucl_object_key (elt), "minProperties") == 0 + && ucl_object_toint_safe (elt, &minmax)) { + if (obj->len < minmax) { + ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj, + "object has not enough properties: %u, minimum is: %u", + obj->len, (unsigned)minmax); + ret = false; + break; + } + } + else if (strcmp (ucl_object_key (elt), "maxProperties") == 0 + && ucl_object_toint_safe (elt, &minmax)) { + if (obj->len > minmax) { + ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj, + "object has too many properties: %u, maximum is: %u", + obj->len, (unsigned)minmax); + ret = false; + break; + } + } + else if (strcmp (ucl_object_key (elt), "patternProperties") == 0) { + piter = NULL; + while (ret && (prop = ucl_iterate_object (elt, &piter, true)) != NULL) { + found = ucl_schema_test_pattern (obj, ucl_object_key (prop)); + if (found) { + ret = ucl_schema_validate (prop, found, true, err, root); + } + } + } + else if (elt->type == UCL_OBJECT && + strcmp (ucl_object_key (elt), "dependencies") == 0) { + ret = ucl_schema_validate_dependencies (elt, obj, err, root); + } + } + + if (ret) { + /* Additional properties */ + if (!allow_additional || additional_schema != NULL) { + /* Check if we have exactly the same properties in schema and object */ + iter = NULL; + prop = ucl_object_find_key (schema, "properties"); + while ((elt = ucl_iterate_object (obj, &iter, true)) != NULL) { + found = ucl_object_find_key (prop, ucl_object_key (elt)); + if (found == NULL) { + /* Try patternProperties */ + piter = NULL; + pat = ucl_object_find_key (schema, "patternProperties"); + while ((pelt = ucl_iterate_object (pat, &piter, true)) != NULL) { + found = ucl_schema_test_pattern (obj, ucl_object_key (pelt)); + if (found != NULL) { + break; + } + } + } + if (found == NULL) { + if (!allow_additional) { + ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj, + "object has non-allowed property %s", + ucl_object_key (elt)); + ret = false; + break; + } + else if (additional_schema != NULL) { + if (!ucl_schema_validate (additional_schema, elt, true, err, root)) { + ret = false; + break; + } + } + } + } + } + /* Required properties */ + if (required != NULL) { + iter = NULL; + while ((elt = ucl_iterate_object (required, &iter, true)) != NULL) { + if (ucl_object_find_key (obj, ucl_object_tostring (elt)) == NULL) { + ucl_schema_create_error (err, UCL_SCHEMA_MISSING_PROPERTY, obj, + "object has missing property %s", + ucl_object_tostring (elt)); + ret = false; + break; + } + } + } + } + + + return ret; +} + +static bool +ucl_schema_validate_number (const ucl_object_t *schema, + const ucl_object_t *obj, struct ucl_schema_error *err) +{ + const ucl_object_t *elt, *test; + ucl_object_iter_t iter = NULL; + bool ret = true, exclusive = false; + double constraint, val; + const double alpha = 1e-16; + + while (ret && (elt = ucl_iterate_object (schema, &iter, true)) != NULL) { + if ((elt->type == UCL_FLOAT || elt->type == UCL_INT) && + strcmp (ucl_object_key (elt), "multipleOf") == 0) { + constraint = ucl_object_todouble (elt); + if (constraint <= 0) { + ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, elt, + "multipleOf must be greater than zero"); + ret = false; + break; + } + val = ucl_object_todouble (obj); + if (fabs (remainder (val, constraint)) > alpha) { + ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj, + "number %.4f is not multiple of %.4f, remainder is %.7f", + val, constraint); + ret = false; + break; + } + } + else if ((elt->type == UCL_FLOAT || elt->type == UCL_INT) && + strcmp (ucl_object_key (elt), "maximum") == 0) { + constraint = ucl_object_todouble (elt); + test = ucl_object_find_key (schema, "exclusiveMaximum"); + if (test && test->type == UCL_BOOLEAN) { + exclusive = ucl_object_toboolean (test); + } + val = ucl_object_todouble (obj); + if (val > constraint || (exclusive && val >= constraint)) { + ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj, + "number is too big: %.3f, maximum is: %.3f", + val, constraint); + ret = false; + break; + } + } + else if ((elt->type == UCL_FLOAT || elt->type == UCL_INT) && + strcmp (ucl_object_key (elt), "minimum") == 0) { + constraint = ucl_object_todouble (elt); + test = ucl_object_find_key (schema, "exclusiveMinimum"); + if (test && test->type == UCL_BOOLEAN) { + exclusive = ucl_object_toboolean (test); + } + val = ucl_object_todouble (obj); + if (val < constraint || (exclusive && val <= constraint)) { + ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj, + "number is too small: %.3f, minimum is: %.3f", + val, constraint); + ret = false; + break; + } + } + } + + return ret; +} + +static bool +ucl_schema_validate_string (const ucl_object_t *schema, + const ucl_object_t *obj, struct ucl_schema_error *err) +{ + const ucl_object_t *elt; + ucl_object_iter_t iter = NULL; + bool ret = true; + int64_t constraint; +#ifdef HAVE_REGEX_H + regex_t re; +#endif + + while (ret && (elt = ucl_iterate_object (schema, &iter, true)) != NULL) { + if (elt->type == UCL_INT && + strcmp (ucl_object_key (elt), "maxLength") == 0) { + constraint = ucl_object_toint (elt); + if (obj->len > constraint) { + ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj, + "string is too big: %.3f, maximum is: %.3f", + obj->len, constraint); + ret = false; + break; + } + } + else if (elt->type == UCL_INT && + strcmp (ucl_object_key (elt), "minLength") == 0) { + constraint = ucl_object_toint (elt); + if (obj->len < constraint) { + ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj, + "string is too short: %.3f, minimum is: %.3f", + obj->len, constraint); + ret = false; + break; + } + } +#ifdef HAVE_REGEX_H + else if (elt->type == UCL_STRING && + strcmp (ucl_object_key (elt), "pattern") == 0) { + if (regcomp (&re, ucl_object_tostring (elt), + REG_EXTENDED | REG_NOSUB) != 0) { + ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, elt, + "cannot compile pattern %s", ucl_object_tostring (elt)); + ret = false; + break; + } + if (regexec (&re, ucl_object_tostring (obj), 0, NULL, 0) != 0) { + ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj, + "string doesn't match regexp %s", + ucl_object_tostring (elt)); + ret = false; + } + regfree (&re); + } +#endif + } + + return ret; +} + +struct ucl_compare_node { + const ucl_object_t *obj; + TREE_ENTRY(ucl_compare_node) link; + struct ucl_compare_node *next; +}; + +typedef TREE_HEAD(_tree, ucl_compare_node) ucl_compare_tree_t; + +TREE_DEFINE(ucl_compare_node, link) + +static int +ucl_schema_elt_compare (struct ucl_compare_node *n1, struct ucl_compare_node *n2) +{ + const ucl_object_t *o1 = n1->obj, *o2 = n2->obj; + + return ucl_object_compare (o1, o2); +} + +static bool +ucl_schema_array_is_unique (const ucl_object_t *obj, struct ucl_schema_error *err) +{ + ucl_compare_tree_t tree = TREE_INITIALIZER (ucl_schema_elt_compare); + ucl_object_iter_t iter = NULL; + const ucl_object_t *elt; + struct ucl_compare_node *node, test, *nodes = NULL, *tmp; + bool ret = true; + + while ((elt = ucl_iterate_object (obj, &iter, true)) != NULL) { + test.obj = elt; + node = TREE_FIND (&tree, ucl_compare_node, link, &test); + if (node != NULL) { + ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, elt, + "duplicate values detected while uniqueItems is true"); + ret = false; + break; + } + node = calloc (1, sizeof (*node)); + if (node == NULL) { + ucl_schema_create_error (err, UCL_SCHEMA_UNKNOWN, elt, + "cannot allocate tree node"); + ret = false; + break; + } + node->obj = elt; + TREE_INSERT (&tree, ucl_compare_node, link, node); + LL_PREPEND (nodes, node); + } + + LL_FOREACH_SAFE (nodes, node, tmp) { + free (node); + } + + return ret; +} + +static bool +ucl_schema_validate_array (const ucl_object_t *schema, + const ucl_object_t *obj, struct ucl_schema_error *err, + const ucl_object_t *root) +{ + const ucl_object_t *elt, *it, *found, *additional_schema = NULL, + *first_unvalidated = NULL; + ucl_object_iter_t iter = NULL, piter = NULL; + bool ret = true, allow_additional = true, need_unique = false; + int64_t minmax; + + while (ret && (elt = ucl_iterate_object (schema, &iter, true)) != NULL) { + if (strcmp (ucl_object_key (elt), "items") == 0) { + if (elt->type == UCL_ARRAY) { + found = obj->value.av; + while (ret && (it = ucl_iterate_object (elt, &piter, true)) != NULL) { + if (found) { + ret = ucl_schema_validate (it, found, false, err, root); + found = found->next; + } + } + if (found != NULL) { + /* The first element that is not validated */ + first_unvalidated = found; + } + } + else if (elt->type == UCL_OBJECT) { + /* Validate all items using the specified schema */ + while (ret && (it = ucl_iterate_object (obj, &piter, true)) != NULL) { + ret = ucl_schema_validate (elt, it, false, err, root); + } + } + else { + ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, elt, + "items attribute is invalid in schema"); + ret = false; + break; + } + } + else if (strcmp (ucl_object_key (elt), "additionalItems") == 0) { + if (elt->type == UCL_BOOLEAN) { + if (!ucl_object_toboolean (elt)) { + /* Deny additional fields completely */ + allow_additional = false; + } + } + else if (elt->type == UCL_OBJECT) { + /* Define validator for additional fields */ + additional_schema = elt; + } + else { + ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, elt, + "additionalItems attribute is invalid in schema"); + ret = false; + break; + } + } + else if (elt->type == UCL_BOOLEAN && + strcmp (ucl_object_key (elt), "uniqueItems") == 0) { + need_unique = ucl_object_toboolean (elt); + } + else if (strcmp (ucl_object_key (elt), "minItems") == 0 + && ucl_object_toint_safe (elt, &minmax)) { + if (obj->len < minmax) { + ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj, + "array has not enough items: %u, minimum is: %u", + obj->len, (unsigned)minmax); + ret = false; + break; + } + } + else if (strcmp (ucl_object_key (elt), "maxItems") == 0 + && ucl_object_toint_safe (elt, &minmax)) { + if (obj->len > minmax) { + ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj, + "array has too many items: %u, maximum is: %u", + obj->len, (unsigned)minmax); + ret = false; + break; + } + } + } + + if (ret) { + /* Additional properties */ + if (!allow_additional || additional_schema != NULL) { + if (first_unvalidated != NULL) { + if (!allow_additional) { + ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj, + "array has undefined item"); + ret = false; + } + else if (additional_schema != NULL) { + elt = first_unvalidated; + while (elt) { + if (!ucl_schema_validate (additional_schema, elt, false, + err, root)) { + ret = false; + break; + } + elt = elt->next; + } + } + } + } + /* Required properties */ + if (ret && need_unique) { + ret = ucl_schema_array_is_unique (obj, err); + } + } + + return ret; +} + +/* + * Returns whether this object is allowed for this type + */ +static bool +ucl_schema_type_is_allowed (const ucl_object_t *type, const ucl_object_t *obj, + struct ucl_schema_error *err) +{ + ucl_object_iter_t iter = NULL; + const ucl_object_t *elt; + const char *type_str; + ucl_type_t t; + + if (type == NULL) { + /* Any type is allowed */ + return true; + } + + if (type->type == UCL_ARRAY) { + /* One of allowed types */ + while ((elt = ucl_iterate_object (type, &iter, true)) != NULL) { + if (ucl_schema_type_is_allowed (elt, obj, err)) { + return true; + } + } + } + else if (type->type == UCL_STRING) { + type_str = ucl_object_tostring (type); + if (!ucl_string_to_type (type_str, &t)) { + ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, type, + "Type attribute is invalid in schema"); + return false; + } + if (obj->type != t) { + /* Some types are actually compatible */ + if (obj->type == UCL_TIME && t == UCL_FLOAT) { + return true; + } + else if (obj->type == UCL_INT && t == UCL_FLOAT) { + return true; + } + else { + ucl_schema_create_error (err, UCL_SCHEMA_TYPE_MISMATCH, obj, + "Invalid type of %s, expected %s", + ucl_object_type_to_string (obj->type), + ucl_object_type_to_string (t)); + } + } + else { + /* Types are equal */ + return true; + } + } + + return false; +} + +/* + * Check if object is equal to one of elements of enum + */ +static bool +ucl_schema_validate_enum (const ucl_object_t *en, const ucl_object_t *obj, + struct ucl_schema_error *err) +{ + ucl_object_iter_t iter = NULL; + const ucl_object_t *elt; + bool ret = false; + + while ((elt = ucl_iterate_object (en, &iter, true)) != NULL) { + if (ucl_object_compare (elt, obj) == 0) { + ret = true; + break; + } + } + + if (!ret) { + ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj, + "object is not one of enumerated patterns"); + } + + return ret; +} + + +/* + * Check a single ref component + */ +static const ucl_object_t * +ucl_schema_resolve_ref_component (const ucl_object_t *cur, + const char *refc, int len, + struct ucl_schema_error *err) +{ + const ucl_object_t *res = NULL; + char *err_str; + int num, i; + + if (cur->type == UCL_OBJECT) { + /* Find a key inside an object */ + res = ucl_object_find_keyl (cur, refc, len); + if (res == NULL) { + ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, cur, + "reference %s is invalid, missing path component", refc); + return NULL; + } + } + else if (cur->type == UCL_ARRAY) { + /* We must figure out a number inside array */ + num = strtoul (refc, &err_str, 10); + if (err_str != NULL && *err_str != '/' && *err_str != '\0') { + ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, cur, + "reference %s is invalid, invalid item number", refc); + return NULL; + } + res = cur->value.av; + i = 0; + while (res != NULL) { + if (i == num) { + break; + } + res = res->next; + } + if (res == NULL) { + ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, cur, + "reference %s is invalid, item number %d does not exist", + refc, num); + return NULL; + } + } + else { + ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, res, + "reference %s is invalid, contains primitive object in the path", + refc); + return NULL; + } + + return res; +} +/* + * Find reference schema + */ +static const ucl_object_t * +ucl_schema_resolve_ref (const ucl_object_t *root, const char *ref, + struct ucl_schema_error *err) +{ + const char *p, *c; + const ucl_object_t *res = NULL; + + + if (ref[0] != '#') { + ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, root, + "reference %s is invalid, not started with #", ref); + return NULL; + } + if (ref[1] == '/') { + p = &ref[2]; + } + else if (ref[1] == '\0') { + return root; + } + else { + ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, root, + "reference %s is invalid, not started with #/", ref); + return NULL; + } + + c = p; + res = root; + + while (*p != '\0') { + if (*p == '/') { + if (p - c == 0) { + ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, res, + "reference %s is invalid, empty path component", ref); + return NULL; + } + /* Now we have some url part, so we need to figure out where we are */ + res = ucl_schema_resolve_ref_component (res, c, p - c, err); + if (res == NULL) { + return NULL; + } + c = p + 1; + } + p ++; + } + + if (p - c != 0) { + res = ucl_schema_resolve_ref_component (res, c, p - c, err); + } + + if (res == NULL || res->type != UCL_OBJECT) { + ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, res, + "reference %s is invalid, cannot find specified object", + ref); + return NULL; + } + + return res; +} + +static bool +ucl_schema_validate_values (const ucl_object_t *schema, const ucl_object_t *obj, + struct ucl_schema_error *err) +{ + const ucl_object_t *elt, *cur; + int64_t constraint, i; + + elt = ucl_object_find_key (schema, "maxValues"); + if (elt != NULL && elt->type == UCL_INT) { + constraint = ucl_object_toint (elt); + cur = obj; + i = 0; + while (cur) { + if (i > constraint) { + ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj, + "object has more values than defined: %ld", + (long int)constraint); + return false; + } + i ++; + cur = cur->next; + } + } + elt = ucl_object_find_key (schema, "minValues"); + if (elt != NULL && elt->type == UCL_INT) { + constraint = ucl_object_toint (elt); + cur = obj; + i = 0; + while (cur) { + if (i >= constraint) { + break; + } + i ++; + cur = cur->next; + } + if (i < constraint) { + ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj, + "object has less values than defined: %ld", + (long int)constraint); + return false; + } + } + + return true; +} + +static bool +ucl_schema_validate (const ucl_object_t *schema, + const ucl_object_t *obj, bool try_array, + struct ucl_schema_error *err, + const ucl_object_t *root) +{ + const ucl_object_t *elt, *cur; + ucl_object_iter_t iter = NULL; + bool ret; + + if (schema->type != UCL_OBJECT) { + ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, schema, + "schema is %s instead of object", ucl_object_type_to_string (schema->type)); + return false; + } + + if (try_array) { + /* + * Special case for multiple values + */ + if (!ucl_schema_validate_values (schema, obj, err)) { + return false; + } + LL_FOREACH (obj, cur) { + if (!ucl_schema_validate (schema, cur, false, err, root)) { + return false; + } + } + return true; + } + + elt = ucl_object_find_key (schema, "enum"); + if (elt != NULL && elt->type == UCL_ARRAY) { + if (!ucl_schema_validate_enum (elt, obj, err)) { + return false; + } + } + + elt = ucl_object_find_key (schema, "allOf"); + if (elt != NULL && elt->type == UCL_ARRAY) { + iter = NULL; + while ((cur = ucl_iterate_object (elt, &iter, true)) != NULL) { + ret = ucl_schema_validate (cur, obj, true, err, root); + if (!ret) { + return false; + } + } + } + + elt = ucl_object_find_key (schema, "anyOf"); + if (elt != NULL && elt->type == UCL_ARRAY) { + iter = NULL; + while ((cur = ucl_iterate_object (elt, &iter, true)) != NULL) { + ret = ucl_schema_validate (cur, obj, true, err, root); + if (ret) { + break; + } + } + if (!ret) { + return false; + } + else { + /* Reset error */ + err->code = UCL_SCHEMA_OK; + } + } + + elt = ucl_object_find_key (schema, "oneOf"); + if (elt != NULL && elt->type == UCL_ARRAY) { + iter = NULL; + ret = false; + while ((cur = ucl_iterate_object (elt, &iter, true)) != NULL) { + if (!ret) { + ret = ucl_schema_validate (cur, obj, true, err, root); + } + else if (ucl_schema_validate (cur, obj, true, err, root)) { + ret = false; + break; + } + } + if (!ret) { + return false; + } + } + + elt = ucl_object_find_key (schema, "not"); + if (elt != NULL && elt->type == UCL_OBJECT) { + if (ucl_schema_validate (elt, obj, true, err, root)) { + return false; + } + else { + /* Reset error */ + err->code = UCL_SCHEMA_OK; + } + } + + elt = ucl_object_find_key (schema, "$ref"); + if (elt != NULL) { + cur = ucl_schema_resolve_ref (root, ucl_object_tostring (elt), err); + if (cur == NULL) { + return false; + } + if (!ucl_schema_validate (cur, obj, try_array, err, root)) { + return false; + } + } + + elt = ucl_object_find_key (schema, "type"); + if (!ucl_schema_type_is_allowed (elt, obj, err)) { + return false; + } + + switch (obj->type) { + case UCL_OBJECT: + return ucl_schema_validate_object (schema, obj, err, root); + break; + case UCL_ARRAY: + return ucl_schema_validate_array (schema, obj, err, root); + break; + case UCL_INT: + case UCL_FLOAT: + return ucl_schema_validate_number (schema, obj, err); + break; + case UCL_STRING: + return ucl_schema_validate_string (schema, obj, err); + break; + default: + break; + } + + return true; +} + +bool +ucl_object_validate (const ucl_object_t *schema, + const ucl_object_t *obj, struct ucl_schema_error *err) +{ + return ucl_schema_validate (schema, obj, true, err, schema); +} diff --git a/contrib/libucl/src/ucl_util.c b/contrib/libucl/src/ucl_util.c new file mode 100644 index 0000000..41702e9 --- /dev/null +++ b/contrib/libucl/src/ucl_util.c @@ -0,0 +1,2407 @@ +/* Copyright (c) 2013, 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 ''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. + */ + +#include "ucl.h" +#include "ucl_internal.h" +#include "ucl_chartable.h" + +#include <glob.h> + +#ifdef HAVE_LIBGEN_H +#include <libgen.h> /* For dirname */ +#endif + +#ifdef HAVE_OPENSSL +#include <openssl/err.h> +#include <openssl/sha.h> +#include <openssl/rsa.h> +#include <openssl/ssl.h> +#include <openssl/evp.h> +#endif + +#ifdef CURL_FOUND +#include <curl/curl.h> +#endif +#ifdef HAVE_FETCH_H +#include <fetch.h> +#endif + +#ifdef _WIN32 +#include <windows.h> + +#ifndef PROT_READ +#define PROT_READ 1 +#endif +#ifndef PROT_WRITE +#define PROT_WRITE 2 +#endif +#ifndef PROT_READWRITE +#define PROT_READWRITE 3 +#endif +#ifndef MAP_SHARED +#define MAP_SHARED 1 +#endif +#ifndef MAP_PRIVATE +#define MAP_PRIVATE 2 +#endif +#ifndef MAP_FAILED +#define MAP_FAILED ((void *) -1) +#endif + +static void *ucl_mmap(char *addr, size_t length, int prot, int access, int fd, off_t offset) +{ + void *map = NULL; + HANDLE handle = INVALID_HANDLE_VALUE; + + switch (prot) { + default: + case PROT_READ: + { + handle = CreateFileMapping((HANDLE) _get_osfhandle(fd), 0, PAGE_READONLY, 0, length, 0); + if (!handle) break; + map = (void *) MapViewOfFile(handle, FILE_MAP_READ, 0, 0, length); + CloseHandle(handle); + break; + } + case PROT_WRITE: + { + handle = CreateFileMapping((HANDLE) _get_osfhandle(fd), 0, PAGE_READWRITE, 0, length, 0); + if (!handle) break; + map = (void *) MapViewOfFile(handle, FILE_MAP_WRITE, 0, 0, length); + CloseHandle(handle); + break; + } + case PROT_READWRITE: + { + handle = CreateFileMapping((HANDLE) _get_osfhandle(fd), 0, PAGE_READWRITE, 0, length, 0); + if (!handle) break; + map = (void *) MapViewOfFile(handle, FILE_MAP_ALL_ACCESS, 0, 0, length); + CloseHandle(handle); + break; + } + } + if (map == (void *) NULL) { + return (void *) MAP_FAILED; + } + return (void *) ((char *) map + offset); +} + +static int ucl_munmap(void *map,size_t length) +{ + if (!UnmapViewOfFile(map)) { + return(-1); + } + return(0); +} + +static char* ucl_realpath(const char *path, char *resolved_path) { + char *p; + char tmp[MAX_PATH + 1]; + strncpy(tmp, path, sizeof(tmp)-1); + p = tmp; + while(*p) { + if (*p == '/') *p = '\\'; + p++; + } + return _fullpath(resolved_path, tmp, MAX_PATH); +} +#else +#define ucl_mmap mmap +#define ucl_munmap munmap +#define ucl_realpath realpath +#endif + +typedef void (*ucl_object_dtor) (ucl_object_t *obj); +static void ucl_object_free_internal (ucl_object_t *obj, bool allow_rec, + ucl_object_dtor dtor); +static void ucl_object_dtor_unref (ucl_object_t *obj); + +static void +ucl_object_dtor_free (ucl_object_t *obj) +{ + if (obj->trash_stack[UCL_TRASH_KEY] != NULL) { + UCL_FREE (obj->hh.keylen, obj->trash_stack[UCL_TRASH_KEY]); + } + if (obj->trash_stack[UCL_TRASH_VALUE] != NULL) { + UCL_FREE (obj->len, obj->trash_stack[UCL_TRASH_VALUE]); + } + /* Do not free ephemeral objects */ + if ((obj->flags & UCL_OBJECT_EPHEMERAL) == 0) { + if (obj->type != UCL_USERDATA) { + UCL_FREE (sizeof (ucl_object_t), obj); + } + else { + struct ucl_object_userdata *ud = (struct ucl_object_userdata *)obj; + if (ud->dtor) { + ud->dtor (obj->value.ud); + } + UCL_FREE (sizeof (*ud), obj); + } + } +} + +/* + * This is a helper function that performs exactly the same as + * `ucl_object_unref` but it doesn't iterate over elements allowing + * to use it for individual elements of arrays and multiple values + */ +static void +ucl_object_dtor_unref_single (ucl_object_t *obj) +{ + if (obj != NULL) { +#ifdef HAVE_ATOMIC_BUILTINS + unsigned int rc = __sync_sub_and_fetch (&obj->ref, 1); + if (rc == 0) { +#else + if (--obj->ref == 0) { +#endif + ucl_object_free_internal (obj, false, ucl_object_dtor_unref); + } + } +} + +static void +ucl_object_dtor_unref (ucl_object_t *obj) +{ + if (obj->ref == 0) { + ucl_object_dtor_free (obj); + } + else { + /* This may cause dtor unref being called one more time */ + ucl_object_dtor_unref_single (obj); + } +} + +static void +ucl_object_free_internal (ucl_object_t *obj, bool allow_rec, ucl_object_dtor dtor) +{ + ucl_object_t *sub, *tmp; + + while (obj != NULL) { + if (obj->type == UCL_ARRAY) { + sub = obj->value.av; + while (sub != NULL) { + tmp = sub->next; + dtor (sub); + sub = tmp; + } + } + else if (obj->type == UCL_OBJECT) { + if (obj->value.ov != NULL) { + ucl_hash_destroy (obj->value.ov, (ucl_hash_free_func *)dtor); + } + } + tmp = obj->next; + dtor (obj); + obj = tmp; + + if (!allow_rec) { + break; + } + } +} + +void +ucl_object_free (ucl_object_t *obj) +{ + ucl_object_free_internal (obj, true, ucl_object_dtor_free); +} + +size_t +ucl_unescape_json_string (char *str, size_t len) +{ + char *t = str, *h = str; + int i, uval; + + if (len <= 1) { + return len; + } + /* t is target (tortoise), h is source (hare) */ + + while (len) { + if (*h == '\\') { + h ++; + switch (*h) { + case 'n': + *t++ = '\n'; + break; + case 'r': + *t++ = '\r'; + break; + case 'b': + *t++ = '\b'; + break; + case 't': + *t++ = '\t'; + break; + case 'f': + *t++ = '\f'; + break; + case '\\': + *t++ = '\\'; + break; + case '"': + *t++ = '"'; + break; + case 'u': + /* Unicode escape */ + uval = 0; + if (len > 3) { + for (i = 0; i < 4; i++) { + uval <<= 4; + if (isdigit (h[i])) { + uval += h[i] - '0'; + } + else if (h[i] >= 'a' && h[i] <= 'f') { + uval += h[i] - 'a' + 10; + } + else if (h[i] >= 'A' && h[i] <= 'F') { + uval += h[i] - 'A' + 10; + } + else { + break; + } + } + h += 3; + len -= 3; + /* Encode */ + if(uval < 0x80) { + t[0] = (char)uval; + t ++; + } + else if(uval < 0x800) { + t[0] = 0xC0 + ((uval & 0x7C0) >> 6); + t[1] = 0x80 + ((uval & 0x03F)); + t += 2; + } + else if(uval < 0x10000) { + t[0] = 0xE0 + ((uval & 0xF000) >> 12); + t[1] = 0x80 + ((uval & 0x0FC0) >> 6); + t[2] = 0x80 + ((uval & 0x003F)); + t += 3; + } + else if(uval <= 0x10FFFF) { + t[0] = 0xF0 + ((uval & 0x1C0000) >> 18); + t[1] = 0x80 + ((uval & 0x03F000) >> 12); + t[2] = 0x80 + ((uval & 0x000FC0) >> 6); + t[3] = 0x80 + ((uval & 0x00003F)); + t += 4; + } + else { + *t++ = '?'; + } + } + else { + *t++ = 'u'; + } + break; + default: + *t++ = *h; + break; + } + h ++; + len --; + } + else { + *t++ = *h++; + } + len --; + } + *t = '\0'; + + return (t - str); +} + +char * +ucl_copy_key_trash (const ucl_object_t *obj) +{ + ucl_object_t *deconst; + + if (obj == NULL) { + return NULL; + } + if (obj->trash_stack[UCL_TRASH_KEY] == NULL && obj->key != NULL) { + deconst = __DECONST (ucl_object_t *, obj); + deconst->trash_stack[UCL_TRASH_KEY] = malloc (obj->keylen + 1); + if (deconst->trash_stack[UCL_TRASH_KEY] != NULL) { + memcpy (deconst->trash_stack[UCL_TRASH_KEY], obj->key, obj->keylen); + deconst->trash_stack[UCL_TRASH_KEY][obj->keylen] = '\0'; + } + deconst->key = obj->trash_stack[UCL_TRASH_KEY]; + deconst->flags |= UCL_OBJECT_ALLOCATED_KEY; + } + + return obj->trash_stack[UCL_TRASH_KEY]; +} + +char * +ucl_copy_value_trash (const ucl_object_t *obj) +{ + ucl_object_t *deconst; + + if (obj == NULL) { + return NULL; + } + if (obj->trash_stack[UCL_TRASH_VALUE] == NULL) { + deconst = __DECONST (ucl_object_t *, obj); + if (obj->type == UCL_STRING) { + + /* Special case for strings */ + deconst->trash_stack[UCL_TRASH_VALUE] = malloc (obj->len + 1); + if (deconst->trash_stack[UCL_TRASH_VALUE] != NULL) { + memcpy (deconst->trash_stack[UCL_TRASH_VALUE], obj->value.sv, obj->len); + deconst->trash_stack[UCL_TRASH_VALUE][obj->len] = '\0'; + deconst->value.sv = obj->trash_stack[UCL_TRASH_VALUE]; + } + } + else { + /* Just emit value in json notation */ + deconst->trash_stack[UCL_TRASH_VALUE] = ucl_object_emit_single_json (obj); + deconst->len = strlen (obj->trash_stack[UCL_TRASH_VALUE]); + } + deconst->flags |= UCL_OBJECT_ALLOCATED_VALUE; + } + return obj->trash_stack[UCL_TRASH_VALUE]; +} + +UCL_EXTERN ucl_object_t* +ucl_parser_get_object (struct ucl_parser *parser) +{ + if (parser->state != UCL_STATE_ERROR && parser->top_obj != NULL) { + return ucl_object_ref (parser->top_obj); + } + + return NULL; +} + +UCL_EXTERN void +ucl_parser_free (struct ucl_parser *parser) +{ + struct ucl_stack *stack, *stmp; + struct ucl_macro *macro, *mtmp; + struct ucl_chunk *chunk, *ctmp; + struct ucl_pubkey *key, *ktmp; + struct ucl_variable *var, *vtmp; + + if (parser == NULL) { + return; + } + + if (parser->top_obj != NULL) { + ucl_object_unref (parser->top_obj); + } + + LL_FOREACH_SAFE (parser->stack, stack, stmp) { + free (stack); + } + HASH_ITER (hh, parser->macroes, macro, mtmp) { + free (macro->name); + HASH_DEL (parser->macroes, macro); + UCL_FREE (sizeof (struct ucl_macro), macro); + } + LL_FOREACH_SAFE (parser->chunks, chunk, ctmp) { + UCL_FREE (sizeof (struct ucl_chunk), chunk); + } + LL_FOREACH_SAFE (parser->keys, key, ktmp) { + UCL_FREE (sizeof (struct ucl_pubkey), key); + } + LL_FOREACH_SAFE (parser->variables, var, vtmp) { + free (var->value); + free (var->var); + UCL_FREE (sizeof (struct ucl_variable), var); + } + + if (parser->err != NULL) { + utstring_free (parser->err); + } + + if (parser->cur_file) { + free (parser->cur_file); + } + + UCL_FREE (sizeof (struct ucl_parser), parser); +} + +UCL_EXTERN const char * +ucl_parser_get_error(struct ucl_parser *parser) +{ + if (parser == NULL) { + return NULL; + } + + if (parser->err == NULL) + return NULL; + + return utstring_body(parser->err); +} + +UCL_EXTERN bool +ucl_pubkey_add (struct ucl_parser *parser, const unsigned char *key, size_t len) +{ +#ifndef HAVE_OPENSSL + ucl_create_err (&parser->err, "cannot check signatures without openssl"); + return false; +#else +# if (OPENSSL_VERSION_NUMBER < 0x10000000L) + ucl_create_err (&parser->err, "cannot check signatures, openssl version is unsupported"); + return EXIT_FAILURE; +# else + struct ucl_pubkey *nkey; + BIO *mem; + + mem = BIO_new_mem_buf ((void *)key, len); + nkey = UCL_ALLOC (sizeof (struct ucl_pubkey)); + if (nkey == NULL) { + ucl_create_err (&parser->err, "cannot allocate memory for key"); + return false; + } + nkey->key = PEM_read_bio_PUBKEY (mem, &nkey->key, NULL, NULL); + BIO_free (mem); + if (nkey->key == NULL) { + UCL_FREE (sizeof (struct ucl_pubkey), nkey); + ucl_create_err (&parser->err, "%s", + ERR_error_string (ERR_get_error (), NULL)); + return false; + } + LL_PREPEND (parser->keys, nkey); +# endif +#endif + return true; +} + +#ifdef CURL_FOUND +struct ucl_curl_cbdata { + unsigned char *buf; + size_t buflen; +}; + +static size_t +ucl_curl_write_callback (void* contents, size_t size, size_t nmemb, void* ud) +{ + struct ucl_curl_cbdata *cbdata = ud; + size_t realsize = size * nmemb; + + cbdata->buf = realloc (cbdata->buf, cbdata->buflen + realsize + 1); + if (cbdata->buf == NULL) { + return 0; + } + + memcpy (&(cbdata->buf[cbdata->buflen]), contents, realsize); + cbdata->buflen += realsize; + cbdata->buf[cbdata->buflen] = 0; + + return realsize; +} +#endif + +/** + * Fetch a url and save results to the memory buffer + * @param url url to fetch + * @param len length of url + * @param buf target buffer + * @param buflen target length + * @return + */ +static bool +ucl_fetch_url (const unsigned char *url, unsigned char **buf, size_t *buflen, + UT_string **err, bool must_exist) +{ + +#ifdef HAVE_FETCH_H + struct url *fetch_url; + struct url_stat us; + FILE *in; + + fetch_url = fetchParseURL (url); + if (fetch_url == NULL) { + ucl_create_err (err, "invalid URL %s: %s", + url, strerror (errno)); + return false; + } + if ((in = fetchXGet (fetch_url, &us, "")) == NULL) { + if (!must_exist) { + ucl_create_err (err, "cannot fetch URL %s: %s", + url, strerror (errno)); + } + fetchFreeURL (fetch_url); + return false; + } + + *buflen = us.size; + *buf = malloc (*buflen); + if (*buf == NULL) { + ucl_create_err (err, "cannot allocate buffer for URL %s: %s", + url, strerror (errno)); + fclose (in); + fetchFreeURL (fetch_url); + return false; + } + + if (fread (*buf, *buflen, 1, in) != 1) { + ucl_create_err (err, "cannot read URL %s: %s", + url, strerror (errno)); + fclose (in); + fetchFreeURL (fetch_url); + return false; + } + + fetchFreeURL (fetch_url); + return true; +#elif defined(CURL_FOUND) + CURL *curl; + int r; + struct ucl_curl_cbdata cbdata; + + curl = curl_easy_init (); + if (curl == NULL) { + ucl_create_err (err, "CURL interface is broken"); + return false; + } + if ((r = curl_easy_setopt (curl, CURLOPT_URL, url)) != CURLE_OK) { + ucl_create_err (err, "invalid URL %s: %s", + url, curl_easy_strerror (r)); + curl_easy_cleanup (curl); + return false; + } + curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, ucl_curl_write_callback); + cbdata.buf = *buf; + cbdata.buflen = *buflen; + curl_easy_setopt (curl, CURLOPT_WRITEDATA, &cbdata); + + if ((r = curl_easy_perform (curl)) != CURLE_OK) { + if (!must_exist) { + ucl_create_err (err, "error fetching URL %s: %s", + url, curl_easy_strerror (r)); + } + curl_easy_cleanup (curl); + if (cbdata.buf) { + free (cbdata.buf); + } + return false; + } + *buf = cbdata.buf; + *buflen = cbdata.buflen; + + return true; +#else + ucl_create_err (err, "URL support is disabled"); + return false; +#endif +} + +/** + * Fetch a file and save results to the memory buffer + * @param filename filename to fetch + * @param len length of filename + * @param buf target buffer + * @param buflen target length + * @return + */ +static bool +ucl_fetch_file (const unsigned char *filename, unsigned char **buf, size_t *buflen, + UT_string **err, bool must_exist) +{ + int fd; + struct stat st; + + if (stat (filename, &st) == -1 || !S_ISREG (st.st_mode)) { + if (must_exist) { + ucl_create_err (err, "cannot stat file %s: %s", + filename, strerror (errno)); + } + return false; + } + if (st.st_size == 0) { + /* Do not map empty files */ + *buf = ""; + *buflen = 0; + } + else { + if ((fd = open (filename, O_RDONLY)) == -1) { + ucl_create_err (err, "cannot open file %s: %s", + filename, strerror (errno)); + return false; + } + if ((*buf = ucl_mmap (NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0)) == MAP_FAILED) { + close (fd); + ucl_create_err (err, "cannot mmap file %s: %s", + filename, strerror (errno)); + return false; + } + *buflen = st.st_size; + close (fd); + } + + return true; +} + + +#if (defined(HAVE_OPENSSL) && OPENSSL_VERSION_NUMBER >= 0x10000000L) +static inline bool +ucl_sig_check (const unsigned char *data, size_t datalen, + const unsigned char *sig, size_t siglen, struct ucl_parser *parser) +{ + struct ucl_pubkey *key; + char dig[EVP_MAX_MD_SIZE]; + unsigned int diglen; + EVP_PKEY_CTX *key_ctx; + EVP_MD_CTX *sign_ctx = NULL; + + sign_ctx = EVP_MD_CTX_create (); + + LL_FOREACH (parser->keys, key) { + key_ctx = EVP_PKEY_CTX_new (key->key, NULL); + if (key_ctx != NULL) { + if (EVP_PKEY_verify_init (key_ctx) <= 0) { + EVP_PKEY_CTX_free (key_ctx); + continue; + } + if (EVP_PKEY_CTX_set_rsa_padding (key_ctx, RSA_PKCS1_PADDING) <= 0) { + EVP_PKEY_CTX_free (key_ctx); + continue; + } + if (EVP_PKEY_CTX_set_signature_md (key_ctx, EVP_sha256 ()) <= 0) { + EVP_PKEY_CTX_free (key_ctx); + continue; + } + EVP_DigestInit (sign_ctx, EVP_sha256 ()); + EVP_DigestUpdate (sign_ctx, data, datalen); + EVP_DigestFinal (sign_ctx, dig, &diglen); + + if (EVP_PKEY_verify (key_ctx, sig, siglen, dig, diglen) == 1) { + EVP_MD_CTX_destroy (sign_ctx); + EVP_PKEY_CTX_free (key_ctx); + return true; + } + + EVP_PKEY_CTX_free (key_ctx); + } + } + + EVP_MD_CTX_destroy (sign_ctx); + + return false; +} +#endif + +/** + * Include an url to configuration + * @param data + * @param len + * @param parser + * @param err + * @return + */ +static bool +ucl_include_url (const unsigned char *data, size_t len, + struct ucl_parser *parser, bool check_signature, bool must_exist, + unsigned priority) +{ + + bool res; + unsigned char *buf = NULL; + size_t buflen = 0; + struct ucl_chunk *chunk; + char urlbuf[PATH_MAX]; + int prev_state; + + snprintf (urlbuf, sizeof (urlbuf), "%.*s", (int)len, data); + + if (!ucl_fetch_url (urlbuf, &buf, &buflen, &parser->err, must_exist)) { + return (!must_exist || false); + } + + if (check_signature) { +#if (defined(HAVE_OPENSSL) && OPENSSL_VERSION_NUMBER >= 0x10000000L) + unsigned char *sigbuf = NULL; + size_t siglen = 0; + /* We need to check signature first */ + snprintf (urlbuf, sizeof (urlbuf), "%.*s.sig", (int)len, data); + if (!ucl_fetch_url (urlbuf, &sigbuf, &siglen, &parser->err, true)) { + return false; + } + if (!ucl_sig_check (buf, buflen, sigbuf, siglen, parser)) { + ucl_create_err (&parser->err, "cannot verify url %s: %s", + urlbuf, + ERR_error_string (ERR_get_error (), NULL)); + if (siglen > 0) { + ucl_munmap (sigbuf, siglen); + } + return false; + } + if (siglen > 0) { + ucl_munmap (sigbuf, siglen); + } +#endif + } + + prev_state = parser->state; + parser->state = UCL_STATE_INIT; + + res = ucl_parser_add_chunk_priority (parser, buf, buflen, priority); + if (res == true) { + /* Remove chunk from the stack */ + chunk = parser->chunks; + if (chunk != NULL) { + parser->chunks = chunk->next; + UCL_FREE (sizeof (struct ucl_chunk), chunk); + } + } + + parser->state = prev_state; + free (buf); + + return res; +} + +/** + * Include a single file to the parser + * @param data + * @param len + * @param parser + * @param check_signature + * @param must_exist + * @param allow_glob + * @param priority + * @return + */ +static bool +ucl_include_file_single (const unsigned char *data, size_t len, + struct ucl_parser *parser, bool check_signature, bool must_exist, + unsigned priority) +{ + bool res; + struct ucl_chunk *chunk; + unsigned char *buf = NULL; + char *old_curfile; + size_t buflen; + char filebuf[PATH_MAX], realbuf[PATH_MAX]; + int prev_state; + struct ucl_variable *cur_var, *tmp_var, *old_curdir = NULL, + *old_filename = NULL; + + snprintf (filebuf, sizeof (filebuf), "%.*s", (int)len, data); + if (ucl_realpath (filebuf, realbuf) == NULL) { + if (!must_exist) { + return true; + } + ucl_create_err (&parser->err, "cannot open file %s: %s", + filebuf, + strerror (errno)); + return false; + } + + if (parser->cur_file && strcmp (realbuf, parser->cur_file) == 0) { + /* We are likely including the file itself */ + ucl_create_err (&parser->err, "trying to include the file %s from itself", + realbuf); + return false; + } + + if (!ucl_fetch_file (realbuf, &buf, &buflen, &parser->err, must_exist)) { + return (!must_exist || false); + } + + if (check_signature) { +#if (defined(HAVE_OPENSSL) && OPENSSL_VERSION_NUMBER >= 0x10000000L) + unsigned char *sigbuf = NULL; + size_t siglen = 0; + /* We need to check signature first */ + snprintf (filebuf, sizeof (filebuf), "%s.sig", realbuf); + if (!ucl_fetch_file (filebuf, &sigbuf, &siglen, &parser->err, true)) { + return false; + } + if (!ucl_sig_check (buf, buflen, sigbuf, siglen, parser)) { + ucl_create_err (&parser->err, "cannot verify file %s: %s", + filebuf, + ERR_error_string (ERR_get_error (), NULL)); + if (siglen > 0) { + ucl_munmap (sigbuf, siglen); + } + return false; + } + if (siglen > 0) { + ucl_munmap (sigbuf, siglen); + } +#endif + } + + old_curfile = parser->cur_file; + parser->cur_file = strdup (realbuf); + + /* Store old file vars */ + DL_FOREACH_SAFE (parser->variables, cur_var, tmp_var) { + if (strcmp (cur_var->var, "CURDIR") == 0) { + old_curdir = cur_var; + DL_DELETE (parser->variables, cur_var); + } + else if (strcmp (cur_var->var, "FILENAME") == 0) { + old_filename = cur_var; + DL_DELETE (parser->variables, cur_var); + } + } + + ucl_parser_set_filevars (parser, realbuf, false); + + prev_state = parser->state; + parser->state = UCL_STATE_INIT; + + res = ucl_parser_add_chunk_priority (parser, buf, buflen, priority); + if (!res && !must_exist) { + /* Free error */ + utstring_free (parser->err); + parser->err = NULL; + parser->state = UCL_STATE_AFTER_VALUE; + } + + /* Remove chunk from the stack */ + chunk = parser->chunks; + if (chunk != NULL) { + parser->chunks = chunk->next; + UCL_FREE (sizeof (struct ucl_chunk), chunk); + parser->recursion --; + } + + /* Restore old file vars */ + parser->cur_file = old_curfile; + DL_FOREACH_SAFE (parser->variables, cur_var, tmp_var) { + if (strcmp (cur_var->var, "CURDIR") == 0 && old_curdir) { + DL_DELETE (parser->variables, cur_var); + free (cur_var->var); + free (cur_var->value); + UCL_FREE (sizeof (struct ucl_variable), cur_var); + } + else if (strcmp (cur_var->var, "FILENAME") == 0 && old_filename) { + DL_DELETE (parser->variables, cur_var); + free (cur_var->var); + free (cur_var->value); + UCL_FREE (sizeof (struct ucl_variable), cur_var); + } + } + if (old_filename) { + DL_APPEND (parser->variables, old_filename); + } + if (old_curdir) { + DL_APPEND (parser->variables, old_curdir); + } + if (old_curfile) { + free (old_curfile); + } + + parser->state = prev_state; + + if (buflen > 0) { + ucl_munmap (buf, buflen); + } + + return res; +} + +/** + * Include a file to configuration + * @param data + * @param len + * @param parser + * @param err + * @return + */ +static bool +ucl_include_file (const unsigned char *data, size_t len, + struct ucl_parser *parser, bool check_signature, bool must_exist, + bool allow_glob, unsigned priority) +{ + const unsigned char *p = data, *end = data + len; + bool need_glob = false; + int cnt = 0; + glob_t globbuf; + char glob_pattern[PATH_MAX]; + size_t i; + + if (!allow_glob) { + return ucl_include_file_single (data, len, parser, check_signature, + must_exist, priority); + } + else { + /* Check for special symbols in a filename */ + while (p != end) { + if (*p == '*' || *p == '?') { + need_glob = true; + break; + } + p ++; + } + if (need_glob) { + memset (&globbuf, 0, sizeof (globbuf)); + ucl_strlcpy (glob_pattern, (const char *)data, sizeof (glob_pattern)); + if (glob (glob_pattern, 0, NULL, &globbuf) != 0) { + return (!must_exist || false); + } + for (i = 0; i < globbuf.gl_pathc; i ++) { + if (!ucl_include_file_single ((unsigned char *)globbuf.gl_pathv[i], + strlen (globbuf.gl_pathv[i]), parser, check_signature, + must_exist, priority)) { + globfree (&globbuf); + return false; + } + cnt ++; + } + globfree (&globbuf); + + if (cnt == 0 && must_exist) { + ucl_create_err (&parser->err, "cannot match any files for pattern %s", + glob_pattern); + return false; + } + } + else { + return ucl_include_file_single (data, len, parser, check_signature, + must_exist, priority); + } + } + + return true; +} + +/** + * Common function to handle .*include* macros + * @param data + * @param len + * @param args + * @param parser + * @param default_try + * @param default_sign + * @return + */ +static bool +ucl_include_common (const unsigned char *data, size_t len, + const ucl_object_t *args, struct ucl_parser *parser, + bool default_try, + bool default_sign) +{ + bool try_load, allow_glob, allow_url, need_sign; + unsigned priority; + const ucl_object_t *param; + ucl_object_iter_t it = NULL; + + /* Default values */ + try_load = default_try; + allow_glob = false; + allow_url = true; + need_sign = default_sign; + priority = 0; + + /* Process arguments */ + if (args != NULL && args->type == UCL_OBJECT) { + while ((param = ucl_iterate_object (args, &it, true)) != NULL) { + if (param->type == UCL_BOOLEAN) { + if (strcmp (param->key, "try") == 0) { + try_load = ucl_object_toboolean (param); + } + else if (strcmp (param->key, "sign") == 0) { + need_sign = ucl_object_toboolean (param); + } + else if (strcmp (param->key, "glob") == 0) { + allow_glob = ucl_object_toboolean (param); + } + else if (strcmp (param->key, "url") == 0) { + allow_url = ucl_object_toboolean (param); + } + } + else if (param->type == UCL_INT) { + if (strcmp (param->key, "priority") == 0) { + priority = ucl_object_toint (param); + } + } + } + } + + if (*data == '/' || *data == '.') { + /* Try to load a file */ + return ucl_include_file (data, len, parser, need_sign, !try_load, + allow_glob, priority); + } + else if (allow_url) { + /* Globbing is not used for URL's */ + return ucl_include_url (data, len, parser, need_sign, !try_load, + priority); + } + + return false; +} + +/** + * Handle include macro + * @param data include data + * @param len length of data + * @param ud user data + * @param err error ptr + * @return + */ +UCL_EXTERN bool +ucl_include_handler (const unsigned char *data, size_t len, + const ucl_object_t *args, void* ud) +{ + struct ucl_parser *parser = ud; + + return ucl_include_common (data, len, args, parser, false, false); +} + +/** + * Handle includes macro + * @param data include data + * @param len length of data + * @param ud user data + * @param err error ptr + * @return + */ +UCL_EXTERN bool +ucl_includes_handler (const unsigned char *data, size_t len, + const ucl_object_t *args, void* ud) +{ + struct ucl_parser *parser = ud; + + return ucl_include_common (data, len, args, parser, false, true); +} + + +UCL_EXTERN bool +ucl_try_include_handler (const unsigned char *data, size_t len, + const ucl_object_t *args, void* ud) +{ + struct ucl_parser *parser = ud; + + return ucl_include_common (data, len, args, parser, true, false); +} + +UCL_EXTERN bool +ucl_parser_set_filevars (struct ucl_parser *parser, const char *filename, bool need_expand) +{ + char realbuf[PATH_MAX], *curdir; + + if (filename != NULL) { + if (need_expand) { + if (ucl_realpath (filename, realbuf) == NULL) { + return false; + } + } + else { + ucl_strlcpy (realbuf, filename, sizeof (realbuf)); + } + + /* Define variables */ + ucl_parser_register_variable (parser, "FILENAME", realbuf); + curdir = dirname (realbuf); + ucl_parser_register_variable (parser, "CURDIR", curdir); + } + else { + /* Set everything from the current dir */ + curdir = getcwd (realbuf, sizeof (realbuf)); + ucl_parser_register_variable (parser, "FILENAME", "undef"); + ucl_parser_register_variable (parser, "CURDIR", curdir); + } + + return true; +} + +UCL_EXTERN bool +ucl_parser_add_file (struct ucl_parser *parser, const char *filename) +{ + unsigned char *buf; + size_t len; + bool ret; + char realbuf[PATH_MAX]; + + if (ucl_realpath (filename, realbuf) == NULL) { + ucl_create_err (&parser->err, "cannot open file %s: %s", + filename, + strerror (errno)); + return false; + } + + if (!ucl_fetch_file (realbuf, &buf, &len, &parser->err, true)) { + return false; + } + + if (parser->cur_file) { + free (parser->cur_file); + } + parser->cur_file = strdup (realbuf); + ucl_parser_set_filevars (parser, realbuf, false); + ret = ucl_parser_add_chunk (parser, buf, len); + + if (len > 0) { + ucl_munmap (buf, len); + } + + return ret; +} + +UCL_EXTERN bool +ucl_parser_add_fd (struct ucl_parser *parser, int fd) +{ + unsigned char *buf; + size_t len; + bool ret; + struct stat st; + + if (fstat (fd, &st) == -1) { + ucl_create_err (&parser->err, "cannot stat fd %d: %s", + fd, strerror (errno)); + return false; + } + if ((buf = ucl_mmap (NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0)) == MAP_FAILED) { + ucl_create_err (&parser->err, "cannot mmap fd %d: %s", + fd, strerror (errno)); + return false; + } + + if (parser->cur_file) { + free (parser->cur_file); + } + parser->cur_file = NULL; + len = st.st_size; + ret = ucl_parser_add_chunk (parser, buf, len); + + if (len > 0) { + ucl_munmap (buf, len); + } + + return ret; +} + +size_t +ucl_strlcpy (char *dst, const char *src, size_t siz) +{ + char *d = dst; + const char *s = src; + size_t n = siz; + + /* Copy as many bytes as will fit */ + if (n != 0) { + while (--n != 0) { + if ((*d++ = *s++) == '\0') { + break; + } + } + } + + if (n == 0 && siz != 0) { + *d = '\0'; + } + + return (s - src - 1); /* count does not include NUL */ +} + +size_t +ucl_strlcpy_unsafe (char *dst, const char *src, size_t siz) +{ + memcpy (dst, src, siz - 1); + dst[siz - 1] = '\0'; + + return siz - 1; +} + +size_t +ucl_strlcpy_tolower (char *dst, const char *src, size_t siz) +{ + char *d = dst; + const char *s = src; + size_t n = siz; + + /* Copy as many bytes as will fit */ + if (n != 0) { + while (--n != 0) { + if ((*d++ = tolower (*s++)) == '\0') { + break; + } + } + } + + if (n == 0 && siz != 0) { + *d = '\0'; + } + + return (s - src); /* count does not include NUL */ +} + +ucl_object_t * +ucl_object_fromstring_common (const char *str, size_t len, enum ucl_string_flags flags) +{ + ucl_object_t *obj; + const char *start, *end, *p, *pos; + char *dst, *d; + size_t escaped_len; + + if (str == NULL) { + return NULL; + } + + obj = ucl_object_new (); + if (obj) { + if (len == 0) { + len = strlen (str); + } + if (flags & UCL_STRING_TRIM) { + /* Skip leading spaces */ + for (start = str; (size_t)(start - str) < len; start ++) { + if (!ucl_test_character (*start, UCL_CHARACTER_WHITESPACE_UNSAFE)) { + break; + } + } + /* Skip trailing spaces */ + for (end = str + len - 1; end > start; end --) { + if (!ucl_test_character (*end, UCL_CHARACTER_WHITESPACE_UNSAFE)) { + break; + } + } + end ++; + } + else { + start = str; + end = str + len; + } + + obj->type = UCL_STRING; + if (flags & UCL_STRING_ESCAPE) { + for (p = start, escaped_len = 0; p < end; p ++, escaped_len ++) { + if (ucl_test_character (*p, UCL_CHARACTER_JSON_UNSAFE)) { + escaped_len ++; + } + } + dst = malloc (escaped_len + 1); + if (dst != NULL) { + for (p = start, d = dst; p < end; p ++, d ++) { + if (ucl_test_character (*p, UCL_CHARACTER_JSON_UNSAFE)) { + switch (*p) { + case '\n': + *d++ = '\\'; + *d = 'n'; + break; + case '\r': + *d++ = '\\'; + *d = 'r'; + break; + case '\b': + *d++ = '\\'; + *d = 'b'; + break; + case '\t': + *d++ = '\\'; + *d = 't'; + break; + case '\f': + *d++ = '\\'; + *d = 'f'; + break; + case '\\': + *d++ = '\\'; + *d = '\\'; + break; + case '"': + *d++ = '\\'; + *d = '"'; + break; + } + } + else { + *d = *p; + } + } + *d = '\0'; + obj->value.sv = dst; + obj->trash_stack[UCL_TRASH_VALUE] = dst; + obj->len = escaped_len; + } + } + else { + dst = malloc (end - start + 1); + if (dst != NULL) { + ucl_strlcpy_unsafe (dst, start, end - start + 1); + obj->value.sv = dst; + obj->trash_stack[UCL_TRASH_VALUE] = dst; + obj->len = end - start; + } + } + if ((flags & UCL_STRING_PARSE) && dst != NULL) { + /* Parse what we have */ + if (flags & UCL_STRING_PARSE_BOOLEAN) { + if (!ucl_maybe_parse_boolean (obj, dst, obj->len) && (flags & UCL_STRING_PARSE_NUMBER)) { + ucl_maybe_parse_number (obj, dst, dst + obj->len, &pos, + flags & UCL_STRING_PARSE_DOUBLE, + flags & UCL_STRING_PARSE_BYTES, + flags & UCL_STRING_PARSE_TIME); + } + } + else { + ucl_maybe_parse_number (obj, dst, dst + obj->len, &pos, + flags & UCL_STRING_PARSE_DOUBLE, + flags & UCL_STRING_PARSE_BYTES, + flags & UCL_STRING_PARSE_TIME); + } + } + } + + return obj; +} + +static bool +ucl_object_insert_key_common (ucl_object_t *top, ucl_object_t *elt, + const char *key, size_t keylen, bool copy_key, bool merge, bool replace) +{ + ucl_object_t *found, *tmp; + const ucl_object_t *cur; + ucl_object_iter_t it = NULL; + const char *p; + int ret = true; + + if (elt == NULL || key == NULL) { + return false; + } + + if (top == NULL) { + return false; + } + + if (top->type != UCL_OBJECT) { + /* It is possible to convert NULL type to an object */ + if (top->type == UCL_NULL) { + top->type = UCL_OBJECT; + } + else { + /* Refuse converting of other object types */ + return false; + } + } + + if (top->value.ov == NULL) { + top->value.ov = ucl_hash_create (); + } + + if (keylen == 0) { + keylen = strlen (key); + } + + for (p = key; p < key + keylen; p ++) { + if (ucl_test_character (*p, UCL_CHARACTER_UCL_UNSAFE)) { + elt->flags |= UCL_OBJECT_NEED_KEY_ESCAPE; + break; + } + } + + /* workaround for some use cases */ + if (elt->trash_stack[UCL_TRASH_KEY] != NULL && + key != (const char *)elt->trash_stack[UCL_TRASH_KEY]) { + /* Remove copied key */ + free (elt->trash_stack[UCL_TRASH_KEY]); + elt->trash_stack[UCL_TRASH_KEY] = NULL; + elt->flags &= ~UCL_OBJECT_ALLOCATED_KEY; + } + + elt->key = key; + elt->keylen = keylen; + + if (copy_key) { + ucl_copy_key_trash (elt); + } + + found = __DECONST (ucl_object_t *, ucl_hash_search_obj (top->value.ov, elt)); + + if (found == NULL) { + top->value.ov = ucl_hash_insert_object (top->value.ov, elt); + top->len ++; + if (replace) { + ret = false; + } + } + else { + if (replace) { + ucl_hash_replace (top->value.ov, found, elt); + ucl_object_unref (found); + } + else if (merge) { + if (found->type != UCL_OBJECT && elt->type == UCL_OBJECT) { + /* Insert old elt to new one */ + ucl_object_insert_key_common (elt, found, found->key, + found->keylen, copy_key, false, false); + ucl_hash_delete (top->value.ov, found); + top->value.ov = ucl_hash_insert_object (top->value.ov, elt); + } + else if (found->type == UCL_OBJECT && elt->type != UCL_OBJECT) { + /* Insert new to old */ + ucl_object_insert_key_common (found, elt, elt->key, + elt->keylen, copy_key, false, false); + } + else if (found->type == UCL_OBJECT && elt->type == UCL_OBJECT) { + /* Mix two hashes */ + while ((cur = ucl_iterate_object (elt, &it, true)) != NULL) { + tmp = ucl_object_ref (cur); + ucl_object_insert_key_common (found, tmp, cur->key, + cur->keylen, copy_key, false, false); + } + ucl_object_unref (elt); + } + else { + /* Just make a list of scalars */ + DL_APPEND (found, elt); + } + } + else { + DL_APPEND (found, elt); + } + } + + return ret; +} + +bool +ucl_object_delete_keyl (ucl_object_t *top, const char *key, size_t keylen) +{ + ucl_object_t *found; + + if (top == NULL || key == NULL) { + return false; + } + + found = __DECONST (ucl_object_t *, ucl_object_find_keyl (top, key, keylen)); + + if (found == NULL) { + return false; + } + + ucl_hash_delete (top->value.ov, found); + ucl_object_unref (found); + top->len --; + + return true; +} + +bool +ucl_object_delete_key (ucl_object_t *top, const char *key) +{ + return ucl_object_delete_keyl (top, key, strlen(key)); +} + +ucl_object_t* +ucl_object_pop_keyl (ucl_object_t *top, const char *key, size_t keylen) +{ + const ucl_object_t *found; + + if (top == NULL || key == NULL) { + return false; + } + found = ucl_object_find_keyl (top, key, keylen); + + if (found == NULL) { + return NULL; + } + ucl_hash_delete (top->value.ov, found); + top->len --; + + return __DECONST (ucl_object_t *, found); +} + +ucl_object_t* +ucl_object_pop_key (ucl_object_t *top, const char *key) +{ + return ucl_object_pop_keyl (top, key, strlen(key)); +} + +bool +ucl_object_insert_key (ucl_object_t *top, ucl_object_t *elt, + const char *key, size_t keylen, bool copy_key) +{ + return ucl_object_insert_key_common (top, elt, key, keylen, copy_key, false, false); +} + +bool +ucl_object_insert_key_merged (ucl_object_t *top, ucl_object_t *elt, + const char *key, size_t keylen, bool copy_key) +{ + return ucl_object_insert_key_common (top, elt, key, keylen, copy_key, true, false); +} + +bool +ucl_object_replace_key (ucl_object_t *top, ucl_object_t *elt, + const char *key, size_t keylen, bool copy_key) +{ + return ucl_object_insert_key_common (top, elt, key, keylen, copy_key, false, true); +} + +bool +ucl_object_merge (ucl_object_t *top, ucl_object_t *elt, bool copy) +{ + ucl_object_t *cur = NULL, *cp = NULL, *found = NULL; + ucl_object_iter_t iter = NULL; + + if (top == NULL || top->type != UCL_OBJECT || elt == NULL || elt->type != UCL_OBJECT) { + return false; + } + + /* Mix two hashes */ + while ((cur = (ucl_object_t*)ucl_hash_iterate (elt->value.ov, &iter))) { + if (copy) { + cp = ucl_object_copy (cur); + } + else { + cp = ucl_object_ref (cur); + } + found = __DECONST(ucl_object_t *, ucl_hash_search (top->value.ov, cp->key, cp->keylen)); + if (found == NULL) { + /* The key does not exist */ + top->value.ov = ucl_hash_insert_object (top->value.ov, cp); + top->len ++; + } + else { + /* The key already exists, replace it */ + ucl_hash_replace (top->value.ov, found, cp); + ucl_object_unref (found); + } + } + + return true; +} + +const ucl_object_t * +ucl_object_find_keyl (const ucl_object_t *obj, const char *key, size_t klen) +{ + const ucl_object_t *ret; + ucl_object_t srch; + + if (obj == NULL || obj->type != UCL_OBJECT || key == NULL) { + return NULL; + } + + srch.key = key; + srch.keylen = klen; + ret = ucl_hash_search_obj (obj->value.ov, &srch); + + return ret; +} + +const ucl_object_t * +ucl_object_find_key (const ucl_object_t *obj, const char *key) +{ + if (key == NULL) + return NULL; + + return ucl_object_find_keyl (obj, key, strlen(key)); +} + +const ucl_object_t* +ucl_iterate_object (const ucl_object_t *obj, ucl_object_iter_t *iter, bool expand_values) +{ + const ucl_object_t *elt; + + if (obj == NULL || iter == NULL) { + return NULL; + } + + if (expand_values) { + switch (obj->type) { + case UCL_OBJECT: + return (const ucl_object_t*)ucl_hash_iterate (obj->value.ov, iter); + break; + case UCL_ARRAY: + elt = *iter; + if (elt == NULL) { + elt = obj->value.av; + if (elt == NULL) { + return NULL; + } + } + else if (elt == obj->value.av) { + return NULL; + } + *iter = elt->next ? elt->next : obj->value.av; + return elt; + default: + /* Go to linear iteration */ + break; + } + } + /* Treat everything as a linear list */ + elt = *iter; + if (elt == NULL) { + elt = obj; + } + else if (elt == obj) { + return NULL; + } + *iter = __DECONST (void *, elt->next ? elt->next : obj); + return elt; + + /* Not reached */ + return NULL; +} + +const ucl_object_t * +ucl_lookup_path (const ucl_object_t *top, const char *path_in) { + const ucl_object_t *o = NULL, *found; + const char *p, *c; + char *err_str; + unsigned index; + + if (path_in == NULL || top == NULL) { + return NULL; + } + + found = NULL; + p = path_in; + + /* Skip leading dots */ + while (*p == '.') { + p ++; + } + + c = p; + while (*p != '\0') { + p ++; + if (*p == '.' || *p == '\0') { + if (p > c) { + switch (top->type) { + case UCL_ARRAY: + /* Key should be an int */ + index = strtoul (c, &err_str, 10); + if (err_str != NULL && (*err_str != '.' && *err_str != '\0')) { + return NULL; + } + o = ucl_array_find_index (top, index); + break; + default: + o = ucl_object_find_keyl (top, c, p - c); + break; + } + if (o == NULL) { + return NULL; + } + top = o; + } + if (*p != '\0') { + c = p + 1; + } + } + } + found = o; + + return found; +} + + +ucl_object_t * +ucl_object_new (void) +{ + return ucl_object_typed_new (UCL_NULL); +} + +ucl_object_t * +ucl_object_typed_new (ucl_type_t type) +{ + return ucl_object_new_full (type, 0); +} + +ucl_object_t * +ucl_object_new_full (ucl_type_t type, unsigned priority) +{ + ucl_object_t *new; + + if (type != UCL_USERDATA) { + new = UCL_ALLOC (sizeof (ucl_object_t)); + if (new != NULL) { + memset (new, 0, sizeof (ucl_object_t)); + new->ref = 1; + new->type = (type <= UCL_NULL ? type : UCL_NULL); + new->next = NULL; + new->prev = new; + ucl_object_set_priority (new, priority); + } + } + else { + new = ucl_object_new_userdata (NULL, NULL); + ucl_object_set_priority (new, priority); + } + + return new; +} + +ucl_object_t* +ucl_object_new_userdata (ucl_userdata_dtor dtor, ucl_userdata_emitter emitter) +{ + struct ucl_object_userdata *new; + size_t nsize = sizeof (*new); + + new = UCL_ALLOC (nsize); + if (new != NULL) { + memset (new, 0, nsize); + new->obj.ref = 1; + new->obj.type = UCL_USERDATA; + new->obj.next = NULL; + new->obj.prev = (ucl_object_t *)new; + new->dtor = dtor; + new->emitter = emitter; + } + + return (ucl_object_t *)new; +} + +ucl_type_t +ucl_object_type (const ucl_object_t *obj) +{ + return obj->type; +} + +ucl_object_t* +ucl_object_fromstring (const char *str) +{ + return ucl_object_fromstring_common (str, 0, UCL_STRING_ESCAPE); +} + +ucl_object_t * +ucl_object_fromlstring (const char *str, size_t len) +{ + return ucl_object_fromstring_common (str, len, UCL_STRING_ESCAPE); +} + +ucl_object_t * +ucl_object_fromint (int64_t iv) +{ + ucl_object_t *obj; + + obj = ucl_object_new (); + if (obj != NULL) { + obj->type = UCL_INT; + obj->value.iv = iv; + } + + return obj; +} + +ucl_object_t * +ucl_object_fromdouble (double dv) +{ + ucl_object_t *obj; + + obj = ucl_object_new (); + if (obj != NULL) { + obj->type = UCL_FLOAT; + obj->value.dv = dv; + } + + return obj; +} + +ucl_object_t* +ucl_object_frombool (bool bv) +{ + ucl_object_t *obj; + + obj = ucl_object_new (); + if (obj != NULL) { + obj->type = UCL_BOOLEAN; + obj->value.iv = bv; + } + + return obj; +} + +bool +ucl_array_append (ucl_object_t *top, ucl_object_t *elt) +{ + ucl_object_t *head; + + if (elt == NULL || top == NULL) { + return false; + } + + head = top->value.av; + if (head == NULL) { + top->value.av = elt; + elt->prev = elt; + } + else { + elt->prev = head->prev; + head->prev->next = elt; + head->prev = elt; + } + elt->next = NULL; + top->len ++; + + return true; +} + +bool +ucl_array_prepend (ucl_object_t *top, ucl_object_t *elt) +{ + ucl_object_t *head; + + if (elt == NULL || top == NULL) { + return false; + } + + + head = top->value.av; + if (head == NULL) { + top->value.av = elt; + elt->prev = elt; + } + else { + elt->prev = head->prev; + head->prev = elt; + } + elt->next = head; + top->value.av = elt; + top->len ++; + + return true; +} + +bool +ucl_array_merge (ucl_object_t *top, ucl_object_t *elt, bool copy) +{ + ucl_object_t *cur, *tmp, *cp; + + if (elt == NULL || top == NULL || top->type != UCL_ARRAY || elt->type != UCL_ARRAY) { + return false; + } + + DL_FOREACH_SAFE (elt->value.av, cur, tmp) { + if (copy) { + cp = ucl_object_copy (cur); + } + else { + cp = ucl_object_ref (cur); + } + if (cp != NULL) { + ucl_array_append (top, cp); + } + } + + return true; +} + +ucl_object_t * +ucl_array_delete (ucl_object_t *top, ucl_object_t *elt) +{ + ucl_object_t *head; + + if (top == NULL || top->type != UCL_ARRAY || top->value.av == NULL) { + return NULL; + } + head = top->value.av; + + if (elt->prev == elt) { + top->value.av = NULL; + } + else if (elt == head) { + elt->next->prev = elt->prev; + top->value.av = elt->next; + } + else { + elt->prev->next = elt->next; + if (elt->next) { + elt->next->prev = elt->prev; + } + else { + head->prev = elt->prev; + } + } + elt->next = NULL; + elt->prev = elt; + top->len --; + + return elt; +} + +const ucl_object_t * +ucl_array_head (const ucl_object_t *top) +{ + if (top == NULL || top->type != UCL_ARRAY || top->value.av == NULL) { + return NULL; + } + return top->value.av; +} + +const ucl_object_t * +ucl_array_tail (const ucl_object_t *top) +{ + if (top == NULL || top->type != UCL_ARRAY || top->value.av == NULL) { + return NULL; + } + return top->value.av->prev; +} + +ucl_object_t * +ucl_array_pop_last (ucl_object_t *top) +{ + return ucl_array_delete (top, __DECONST(ucl_object_t *, ucl_array_tail (top))); +} + +ucl_object_t * +ucl_array_pop_first (ucl_object_t *top) +{ + return ucl_array_delete (top, __DECONST(ucl_object_t *, ucl_array_head (top))); +} + +const ucl_object_t * +ucl_array_find_index (const ucl_object_t *top, unsigned int index) +{ + ucl_object_iter_t it = NULL; + const ucl_object_t *ret; + + if (top == NULL || top->type != UCL_ARRAY || top->len == 0 || + (index + 1) > top->len) { + return NULL; + } + + while ((ret = ucl_iterate_object (top, &it, true)) != NULL) { + if (index == 0) { + return ret; + } + --index; + } + + return NULL; +} + +ucl_object_t * +ucl_array_replace_index (ucl_object_t *top, ucl_object_t *elt, + unsigned int index) +{ + ucl_object_t *cur, *tmp; + + if (top == NULL || top->type != UCL_ARRAY || elt == NULL || + top->len == 0 || (index + 1) > top->len) { + return NULL; + } + + DL_FOREACH_SAFE (top->value.av, cur, tmp) { + if (index == 0) { + DL_REPLACE_ELEM (top->value.av, cur, elt); + return cur; + } + --index; + } + + return NULL; +} + +ucl_object_t * +ucl_elt_append (ucl_object_t *head, ucl_object_t *elt) +{ + + if (head == NULL) { + elt->next = NULL; + elt->prev = elt; + head = elt; + } + else { + elt->prev = head->prev; + head->prev->next = elt; + head->prev = elt; + elt->next = NULL; + } + + return head; +} + +bool +ucl_object_todouble_safe (const ucl_object_t *obj, double *target) +{ + if (obj == NULL || target == NULL) { + return false; + } + switch (obj->type) { + case UCL_INT: + *target = obj->value.iv; /* Probaly could cause overflow */ + break; + case UCL_FLOAT: + case UCL_TIME: + *target = obj->value.dv; + break; + default: + return false; + } + + return true; +} + +double +ucl_object_todouble (const ucl_object_t *obj) +{ + double result = 0.; + + ucl_object_todouble_safe (obj, &result); + return result; +} + +bool +ucl_object_toint_safe (const ucl_object_t *obj, int64_t *target) +{ + if (obj == NULL || target == NULL) { + return false; + } + switch (obj->type) { + case UCL_INT: + *target = obj->value.iv; + break; + case UCL_FLOAT: + case UCL_TIME: + *target = obj->value.dv; /* Loosing of decimal points */ + break; + default: + return false; + } + + return true; +} + +int64_t +ucl_object_toint (const ucl_object_t *obj) +{ + int64_t result = 0; + + ucl_object_toint_safe (obj, &result); + return result; +} + +bool +ucl_object_toboolean_safe (const ucl_object_t *obj, bool *target) +{ + if (obj == NULL || target == NULL) { + return false; + } + switch (obj->type) { + case UCL_BOOLEAN: + *target = (obj->value.iv == true); + break; + default: + return false; + } + + return true; +} + +bool +ucl_object_toboolean (const ucl_object_t *obj) +{ + bool result = false; + + ucl_object_toboolean_safe (obj, &result); + return result; +} + +bool +ucl_object_tostring_safe (const ucl_object_t *obj, const char **target) +{ + if (obj == NULL || target == NULL) { + return false; + } + + switch (obj->type) { + case UCL_STRING: + *target = ucl_copy_value_trash (obj); + break; + default: + return false; + } + + return true; +} + +const char * +ucl_object_tostring (const ucl_object_t *obj) +{ + const char *result = NULL; + + ucl_object_tostring_safe (obj, &result); + return result; +} + +const char * +ucl_object_tostring_forced (const ucl_object_t *obj) +{ + return ucl_copy_value_trash (obj); +} + +bool +ucl_object_tolstring_safe (const ucl_object_t *obj, const char **target, size_t *tlen) +{ + if (obj == NULL || target == NULL) { + return false; + } + switch (obj->type) { + case UCL_STRING: + *target = obj->value.sv; + if (tlen != NULL) { + *tlen = obj->len; + } + break; + default: + return false; + } + + return true; +} + +const char * +ucl_object_tolstring (const ucl_object_t *obj, size_t *tlen) +{ + const char *result = NULL; + + ucl_object_tolstring_safe (obj, &result, tlen); + return result; +} + +const char * +ucl_object_key (const ucl_object_t *obj) +{ + return ucl_copy_key_trash (obj); +} + +const char * +ucl_object_keyl (const ucl_object_t *obj, size_t *len) +{ + if (len == NULL || obj == NULL) { + return NULL; + } + *len = obj->keylen; + return obj->key; +} + +ucl_object_t * +ucl_object_ref (const ucl_object_t *obj) +{ + ucl_object_t *res = NULL; + + if (obj != NULL) { + if (obj->flags & UCL_OBJECT_EPHEMERAL) { + /* + * Use deep copy for ephemeral objects, note that its refcount + * is NOT increased, since ephemeral objects does not need refcount + * at all + */ + res = ucl_object_copy (obj); + } + else { + res = __DECONST (ucl_object_t *, obj); +#ifdef HAVE_ATOMIC_BUILTINS + (void)__sync_add_and_fetch (&res->ref, 1); +#else + res->ref ++; +#endif + } + } + return res; +} + +static ucl_object_t * +ucl_object_copy_internal (const ucl_object_t *other, bool allow_array) +{ + + ucl_object_t *new; + ucl_object_iter_t it = NULL; + const ucl_object_t *cur; + + new = malloc (sizeof (*new)); + + if (new != NULL) { + memcpy (new, other, sizeof (*new)); + if (other->flags & UCL_OBJECT_EPHEMERAL) { + /* Copied object is always non ephemeral */ + new->flags &= ~UCL_OBJECT_EPHEMERAL; + } + new->ref = 1; + /* Unlink from others */ + new->next = NULL; + new->prev = new; + + /* deep copy of values stored */ + if (other->trash_stack[UCL_TRASH_KEY] != NULL) { + new->trash_stack[UCL_TRASH_KEY] = + strdup (other->trash_stack[UCL_TRASH_KEY]); + if (other->key == (const char *)other->trash_stack[UCL_TRASH_KEY]) { + new->key = new->trash_stack[UCL_TRASH_KEY]; + } + } + if (other->trash_stack[UCL_TRASH_VALUE] != NULL) { + new->trash_stack[UCL_TRASH_VALUE] = + strdup (other->trash_stack[UCL_TRASH_VALUE]); + if (new->type == UCL_STRING) { + new->value.sv = new->trash_stack[UCL_TRASH_VALUE]; + } + } + + if (other->type == UCL_ARRAY || other->type == UCL_OBJECT) { + /* reset old value */ + memset (&new->value, 0, sizeof (new->value)); + + while ((cur = ucl_iterate_object (other, &it, true)) != NULL) { + if (other->type == UCL_ARRAY) { + ucl_array_append (new, ucl_object_copy_internal (cur, false)); + } + else { + ucl_object_t *cp = ucl_object_copy_internal (cur, true); + if (cp != NULL) { + ucl_object_insert_key (new, cp, cp->key, cp->keylen, + false); + } + } + } + } + else if (allow_array && other->next != NULL) { + LL_FOREACH (other->next, cur) { + ucl_object_t *cp = ucl_object_copy_internal (cur, false); + if (cp != NULL) { + DL_APPEND (new, cp); + } + } + } + } + + return new; +} + +ucl_object_t * +ucl_object_copy (const ucl_object_t *other) +{ + return ucl_object_copy_internal (other, true); +} + +void +ucl_object_unref (ucl_object_t *obj) +{ + if (obj != NULL) { +#ifdef HAVE_ATOMIC_BUILTINS + unsigned int rc = __sync_sub_and_fetch (&obj->ref, 1); + if (rc == 0) { +#else + if (--obj->ref == 0) { +#endif + ucl_object_free_internal (obj, true, ucl_object_dtor_unref); + } + } +} + +int +ucl_object_compare (const ucl_object_t *o1, const ucl_object_t *o2) +{ + const ucl_object_t *it1, *it2; + ucl_object_iter_t iter = NULL; + int ret = 0; + + if (o1->type != o2->type) { + return (o1->type) - (o2->type); + } + + switch (o1->type) { + case UCL_STRING: + if (o1->len == o2->len) { + ret = strcmp (ucl_object_tostring(o1), ucl_object_tostring(o2)); + } + else { + ret = o1->len - o2->len; + } + break; + case UCL_FLOAT: + case UCL_INT: + case UCL_TIME: + ret = ucl_object_todouble (o1) - ucl_object_todouble (o2); + break; + case UCL_BOOLEAN: + ret = ucl_object_toboolean (o1) - ucl_object_toboolean (o2); + break; + case UCL_ARRAY: + if (o1->len == o2->len) { + it1 = o1->value.av; + it2 = o2->value.av; + /* Compare all elements in both arrays */ + while (it1 != NULL && it2 != NULL) { + ret = ucl_object_compare (it1, it2); + if (ret != 0) { + break; + } + it1 = it1->next; + it2 = it2->next; + } + } + else { + ret = o1->len - o2->len; + } + break; + case UCL_OBJECT: + if (o1->len == o2->len) { + while ((it1 = ucl_iterate_object (o1, &iter, true)) != NULL) { + it2 = ucl_object_find_key (o2, ucl_object_key (it1)); + if (it2 == NULL) { + ret = 1; + break; + } + ret = ucl_object_compare (it1, it2); + if (ret != 0) { + break; + } + } + } + else { + ret = o1->len - o2->len; + } + break; + default: + ret = 0; + break; + } + + return ret; +} + +void +ucl_object_array_sort (ucl_object_t *ar, + int (*cmp)(const ucl_object_t *o1, const ucl_object_t *o2)) +{ + if (cmp == NULL || ar == NULL || ar->type != UCL_ARRAY) { + return; + } + + DL_SORT (ar->value.av, cmp); +} + +#define PRIOBITS 4 + +unsigned int +ucl_object_get_priority (const ucl_object_t *obj) +{ + if (obj == NULL) { + return 0; + } + + return (obj->flags >> ((sizeof (obj->flags) * NBBY) - PRIOBITS)); +} + +void +ucl_object_set_priority (ucl_object_t *obj, + unsigned int priority) +{ + if (obj != NULL) { + priority &= (0x1 << PRIOBITS) - 1; + obj->flags |= priority << ((sizeof (obj->flags) * NBBY) - PRIOBITS); + } +} diff --git a/contrib/libucl/src/xxhash.c b/contrib/libucl/src/xxhash.c new file mode 100644 index 0000000..fc44633 --- /dev/null +++ b/contrib/libucl/src/xxhash.c @@ -0,0 +1,475 @@ +/* +xxHash - Fast Hash algorithm +Copyright (C) 2012-2013, Yann Collet. +BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + +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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"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 THE COPYRIGHT +OWNER OR CONTRIBUTORS 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. + +You can contact the author at : +- xxHash source repository : http://code.google.com/p/xxhash/ +*/ + + +//************************************** +// Tuning parameters +//************************************** +// Unaligned memory access is automatically enabled for "common" CPU, such as x86. +// For others CPU, the compiler will be more cautious, and insert extra code to ensure aligned access is respected. +// If you know your target CPU supports unaligned memory access, you want to force this option manually to improve performance. +// You can also enable this parameter if you know your input data will always be aligned (boundaries of 4, for U32). +#if defined(__ARM_FEATURE_UNALIGNED) || defined(__i386) || defined(_M_IX86) || defined(__x86_64__) || defined(_M_X64) +# define XXH_USE_UNALIGNED_ACCESS 1 +#endif + +// XXH_ACCEPT_NULL_INPUT_POINTER : +// If the input pointer is a null pointer, xxHash default behavior is to trigger a memory access error, since it is a bad pointer. +// When this option is enabled, xxHash output for null input pointers will be the same as a null-length input. +// This option has a very small performance cost (only measurable on small inputs). +// By default, this option is disabled. To enable it, uncomment below define : +//#define XXH_ACCEPT_NULL_INPUT_POINTER 1 + +// XXH_FORCE_NATIVE_FORMAT : +// By default, xxHash library provides endian-independant Hash values, based on little-endian convention. +// Results are therefore identical for little-endian and big-endian CPU. +// This comes at a performance cost for big-endian CPU, since some swapping is required to emulate little-endian format. +// Should endian-independance be of no importance for your application, you may set the #define below to 1. +// It will improve speed for Big-endian CPU. +// This option has no impact on Little_Endian CPU. +#define XXH_FORCE_NATIVE_FORMAT 0 + + +//************************************** +// Compiler Specific Options +//************************************** +// Disable some Visual warning messages +#ifdef _MSC_VER // Visual Studio +# pragma warning(disable : 4127) // disable: C4127: conditional expression is constant +#endif + +#ifdef _MSC_VER // Visual Studio +# define forceinline static __forceinline +#else +# ifdef __GNUC__ +# define forceinline static inline __attribute__((always_inline)) +# else +# define forceinline static inline +# endif +#endif + + +//************************************** +// Includes & Memory related functions +//************************************** +#include "xxhash.h" +// Modify the local functions below should you wish to use some other memory related routines +// for malloc(), free() +#include <stdlib.h> +forceinline void* XXH_malloc(size_t s) { return malloc(s); } +forceinline void XXH_free (void* p) { free(p); } +// for memcpy() +#include <string.h> +forceinline void* XXH_memcpy(void* dest, const void* src, size_t size) { return memcpy(dest,src,size); } + + +//************************************** +// Basic Types +//************************************** +#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L // C99 +# include <stdint.h> + typedef uint8_t BYTE; + typedef uint16_t U16; + typedef uint32_t U32; + typedef int32_t S32; + typedef uint64_t U64; +#else + typedef unsigned char BYTE; + typedef unsigned short U16; + typedef unsigned int U32; + typedef signed int S32; + typedef unsigned long long U64; +#endif + +#if defined(__GNUC__) && !defined(XXH_USE_UNALIGNED_ACCESS) +# define _PACKED __attribute__ ((packed)) +#else +# define _PACKED +#endif + +#if !defined(XXH_USE_UNALIGNED_ACCESS) && !defined(__GNUC__) +# ifdef __IBMC__ +# pragma pack(1) +# else +# pragma pack(push, 1) +# endif +#endif + +typedef struct _U32_S { U32 v; } _PACKED U32_S; + +#if !defined(XXH_USE_UNALIGNED_ACCESS) && !defined(__GNUC__) +# pragma pack(pop) +#endif + +#define A32(x) (((U32_S *)(x))->v) + + +//*************************************** +// Compiler-specific Functions and Macros +//*************************************** +#define GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) + +// Note : although _rotl exists for minGW (GCC under windows), performance seems poor +#if defined(_MSC_VER) +# define XXH_rotl32(x,r) _rotl(x,r) +#else +# define XXH_rotl32(x,r) ((x << r) | (x >> (32 - r))) +#endif + +#if defined(_MSC_VER) // Visual Studio +# define XXH_swap32 _byteswap_ulong +#elif GCC_VERSION >= 403 +# define XXH_swap32 __builtin_bswap32 +#else +static inline U32 XXH_swap32 (U32 x) { + return ((x << 24) & 0xff000000 ) | + ((x << 8) & 0x00ff0000 ) | + ((x >> 8) & 0x0000ff00 ) | + ((x >> 24) & 0x000000ff );} +#endif + + +//************************************** +// Constants +//************************************** +#define PRIME32_1 2654435761U +#define PRIME32_2 2246822519U +#define PRIME32_3 3266489917U +#define PRIME32_4 668265263U +#define PRIME32_5 374761393U + + +//************************************** +// Architecture Macros +//************************************** +typedef enum { XXH_bigEndian=0, XXH_littleEndian=1 } XXH_endianess; +#ifndef XXH_CPU_LITTLE_ENDIAN // It is possible to define XXH_CPU_LITTLE_ENDIAN externally, for example using a compiler switch + static const int one = 1; +# define XXH_CPU_LITTLE_ENDIAN (*(char*)(&one)) +#endif + + +//************************************** +// Macros +//************************************** +#define XXH_STATIC_ASSERT(c) { enum { XXH_static_assert = 1/(!!(c)) }; } // use only *after* variable declarations + + +//**************************** +// Memory reads +//**************************** +typedef enum { XXH_aligned, XXH_unaligned } XXH_alignment; + +forceinline U32 XXH_readLE32_align(const U32* ptr, XXH_endianess endian, XXH_alignment align) +{ + if (align==XXH_unaligned) + return endian==XXH_littleEndian ? A32(ptr) : XXH_swap32(A32(ptr)); + else + return endian==XXH_littleEndian ? *ptr : XXH_swap32(*ptr); +} + +forceinline U32 XXH_readLE32(const U32* ptr, XXH_endianess endian) { return XXH_readLE32_align(ptr, endian, XXH_unaligned); } + + +//**************************** +// Simple Hash Functions +//**************************** +forceinline U32 XXH32_endian_align(const void* input, int len, U32 seed, XXH_endianess endian, XXH_alignment align) +{ + const BYTE* p = (const BYTE*)input; + const BYTE* const bEnd = p + len; + U32 h32; + +#ifdef XXH_ACCEPT_NULL_INPUT_POINTER + if (p==NULL) { len=0; p=(const BYTE*)(size_t)16; } +#endif + + if (len>=16) + { + const BYTE* const limit = bEnd - 16; + U32 v1 = seed + PRIME32_1 + PRIME32_2; + U32 v2 = seed + PRIME32_2; + U32 v3 = seed + 0; + U32 v4 = seed - PRIME32_1; + + do + { + v1 += XXH_readLE32_align((const U32*)p, endian, align) * PRIME32_2; v1 = XXH_rotl32(v1, 13); v1 *= PRIME32_1; p+=4; + v2 += XXH_readLE32_align((const U32*)p, endian, align) * PRIME32_2; v2 = XXH_rotl32(v2, 13); v2 *= PRIME32_1; p+=4; + v3 += XXH_readLE32_align((const U32*)p, endian, align) * PRIME32_2; v3 = XXH_rotl32(v3, 13); v3 *= PRIME32_1; p+=4; + v4 += XXH_readLE32_align((const U32*)p, endian, align) * PRIME32_2; v4 = XXH_rotl32(v4, 13); v4 *= PRIME32_1; p+=4; + } while (p<=limit); + + h32 = XXH_rotl32(v1, 1) + XXH_rotl32(v2, 7) + XXH_rotl32(v3, 12) + XXH_rotl32(v4, 18); + } + else + { + h32 = seed + PRIME32_5; + } + + h32 += (U32) len; + + while (p<=bEnd-4) + { + h32 += XXH_readLE32_align((const U32*)p, endian, align) * PRIME32_3; + h32 = XXH_rotl32(h32, 17) * PRIME32_4 ; + p+=4; + } + + while (p<bEnd) + { + h32 += (*p) * PRIME32_5; + h32 = XXH_rotl32(h32, 11) * PRIME32_1 ; + p++; + } + + h32 ^= h32 >> 15; + h32 *= PRIME32_2; + h32 ^= h32 >> 13; + h32 *= PRIME32_3; + h32 ^= h32 >> 16; + + return h32; +} + + +U32 XXH32(const void* input, int len, U32 seed) +{ +#if 0 + // Simple version, good for code maintenance, but unfortunately slow for small inputs + void* state = XXH32_init(seed); + XXH32_update(state, input, len); + return XXH32_digest(state); +#else + XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN; + +# if !defined(XXH_USE_UNALIGNED_ACCESS) + if (!(((size_t)input) & 3)) // Input is aligned, let's leverage the speed advantage + { + if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT) + return XXH32_endian_align(input, len, seed, XXH_littleEndian, XXH_aligned); + else + return XXH32_endian_align(input, len, seed, XXH_bigEndian, XXH_aligned); + } +# endif + + if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT) + return XXH32_endian_align(input, len, seed, XXH_littleEndian, XXH_unaligned); + else + return XXH32_endian_align(input, len, seed, XXH_bigEndian, XXH_unaligned); +#endif +} + + +//**************************** +// Advanced Hash Functions +//**************************** + +struct XXH_state32_t +{ + U64 total_len; + U32 seed; + U32 v1; + U32 v2; + U32 v3; + U32 v4; + int memsize; + char memory[16]; +}; + + +int XXH32_sizeofState(void) +{ + XXH_STATIC_ASSERT(XXH32_SIZEOFSTATE >= sizeof(struct XXH_state32_t)); // A compilation error here means XXH32_SIZEOFSTATE is not large enough + return sizeof(struct XXH_state32_t); +} + + +XXH_errorcode XXH32_resetState(void* state_in, U32 seed) +{ + struct XXH_state32_t * state = (struct XXH_state32_t *) state_in; + state->seed = seed; + state->v1 = seed + PRIME32_1 + PRIME32_2; + state->v2 = seed + PRIME32_2; + state->v3 = seed + 0; + state->v4 = seed - PRIME32_1; + state->total_len = 0; + state->memsize = 0; + return XXH_OK; +} + + +void* XXH32_init (U32 seed) +{ + void* state = XXH_malloc (sizeof(struct XXH_state32_t)); + XXH32_resetState(state, seed); + return state; +} + + +forceinline XXH_errorcode XXH32_update_endian (void* state_in, const void* input, int len, XXH_endianess endian) +{ + struct XXH_state32_t * state = (struct XXH_state32_t *) state_in; + const BYTE* p = (const BYTE*)input; + const BYTE* const bEnd = p + len; + +#ifdef XXH_ACCEPT_NULL_INPUT_POINTER + if (input==NULL) return XXH_ERROR; +#endif + + state->total_len += len; + + if (state->memsize + len < 16) // fill in tmp buffer + { + XXH_memcpy(state->memory + state->memsize, input, len); + state->memsize += len; + return XXH_OK; + } + + if (state->memsize) // some data left from previous update + { + XXH_memcpy(state->memory + state->memsize, input, 16-state->memsize); + { + const U32* p32 = (const U32*)state->memory; + state->v1 += XXH_readLE32(p32, endian) * PRIME32_2; state->v1 = XXH_rotl32(state->v1, 13); state->v1 *= PRIME32_1; p32++; + state->v2 += XXH_readLE32(p32, endian) * PRIME32_2; state->v2 = XXH_rotl32(state->v2, 13); state->v2 *= PRIME32_1; p32++; + state->v3 += XXH_readLE32(p32, endian) * PRIME32_2; state->v3 = XXH_rotl32(state->v3, 13); state->v3 *= PRIME32_1; p32++; + state->v4 += XXH_readLE32(p32, endian) * PRIME32_2; state->v4 = XXH_rotl32(state->v4, 13); state->v4 *= PRIME32_1; p32++; + } + p += 16-state->memsize; + state->memsize = 0; + } + + if (p <= bEnd-16) + { + const BYTE* const limit = bEnd - 16; + U32 v1 = state->v1; + U32 v2 = state->v2; + U32 v3 = state->v3; + U32 v4 = state->v4; + + do + { + v1 += XXH_readLE32((const U32*)p, endian) * PRIME32_2; v1 = XXH_rotl32(v1, 13); v1 *= PRIME32_1; p+=4; + v2 += XXH_readLE32((const U32*)p, endian) * PRIME32_2; v2 = XXH_rotl32(v2, 13); v2 *= PRIME32_1; p+=4; + v3 += XXH_readLE32((const U32*)p, endian) * PRIME32_2; v3 = XXH_rotl32(v3, 13); v3 *= PRIME32_1; p+=4; + v4 += XXH_readLE32((const U32*)p, endian) * PRIME32_2; v4 = XXH_rotl32(v4, 13); v4 *= PRIME32_1; p+=4; + } while (p<=limit); + + state->v1 = v1; + state->v2 = v2; + state->v3 = v3; + state->v4 = v4; + } + + if (p < bEnd) + { + XXH_memcpy(state->memory, p, bEnd-p); + state->memsize = (int)(bEnd-p); + } + + return XXH_OK; +} + +XXH_errorcode XXH32_update (void* state_in, const void* input, int len) +{ + XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN; + + if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT) + return XXH32_update_endian(state_in, input, len, XXH_littleEndian); + else + return XXH32_update_endian(state_in, input, len, XXH_bigEndian); +} + + + +forceinline U32 XXH32_intermediateDigest_endian (void* state_in, XXH_endianess endian) +{ + struct XXH_state32_t * state = (struct XXH_state32_t *) state_in; + const BYTE * p = (const BYTE*)state->memory; + BYTE* bEnd = (BYTE*)state->memory + state->memsize; + U32 h32; + + if (state->total_len >= 16) + { + h32 = XXH_rotl32(state->v1, 1) + XXH_rotl32(state->v2, 7) + XXH_rotl32(state->v3, 12) + XXH_rotl32(state->v4, 18); + } + else + { + h32 = state->seed + PRIME32_5; + } + + h32 += (U32) state->total_len; + + while (p<=bEnd-4) + { + h32 += XXH_readLE32((const U32*)p, endian) * PRIME32_3; + h32 = XXH_rotl32(h32, 17) * PRIME32_4; + p+=4; + } + + while (p<bEnd) + { + h32 += (*p) * PRIME32_5; + h32 = XXH_rotl32(h32, 11) * PRIME32_1; + p++; + } + + h32 ^= h32 >> 15; + h32 *= PRIME32_2; + h32 ^= h32 >> 13; + h32 *= PRIME32_3; + h32 ^= h32 >> 16; + + return h32; +} + + +U32 XXH32_intermediateDigest (void* state_in) +{ + XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN; + + if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT) + return XXH32_intermediateDigest_endian(state_in, XXH_littleEndian); + else + return XXH32_intermediateDigest_endian(state_in, XXH_bigEndian); +} + + +U32 XXH32_digest (void* state_in) +{ + U32 h32 = XXH32_intermediateDigest(state_in); + + XXH_free(state_in); + + return h32; +} diff --git a/contrib/libucl/src/xxhash.h b/contrib/libucl/src/xxhash.h new file mode 100644 index 0000000..b892388 --- /dev/null +++ b/contrib/libucl/src/xxhash.h @@ -0,0 +1,164 @@ +/* + xxHash - Fast Hash algorithm + Header File + Copyright (C) 2012-2013, Yann Collet. + BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + + 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "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 THE COPYRIGHT + OWNER OR CONTRIBUTORS 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. + + You can contact the author at : + - xxHash source repository : http://code.google.com/p/xxhash/ +*/ + +/* Notice extracted from xxHash homepage : + +xxHash is an extremely fast Hash algorithm, running at RAM speed limits. +It also successfully passes all tests from the SMHasher suite. + +Comparison (single thread, Windows Seven 32 bits, using SMHasher on a Core 2 Duo @3GHz) + +Name Speed Q.Score Author +xxHash 5.4 GB/s 10 +CrapWow 3.2 GB/s 2 Andrew +MumurHash 3a 2.7 GB/s 10 Austin Appleby +SpookyHash 2.0 GB/s 10 Bob Jenkins +SBox 1.4 GB/s 9 Bret Mulvey +Lookup3 1.2 GB/s 9 Bob Jenkins +SuperFastHash 1.2 GB/s 1 Paul Hsieh +CityHash64 1.05 GB/s 10 Pike & Alakuijala +FNV 0.55 GB/s 5 Fowler, Noll, Vo +CRC32 0.43 GB/s 9 +MD5-32 0.33 GB/s 10 Ronald L. Rivest +SHA1-32 0.28 GB/s 10 + +Q.Score is a measure of quality of the hash function. +It depends on successfully passing SMHasher test set. +10 is a perfect score. +*/ + +#pragma once + +#if defined (__cplusplus) +extern "C" { +#endif + + +//**************************** +// Type +//**************************** +typedef enum { XXH_OK=0, XXH_ERROR } XXH_errorcode; + + + +//**************************** +// Simple Hash Functions +//**************************** + +unsigned int XXH32 (const void* input, int len, unsigned int seed); + +/* +XXH32() : + Calculate the 32-bits hash of sequence of length "len" stored at memory address "input". + The memory between input & input+len must be valid (allocated and read-accessible). + "seed" can be used to alter the result predictably. + This function successfully passes all SMHasher tests. + Speed on Core 2 Duo @ 3 GHz (single thread, SMHasher benchmark) : 5.4 GB/s + Note that "len" is type "int", which means it is limited to 2^31-1. + If your data is larger, use the advanced functions below. +*/ + + + +//**************************** +// Advanced Hash Functions +//**************************** + +void* XXH32_init (unsigned int seed); +XXH_errorcode XXH32_update (void* state, const void* input, int len); +unsigned int XXH32_digest (void* state); + +/* +These functions calculate the xxhash of an input provided in several small packets, +as opposed to an input provided as a single block. + +It must be started with : +void* XXH32_init() +The function returns a pointer which holds the state of calculation. + +This pointer must be provided as "void* state" parameter for XXH32_update(). +XXH32_update() can be called as many times as necessary. +The user must provide a valid (allocated) input. +The function returns an error code, with 0 meaning OK, and any other value meaning there is an error. +Note that "len" is type "int", which means it is limited to 2^31-1. +If your data is larger, it is recommended to chunk your data into blocks +of size for example 2^30 (1GB) to avoid any "int" overflow issue. + +Finally, you can end the calculation anytime, by using XXH32_digest(). +This function returns the final 32-bits hash. +You must provide the same "void* state" parameter created by XXH32_init(). +Memory will be freed by XXH32_digest(). +*/ + + +int XXH32_sizeofState(void); +XXH_errorcode XXH32_resetState(void* state, unsigned int seed); + +#define XXH32_SIZEOFSTATE 48 +typedef struct { long long ll[(XXH32_SIZEOFSTATE+(sizeof(long long)-1))/sizeof(long long)]; } XXH32_stateSpace_t; +/* +These functions allow user application to make its own allocation for state. + +XXH32_sizeofState() is used to know how much space must be allocated for the xxHash 32-bits state. +Note that the state must be aligned to access 'long long' fields. Memory must be allocated and referenced by a pointer. +This pointer must then be provided as 'state' into XXH32_resetState(), which initializes the state. + +For static allocation purposes (such as allocation on stack, or freestanding systems without malloc()), +use the structure XXH32_stateSpace_t, which will ensure that memory space is large enough and correctly aligned to access 'long long' fields. +*/ + + +unsigned int XXH32_intermediateDigest (void* state); +/* +This function does the same as XXH32_digest(), generating a 32-bit hash, +but preserve memory context. +This way, it becomes possible to generate intermediate hashes, and then continue feeding data with XXH32_update(). +To free memory context, use XXH32_digest(), or free(). +*/ + + + +//**************************** +// Deprecated function names +//**************************** +// The following translations are provided to ease code transition +// You are encouraged to no longer this function names +#define XXH32_feed XXH32_update +#define XXH32_result XXH32_digest +#define XXH32_getIntermediateResult XXH32_intermediateDigest + + + +#if defined (__cplusplus) +} +#endif |