summaryrefslogtreecommitdiffstats
path: root/contrib/libucl/src
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/libucl/src')
-rw-r--r--contrib/libucl/src/Makefile.am27
-rw-r--r--contrib/libucl/src/tree.h212
-rw-r--r--contrib/libucl/src/ucl_chartable.h267
-rw-r--r--contrib/libucl/src/ucl_emitter.c472
-rw-r--r--contrib/libucl/src/ucl_emitter_streamline.c166
-rw-r--r--contrib/libucl/src/ucl_emitter_utils.c466
-rw-r--r--contrib/libucl/src/ucl_hash.c126
-rw-r--r--contrib/libucl/src/ucl_hash.h93
-rw-r--r--contrib/libucl/src/ucl_internal.h368
-rw-r--r--contrib/libucl/src/ucl_parser.c1981
-rw-r--r--contrib/libucl/src/ucl_schema.c1014
-rw-r--r--contrib/libucl/src/ucl_util.c1958
-rw-r--r--contrib/libucl/src/xxhash.c475
-rw-r--r--contrib/libucl/src/xxhash.h164
14 files changed, 7789 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..11fe50c
--- /dev/null
+++ b/contrib/libucl/src/ucl_emitter.c
@@ -0,0 +1,472 @@
+/* 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 (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->ident --;
+ 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->ident, 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->ident --;
+ 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->ident, 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->ident ++;
+
+ 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->ident ++;
+ }
+
+ 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->ident, 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;
+
+ if (ctx->id != UCL_EMIT_CONFIG && !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->ident, 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);
+ 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:
+ 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.ident = 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..acf4a30
--- /dev/null
+++ b/contrib/libucl/src/ucl_emitter_streamline.c
@@ -0,0 +1,166 @@
+/* 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..c619ab9
--- /dev/null
+++ b/contrib/libucl/src/ucl_emitter_utils.c
@@ -0,0 +1,466 @@
+/* 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;
+
+ if (ctx->id != UCL_EMIT_YAML) {
+ 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);
+ }
+ if (ctx->id != UCL_EMIT_YAML) {
+ func->ucl_emitter_append_character ('"', 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);
+ 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) {
+ write (fd, &c, 1);
+ }
+ else {
+ buf = malloc (len);
+ if (buf == NULL) {
+ /* Fallback */
+ while (len --) {
+ write (fd, &c, 1);
+ }
+ }
+ else {
+ memset (buf, c, len);
+ write (fd, buf, len);
+ free (buf);
+ }
+ }
+
+ return 0;
+}
+
+static int
+ucl_fd_append_len (const unsigned char *str, size_t len, void *ud)
+{
+ int fd = *(int *)ud;
+
+ write (fd, str, len);
+
+ return 0;
+}
+
+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);
+ write (fd, intbuf, strlen (intbuf));
+
+ return 0;
+}
+
+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);
+ }
+
+ write (fd, nbuf, strlen (nbuf));
+
+ return 0;
+}
+
+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;
+}
diff --git a/contrib/libucl/src/ucl_hash.c b/contrib/libucl/src/ucl_hash.c
new file mode 100644
index 0000000..c2e80cb
--- /dev/null
+++ b/contrib/libucl/src/ucl_hash.c
@@ -0,0 +1,126 @@
+/* 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);
+}
+
+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);
+ }
+}
diff --git a/contrib/libucl/src/ucl_hash.h b/contrib/libucl/src/ucl_hash.h
new file mode 100644
index 0000000..cbbf005
--- /dev/null
+++ b/contrib/libucl/src/ucl_hash.h
@@ -0,0 +1,93 @@
+/* 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);
+
+/**
+ * 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..0ac8de8
--- /dev/null
+++ b/contrib/libucl/src/ucl_internal.h
@@ -0,0 +1,368 @@
+/* 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;
+ 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 *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;
+ 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;
+};
+
+/**
+ * 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, void* ud);
+
+bool ucl_try_include_handler (const unsigned char *data, size_t len, 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, 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 unsigned char *p = 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);
+
+/**
+ * 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);
+
+/**
+ * Emit a single object to string
+ * @param obj
+ * @return
+ */
+unsigned char * ucl_object_emit_single_json (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..e51a54b
--- /dev/null
+++ b/contrib/libucl/src/ucl_parser.c
@@ -0,0 +1,1981 @@
+/* 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 rcl_parser.c
+ * The implementation of rcl 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_chunk *chunk, int code, const char *str, UT_string **err)
+{
+ if (chunk->pos < chunk->end) {
+ if (isgraph (*chunk->pos)) {
+ ucl_create_err (err, "error on line %d at column %d: '%s', character: '%c'",
+ chunk->line, chunk->column, str, *chunk->pos);
+ }
+ else {
+ ucl_create_err (err, "error on line %d at column %d: '%s', character: '0x%02x'",
+ chunk->line, chunk->column, str, (int)*chunk->pos);
+ }
+ }
+ else {
+ ucl_create_err (err, "error at the end of chunk: %s", 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;
+
+ p = chunk->pos;
+
+start:
+ if (*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 (*p == '/' && chunk->remain >= 2) {
+ if (p[1] == '*') {
+ ucl_chunk_skipc (chunk, p);
+ comments_nested ++;
+ ucl_chunk_skipc (chunk, p);
+
+ while (p < chunk->end) {
+ 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 (chunk, 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->chunks, 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;
+ }
+ }
+ *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_typed_new (UCL_OBJECT);
+ }
+ else {
+ obj->type = UCL_OBJECT;
+ }
+ obj->value.ov = ucl_hash_create ();
+ parser->state = UCL_STATE_KEY;
+ }
+ else {
+ if (obj == NULL) {
+ obj = ucl_object_typed_new (UCL_ARRAY);
+ }
+ else {
+ obj->type = UCL_ARRAY;
+ }
+ parser->state = UCL_STATE_VALUE;
+ }
+
+ st = UCL_ALLOC (sizeof (struct ucl_stack));
+ if (st == NULL) {
+ ucl_set_err (parser->chunks, 0, "cannot allocate memory for an object", &parser->err);
+ 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' ||
+ ucl_test_character (*endptr, UCL_CHARACTER_WHITESPACE_UNSAFE)) {
+ 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;
+ }
+ }
+
+ *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 (chunk, 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 (chunk, UCL_ESYNTAX, "unexpected newline", &parser->err);
+ }
+ else {
+ ucl_set_err (chunk, 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 (chunk, 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 (chunk, UCL_ESYNTAX, "invalid utf escape", &parser->err);
+ return false;
+ }
+ ucl_chunk_skipc (chunk, p);
+ }
+ if (p >= chunk->end) {
+ ucl_set_err (chunk, 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 (chunk, UCL_ESYNTAX, "no quote at the end of json string", &parser->err);
+ return false;
+}
+
+/**
+ * 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 (chunk, 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 (chunk, 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 (chunk, 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 (chunk, 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 (chunk, 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 (chunk, 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 ();
+ 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 (chunk, 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 {
+ DL_APPEND (tobj, nobj);
+ }
+
+ 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);
+ }
+
+ if (p >= chunk->end) {
+ ucl_set_err (chunk, UCL_ESYNTAX, "unfinished value", &parser->err);
+ return false;
+ }
+
+ 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;
+ 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 && (p[term_len] == '\n' || p[term_len] == '\r')) {
+ 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 ();
+ 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 (chunk, 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 (chunk, 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 (chunk, 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 (chunk, 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 (chunk, 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;
+}
+
+/**
+ * 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;
+ 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;
+
+ 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
+ */
+ obj = parser->cur_obj;
+ 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 (chunk, 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)) {
+ 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 */
+ 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])) {
+ /* Skip comment */
+ if (!ucl_skip_comments (parser)) {
+ return false;
+ }
+ p = chunk->pos;
+ }
+ break;
+ }
+ ucl_chunk_skipc (chunk, p);
+ }
+ parser->state = UCL_STATE_MACRO;
+ }
+ break;
+ case UCL_STATE_MACRO:
+ if (!ucl_parse_macro_value (parser, chunk, macro,
+ &macro_start, &macro_len)) {
+ parser->prev_state = parser->state;
+ parser->state = UCL_STATE_ERROR;
+ return false;
+ }
+ macro_len = ucl_expand_variable (parser, &macro_escaped, macro_start, macro_len);
+ parser->state = parser->prev_state;
+ if (macro_escaped == NULL) {
+ if (!macro->handler (macro_start, macro_len, macro->ud)) {
+ return false;
+ }
+ }
+ else {
+ if (!macro->handler (macro_escaped, macro_len, macro->ud)) {
+ UCL_FREE (macro_len + 1, macro_escaped);
+ return false;
+ }
+ UCL_FREE (macro_len + 1, macro_escaped);
+ }
+ p = chunk->pos;
+ break;
+ default:
+ /* TODO: add all states */
+ ucl_set_err (chunk, 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 */
+ LL_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);
+
+ LL_PREPEND (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 (struct ucl_parser *parser, const unsigned char *data,
+ size_t len)
+{
+ struct ucl_chunk *chunk;
+
+ if (data == NULL || len == 0) {
+ ucl_create_err (&parser->err, "invalid chunk added");
+ return false;
+ }
+ 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;
+ 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_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 (&reg, pattern, REG_EXTENDED | REG_NOSUB) == 0) {
+ while ((elt = ucl_iterate_object (obj, &iter, true)) != NULL) {
+ if (regexec (&reg, ucl_object_key (elt), 0, NULL, 0) == 0) {
+ res = elt;
+ break;
+ }
+ }
+ regfree (&reg);
+ }
+#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..63f5e62
--- /dev/null
+++ b/contrib/libucl/src/ucl_util.c
@@ -0,0 +1,1958 @@
+/* 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"
+
+#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
+
+/**
+ * @file rcl_util.c
+ * Utilities for rcl parsing
+ */
+
+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]);
+ }
+ UCL_FREE (sizeof (ucl_object_t), 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);
+ }
+
+ 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)
+{
+
+ 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 (parser, buf, buflen);
+ 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 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 res;
+ struct ucl_chunk *chunk;
+ unsigned char *buf = NULL;
+ size_t buflen;
+ char filebuf[PATH_MAX], realbuf[PATH_MAX];
+ int prev_state;
+
+ 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 (!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
+ }
+
+ ucl_parser_set_filevars (parser, realbuf, false);
+
+ prev_state = parser->state;
+ parser->state = UCL_STATE_INIT;
+
+ res = ucl_parser_add_chunk (parser, buf, buflen);
+ 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;
+
+ if (buflen > 0) {
+ ucl_munmap (buf, buflen);
+ }
+
+ return res;
+}
+
+/**
+ * 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, void* ud)
+{
+ struct ucl_parser *parser = ud;
+
+ if (*data == '/' || *data == '.') {
+ /* Try to load a file */
+ return ucl_include_file (data, len, parser, false, true);
+ }
+
+ return ucl_include_url (data, len, parser, false, true);
+}
+
+/**
+ * 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, void* ud)
+{
+ struct ucl_parser *parser = ud;
+
+ if (*data == '/' || *data == '.') {
+ /* Try to load a file */
+ return ucl_include_file (data, len, parser, true, true);
+ }
+
+ return ucl_include_url (data, len, parser, true, true);
+}
+
+
+UCL_EXTERN bool
+ucl_try_include_handler (const unsigned char *data, size_t len, void* ud)
+{
+ struct ucl_parser *parser = ud;
+
+ if (*data == '/' || *data == '.') {
+ /* Try to load a file */
+ return ucl_include_file (data, len, parser, false, false);
+ }
+
+ return ucl_include_url (data, len, parser, false, 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;
+ }
+
+ ucl_parser_set_filevars (parser, realbuf, false);
+ 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;
+ }
+ }
+
+ 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) {
+ top->value.ov = ucl_hash_insert_object (top->value.ov, elt);
+ DL_APPEND (found, elt);
+ top->len ++;
+ if (replace) {
+ ret = false;
+ }
+ }
+ else {
+ if (replace) {
+ ucl_hash_delete (top->value.ov, found);
+ ucl_object_unref (found);
+ top->value.ov = ucl_hash_insert_object (top->value.ov, elt);
+ found = NULL;
+ DL_APPEND (found, elt);
+ }
+ 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);
+}
+
+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;
+ if (elt == NULL) {
+ return NULL;
+ }
+ }
+ 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)
+{
+ ucl_object_t *new;
+ new = malloc (sizeof (ucl_object_t));
+ if (new != NULL) {
+ memset (new, 0, sizeof (ucl_object_t));
+ new->ref = 1;
+ new->type = UCL_NULL;
+ }
+ return new;
+}
+
+ucl_object_t *
+ucl_object_typed_new (ucl_type_t type)
+{
+ ucl_object_t *new;
+ new = malloc (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);
+ }
+ return 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;
+}
+
+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_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) {
+ res = __DECONST (ucl_object_t *, obj);
+#ifdef HAVE_ATOMIC_BUILTINS
+ (void)__sync_add_and_fetch (&res->ref, 1);
+#else
+ res->ref ++;
+#endif
+ }
+ return res;
+}
+
+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);
+}
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
OpenPOWER on IntegriCloud