]> git.ipfire.org Git - thirdparty/rspamd.git/commitdiff
Move ucl and rdns to contrib.
authorVsevolod Stakhov <vsevolod@highsecure.ru>
Sat, 21 Feb 2015 18:11:12 +0000 (18:11 +0000)
committerVsevolod Stakhov <vsevolod@highsecure.ru>
Sat, 21 Feb 2015 18:11:12 +0000 (18:11 +0000)
44 files changed:
.gitmodules
CMakeLists.txt
contrib/librdns/CMakeLists.txt [new file with mode: 0644]
contrib/librdns/compression.c [new file with mode: 0644]
contrib/librdns/compression.h [new file with mode: 0644]
contrib/librdns/curve.c [new file with mode: 0644]
contrib/librdns/dns_private.h [new file with mode: 0644]
contrib/librdns/logger.c [new file with mode: 0644]
contrib/librdns/logger.h [new file with mode: 0644]
contrib/librdns/packet.c [new file with mode: 0644]
contrib/librdns/packet.h [new file with mode: 0644]
contrib/librdns/parse.c [new file with mode: 0644]
contrib/librdns/parse.h [new file with mode: 0644]
contrib/librdns/punycode.c [new file with mode: 0644]
contrib/librdns/punycode.h [new file with mode: 0644]
contrib/librdns/rdns.h [new file with mode: 0644]
contrib/librdns/rdns_curve.h [new file with mode: 0644]
contrib/librdns/rdns_ev.h [new file with mode: 0644]
contrib/librdns/rdns_event.h [new file with mode: 0644]
contrib/librdns/ref.h [new file with mode: 0644]
contrib/librdns/resolver.c [new file with mode: 0644]
contrib/librdns/upstream.h [new file with mode: 0644]
contrib/librdns/util.c [new file with mode: 0644]
contrib/librdns/util.h [new file with mode: 0644]
contrib/libucl/CMakeLists.txt [new file with mode: 0644]
contrib/libucl/khash.h [new file with mode: 0644]
contrib/libucl/kvec.h [new file with mode: 0644]
contrib/libucl/lua_ucl.c [new file with mode: 0644]
contrib/libucl/lua_ucl.h [new file with mode: 0644]
contrib/libucl/tree.h [new file with mode: 0644]
contrib/libucl/ucl.h [new file with mode: 0644]
contrib/libucl/ucl_chartable.h [new file with mode: 0644]
contrib/libucl/ucl_emitter.c [new file with mode: 0644]
contrib/libucl/ucl_emitter_streamline.c [new file with mode: 0644]
contrib/libucl/ucl_emitter_utils.c [new file with mode: 0644]
contrib/libucl/ucl_hash.c [new file with mode: 0644]
contrib/libucl/ucl_hash.h [new file with mode: 0644]
contrib/libucl/ucl_internal.h [new file with mode: 0644]
contrib/libucl/ucl_parser.c [new file with mode: 0644]
contrib/libucl/ucl_schema.c [new file with mode: 0644]
contrib/libucl/ucl_util.c [new file with mode: 0644]
contrib/libucl/xxhash.c [new file with mode: 0644]
contrib/libucl/xxhash.h [new file with mode: 0644]
src/CMakeLists.txt

index 9949f55e5de3d34864f471a7fea03a12b215ffe7..037d1fc0d15c614e8904092e0e40242f18742c03 100644 (file)
@@ -1,12 +1,6 @@
-[submodule "src/rdns"]
-       path = src/rdns
-       url = git://github.com/vstakhov/librdns
 [submodule "interface"]
        path = interface
        url = git://github.com/vstakhov/rspamd-interface
-[submodule "src/ucl"]
-       path = src/ucl
-       url = git://github.com/vstakhov/libucl
 [submodule "doc/doxydown"]
        path = doc/doxydown
        url = https://github.com/vstakhov/doxydown.git
index 25d9cfb5b1770e81f313a40a7fcdc265061fc0ab..89d1cd401d007ca25d8c60b3e541b12c87ca6dd4 100644 (file)
@@ -887,7 +887,7 @@ ENDIF(HG)
 INCLUDE_DIRECTORIES("${CMAKE_SOURCE_DIR}/src" 
                                        "${CMAKE_BINARY_DIR}/src"
                                        "${CMAKE_BINARY_DIR}/src/libcryptobox"
-                                       "${CMAKE_SOURCE_DIR}/src/ucl/include"
+                                       "${CMAKE_SOURCE_DIR}/contrib/libucl"
                                        "${CMAKE_SOURCE_DIR}/contrib/uthash"
                                        "${CMAKE_SOURCE_DIR}/contrib/http-parser"
                                        "${CMAKE_SOURCE_DIR}/contrib/libottery"
@@ -895,7 +895,7 @@ INCLUDE_DIRECTORIES("${CMAKE_SOURCE_DIR}/src"
                                        "${CMAKE_SOURCE_DIR}/contrib/snowball/include"
                                        "${CMAKE_SOURCE_DIR}/contrib/siphash"
                                        "${CMAKE_SOURCE_DIR}/contrib/blake2"
-                                       "${CMAKE_SOURCE_DIR}/src/rdns/include")
+                                       "${CMAKE_SOURCE_DIR}/contrib/librdns")
 
 ################################ SUBDIRS SECTION ###########################
 
@@ -916,7 +916,6 @@ LIST(APPEND RSPAMD_REQUIRED_LIBRARIES xxhash)
 LIST(APPEND RSPAMD_REQUIRED_LIBRARIES siphash)
 LIST(APPEND RSPAMD_REQUIRED_LIBRARIES blake2)
 LIST(APPEND RSPAMD_REQUIRED_LIBRARIES sqlite3)
-LIST(APPEND RSPAMD_REQUIRED_LIBRARIES lua-ucl)
 IF(OPENSSL_FOUND)
        LIST(APPEND RSPAMD_REQUIRED_LIBRARIES ${OPENSSL_LIBRARIES})
 ENDIF(OPENSSL_FOUND)
@@ -949,6 +948,8 @@ ADD_SUBDIRECTORY(contrib/libottery)
 ADD_SUBDIRECTORY(contrib/snowball)
 ADD_SUBDIRECTORY(contrib/siphash)
 ADD_SUBDIRECTORY(contrib/blake2)
+ADD_SUBDIRECTORY(contrib/libucl)
+ADD_SUBDIRECTORY(contrib/librdns)
 ADD_SUBDIRECTORY(src)
 
 ADD_SUBDIRECTORY(test)
diff --git a/contrib/librdns/CMakeLists.txt b/contrib/librdns/CMakeLists.txt
new file mode 100644 (file)
index 0000000..f2cd9a3
--- /dev/null
@@ -0,0 +1,10 @@
+SET(LIBRDNSSRC                 util.c
+                                               logger.c
+                                               compression.c
+                                               punycode.c
+                                               curve.c
+                                               parse.c
+                                               packet.c
+                                               resolver.c)
+
+ADD_LIBRARY(rdns STATIC ${LIBRDNSSRC})
\ No newline at end of file
diff --git a/contrib/librdns/compression.c b/contrib/librdns/compression.c
new file mode 100644 (file)
index 0000000..ac31a92
--- /dev/null
@@ -0,0 +1,154 @@
+/* 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.
+ */
+
+#include "compression.h"
+#include "logger.h"
+
+static struct rdns_compression_entry *
+rdns_can_compress (const char *pos, struct rdns_compression_entry *comp)
+{
+       struct rdns_compression_entry *res;
+
+       HASH_FIND_STR (comp, pos, res);
+
+       return res;
+}
+
+static unsigned int
+rdns_calculate_label_len (const char *pos, const char *end)
+{
+       const char *p = pos;
+       unsigned int res = 0;
+
+       while (p != end) {
+               if (*p == '.') {
+                       break;
+               }
+               res ++;
+               p ++;
+       }
+       return res;
+}
+
+static void
+rdns_add_compressed (const char *pos, const char *end,
+               struct rdns_compression_entry **comp, int offset)
+{
+       struct rdns_compression_entry *new;
+
+       assert (offset >= 0);
+       new = malloc (sizeof (*new));
+       if (new != NULL) {
+               new->label = pos;
+               new->offset = offset;
+               HASH_ADD_KEYPTR (hh, *comp, pos, (end - pos), new);
+       }
+}
+
+void
+rnds_compression_free (struct rdns_compression_entry *comp)
+{
+       struct rdns_compression_entry *cur, *tmp;
+
+       if (comp) {
+               free (comp->hh.tbl->buckets);
+               free (comp->hh.tbl);
+
+               HASH_ITER (hh, comp, cur, tmp) {
+                       free (cur);
+               }
+       }
+}
+
+bool
+rdns_write_name_compressed (struct rdns_request *req,
+               const char *name, unsigned int namelen,
+               struct rdns_compression_entry **comp)
+{
+       uint8_t *target = req->packet + req->pos;
+       const char *pos = name, *end = name + namelen;
+       unsigned int remain = req->packet_len - req->pos - 5, label_len;
+       struct rdns_compression_entry *head = NULL, *test;
+       struct rdns_resolver *resolver = req->resolver;
+       uint16_t pointer;
+
+       if (comp != NULL) {
+               head = *comp;
+       }
+
+       while (pos < end && remain > 0) {
+               if (head != NULL) {
+                       test = rdns_can_compress (pos, head);
+                       if (test != NULL) {
+                               if (remain < 2) {
+                                       rdns_info ("no buffer remain for constructing query");
+                                       return false;
+                               }
+
+                               pointer = htons ((uint16_t)test->offset) | DNS_COMPRESSION_BITS;
+                               memcpy (target, &pointer, sizeof (pointer));
+                               req->pos += 2;
+
+                               return true;
+                       }
+               }
+
+
+               label_len = rdns_calculate_label_len (pos, end);
+               if (label_len == 0) {
+                       /* We have empty label it is allowed only if pos == end - 1 */
+                       if (pos == end - 1) {
+                               break;
+                       }
+                       else {
+                               rdns_err ("double dots in the name requested");
+                               return false;
+                       }
+               }
+               else if (label_len > DNS_D_MAXLABEL) {
+                       rdns_err ("too large label: %d", (int)label_len);
+                       return false;
+               }
+
+               if (label_len + 1 > remain) {
+                       rdns_info ("no buffer remain for constructing query, strip %d to %d",
+                                       (int)label_len, (int)remain);
+                       label_len = remain - 1;
+               }
+
+               if (comp != NULL) {
+                       rdns_add_compressed (pos, end, comp, target - req->packet);
+               }
+               /* Write label as is */
+               *target++ = (uint8_t)label_len;
+               memcpy (target, pos, label_len);
+               target += label_len;
+               pos += label_len + 1;
+       }
+
+       /* Termination label */
+       *target++ = '\0';
+       req->pos = target - req->packet;
+
+       return true;
+}
diff --git a/contrib/librdns/compression.h b/contrib/librdns/compression.h
new file mode 100644 (file)
index 0000000..adae2f3
--- /dev/null
@@ -0,0 +1,45 @@
+/* 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.
+ */
+#ifndef COMPRESSION_H_
+#define COMPRESSION_H_
+
+#include "uthash.h"
+#include "dns_private.h"
+
+struct rdns_compression_entry {
+       const char *label;       /**< label packed */
+       unsigned int offset; /**< offset in the packet */
+       UT_hash_handle hh; /**< hash handle */
+};
+
+/**
+ * Try to compress name passed or write it 'as is'
+ * @return
+ */
+bool rdns_write_name_compressed (struct rdns_request *req,
+               const char *name, unsigned int namelen,
+               struct rdns_compression_entry **comp);
+
+void rnds_compression_free (struct rdns_compression_entry *comp);
+
+#endif /* COMPRESSION_H_ */
diff --git a/contrib/librdns/curve.c b/contrib/librdns/curve.c
new file mode 100644 (file)
index 0000000..4c2a641
--- /dev/null
@@ -0,0 +1,472 @@
+/*
+ * 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 <sys/types.h>
+#include <sys/uio.h>
+#include <unistd.h>
+
+#include "rdns.h"
+#include "dns_private.h"
+#include "rdns_curve.h"
+#include "ottery.h"
+#include "ref.h"
+#include "logger.h"
+
+#ifdef TWEETNACL
+
+#include <tweetnacl.h>
+
+void
+randombytes(uint8_t *data, uint64_t len)
+{
+       ottery_rand_bytes (data, len);
+}
+void sodium_memzero (uint8_t *data, uint64_t len)
+{
+       volatile uint8_t *p = data;
+
+       while (len--) {
+               *p = '\0';
+       }
+}
+void sodium_init(void)
+{
+
+}
+
+ssize_t rdns_curve_send (struct rdns_request *req, void *plugin_data);
+ssize_t rdns_curve_recv (struct rdns_io_channel *ioc, void *buf, size_t len,
+               void *plugin_data, struct rdns_request **req_out);
+void rdns_curve_finish_request (struct rdns_request *req, void *plugin_data);
+void rdns_curve_dtor (struct rdns_resolver *resolver, void *plugin_data);
+
+struct rdns_curve_entry {
+       char *name;
+       unsigned char pk[crypto_box_PUBLICKEYBYTES];
+       UT_hash_handle hh;
+};
+
+struct rdns_curve_nm_entry {
+       unsigned char k[crypto_box_BEFORENMBYTES];
+       struct rdns_curve_entry *entry;
+       struct rdns_curve_nm_entry *prev, *next;
+};
+
+struct rdns_curve_client_key {
+       unsigned char pk[crypto_box_PUBLICKEYBYTES];
+       unsigned char sk[crypto_box_SECRETKEYBYTES];
+       struct rdns_curve_nm_entry *nms;
+       uint64_t counter;
+       unsigned int uses;
+       ref_entry_t ref;
+};
+
+struct rdns_curve_request {
+       struct rdns_request *req;
+       struct rdns_curve_client_key *key;
+       struct rdns_curve_entry *entry;
+       struct rdns_curve_nm_entry *nm;
+       unsigned char nonce[crypto_box_NONCEBYTES];
+       UT_hash_handle hh;
+};
+
+struct rdns_curve_ctx {
+       struct rdns_curve_entry *entries;
+       struct rdns_curve_client_key *cur_key;
+       struct rdns_curve_request *requests;
+       double key_refresh_interval;
+       void *key_refresh_event;
+       struct rdns_resolver *resolver;
+};
+
+static struct rdns_curve_client_key *
+rdns_curve_client_key_new (struct rdns_curve_ctx *ctx)
+{
+       struct rdns_curve_client_key *new;
+       struct rdns_curve_nm_entry *nm;
+       struct rdns_curve_entry *entry, *tmp;
+
+       new = calloc (1, sizeof (struct rdns_curve_client_key));
+       crypto_box_keypair (new->pk, new->sk);
+
+       HASH_ITER (hh, ctx->entries, entry, tmp) {
+               nm = calloc (1, sizeof (struct rdns_curve_nm_entry));
+               nm->entry = entry;
+               crypto_box_beforenm (nm->k, entry->pk, new->sk);
+
+               DL_APPEND (new->nms, nm);
+       }
+
+       new->counter = ottery_rand_uint64 ();
+
+       return new;
+}
+
+static struct rdns_curve_nm_entry *
+rdns_curve_find_nm (struct rdns_curve_client_key *key, struct rdns_curve_entry *entry)
+{
+       struct rdns_curve_nm_entry *nm;
+
+       DL_FOREACH (key->nms, nm) {
+               if (nm->entry == entry) {
+                       return nm;
+               }
+       }
+
+       return NULL;
+}
+
+static void
+rdns_curve_client_key_free (struct rdns_curve_client_key *key)
+{
+       struct rdns_curve_nm_entry *nm, *tmp;
+
+       DL_FOREACH_SAFE (key->nms, nm, tmp) {
+               sodium_memzero (nm->k, sizeof (nm->k));
+               free (nm);
+       }
+       sodium_memzero (key->sk, sizeof (key->sk));
+       free (key);
+}
+
+struct rdns_curve_ctx*
+rdns_curve_ctx_new (double key_refresh_interval)
+{
+       struct rdns_curve_ctx *new;
+
+       new = calloc (1, sizeof (struct rdns_curve_ctx));
+       new->key_refresh_interval = key_refresh_interval;
+
+       return new;
+}
+
+void
+rdns_curve_ctx_add_key (struct rdns_curve_ctx *ctx,
+               const char *name, const unsigned char *pubkey)
+{
+       struct rdns_curve_entry *entry;
+       bool success = true;
+
+       entry = malloc (sizeof (struct rdns_curve_entry));
+       if (entry != NULL) {
+               entry->name = strdup (name);
+               if (entry->name == NULL) {
+                       success = false;
+               }
+               memcpy (entry->pk, pubkey, sizeof (entry->pk));
+               if (success) {
+                       HASH_ADD_KEYPTR (hh, ctx->entries, entry->name, strlen (entry->name), entry);
+               }
+       }
+}
+
+#define rdns_curve_write_hex(in, out, offset, base) do {                                       \
+    *(out) |= ((in)[(offset)] - (base)) << ((1 - offset) * 4);                         \
+} while (0)
+
+static bool
+rdns_curve_hex_to_byte (const char *in, unsigned char *out)
+{
+       int i;
+
+       for (i = 0; i <= 1; i ++) {
+               if (in[i] >= '0' && in[i] <= '9') {
+                       rdns_curve_write_hex (in, out, i, '0');
+               }
+               else if (in[i] >= 'a' && in[i] <= 'f') {
+                       rdns_curve_write_hex (in, out, i, 'a' - 10);
+               }
+               else if (in[i] >= 'A' && in[i] <= 'F') {
+                       rdns_curve_write_hex (in, out, i, 'A' - 10);
+               }
+               else {
+                       return false;
+               }
+       }
+       return true;
+}
+
+#undef rdns_curve_write_hex
+
+unsigned char *
+rdns_curve_key_from_hex (const char *hex)
+{
+       unsigned int len = strlen (hex), i;
+       unsigned char *res = NULL;
+
+       if (len == crypto_box_PUBLICKEYBYTES * 2) {
+               res = calloc (1, crypto_box_PUBLICKEYBYTES);
+               for (i = 0; i < crypto_box_PUBLICKEYBYTES; i ++) {
+                       if (!rdns_curve_hex_to_byte (&hex[i * 2], &res[i])) {
+                               free (res);
+                               return NULL;
+                       }
+               }
+       }
+
+       return res;
+}
+
+void
+rdns_curve_ctx_destroy (struct rdns_curve_ctx *ctx)
+{
+       struct rdns_curve_entry *entry, *tmp;
+
+       HASH_ITER (hh, ctx->entries, entry, tmp) {
+               free (entry->name);
+               free (entry);
+       }
+
+       free (ctx);
+}
+
+static void
+rdns_curve_refresh_key_callback (void *user_data)
+{
+       struct rdns_curve_ctx *ctx = user_data;
+       struct rdns_resolver *resolver;
+
+       resolver = ctx->resolver;
+       rdns_info ("refresh dnscurve keys");
+       REF_RELEASE (ctx->cur_key);
+       ctx->cur_key = rdns_curve_client_key_new (ctx);
+       REF_INIT_RETAIN (ctx->cur_key, rdns_curve_client_key_free);
+}
+
+void
+rdns_curve_register_plugin (struct rdns_resolver *resolver,
+               struct rdns_curve_ctx *ctx)
+{
+       struct rdns_plugin *plugin;
+
+       if (!resolver->async_binded) {
+               return;
+       }
+
+       plugin = calloc (1, sizeof (struct rdns_plugin));
+       if (plugin != NULL) {
+               plugin->data = ctx;
+               plugin->type = RDNS_PLUGIN_CURVE;
+               plugin->cb.curve_plugin.send_cb = rdns_curve_send;
+               plugin->cb.curve_plugin.recv_cb = rdns_curve_recv;
+               plugin->cb.curve_plugin.finish_cb = rdns_curve_finish_request;
+               plugin->dtor = rdns_curve_dtor;
+               sodium_init ();
+               ctx->cur_key = rdns_curve_client_key_new (ctx);
+               REF_INIT_RETAIN (ctx->cur_key, rdns_curve_client_key_free);
+
+               if (ctx->key_refresh_interval > 0) {
+                       ctx->key_refresh_event = resolver->async->add_periodic (
+                                       resolver->async->data, ctx->key_refresh_interval,
+                                       rdns_curve_refresh_key_callback, ctx);
+               }
+               ctx->resolver = resolver;
+               rdns_resolver_register_plugin (resolver, plugin);
+       }
+}
+
+ssize_t
+rdns_curve_send (struct rdns_request *req, void *plugin_data)
+{
+       struct rdns_curve_ctx *ctx = (struct rdns_curve_ctx *)plugin_data;
+       struct rdns_curve_entry *entry;
+       struct iovec iov[4];
+       unsigned char *m;
+       static const char qmagic[] = "Q6fnvWj8";
+       struct rdns_curve_request *creq;
+       struct rdns_curve_nm_entry *nm;
+       ssize_t ret, boxed_len;
+
+       /* Check for key */
+       HASH_FIND_STR (ctx->entries, req->io->srv->name, entry);
+       if (entry != NULL) {
+               nm = rdns_curve_find_nm (ctx->cur_key, entry);
+               creq = malloc (sizeof (struct rdns_curve_request));
+               if (creq == NULL) {
+                       return -1;
+               }
+
+               boxed_len = req->pos + crypto_box_ZEROBYTES;
+               m = malloc (boxed_len);
+               if (m == NULL) {
+                       return -1;
+               }
+
+               /* Ottery is faster than sodium native PRG that uses /dev/random only */
+               memcpy (creq->nonce, &ctx->cur_key->counter, sizeof (uint64_t));
+               ottery_rand_bytes (creq->nonce + sizeof (uint64_t), 12 - sizeof (uint64_t));
+               sodium_memzero (creq->nonce + 12, crypto_box_NONCEBYTES - 12);
+
+               sodium_memzero (m, crypto_box_ZEROBYTES);
+               memcpy (m + crypto_box_ZEROBYTES, req->packet, req->pos);
+
+               if (crypto_box_afternm (m, m, boxed_len,
+                               creq->nonce, nm->k) == -1) {
+                       sodium_memzero (m, boxed_len);
+                       free (m);
+                       return -1;
+               }
+
+               creq->key = ctx->cur_key;
+               REF_RETAIN (ctx->cur_key);
+               creq->entry = entry;
+               creq->req = req;
+               creq->nm = nm;
+               HASH_ADD_KEYPTR (hh, ctx->requests, creq->nonce, 12, creq);
+               req->curve_plugin_data = creq;
+
+               ctx->cur_key->counter ++;
+               ctx->cur_key->uses ++;
+
+               /* Now form a dnscurve packet */
+               iov[0].iov_base = (void *)qmagic;
+               iov[0].iov_len = sizeof (qmagic) - 1;
+               iov[1].iov_base = ctx->cur_key->pk;
+               iov[1].iov_len = sizeof (ctx->cur_key->pk);
+               iov[2].iov_base = creq->nonce;
+               iov[2].iov_len = 12;
+               iov[3].iov_base = m + crypto_box_BOXZEROBYTES;
+               iov[3].iov_len = boxed_len - crypto_box_BOXZEROBYTES;
+
+               ret = writev (req->io->sock, iov, sizeof (iov) / sizeof (iov[0]));
+               sodium_memzero (m, boxed_len);
+               free (m);
+       }
+       else {
+               ret = write (req->io->sock, req->packet, req->pos);
+               req->curve_plugin_data = NULL;
+       }
+
+       return ret;
+}
+
+ssize_t
+rdns_curve_recv (struct rdns_io_channel *ioc, void *buf, size_t len, void *plugin_data,
+               struct rdns_request **req_out)
+{
+       struct rdns_curve_ctx *ctx = (struct rdns_curve_ctx *)plugin_data;
+       ssize_t ret, boxlen;
+       static const char rmagic[] = "R6fnvWJ8";
+       unsigned char *p, *box;
+       unsigned char enonce[crypto_box_NONCEBYTES];
+       struct rdns_curve_request *creq;
+       struct rdns_resolver *resolver;
+
+       resolver = ctx->resolver;
+       ret = read (ioc->sock, buf, len);
+
+       if (ret <= 0 || ret < 64) {
+               /* Definitely not a DNSCurve packet */
+               return ret;
+       }
+
+       if (memcmp (buf, rmagic, sizeof (rmagic) - 1) == 0) {
+               /* Likely DNSCurve packet */
+               p = ((unsigned char *)buf) + 8;
+               HASH_FIND (hh, ctx->requests, p, 12, creq);
+               if (creq == NULL) {
+                       rdns_info ("unable to find nonce in the internal hash");
+                       return ret;
+               }
+               memcpy (enonce, p, crypto_box_NONCEBYTES);
+               p += crypto_box_NONCEBYTES;
+               boxlen = ret - crypto_box_NONCEBYTES +
+                               crypto_box_BOXZEROBYTES -
+                               sizeof (rmagic) + 1;
+               if (boxlen < 0) {
+                       return ret;
+               }
+               box = malloc (boxlen);
+               sodium_memzero (box, crypto_box_BOXZEROBYTES);
+               memcpy (box + crypto_box_BOXZEROBYTES, p,
+                               boxlen - crypto_box_BOXZEROBYTES);
+
+               if (crypto_box_open_afternm (box, box, boxlen, enonce, creq->nm->k) != -1) {
+                       memcpy (buf, box + crypto_box_ZEROBYTES,
+                                       boxlen - crypto_box_ZEROBYTES);
+                       ret = boxlen - crypto_box_ZEROBYTES;
+                       *req_out = creq->req;
+               }
+               else {
+                       rdns_info ("unable open cryptobox of size %d", (int)boxlen);
+               }
+               free (box);
+       }
+
+       return ret;
+}
+
+void
+rdns_curve_finish_request (struct rdns_request *req, void *plugin_data)
+{
+       struct rdns_curve_ctx *ctx = (struct rdns_curve_ctx *)plugin_data;
+       struct rdns_curve_request *creq = req->curve_plugin_data;
+
+       if (creq != NULL) {
+               REF_RELEASE (creq->key);
+               HASH_DELETE (hh, ctx->requests, creq);
+       }
+}
+
+void
+rdns_curve_dtor (struct rdns_resolver *resolver, void *plugin_data)
+{
+       struct rdns_curve_ctx *ctx = (struct rdns_curve_ctx *)plugin_data;
+
+       if (ctx->key_refresh_event != NULL) {
+               resolver->async->del_periodic (resolver->async->data,
+                               ctx->key_refresh_event);
+       }
+       REF_RELEASE (ctx->cur_key);
+}
+
+#else
+
+/* Fake functions */
+struct rdns_curve_ctx* rdns_curve_ctx_new (double rekey_interval)
+{
+       return NULL;
+}
+void rdns_curve_ctx_add_key (struct rdns_curve_ctx *ctx,
+               const char *name, const unsigned char *pubkey)
+{
+
+}
+void rdns_curve_ctx_destroy (struct rdns_curve_ctx *ctx)
+{
+
+}
+void rdns_curve_register_plugin (struct rdns_resolver *resolver,
+               struct rdns_curve_ctx *ctx)
+{
+
+}
+
+unsigned char *
+rdns_curve_key_from_hex (const char *hex)
+{
+       return NULL;
+}
+#endif
diff --git a/contrib/librdns/dns_private.h b/contrib/librdns/dns_private.h
new file mode 100644 (file)
index 0000000..d23f845
--- /dev/null
@@ -0,0 +1,262 @@
+/* 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.
+ */
+
+#ifndef DNS_PRIVATE_H_
+#define DNS_PRIVATE_H_
+
+#include "uthash.h"
+#include "utlist.h"
+#include "rdns.h"
+#include "upstream.h"
+#include "ref.h"
+
+static const unsigned base = 36;
+static const unsigned t_min = 1;
+static const unsigned t_max = 26;
+static const unsigned skew = 38;
+static const unsigned damp = 700;
+static const unsigned initial_n = 128;
+static const unsigned initial_bias = 72;
+
+static const int dns_port = 53;
+static const int default_io_cnt = 8;
+
+#define UDP_PACKET_SIZE 4096
+
+#define DNS_COMPRESSION_BITS 0xC0
+
+#define DNS_D_MAXLABEL  63      /* + 1 '\0' */
+#define DNS_D_MAXNAME   255     /* + 1 '\0' */
+
+#define RESOLV_CONF "/etc/resolv.conf"
+
+/**
+ * Represents DNS server
+ */
+struct rdns_server {
+       char *name;
+       unsigned int port;
+       unsigned int io_cnt;
+
+       struct rdns_io_channel **io_channels;
+       upstream_entry_t up;
+};
+
+struct rdns_request {
+       struct rdns_resolver *resolver;
+       struct rdns_async_context *async;
+       struct rdns_io_channel *io;
+       struct rdns_reply *reply;
+       enum rdns_request_type type;
+
+       double timeout;
+       unsigned int retransmits;
+
+       int id;
+       struct rdns_request_name *requested_names;
+       unsigned int qcount;
+
+       enum {
+               RDNS_REQUEST_NEW = 0,
+               RDNS_REQUEST_REGISTERED = 1,
+               RDNS_REQUEST_SENT,
+               RDNS_REQUEST_REPLIED
+       } state;
+
+       uint8_t *packet;
+       off_t pos;
+       unsigned int packet_len;
+
+       dns_callback_type func;
+       void *arg;
+
+       void *async_event;
+
+#ifdef TWEETNACL
+       void *curve_plugin_data;
+#endif
+
+       UT_hash_handle hh;
+       ref_entry_t ref;
+};
+
+/**
+ * IO channel for a specific DNS server
+ */
+struct rdns_io_channel {
+       struct rdns_server *srv;
+       struct rdns_resolver *resolver;
+       int sock; /**< persistent socket                                          */
+       bool active;
+       void *async_io; /** async opaque ptr */
+       struct rdns_request *requests; /**< requests in flight                                         */
+       uint64_t uses;
+       ref_entry_t ref;
+};
+
+
+struct rdns_resolver {
+       struct rdns_server *servers;
+       struct rdns_io_channel *io_channels; /**< hash of io chains indexed by socket        */
+       struct rdns_async_context *async; /** async callbacks */
+       void *periodic; /** periodic event for resolver */
+
+       struct rdns_plugin *curve_plugin;
+
+       rdns_log_function logger;
+       void *log_data;
+       enum rdns_log_level log_level;
+
+       uint64_t max_ioc_uses;
+       void *refresh_ioc_periodic;
+
+       bool async_binded;
+       bool initialized;
+       ref_entry_t ref;
+};
+
+struct dns_header;
+struct dns_query;
+
+/* Internal DNS structs */
+
+struct dns_header {
+       unsigned int qid :16;
+
+#if BYTE_ORDER == BIG_ENDIAN
+       unsigned int qr:1;
+       unsigned int opcode:4;
+       unsigned int aa:1;
+       unsigned int tc:1;
+       unsigned int rd:1;
+
+       unsigned int ra:1;
+       unsigned int unused:3;
+       unsigned int rcode:4;
+#else
+       unsigned int rd :1;
+       unsigned int tc :1;
+       unsigned int aa :1;
+       unsigned int opcode :4;
+       unsigned int qr :1;
+
+       unsigned int rcode :4;
+       unsigned int unused :3;
+       unsigned int ra :1;
+#endif
+
+       unsigned int qdcount :16;
+       unsigned int ancount :16;
+       unsigned int nscount :16;
+       unsigned int arcount :16;
+};
+
+enum dns_section {
+       DNS_S_QD = 0x01,
+#define DNS_S_QUESTION          DNS_S_QD
+
+       DNS_S_AN = 0x02,
+#define DNS_S_ANSWER            DNS_S_AN
+
+       DNS_S_NS = 0x04,
+#define DNS_S_AUTHORITY         DNS_S_NS
+
+       DNS_S_AR = 0x08,
+#define DNS_S_ADDITIONAL        DNS_S_AR
+
+       DNS_S_ALL = 0x0f
+};
+/* enum dns_section */
+
+enum dns_opcode {
+       DNS_OP_QUERY = 0,
+       DNS_OP_IQUERY = 1,
+       DNS_OP_STATUS = 2,
+       DNS_OP_NOTIFY = 4,
+       DNS_OP_UPDATE = 5,
+};
+/* dns_opcode */
+
+enum dns_class {
+       DNS_C_IN = 1,
+
+       DNS_C_ANY = 255
+};
+/* enum dns_class */
+
+struct dns_query {
+       char *qname;
+       unsigned int qtype :16;
+       unsigned int qclass :16;
+};
+
+enum dns_type {
+       DNS_T_A = RDNS_REQUEST_A,
+       DNS_T_NS = RDNS_REQUEST_NS,
+       DNS_T_CNAME = 5,
+       DNS_T_SOA = RDNS_REQUEST_SOA,
+       DNS_T_PTR = RDNS_REQUEST_PTR,
+       DNS_T_MX = RDNS_REQUEST_MX,
+       DNS_T_TXT = RDNS_REQUEST_TXT,
+       DNS_T_AAAA = RDNS_REQUEST_AAAA,
+       DNS_T_SRV = RDNS_REQUEST_SRV,
+       DNS_T_OPT = 41,
+       DNS_T_SSHFP = 44,
+       DNS_T_TLSA = RDNS_REQUEST_TLSA,
+       DNS_T_SPF = RDNS_REQUEST_SPF,
+       DNS_T_ALL = RDNS_REQUEST_ANY
+};
+/* enum dns_type */
+
+static const char dns_rcodes[][32] = {
+       [RDNS_RC_NOERROR]  = "no error",
+       [RDNS_RC_FORMERR]  = "query format error",
+       [RDNS_RC_SERVFAIL] = "server fail",
+       [RDNS_RC_NXDOMAIN] = "no records with this name",
+       [RDNS_RC_NOTIMP]   = "not implemented",
+       [RDNS_RC_REFUSED]  = "query refused",
+       [RDNS_RC_YXDOMAIN] = "YXDOMAIN",
+       [RDNS_RC_YXRRSET]  = "YXRRSET",
+       [RDNS_RC_NXRRSET]  = "NXRRSET",
+       [RDNS_RC_NOTAUTH]  = "not authorized",
+       [RDNS_RC_NOTZONE]  = "no such zone",
+       [RDNS_RC_TIMEOUT]  = "query timed out",
+       [RDNS_RC_NETERR]  = "network error",
+       [RDNS_RC_NOREC]  = "requested record is not found"
+};
+
+static const char dns_types[][16] = {
+       [RDNS_REQUEST_A] = "A request",
+       [RDNS_REQUEST_NS] = "NS request",
+       [RDNS_REQUEST_PTR] = "PTR request",
+       [RDNS_REQUEST_MX] = "MX request",
+       [RDNS_REQUEST_TXT] = "TXT request",
+       [RDNS_REQUEST_SRV] = "SRV request",
+       [RDNS_REQUEST_SPF] = "SPF request",
+       [RDNS_REQUEST_AAAA] = "AAAA request",
+       [RDNS_REQUEST_TLSA] = "TLSA request",
+       [RDNS_REQUEST_ANY] = "ANY request"
+};
+
+
+#endif /* DNS_PRIVATE_H_ */
diff --git a/contrib/librdns/logger.c b/contrib/librdns/logger.c
new file mode 100644 (file)
index 0000000..c9ed2d9
--- /dev/null
@@ -0,0 +1,53 @@
+/* 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.
+ */
+
+#include <stdio.h>
+#include "dns_private.h"
+#include "logger.h"
+
+void
+rdns_logger_internal (void *log_data, enum rdns_log_level level,
+               const char *function, const char *format,
+               va_list args)
+{
+       struct rdns_resolver *resolver = log_data;
+
+       if (level <= resolver->log_level) {
+               fprintf (stderr, "rdns: %s: ", function);
+               vfprintf (stderr, format, args);
+               fprintf (stderr, "\n");
+       }
+}
+
+void rdns_logger_helper (struct rdns_resolver *resolver,
+               enum rdns_log_level level,
+               const char *function, const char *format, ...)
+{
+       va_list va;
+
+       if (resolver->logger != NULL) {
+               va_start (va, format);
+               resolver->logger (resolver->log_data, level, function, format, va);
+               va_end (va);
+       }
+}
diff --git a/contrib/librdns/logger.h b/contrib/librdns/logger.h
new file mode 100644 (file)
index 0000000..91108f4
--- /dev/null
@@ -0,0 +1,42 @@
+/* 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.
+ */
+#ifndef LOGGER_H_
+#define LOGGER_H_
+
+#include <stdarg.h>
+#include "dns_private.h"
+
+void rdns_logger_internal (void *log_data, enum rdns_log_level level,
+               const char *function, const char *format,
+               va_list args);
+
+void rdns_logger_helper (struct rdns_resolver *resolver,
+               enum rdns_log_level level,
+               const char *function, const char *format, ...);
+
+#define rdns_err(...) do { rdns_logger_helper (resolver, RDNS_LOG_ERROR, __FUNCTION__, __VA_ARGS__); } while (0)
+#define rdns_warn(...) do { rdns_logger_helper (resolver, RDNS_LOG_WARNING, __FUNCTION__, __VA_ARGS__); } while (0)
+#define rdns_info(...) do { rdns_logger_helper (resolver, RDNS_LOG_INFO, __FUNCTION__, __VA_ARGS__); } while (0)
+#define rdns_debug(...) do { rdns_logger_helper (resolver, RDNS_LOG_DEBUG, __FUNCTION__, __VA_ARGS__); } while (0)
+
+#endif /* LOGGER_H_ */
diff --git a/contrib/librdns/packet.c b/contrib/librdns/packet.c
new file mode 100644 (file)
index 0000000..88e51df
--- /dev/null
@@ -0,0 +1,271 @@
+/* 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.
+ */
+
+#include "rdns.h"
+#include "dns_private.h"
+#include "punycode.h"
+#include "packet.h"
+#include "util.h"
+#include "logger.h"
+#include "compression.h"
+
+void
+rdns_allocate_packet (struct rdns_request* req, unsigned int namelen)
+{
+       namelen += 96 + 2 + 4 + 11; /* EDNS0 RR */
+       req->packet = malloc (namelen);
+       req->pos = 0;
+       req->packet_len = namelen;
+}
+
+
+void
+rdns_make_dns_header (struct rdns_request *req, unsigned int qcount)
+{
+       struct dns_header *header;
+
+       /* Set DNS header values */
+       header = (struct dns_header *)req->packet;
+       memset (header, 0 , sizeof (struct dns_header));
+       header->qid = rdns_permutor_generate_id ();
+       header->rd = 1;
+       header->qdcount = htons (qcount);
+       header->arcount = htons (1);
+       req->pos += sizeof (struct dns_header);
+       req->id = header->qid;
+}
+
+static bool
+rdns_maybe_punycode_label (const uint8_t *begin,
+               uint8_t const **dot, size_t *label_len)
+{
+       bool ret = false;
+       const uint8_t *p = begin;
+
+       *dot = NULL;
+
+       while (*p) {
+               if (*p == '.') {
+                       *dot = p;
+                       break;
+               }
+               else if ((*p) & 0x80) {
+                       ret = true;
+               }
+               p ++;
+       }
+
+       if (*p) {
+               *label_len = p - begin;
+       }
+       else {
+               *label_len = p - begin;
+       }
+
+       return ret;
+}
+
+bool
+rdns_format_dns_name (struct rdns_resolver *resolver, const char *in,
+               size_t inlen,
+               char **out, size_t *outlen)
+{
+       const uint8_t *dot;
+       const uint8_t *p = in, *end = in + inlen;
+       char *o;
+       int labels = 0;
+       size_t label_len, olen, remain;
+       uint32_t *uclabel;
+       size_t punylabel_len, uclabel_len;
+       char tmp_label[DNS_D_MAXLABEL];
+       bool need_encode = false;
+
+       if (inlen == 0) {
+               inlen = strlen (in);
+       }
+
+       /* Check for non-ascii characters */
+       while (p != end) {
+               if (*p >= 0x80) {
+                       need_encode = true;
+               }
+               else if (*p == '.') {
+                       labels ++;
+               }
+               p ++;
+       }
+
+       if (!need_encode) {
+               *out = malloc (inlen + 1);
+               if (*out == NULL) {
+                       return false;
+               }
+               o = *out;
+               memcpy (o, in, inlen);
+               o[inlen] = '\0';
+               *outlen = inlen;
+
+               return true;
+       }
+
+       /* We need to encode */
+
+       p = in;
+       olen = inlen + 1 + sizeof ("xn--") * labels;
+       *out = malloc (olen);
+
+       if (*out == NULL) {
+               return false;
+       }
+
+       o = *out;
+       remain = olen;
+
+       while (p != end) {
+               /* Check label for unicode characters */
+               if (rdns_maybe_punycode_label (p, &dot, &label_len)) {
+                       /* Convert to ucs4 */
+                       if (rdns_utf8_to_ucs4 (p, label_len, &uclabel, &uclabel_len) == 0) {
+                               punylabel_len = DNS_D_MAXLABEL;
+
+                               rdns_punycode_label_toascii (uclabel, uclabel_len,
+                                               tmp_label, &punylabel_len);
+                               if (remain >= punylabel_len + 1) {
+                                       memcpy (o, tmp_label, punylabel_len);
+                                       o += punylabel_len;
+                                       *o++ = '.';
+                                       remain -= punylabel_len + 1;
+                               }
+                               else {
+                                       rdns_info ("no buffer remain for punycoding query");
+                                       free (*out);
+                                       return false;
+                               }
+
+                               free (uclabel);
+
+                               if (dot) {
+                                       p = dot + 1;
+                               }
+                               else {
+                                       break;
+                               }
+                       }
+                       else {
+                               break;
+                       }
+               }
+               else {
+                       if (dot) {
+                               if (label_len > DNS_D_MAXLABEL) {
+                                       rdns_info ("dns name component is longer than 63 bytes, should be stripped");
+                                       label_len = DNS_D_MAXLABEL;
+                               }
+                               if (remain < label_len + 1) {
+                                       rdns_info ("no buffer remain for punycoding query");
+                                       return false;
+                               }
+                               if (label_len == 0) {
+                                       /* Two dots in order, skip this */
+                                       rdns_info ("name contains two or more dots in a row, replace it with one dot");
+                                       p = dot + 1;
+                                       continue;
+                               }
+                               memcpy (o, p, label_len);
+                               o += label_len;
+                               *o++ = '.';
+                               remain -= label_len + 1;
+                               p = dot + 1;
+                       }
+                       else {
+                               if (label_len == 0) {
+                                       /* If name is ended with dot */
+                                       break;
+                               }
+                               if (label_len > DNS_D_MAXLABEL) {
+                                       rdns_info ("dns name component is longer than 63 bytes, should be stripped");
+                                       label_len = DNS_D_MAXLABEL;
+                               }
+                               if (remain < label_len + 1) {
+                                       rdns_info ("no buffer remain for punycoding query");
+                                       return false;
+                               }
+                               memcpy (o, p, label_len);
+                               o += label_len;
+                               *o++ = '.';
+                               remain -= label_len + 1;
+                               p = dot + 1;
+                               break;
+                       }
+               }
+               if (remain == 0) {
+                       rdns_info ("no buffer remain for punycoding query");
+                       return false;
+               }
+       }
+       *o = '\0';
+
+       *outlen = o - *out;
+
+       return true;
+}
+
+bool
+rdns_add_rr (struct rdns_request *req, const char *name, unsigned int len,
+               enum dns_type type, struct rdns_compression_entry **comp)
+{
+       uint16_t *p;
+
+       if (!rdns_write_name_compressed (req, name, len, comp)) {
+               return false;
+       }
+       p = (uint16_t *)(req->packet + req->pos);
+       *p++ = htons (type);
+       *p = htons (DNS_C_IN);
+       req->pos += sizeof (uint16_t) * 2;
+
+       return true;
+}
+
+bool
+rdns_add_edns0 (struct rdns_request *req)
+{
+       uint8_t *p8;
+       uint16_t *p16;
+
+       p8 = (uint8_t *)(req->packet + req->pos);
+       *p8 = '\0'; /* Name is root */
+       p16 = (uint16_t *)(req->packet + req->pos + 1);
+       *p16++ = htons (DNS_T_OPT);
+       /* UDP packet length */
+       *p16++ = htons (UDP_PACKET_SIZE);
+       /* Extended rcode 00 00 */
+       *p16++ = 0;
+       /* Z 10000000 00000000 to allow dnssec, disabled currently */
+       *p16++ = 0;
+       /* Length */
+       *p16 = 0;
+       req->pos += sizeof (uint8_t) + sizeof (uint16_t) * 5;
+
+       return true;
+}
diff --git a/contrib/librdns/packet.h b/contrib/librdns/packet.h
new file mode 100644 (file)
index 0000000..f4c09b9
--- /dev/null
@@ -0,0 +1,71 @@
+/* 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.
+ */
+#ifndef PACKET_H_
+#define PACKET_H_
+
+#include <stdbool.h>
+#include <stdint.h>
+#include "dns_private.h"
+
+struct rdns_compression_entry;
+
+/**
+ * Allocate dns packet suitable to handle up to `namelen` name
+ * @param req request
+ * @param namelen requested name
+ */
+void rdns_allocate_packet (struct rdns_request* req, unsigned int namelen);
+
+/**
+ * Add basic header to the dns packet
+ * @param req
+ */
+void rdns_make_dns_header (struct rdns_request *req, unsigned int qcount);
+
+
+/**
+ * Format DNS name of the packet
+ * @param req request
+ * @param name name string
+ * @param namelen length of name
+ */
+bool rdns_format_dns_name (struct rdns_resolver *resolver,
+               const char *name, size_t namelen,
+               char **out, size_t *outlen);
+
+/**
+ * Add a resource record to the DNS packet
+ * @param req request
+ * @param name requested name
+ * @param type type of resource record
+ */
+bool rdns_add_rr (struct rdns_request *req, const char *name, unsigned int len,
+               enum dns_type type, struct rdns_compression_entry **comp);
+
+/**
+ * Add EDNS0 section
+ * @param req
+ */
+bool rdns_add_edns0 (struct rdns_request *req);
+
+#endif /* PACKET_H_ */
diff --git a/contrib/librdns/parse.c b/contrib/librdns/parse.c
new file mode 100644 (file)
index 0000000..e9527ea
--- /dev/null
@@ -0,0 +1,419 @@
+/* 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.
+ */
+
+#include "rdns.h"
+#include "dns_private.h"
+#include "parse.h"
+#include "logger.h"
+
+static uint8_t *
+rdns_decompress_label (uint8_t *begin, uint16_t *len, uint16_t max)
+{
+       uint16_t offset = (*len);
+
+       if (offset > max) {
+               return NULL;
+       }
+       *len = *(begin + offset);
+       return begin + offset;
+}
+
+#define UNCOMPRESS_DNS_OFFSET(p) (((*(p)) ^ DNS_COMPRESSION_BITS) << 8) + *((p) + 1)
+
+uint8_t *
+rdns_request_reply_cmp (struct rdns_request *req, uint8_t *in, int len)
+{
+       uint8_t *p, *c, *l1, *l2;
+       uint16_t len1, len2;
+       int decompressed = 0;
+       struct rdns_resolver *resolver = req->resolver;
+
+       /* QR format:
+        * labels - len:octets
+        * null label - 0
+        * class - 2 octets
+        * type - 2 octets
+        */
+
+       /* In p we would store current position in reply and in c - position in request */
+       p = in;
+       c = req->packet + req->pos;
+
+       for (;;) {
+               /* Get current label */
+               len1 = *p;
+               len2 = *c;
+               if (p - in > len) {
+                       rdns_info ("invalid dns reply");
+                       return NULL;
+               }
+               /* This may be compressed, so we need to decompress it */
+               if (len1 & DNS_COMPRESSION_BITS) {
+                       len1 = UNCOMPRESS_DNS_OFFSET(p);
+                       l1 = rdns_decompress_label (in, &len1, len);
+                       if (l1 == NULL) {
+                               return NULL;
+                       }
+                       decompressed ++;
+                       l1 ++;
+                       p += 2;
+               }
+               else {
+                       l1 = ++p;
+                       p += len1;
+               }
+               if (len2 & DNS_COMPRESSION_BITS) {
+                       len2 = UNCOMPRESS_DNS_OFFSET(c);
+                       l2 = rdns_decompress_label (c, &len2, len);
+                       if (l2 == NULL) {
+                               rdns_info ("invalid DNS pointer, cannot decompress");
+                               return NULL;
+                       }
+                       decompressed ++;
+                       l2 ++;
+                       c += 2;
+               }
+               else {
+                       l2 = ++c;
+                       c += len2;
+               }
+               if (len1 != len2) {
+                       return NULL;
+               }
+               if (len1 == 0) {
+                       break;
+               }
+
+               if (memcmp (l1, l2, len1) != 0) {
+                       return NULL;
+               }
+               if (decompressed == 2) {
+                       break;
+               }
+       }
+
+       /* p now points to the end of QR section */
+       /* Compare class and type */
+       if (memcmp (p, c, sizeof (uint16_t) * 2) == 0) {
+               req->pos = c - req->packet + sizeof (uint16_t) * 2;
+               return p + sizeof (uint16_t) * 2;
+       }
+       return NULL;
+}
+
+#define MAX_RECURSION_LEVEL 10
+
+bool
+rdns_parse_labels (struct rdns_resolver *resolver,
+               uint8_t *in, char **target, uint8_t **pos, struct rdns_reply *rep,
+               int *remain, bool make_name)
+{
+       uint16_t namelen = 0;
+       uint8_t *p = *pos, *begin = *pos, *l, *t, *end = *pos + *remain, *new_pos = *pos;
+       uint16_t llen;
+       int length = *remain, new_remain = *remain;
+       int ptrs = 0, labels = 0;
+       bool got_compression = false;
+
+       /* First go through labels and calculate name length */
+       while (p - begin < length) {
+               if (ptrs > MAX_RECURSION_LEVEL) {
+                       rdns_info ("dns pointers are nested too much");
+                       return false;
+               }
+               llen = *p;
+               if (llen == 0) {
+                       if (!got_compression) {
+                               /* In case of compression we have already decremented the processing position */
+                               new_remain -= sizeof (uint8_t);
+                               new_pos += sizeof (uint8_t);
+                       }
+                       break;
+               }
+               else if ((llen & DNS_COMPRESSION_BITS)) {
+                       if (end - p > 1) {
+                               ptrs ++;
+                               llen = UNCOMPRESS_DNS_OFFSET(p);
+                               l = rdns_decompress_label (in, &llen, end - in);
+                               if (l == NULL) {
+                                       rdns_info ("invalid DNS pointer");
+                                       return false;
+                               }
+                               if (!got_compression) {
+                                       /* Our label processing is finished actually */
+                                       new_remain -= sizeof (uint16_t);
+                                       new_pos += sizeof (uint16_t);
+                                       got_compression = true;
+                               }
+                               if (l < in || l > begin + length) {
+                                       rdns_info  ("invalid pointer in DNS packet");
+                                       return false;
+                               }
+                               begin = l;
+                               length = end - begin;
+                               p = l + *l + 1;
+                               namelen += *l;
+                               labels ++;
+                       }
+                       else {
+                               rdns_info ("DNS packet has incomplete compressed label, input length: %d bytes, remain: %d",
+                                               *remain, new_remain);
+                               return false;
+                       }
+               }
+               else {
+                       namelen += llen;
+                       p += llen + 1;
+                       labels ++;
+                       if (!got_compression) {
+                               new_remain -= llen + 1;
+                               new_pos += llen + 1;
+                       }
+               }
+       }
+
+       if (!make_name) {
+               goto end;
+       }
+       *target = malloc (namelen + labels + 3);
+       t = (uint8_t *)*target;
+       p = *pos;
+       begin = *pos;
+       length = *remain;
+       /* Now copy labels to name */
+       while (p - begin < length) {
+               llen = *p;
+               if (llen == 0) {
+                       break;
+               }
+               else if (llen & DNS_COMPRESSION_BITS) {
+                       llen = UNCOMPRESS_DNS_OFFSET(p);
+                       l = rdns_decompress_label (in, &llen, end - in);
+                       begin = l;
+                       length = end - begin;
+                       p = l + *l + 1;
+                       memcpy (t, l + 1, *l);
+                       t += *l;
+                       *t ++ = '.';
+               }
+               else {
+                       memcpy (t, p + 1, *p);
+                       t += *p;
+                       *t ++ = '.';
+                       p += *p + 1;
+               }
+       }
+       if (t > (uint8_t *)*target) {
+               *(t - 1) = '\0';
+       }
+       else {
+               /* Handle empty labels */
+               **target = '\0';
+       }
+end:
+       *remain = new_remain;
+       *pos = new_pos;
+
+       return true;
+}
+
+#define GET8(x) do {(x) = ((*p)); p += sizeof (uint8_t); *remain -= sizeof (uint8_t); } while(0)
+#define GET16(x) do {(x) = ((*p) << 8) + *(p + 1); p += sizeof (uint16_t); *remain -= sizeof (uint16_t); } while(0)
+#define GET32(x) do {(x) = ((*p) << 24) + ((*(p + 1)) << 16) + ((*(p + 2)) << 8) + *(p + 3); p += sizeof (uint32_t); *remain -= sizeof (uint32_t); } while(0)
+#define SKIP(type) do { p += sizeof(type); *remain -= sizeof(type); } while (0)
+
+int
+rdns_parse_rr (struct rdns_resolver *resolver,
+               uint8_t *in, struct rdns_reply_entry *elt, uint8_t **pos,
+               struct rdns_reply *rep, int *remain)
+{
+       uint8_t *p = *pos, parts;
+       uint16_t type, datalen, txtlen, copied;
+       int32_t ttl;
+       bool parsed = false;
+
+       /* Skip the whole name */
+       if (! rdns_parse_labels (resolver, in, NULL, &p, rep, remain, false)) {
+               rdns_info ("bad RR name");
+               return -1;
+       }
+       if (*remain < (int)sizeof (uint16_t) * 6) {
+               rdns_info ("stripped dns reply: %d bytes remain", *remain);
+               return -1;
+       }
+       GET16 (type);
+       /* Skip class */
+       SKIP (uint16_t);
+       GET32 (ttl);
+       GET16 (datalen);
+       elt->type = type;
+       /* Now p points to RR data */
+       switch (type) {
+       case DNS_T_A:
+               if (!(datalen & 0x3) && datalen <= *remain) {
+                       memcpy (&elt->content.a.addr, p, sizeof (struct in_addr));
+                       p += datalen;
+                       *remain -= datalen;
+                       parsed = true;
+               }
+               else {
+                       rdns_info ("corrupted A record");
+                       return -1;
+               }
+               break;
+       case DNS_T_AAAA:
+               if (datalen == sizeof (struct in6_addr) && datalen <= *remain) {
+                       memcpy (&elt->content.aaa.addr, p, sizeof (struct in6_addr));
+                       p += datalen;
+                       *remain -= datalen;
+                       parsed = true;
+               }
+               else {
+                       rdns_info ("corrupted AAAA record");
+                       return -1;
+               }
+               break;
+       case DNS_T_PTR:
+               if (! rdns_parse_labels (resolver, in, &elt->content.ptr.name, &p,
+                               rep, remain, true)) {
+                       rdns_info ("invalid labels in PTR record");
+                       return -1;
+               }
+               parsed = true;
+               break;
+       case DNS_T_NS:
+               if (! rdns_parse_labels (resolver, in, &elt->content.ns.name, &p,
+                               rep, remain, true)) {
+                       rdns_info ("invalid labels in NS record");
+                       return -1;
+               }
+               parsed = true;
+               break;
+       case DNS_T_SOA:
+               if (! rdns_parse_labels (resolver, in, &elt->content.soa.mname, &p,
+                               rep, remain, true)) {
+                       rdns_info ("invalid labels in NS record");
+                       return -1;
+               }
+               if (! rdns_parse_labels (resolver, in, &elt->content.soa.admin, &p,
+                               rep, remain, true)) {
+                       rdns_info ("invalid labels in NS record");
+                       return -1;
+               }
+               GET32 (elt->content.soa.serial);
+               GET32 (elt->content.soa.refresh);
+               GET32 (elt->content.soa.retry);
+               GET32 (elt->content.soa.expire);
+               GET32 (elt->content.soa.minimum);
+               parsed = true;
+               break;
+       case DNS_T_MX:
+               GET16 (elt->content.mx.priority);
+               if (! rdns_parse_labels (resolver, in, &elt->content.mx.name, &p,
+                               rep, remain, true)) {
+                       rdns_info ("invalid labels in MX record");
+                       return -1;
+               }
+               parsed = true;
+               break;
+       case DNS_T_TXT:
+       case DNS_T_SPF:
+               elt->content.txt.data = malloc (datalen + 1);
+               if (elt->content.txt.data == NULL) {
+                       rdns_err ("failed to allocate %d bytes for TXT record", (int)datalen + 1);
+                       return -1;
+               }
+               /* Now we should compose data from parts */
+               copied = 0;
+               parts = 0;
+               while (copied + parts < datalen) {
+                       txtlen = *p;
+                       if (txtlen + copied + parts <= datalen) {
+                               parts ++;
+                               memcpy (elt->content.txt.data + copied, p + 1, txtlen);
+                               copied += txtlen;
+                               p += txtlen + 1;
+                               *remain -= txtlen + 1;
+                       }
+                       else {
+                               break;
+                       }
+               }
+               *(elt->content.txt.data + copied) = '\0';
+               parsed = true;
+               elt->type = RDNS_REQUEST_TXT;
+               break;
+       case DNS_T_SRV:
+               if (p - *pos > (int)(*remain - sizeof (uint16_t) * 3)) {
+                       rdns_info ("stripped dns reply while reading SRV record");
+                       return -1;
+               }
+               GET16 (elt->content.srv.priority);
+               GET16 (elt->content.srv.weight);
+               GET16 (elt->content.srv.port);
+               if (! rdns_parse_labels (resolver, in, &elt->content.srv.target,
+                               &p, rep, remain, true)) {
+                       rdns_info ("invalid labels in SRV record");
+                       return -1;
+               }
+               parsed = true;
+               break;
+       case DNS_T_TLSA:
+               if (p - *pos > (int)(*remain - sizeof (uint8_t) * 3) || datalen <= 3) {
+                       rdns_info ("stripped dns reply while reading TLSA record");
+                       return -1;
+               }
+               GET8 (elt->content.tlsa.usage);
+               GET8 (elt->content.tlsa.selector);
+               GET8 (elt->content.tlsa.match_type);
+               datalen -= 3;
+               elt->content.tlsa.data = malloc (datalen);
+               if (elt->content.tlsa.data == NULL) {
+                       rdns_err ("failed to allocate %d bytes for TLSA record", (int)datalen + 1);
+                       return -1;
+               }
+               elt->content.tlsa.datalen = datalen;
+               memcpy (elt->content.tlsa.data, p, datalen);
+               p += datalen;
+               *remain -= datalen;
+               parsed = true;
+               break;
+       case DNS_T_CNAME:
+               /* Skip cname records */
+               p += datalen;
+               *remain -= datalen;
+               break;
+       default:
+               rdns_debug ("unexpected RR type: %d", type);
+               p += datalen;
+               *remain -= datalen;
+               break;
+       }
+       *pos = p;
+
+       if (parsed) {
+               elt->ttl = ttl;
+               return 1;
+       }
+       return 0;
+}
diff --git a/contrib/librdns/parse.h b/contrib/librdns/parse.h
new file mode 100644 (file)
index 0000000..ed8cd70
--- /dev/null
@@ -0,0 +1,65 @@
+/* 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.
+ */
+#ifndef PARSE_H_
+#define PARSE_H_
+
+#include "dns_private.h"
+
+/**
+ * Compare request and reply checking names
+ * @param req request object
+ * @param in incoming packet
+ * @param len length of the incoming packet
+ * @return new position in the incoming packet or NULL if request is not equal to reply
+ */
+uint8_t * rdns_request_reply_cmp (struct rdns_request *req, uint8_t *in, int len);
+
+/**
+ * Parse labels in the packet
+ * @param in incoming packet
+ * @param target target to write the parsed label (out)
+ * @param pos output position in the packet (it/out)
+ * @param rep dns reply
+ * @param remain remaining bytes (in/out)
+ * @param make_name create name or just skip to the next label
+ * @return true if a label has been successfully parsed
+ */
+bool rdns_parse_labels (struct rdns_resolver *resolver,
+               uint8_t *in, char **target,
+               uint8_t **pos, struct rdns_reply *rep,
+               int *remain, bool make_name);
+
+/**
+ * Parse resource record
+ * @param in incoming packet
+ * @param elt new reply entry
+ * @param pos output position in the packet (it/out)
+ * @param rep dns reply
+ * @param remain remaining bytes (in/out)
+ * @return 1 if rr has been parsed, 0 if rr has been skipped and -1 if there was a parsing error
+ */
+int rdns_parse_rr (struct rdns_resolver *resolver,
+               uint8_t *in, struct rdns_reply_entry *elt, uint8_t **pos,
+               struct rdns_reply *rep, int *remain);
+
+#endif /* PARSE_H_ */
diff --git a/contrib/librdns/punycode.c b/contrib/librdns/punycode.c
new file mode 100644 (file)
index 0000000..6ce3484
--- /dev/null
@@ -0,0 +1,297 @@
+/*
+ * Copyright (c) 2014, Vsevolod Stakhov
+ * Copyright (c) 2004, 2006, 2007, 2008 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. 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.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE 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 INSTITUTE 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.
+ */
+
+#include "dns_private.h"
+
+/* Punycode utility */
+static unsigned int
+digit (unsigned n)
+{
+       static const char ascii[] = "abcdefghijklmnopqrstuvwxyz0123456789";
+       return ascii[n];
+}
+
+static unsigned int
+adapt (unsigned int delta, unsigned int numpoints, int first)
+{
+       unsigned int k;
+
+       if (first) {
+               delta = delta / damp;
+       }
+       else {
+               delta /= 2;
+       }
+       delta += delta / numpoints;
+       k = 0;
+       while (delta > ((base - t_min) * t_max) / 2) {
+               delta /= base - t_min;
+               k += base;
+       }
+       return k + (((base - t_min + 1) * delta) / (delta + skew));
+}
+
+/**
+ * Convert an UCS4 string to a puny-coded DNS label string suitable
+ * when combined with delimiters and other labels for DNS lookup.
+ *
+ * @param in an UCS4 string to convert
+ * @param in_len the length of in.
+ * @param out the resulting puny-coded string. The string is not NULL
+ * terminated.
+ * @param out_len before processing out_len should be the length of
+ * the out variable, after processing it will be the length of the out
+ * string.
+ *
+ * @return returns 0 on success, an wind error code otherwise
+ */
+
+bool
+rdns_punycode_label_toascii (const uint32_t *in, size_t in_len, char *out,
+               size_t *out_len)
+{
+       unsigned int n = initial_n;
+       unsigned int delta = 0;
+       unsigned int bias = initial_bias;
+       unsigned int h = 0;
+       unsigned int b;
+       unsigned int i;
+       unsigned int o = 0;
+       unsigned int m;
+
+       for (i = 0; i < in_len; ++i) {
+               if (in[i] < 0x80) {
+                       ++h;
+                       if (o >= *out_len) {
+                               return false;
+                       }
+                       out[o++] = in[i];
+               }
+       }
+       b = h;
+       if (b > 0) {
+               if (o >= *out_len) {
+                       return false;
+               }
+               out[o++] = 0x2D;
+       }
+       /* is this string punycoded */
+       if (h < in_len) {
+               if (o + 4 >= *out_len) {
+                       return false;
+               }
+               memmove (out + 4, out, o);
+               memcpy (out, "xn--", 4);
+               o += 4;
+       }
+
+       while (h < in_len) {
+               m = (unsigned int) -1;
+               for (i = 0; i < in_len; ++i) {
+
+                       if (in[i] < m && in[i] >= n) {
+                               m = in[i];
+                       }
+               }
+               delta += (m - n) * (h + 1);
+               n = m;
+               for (i = 0; i < in_len; ++i) {
+                       if (in[i] < n) {
+                               ++delta;
+                       }
+                       else if (in[i] == n) {
+                               unsigned int q = delta;
+                               unsigned int k;
+                               for (k = base;; k += base) {
+                                       unsigned int t;
+                                       if (k <= bias) {
+                                               t = t_min;
+                                       }
+                                       else if (k >= bias + t_max) {
+                                               t = t_max;
+                                       }
+                                       else {
+                                               t = k - bias;
+                                       }
+                                       if (q < t) {
+                                               break;
+                                       }
+                                       if (o >= *out_len) {
+                                               return -1;
+                                       }
+                                       out[o++] = digit (t + ((q - t) % (base - t)));
+                                       q = (q - t) / (base - t);
+                               }
+                               if (o >= *out_len) {
+                                       return -1;
+                               }
+                               out[o++] = digit (q);
+                               /* output */
+                               bias = adapt (delta, h + 1, h == b);
+                               delta = 0;
+                               ++h;
+                       }
+               }
+               ++delta;
+               ++n;
+       }
+
+       *out_len = o;
+       return true;
+}
+
+static int
+utf8toutf32 (const unsigned char **pp, uint32_t *out, size_t *remain)
+{
+       const unsigned char *p = *pp;
+       unsigned c = *p;
+       size_t reduce;
+
+       if (c & 0x80) {
+               if ((c & 0xE0) == 0xC0 && *remain >= 2) {
+                       const unsigned c2 = *++p;
+                       reduce = 2;
+                       if ((c2 & 0xC0) == 0x80) {
+                               *out = ((c & 0x1F) << 6) | (c2 & 0x3F);
+                       }
+                       else {
+                               return -1;
+                       }
+               }
+               else if ((c & 0xF0) == 0xE0 && *remain >= 3) {
+                       const unsigned c2 = *++p;
+                       if ((c2 & 0xC0) == 0x80) {
+                               const unsigned c3 = *++p;
+                               reduce = 3;
+                               if ((c3 & 0xC0) == 0x80) {
+                                       *out = ((c & 0x0F) << 12) | ((c2 & 0x3F) << 6)
+                                                       | (c3 & 0x3F);
+                               }
+                               else {
+                                       return -1;
+                               }
+                       }
+                       else {
+                               return -1;
+                       }
+               }
+               else if ((c & 0xF8) == 0xF0 && *remain >= 4) {
+                       const unsigned c2 = *++p;
+                       if ((c2 & 0xC0) == 0x80) {
+                               const unsigned c3 = *++p;
+                               if ((c3 & 0xC0) == 0x80) {
+                                       const unsigned c4 = *++p;
+                                       reduce = 4;
+                                       if ((c4 & 0xC0) == 0x80) {
+                                               *out = ((c & 0x07) << 18) | ((c2 & 0x3F) << 12)
+                                                               | ((c3 & 0x3F) << 6) | (c4 & 0x3F);
+                                       }
+                                       else {
+                                               return -1;
+                                       }
+                               }
+                               else {
+                                       return -1;
+                               }
+                       }
+                       else {
+                               return -1;
+                       }
+               }
+               else {
+                       return -1;
+               }
+       }
+       else {
+               *out = c;
+               reduce = 1;
+       }
+
+       *pp = ++p;
+       *remain -= reduce;
+
+       return 0;
+}
+
+/**
+ * Convert an UTF-8 string to an UCS4 string.
+ *
+ * @param in an UTF-8 string to convert.
+ * @param out the resulting UCS4 string
+ * @param out_len before processing out_len should be the length of
+ * the out variable, after processing it will be the length of the out
+ * string.
+ *
+ * @return returns 0 on success, an -1 otherwise
+ * @ingroup wind
+ */
+
+int
+rdns_utf8_to_ucs4 (const char *in, size_t in_len, uint32_t **out, size_t *out_len)
+{
+       const unsigned char *p;
+       size_t remain = in_len, olen = 0;
+       int ret;
+       uint32_t *res;
+
+       p = (const unsigned char *)in;
+       while (remain > 0) {
+               uint32_t u;
+
+               ret = utf8toutf32 (&p, &u, &remain);
+               if (ret != 0) {
+                       return ret;
+               }
+
+               olen ++;
+       }
+       res = malloc (olen * sizeof (uint32_t));
+       if (res == NULL) {
+               return -1;
+       }
+
+       p = (const unsigned char *)in;
+       remain = in_len;
+       olen = 0;
+       while (remain > 0) {
+               uint32_t u;
+
+               (void)utf8toutf32 (&p, &u, &remain);
+               res[olen++] = u;
+       }
+
+       *out_len = olen;
+       *out = res;
+       return 0;
+}
diff --git a/contrib/librdns/punycode.h b/contrib/librdns/punycode.h
new file mode 100644 (file)
index 0000000..300fb92
--- /dev/null
@@ -0,0 +1,59 @@
+/* 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.
+ */
+#ifndef PUNYCODE_H_
+#define PUNYCODE_H_
+
+#include <stdbool.h>
+#include <stdint.h>
+
+/**
+ * Convert an UCS4 string to a puny-coded DNS label string suitable
+ * when combined with delimiters and other labels for DNS lookup.
+ *
+ * @param in an UCS4 string to convert
+ * @param in_len the length of in.
+ * @param out the resulting puny-coded string. The string is not NULL
+ * terminated.
+ * @param out_len before processing out_len should be the length of
+ * the out variable, after processing it will be the length of the out
+ * string.
+ *
+ * @return returns 0 on success, an wind error code otherwise
+ */
+bool rdns_punycode_label_toascii (const uint32_t *in, size_t in_len, char *out, size_t *out_len);
+/**
+ * Convert an UTF-8 string to an UCS4 string.
+ *
+ * @param in an UTF-8 string to convert.
+ * @param out the resulting UCS4 string
+ * @param out_len before processing out_len should be the length of
+ * the out variable, after processing it will be the length of the out
+ * string.
+ *
+ * @return returns 0 on success, an -1 otherwise
+ * @ingroup wind
+ */
+
+int rdns_utf8_to_ucs4 (const char *in, size_t in_len, uint32_t **out, size_t *out_len);
+
+#endif /* PUNYCODE_H_ */
diff --git a/contrib/librdns/rdns.h b/contrib/librdns/rdns.h
new file mode 100644 (file)
index 0000000..a1cfad4
--- /dev/null
@@ -0,0 +1,372 @@
+/*
+ * Copyright (c) 2013-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.
+ */
+
+#ifndef RDNS_H
+#define RDNS_H
+
+#include <sys/types.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdarg.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#ifdef  __cplusplus
+extern "C" {
+#endif
+
+struct rdns_reply;
+struct rdns_request;
+struct rdns_io_channel;
+
+typedef void (*dns_callback_type) (struct rdns_reply *reply, void *arg);
+
+enum rdns_request_type {
+       RDNS_REQUEST_A = 1,
+       RDNS_REQUEST_NS = 2,
+       RDNS_REQUEST_SOA = 6,
+       RDNS_REQUEST_PTR = 12,
+       RDNS_REQUEST_MX = 15,
+       RDNS_REQUEST_TXT = 16,
+       RDNS_REQUEST_SRV = 33,
+       RDNS_REQUEST_SPF = 99,
+       RDNS_REQUEST_AAAA = 28,
+       RDNS_REQUEST_TLSA = 52,
+       RDNS_REQUEST_ANY = 255
+};
+
+union rdns_reply_element_un {
+       struct {
+               struct in_addr addr;
+       } a;
+       struct {
+               struct in6_addr addr;
+       } aaa;
+       struct {
+               char *name;
+       } ptr;
+       struct {
+               char *name;
+       } ns;
+       struct {
+               char *name;
+               uint16_t priority;
+       } mx;
+       struct {
+               char *data;
+       } txt;
+       struct {
+               uint16_t priority;
+               uint16_t weight;
+               uint16_t port;
+               char *target;
+       } srv;
+       struct {
+               char *mname;
+               char *admin;
+               uint32_t serial;
+               int32_t refresh;
+               int32_t retry;
+               int32_t expire;
+               uint32_t minimum;
+       } soa;
+       struct {
+               uint8_t usage;
+               uint8_t selector;
+               uint8_t match_type;
+               uint16_t datalen;
+               uint8_t *data;
+       } tlsa;
+};
+
+struct rdns_reply_entry {
+       union rdns_reply_element_un content;
+       uint16_t type;
+       int32_t ttl;
+       struct rdns_reply_entry *prev, *next;
+};
+
+
+enum dns_rcode {
+       RDNS_RC_NOERROR = 0,
+       RDNS_RC_FORMERR = 1,
+       RDNS_RC_SERVFAIL        = 2,
+       RDNS_RC_NXDOMAIN        = 3,
+       RDNS_RC_NOTIMP  = 4,
+       RDNS_RC_REFUSED = 5,
+       RDNS_RC_YXDOMAIN        = 6,
+       RDNS_RC_YXRRSET = 7,
+       RDNS_RC_NXRRSET = 8,
+       RDNS_RC_NOTAUTH = 9,
+       RDNS_RC_NOTZONE = 10,
+       RDNS_RC_TIMEOUT = 11,
+       RDNS_RC_NETERR = 12,
+       RDNS_RC_NOREC = 13
+};
+       
+struct rdns_reply {
+       struct rdns_request *request;
+       struct rdns_resolver *resolver;
+       struct rdns_reply_entry *entries;
+       const char *requested_name;
+       enum dns_rcode code;
+};
+
+typedef void (*rdns_periodic_callback)(void *user_data);
+
+struct rdns_async_context {
+       void *data;
+       void* (*add_read)(void *priv_data, int fd, void *user_data);
+       void (*del_read)(void *priv_data, void *ev_data);
+       void* (*add_write)(void *priv_data, int fd, void *user_data);
+       void (*del_write)(void *priv_data, void *ev_data);
+       void* (*add_timer)(void *priv_data, double after, void *user_data);
+       void (*repeat_timer)(void *priv_data, void *ev_data);
+       void (*del_timer)(void *priv_data, void *ev_data);
+       void* (*add_periodic)(void *priv_data, double after,
+               rdns_periodic_callback cb, void *user_data);
+       void (*del_periodic)(void *priv_data, void *ev_data);
+       void (*cleanup)(void *priv_data);
+};
+
+/**
+ * Type of rdns plugin
+ */
+enum rdns_plugin_type {
+       RDNS_PLUGIN_CURVE = 0//!< use the specified plugin instead of send/recv functions
+};
+
+typedef ssize_t (*rdns_network_send_callback) (struct rdns_request *req, void *plugin_data);
+typedef ssize_t (*rdns_network_recv_callback) (struct rdns_io_channel *ioc, void *buf,
+               size_t len, void *plugin_data, struct rdns_request **req_out);
+typedef void (*rdns_network_finish_callback) (struct rdns_request *req, void *plugin_data);
+typedef void (*rdns_plugin_dtor_callback) (struct rdns_resolver *resolver, void *plugin_data);
+
+struct rdns_plugin {
+       enum rdns_plugin_type type;
+       union {
+               struct {
+                       rdns_network_send_callback send_cb;
+                       rdns_network_recv_callback recv_cb;
+                       rdns_network_finish_callback finish_cb;
+               } curve_plugin;
+       } cb;
+       rdns_plugin_dtor_callback dtor;
+       void *data;
+};
+
+/*
+ * RDNS logger types
+ */
+/*
+ * These types are somehow compatible with glib
+ */
+enum rdns_log_level {
+         RDNS_LOG_ERROR = 1 << 3,
+         RDNS_LOG_WARNING = 1 << 4,
+         RDNS_LOG_INFO = 1 << 6,
+         RDNS_LOG_DEBUG = 1 << 7
+};
+typedef void (*rdns_log_function) (
+                                                                       void *log_data, //!< opaque data pointer
+                                                                       enum rdns_log_level level, //!< level of message
+                                                                       const char *function, //!< calling function
+                                                                       const char *format, //!< format
+                                                                       va_list args //!< set of arguments
+                                                                       );
+
+struct rdns_request_name {
+       char *name;
+       enum rdns_request_type type;
+       unsigned int len;
+};
+
+/*
+ * RDNS API
+ */
+
+/**
+ * Create DNS resolver structure
+ */
+struct rdns_resolver *rdns_resolver_new (void);
+
+/**
+ * Bind resolver to specified async context
+ * @param ctx
+ */
+void rdns_resolver_async_bind (struct rdns_resolver *resolver,
+               struct rdns_async_context *ctx);
+
+/**
+ * Add new DNS server definition to the resolver
+ * @param resolver resolver object
+ * @param name name of DNS server (should be ipv4 or ipv6 address)
+ * @param priority priority (can be 0 for fair round-robin)
+ * @param io_cnt a number of sockets that are simultaneously opened to this server
+ * @return true if a server has been added to resolver
+ */
+bool rdns_resolver_add_server (struct rdns_resolver *resolver,
+               const char *name, unsigned int port,
+               int priority, unsigned int io_cnt);
+
+
+/**
+ * Load nameservers definition from resolv.conf file
+ * @param resolver resolver object
+ * @param path path to resolv.conf file (/etc/resolv.conf typically)
+ * @return true if resolv.conf has been parsed
+ */
+bool rdns_resolver_parse_resolv_conf (struct rdns_resolver *resolver,
+               const char *path);
+
+/**
+ * Set an external logger function to log messages from the resolver
+ * @param resolver resolver object
+ * @param logger logger callback
+ * @param log_data opaque data
+ */
+void rdns_resolver_set_logger (struct rdns_resolver *resolver,
+               rdns_log_function logger, void *log_data);
+
+/**
+ * Set log level for an internal logger (stderr one)
+ * @param resolver resolver object
+ * @param level desired log level
+ */
+void rdns_resolver_set_log_level (struct rdns_resolver *resolver,
+               enum rdns_log_level level);
+
+
+/**
+ * Set maximum number of dns requests to be sent to a socket to be refreshed
+ * @param resolver resolver object
+ * @param max_ioc_uses unsigned count of socket usage limit
+ * @param check_time specifies how often to check for sockets and refresh them
+ */
+void rdns_resolver_set_max_io_uses (struct rdns_resolver *resolver,
+               uint64_t max_ioc_uses, double check_time);
+
+/**
+ * Register new plugin for rdns resolver
+ * @param resolver
+ * @param plugin
+ */
+void rdns_resolver_register_plugin (struct rdns_resolver *resolver,
+               struct rdns_plugin *plugin);
+
+/**
+ * Init DNS resolver
+ * @param resolver
+ * @return
+ */
+bool rdns_resolver_init (struct rdns_resolver *resolver);
+
+/**
+ * Decrease refcount for a resolver and free it if refcount is 0
+ * @param resolver
+ */
+void rdns_resolver_release (struct rdns_resolver *resolver);
+
+/**
+ * Make a DNS request
+ * @param resolver resolver object
+ * @param cb callback to call on resolve completing
+ * @param ud user data for callback
+ * @param timeout timeout in seconds
+ * @param repeats how much time to retransmit query
+ * @param queries how much RR queries to send
+ * @param ... -> queries in format: <query_type>[,type_argument[,type_argument...]]
+ * @return opaque request object or NULL
+ */
+struct rdns_request* rdns_make_request_full (
+               struct rdns_resolver *resolver,
+               dns_callback_type cb,
+               void *cbdata,
+               double timeout,
+               unsigned int repeats,
+               unsigned int queries,
+               ...
+               );
+
+/**
+ * Get textual presentation of DNS error code
+ */
+const char *rdns_strerror (enum dns_rcode rcode);
+
+/**
+ * Get textual presentation of DNS request type
+ */
+const char *rdns_strtype (enum rdns_request_type type);
+
+/**
+ * Increase refcount for a request
+ * @param req
+ * @return
+ */
+struct rdns_request* rdns_request_retain (struct rdns_request *req);
+
+/**
+ * Decrease refcount for a request and free it if refcount is 0
+ * @param req
+ */
+void rdns_request_release (struct rdns_request *req);
+
+/**
+ * Check whether a request contains `type` request
+ * @param req request object
+ * @param type check for a specified type
+ * @return true if `type` has been requested
+ */
+bool rdns_request_has_type (struct rdns_request *req, enum rdns_request_type type);
+
+/**
+ * Return requested name for a request
+ * @param req request object
+ * @return requested name as it was passed to `rdns_make_request`
+ */
+const struct rdns_request_name* rdns_request_get_name (struct rdns_request *req,
+               unsigned int *count);
+
+/**
+ * Return PTR string for a request (ipv4 or ipv6) addresses
+ * @param str string representation of IP address
+ * @return name to resolve or NULL if `str` is not an IP address; caller must free result when it is unused
+ */
+char * rdns_generate_ptr_from_str (const char *str);
+
+/*
+ * Private functions used by async libraries as callbacks
+ */
+
+void rdns_process_read (int fd, void *arg);
+void rdns_process_timer (void *arg);
+void rdns_process_retransmit (int fd, void *arg);
+
+#ifdef  __cplusplus
+}
+#endif
+
+#endif
diff --git a/contrib/librdns/rdns_curve.h b/contrib/librdns/rdns_curve.h
new file mode 100644 (file)
index 0000000..365e91b
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * 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.
+ */
+#ifndef RDNS_CURVE_H_
+#define RDNS_CURVE_H_
+
+#ifdef  __cplusplus
+extern "C" {
+#endif
+
+struct rdns_curve_ctx;
+
+/**
+ * Create new dnscurve ctx
+ * @return
+ */
+struct rdns_curve_ctx* rdns_curve_ctx_new (double rekey_interval);
+
+/**
+ * Add key for server `name`
+ * @param ctx curve context
+ * @param name name of server (ip address)
+ * @param pubkey pubkey bytes (must be `RDSN_CURVE_PUBKEY_LEN`)
+ */
+void rdns_curve_ctx_add_key (struct rdns_curve_ctx *ctx,
+               const char *name, const unsigned char *pubkey);
+
+/**
+ * Destroy curve context
+ * @param ctx
+ */
+void rdns_curve_ctx_destroy (struct rdns_curve_ctx *ctx);
+
+
+/**
+ * Register DNSCurve plugin (libsodium should be enabled for this)
+ * @param resolver
+ * @param ctx
+ */
+void rdns_curve_register_plugin (struct rdns_resolver *resolver,
+               struct rdns_curve_ctx *ctx);
+
+/**
+ * Create DNSCurve key from the base16 encoded string
+ * @param hex input hex (must be NULL terminated)
+ * @return a key or NULL (not NULL terminated)
+ */
+unsigned char * rdns_curve_key_from_hex (const char *hex);
+
+#ifdef  __cplusplus
+}
+#endif
+
+#endif /* RDNS_CURVE_H_ */
diff --git a/contrib/librdns/rdns_ev.h b/contrib/librdns/rdns_ev.h
new file mode 100644 (file)
index 0000000..1b3554b
--- /dev/null
@@ -0,0 +1,232 @@
+/*
+ * 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.
+ */
+#ifndef RDNS_EV_H_
+#define RDNS_EV_H_
+
+#include <ev.h>
+#include <stdlib.h>
+#include <string.h>
+#include "rdns.h"
+
+#ifdef  __cplusplus
+extern "C" {
+#endif
+
+static void* rdns_libev_add_read (void *priv_data, int fd, void *user_data);
+static void rdns_libev_del_read(void *priv_data, void *ev_data);
+static void* rdns_libev_add_write (void *priv_data, int fd, void *user_data);
+static void rdns_libev_del_write (void *priv_data, void *ev_data);
+static void* rdns_libev_add_timer (void *priv_data, double after, void *user_data);
+static void* rdns_libev_add_periodic (void *priv_data, double after,
+               rdns_periodic_callback cb, void *user_data);
+static void rdns_libev_del_periodic (void *priv_data, void *ev_data);
+static void rdns_libev_repeat_timer (void *priv_data, void *ev_data);
+static void rdns_libev_del_timer (void *priv_data, void *ev_data);
+
+struct rdns_ev_periodic_cbdata {
+       ev_timer *ev;
+       rdns_periodic_callback cb;
+       void *cbdata;
+};
+
+static void
+rdns_bind_libev (struct rdns_resolver *resolver, struct ev_loop *loop)
+{
+       static struct rdns_async_context ev_ctx = {
+               .data = NULL,
+               .add_read = rdns_libev_add_read,
+               .del_read = rdns_libev_del_read,
+               .add_write = rdns_libev_add_write,
+               .del_write = rdns_libev_del_write,
+               .add_timer = rdns_libev_add_timer,
+               .repeat_timer = rdns_libev_repeat_timer,
+               .del_timer = rdns_libev_del_timer,
+               .add_periodic = rdns_libev_add_periodic,
+               .del_periodic = rdns_libev_del_periodic,
+               .cleanup = NULL
+       }, *nctx;
+       void *ptr;
+
+       /* XXX: never got freed */
+       ptr = malloc (sizeof (struct rdns_async_context));
+       if (ptr != NULL) {
+               nctx = (struct rdns_async_context *)ptr;
+               memcpy (ptr, (void *)&ev_ctx, sizeof (struct rdns_async_context));
+               nctx->data = (void*)loop;
+               rdns_resolver_async_bind (resolver, nctx);
+       }
+}
+
+static void
+rdns_libev_read_event (struct ev_loop *loop, ev_io *ev, int revents)
+{
+       rdns_process_read (ev->fd, ev->data);
+}
+
+static void
+rdns_libev_write_event (struct ev_loop *loop, ev_io *ev, int revents)
+{
+       rdns_process_retransmit (ev->fd, ev->data);
+}
+
+static void
+rdns_libev_timer_event (struct ev_loop *loop, ev_timer *ev, int revents)
+{
+       rdns_process_timer (ev->data);
+}
+
+static void
+rdns_libev_periodic_event (struct ev_loop *loop, ev_timer *ev, int revents)
+{
+       struct rdns_ev_periodic_cbdata *cbdata = (struct rdns_ev_periodic_cbdata *)
+                       ev->data;
+       cbdata->cb (cbdata->cbdata);
+}
+
+static void*
+rdns_libev_add_read (void *priv_data, int fd, void *user_data)
+{
+       ev_io *ev;
+       void *ptr;
+
+       ptr = malloc (sizeof (ev_io));
+       if (ptr != NULL) {
+               ev = (ev_io *)ptr;
+               ev_io_init (ev, rdns_libev_read_event, fd, EV_READ);
+               ev->data = user_data;
+               ev_io_start ((struct ev_loop *)priv_data, ev);
+       }
+       return ptr;
+}
+
+static void
+rdns_libev_del_read (void *priv_data, void *ev_data)
+{
+       ev_io *ev = (ev_io*)ev_data;
+       if (ev != NULL) {
+               ev_io_stop ((struct ev_loop *)priv_data, ev);
+               free ((void *)ev);
+       }
+}
+static void*
+rdns_libev_add_write (void *priv_data, int fd, void *user_data)
+{
+       ev_io *ev;
+
+       ev = (ev_io *)malloc (sizeof (ev_io));
+       if (ev != NULL) {
+               ev_io_init (ev, rdns_libev_write_event, fd, EV_WRITE);
+               ev->data = user_data;
+               ev_io_start ((struct ev_loop *)priv_data, ev);
+       }
+       return (void *)ev;
+}
+
+static void
+rdns_libev_del_write (void *priv_data, void *ev_data)
+{
+       ev_io *ev = (ev_io *)ev_data;
+       if (ev != NULL) {
+               ev_io_stop ((struct ev_loop *)priv_data, ev);
+               free ((void *)ev);
+       }
+}
+
+static void*
+rdns_libev_add_timer (void *priv_data, double after, void *user_data)
+{
+       ev_timer *ev;
+       ev = (ev_timer *)malloc (sizeof (ev_timer));
+       if (ev != NULL) {
+               ev_timer_init (ev, rdns_libev_timer_event, after, after);
+               ev->data = user_data;
+               ev_timer_start ((struct ev_loop *)priv_data, ev);
+       }
+       return (void *)ev;
+}
+
+static void*
+rdns_libev_add_periodic (void *priv_data, double after,
+               rdns_periodic_callback cb, void *user_data)
+{
+       ev_timer *ev;
+       struct rdns_ev_periodic_cbdata *cbdata = NULL;
+
+       ev = (ev_timer *)malloc (sizeof (ev_timer));
+       if (ev != NULL) {
+               cbdata = (struct rdns_ev_periodic_cbdata *)
+                               malloc (sizeof (struct rdns_ev_periodic_cbdata));
+               if (cbdata != NULL) {
+                       cbdata->cb = cb;
+                       cbdata->cbdata = user_data;
+                       cbdata->ev = ev;
+                       ev_timer_init (ev, rdns_libev_periodic_event, after, after);
+                       ev->data = cbdata;
+                       ev_timer_start ((struct ev_loop *)priv_data, ev);
+               }
+               else {
+                       free ((void *)ev);
+                       return NULL;
+               }
+       }
+       return (void *)cbdata;
+}
+
+static void
+rdns_libev_del_periodic (void *priv_data, void *ev_data)
+{
+       struct rdns_ev_periodic_cbdata *cbdata = (struct rdns_ev_periodic_cbdata *)
+                       ev_data;
+       if (cbdata != NULL) {
+               ev_timer_stop ((struct ev_loop *)priv_data, (ev_timer *)cbdata->ev);
+               free ((void *)cbdata->ev);
+               free ((void *)cbdata);
+       }
+}
+
+static void
+rdns_libev_repeat_timer (void *priv_data, void *ev_data)
+{
+       ev_timer *ev = (ev_timer *)ev_data;
+       if (ev != NULL) {
+               ev_timer_again ((struct ev_loop *)priv_data, ev);
+       }
+}
+
+static void
+rdns_libev_del_timer (void *priv_data, void *ev_data)
+{
+       ev_timer *ev = (ev_timer *)ev_data;
+       if (ev != NULL) {
+               ev_timer_stop ((struct ev_loop *)priv_data, ev);
+               free ((void *)ev);
+       }
+}
+
+#ifdef  __cplusplus
+}
+#endif
+
+#endif /* RDNS_EV_H_ */
diff --git a/contrib/librdns/rdns_event.h b/contrib/librdns/rdns_event.h
new file mode 100644 (file)
index 0000000..b3fc64a
--- /dev/null
@@ -0,0 +1,231 @@
+/*
+ * 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.
+ */
+#ifndef RDNS_EVENT_H_
+#define RDNS_EVENT_H_
+
+#include <event.h>
+#include <stdlib.h>
+#include <string.h>
+#include "rdns.h"
+
+#ifdef  __cplusplus
+extern "C" {
+#endif
+
+static void* rdns_libevent_add_read (void *priv_data, int fd, void *user_data);
+static void rdns_libevent_del_read(void *priv_data, void *ev_data);
+static void* rdns_libevent_add_write (void *priv_data, int fd, void *user_data);
+static void rdns_libevent_del_write (void *priv_data, void *ev_data);
+static void* rdns_libevent_add_timer (void *priv_data, double after, void *user_data);
+static void* rdns_libevent_add_periodic (void *priv_data, double after,
+               rdns_periodic_callback cb, void *user_data);
+static void rdns_libevent_del_periodic (void *priv_data, void *ev_data);
+static void rdns_libevent_repeat_timer (void *priv_data, void *ev_data);
+static void rdns_libevent_del_timer (void *priv_data, void *ev_data);
+
+struct rdns_event_periodic_cbdata {
+       struct event *ev;
+       rdns_periodic_callback cb;
+       void *cbdata;
+};
+
+static void
+rdns_bind_libevent (struct rdns_resolver *resolver, struct event_base *ev_base)
+{
+       struct rdns_async_context ev_ctx = {
+               .add_read = rdns_libevent_add_read,
+               .del_read = rdns_libevent_del_read,
+               .add_write = rdns_libevent_add_write,
+               .del_write = rdns_libevent_del_write,
+               .add_timer = rdns_libevent_add_timer,
+               .add_periodic = rdns_libevent_add_periodic,
+               .del_periodic = rdns_libevent_del_periodic,
+               .repeat_timer = rdns_libevent_repeat_timer,
+               .del_timer = rdns_libevent_del_timer,
+               .cleanup = NULL
+       }, *nctx;
+
+       /* XXX: never got freed */
+       nctx = malloc (sizeof (struct rdns_async_context));
+       if (nctx != NULL) {
+               memcpy (nctx, &ev_ctx, sizeof (struct rdns_async_context));
+               nctx->data = ev_base;
+       }
+       rdns_resolver_async_bind (resolver, nctx);
+}
+
+static void
+rdns_libevent_read_event (int fd, short what, void *ud)
+{
+       rdns_process_read (fd, ud);
+}
+
+static void
+rdns_libevent_write_event (int fd, short what, void *ud)
+{
+       rdns_process_retransmit (fd, ud);
+}
+
+static void
+rdns_libevent_timer_event (int fd, short what, void *ud)
+{
+       rdns_process_timer (ud);
+}
+
+static void
+rdns_libevent_periodic_event (int fd, short what, void *ud)
+{
+       struct rdns_event_periodic_cbdata *cbdata = ud;
+       cbdata->cb (cbdata->cbdata);
+}
+
+static void*
+rdns_libevent_add_read (void *priv_data, int fd, void *user_data)
+{
+       struct event *ev;
+       ev = malloc (sizeof (struct event));
+       if (ev != NULL) {
+               event_set (ev, fd, EV_READ | EV_PERSIST, rdns_libevent_read_event, user_data);
+               event_base_set (priv_data, ev);
+               event_add (ev, NULL);
+       }
+       return ev;
+}
+
+static void
+rdns_libevent_del_read(void *priv_data, void *ev_data)
+{
+       struct event *ev = ev_data;
+       if (ev != NULL) {
+               event_del (ev);
+               free (ev);
+       }
+}
+static void*
+rdns_libevent_add_write (void *priv_data, int fd, void *user_data)
+{
+       struct event *ev;
+       ev = malloc (sizeof (struct event));
+       if (ev != NULL) {
+               event_set (ev, fd, EV_WRITE | EV_PERSIST,
+                               rdns_libevent_write_event, user_data);
+               event_base_set (priv_data, ev);
+               event_add (ev, NULL);
+       }
+       return ev;
+}
+
+static void
+rdns_libevent_del_write (void *priv_data, void *ev_data)
+{
+       struct event *ev = ev_data;
+       if (ev != NULL) {
+               event_del (ev);
+               free (ev);
+       }
+}
+
+#define rdns_event_double_to_tv(dbl, tv) do {                                                                                  \
+    (tv)->tv_sec = (int)(dbl);                                                                                                 \
+    (tv)->tv_usec = ((dbl) - (int)(dbl))*1000*1000;                                            \
+} while(0)
+
+static void*
+rdns_libevent_add_timer (void *priv_data, double after, void *user_data)
+{
+       struct event *ev;
+       struct timeval tv;
+       ev = malloc (sizeof (struct event));
+       if (ev != NULL) {
+               rdns_event_double_to_tv (after, &tv);
+               event_set (ev, -1, EV_TIMEOUT|EV_PERSIST, rdns_libevent_timer_event, user_data);
+               event_base_set (priv_data, ev);
+               event_add (ev, &tv);
+       }
+       return ev;
+}
+
+static void*
+rdns_libevent_add_periodic (void *priv_data, double after,
+               rdns_periodic_callback cb, void *user_data)
+{
+       struct event *ev;
+       struct timeval tv;
+       struct rdns_event_periodic_cbdata *cbdata = NULL;
+
+       ev = malloc (sizeof (struct event));
+       if (ev != NULL) {
+               cbdata = malloc (sizeof (struct rdns_event_periodic_cbdata));
+               if (cbdata != NULL) {
+                       rdns_event_double_to_tv (after, &tv);
+                       cbdata->cb = cb;
+                       cbdata->cbdata = user_data;
+                       cbdata->ev = ev;
+                       event_set (ev, -1, EV_TIMEOUT|EV_PERSIST, rdns_libevent_periodic_event, cbdata);
+                       event_base_set (priv_data, ev);
+                       event_add (ev, &tv);
+               }
+               else {
+                       free (ev);
+                       return NULL;
+               }
+       }
+       return cbdata;
+}
+
+static void
+rdns_libevent_del_periodic (void *priv_data, void *ev_data)
+{
+       struct rdns_event_periodic_cbdata *cbdata = ev_data;
+       if (cbdata != NULL) {
+               event_del (cbdata->ev);
+               free (cbdata->ev);
+               free (cbdata);
+       }
+}
+
+static void
+rdns_libevent_repeat_timer (void *priv_data, void *ev_data)
+{
+       /* XXX: libevent hides timeval, so timeouts are persistent here */
+}
+
+#undef rdns_event_double_to_tv
+
+static void
+rdns_libevent_del_timer (void *priv_data, void *ev_data)
+{
+       struct event *ev = ev_data;
+       if (ev != NULL) {
+               event_del (ev);
+               free (ev);
+       }
+}
+
+#ifdef  __cplusplus
+}
+#endif
+
+#endif /* RDNS_EV_H_ */
diff --git a/contrib/librdns/ref.h b/contrib/librdns/ref.h
new file mode 100644 (file)
index 0000000..a8016b1
--- /dev/null
@@ -0,0 +1,71 @@
+/* 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.
+ */
+#ifndef REF_H_
+#define REF_H_
+
+/**
+ * @file ref.h
+ * A set of macros to handle refcounts
+ */
+
+typedef void (*ref_dtor_cb_t)(void *data);
+
+typedef struct ref_entry_s {
+       unsigned int refcount;
+       ref_dtor_cb_t dtor;
+} ref_entry_t;
+
+#define REF_INIT(obj, dtor_cb) do {                                                            \
+       (obj)->ref.refcount = 0;                                                                                \
+       (obj)->ref.dtor = (ref_dtor_cb_t)(dtor_cb);                                             \
+} while (0)
+
+#define REF_INIT_RETAIN(obj, dtor_cb) do {                                                     \
+       (obj)->ref.refcount = 1;                                                                                \
+       (obj)->ref.dtor = (ref_dtor_cb_t)(dtor_cb);                                             \
+} while (0)
+
+#ifdef HAVE_ATOMIC_BUILTINS
+#define REF_RETAIN(obj) do {                                                                           \
+    __sync_add_and_fetch (&(obj)->ref.refcount, 1);                                    \
+} while (0)
+
+#define REF_RELEASE(obj) do {                                                                          \
+       unsigned int rc = __sync_sub_and_fetch (&(obj)->ref.refcount, 1); \
+       if (rc == 0 && (obj)->ref.dtor) {                                                               \
+               (obj)->ref.dtor (obj);                                                                          \
+       }                                                                                                                               \
+} while (0)
+#else
+#define REF_RETAIN(obj) do {                                                                           \
+       (obj)->ref.refcount ++;                                                                                 \
+} while (0)
+
+#define REF_RELEASE(obj) do {                                                                          \
+       if (--(obj)->ref.refcount == 0 && (obj)->ref.dtor) {                    \
+               (obj)->ref.dtor (obj);                                                                          \
+       }                                                                                                                               \
+} while (0)
+#endif
+
+#endif /* REF_H_ */
diff --git a/contrib/librdns/resolver.c b/contrib/librdns/resolver.c
new file mode 100644 (file)
index 0000000..9741f7b
--- /dev/null
@@ -0,0 +1,782 @@
+/*
+ * 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 <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <stdarg.h>
+
+#include "rdns.h"
+#include "dns_private.h"
+#include "ottery.h"
+#include "util.h"
+#include "packet.h"
+#include "parse.h"
+#include "logger.h"
+#include "compression.h"
+
+static int
+rdns_send_request (struct rdns_request *req, int fd, bool new_req)
+{
+       int r;
+       struct rdns_server *serv = req->io->srv;
+       struct rdns_resolver *resolver = req->resolver;
+       struct rdns_request *tmp;
+       struct dns_header *header;
+       const int max_id_cycles = 32;
+
+       /* Find ID collision */
+       if (new_req) {
+               r = 0;
+               HASH_FIND_INT (req->io->requests, &req->id, tmp);
+               while (tmp != NULL) {
+                       /* Check for unique id */
+                       header = (struct dns_header *)req->packet;
+                       header->qid = rdns_permutor_generate_id ();
+                       req->id = header->qid;
+                       if (++r > max_id_cycles) {
+                               return -1;
+                       }
+                       HASH_FIND_INT (req->io->requests, &req->id, tmp);
+               }
+       }
+
+       if (resolver->curve_plugin == NULL) {
+               r = send (fd, req->packet, req->pos, 0);
+       }
+       else {
+               r = resolver->curve_plugin->cb.curve_plugin.send_cb (req,
+                               resolver->curve_plugin->data);
+       }
+       if (r == -1) {
+               if (errno == EAGAIN || errno == EINTR) {
+                       if (new_req) {
+                               /* Write when socket is ready */
+                               HASH_ADD_INT (req->io->requests, id, req);
+                               req->async_event = resolver->async->add_write (resolver->async->data,
+                                       fd, req);
+                       }
+                       /*
+                        * If request is already processed then the calling function
+                        * should take care about events processing
+                        */
+                       return 0;
+               } 
+               else {
+                       rdns_debug ("send failed: %s for server %s", strerror (errno), serv->name);
+                       return -1;
+               }
+       }
+       
+       if (new_req) {
+               /* Add request to hash table */
+               HASH_ADD_INT (req->io->requests, id, req);
+               /* Fill timeout */
+               req->async_event = resolver->async->add_timer (resolver->async->data,
+                               req->timeout, req);
+               req->state = RDNS_REQUEST_SENT;
+       }
+
+       return 1;
+}
+
+
+static struct rdns_reply *
+rdns_make_reply (struct rdns_request *req, enum dns_rcode rcode)
+{
+       struct rdns_reply *rep;
+
+       rep = malloc (sizeof (struct rdns_reply));
+       if (rep != NULL) {
+               rep->request = req;
+               rep->resolver = req->resolver;
+               rep->entries = NULL;
+               rep->code = rcode;
+               req->reply = rep;
+       }
+
+       return rep;
+}
+
+static struct rdns_request *
+rdns_find_dns_request (uint8_t *in, struct rdns_io_channel *ioc)
+{
+       struct dns_header *header = (struct dns_header *)in;
+       struct rdns_request *req;
+       int id;
+       struct rdns_resolver *resolver = ioc->resolver;
+       
+       id = header->qid;
+       HASH_FIND_INT (ioc->requests, &id, req);
+       if (req == NULL) {
+               /* No such requests found */
+               rdns_debug ("DNS request with id %d has not been found for IO channel", (int)id);
+       }
+
+       return req;
+}
+
+static bool
+rdns_parse_reply (uint8_t *in, int r, struct rdns_request *req,
+               struct rdns_reply **_rep)
+{
+       struct dns_header *header = (struct dns_header *)in;
+       struct rdns_reply *rep;
+       struct rdns_reply_entry *elt;
+       uint8_t *pos, *npos;
+       struct rdns_resolver *resolver = req->resolver;
+       uint16_t qdcount;
+       int type;
+       bool found = false;
+
+       int i, t;
+
+       /* First check header fields */
+       if (header->qr == 0) {
+               rdns_info ("got request while waiting for reply");
+               return false;
+       }
+
+       qdcount = ntohs (header->qdcount);
+
+       if (qdcount != req->qcount) {
+               rdns_info ("request has %d queries, reply has %d queries", (int)req->qcount, (int)header->qdcount);
+               return false;
+       }
+
+       /* 
+        * Now we have request and query data is now at the end of header, so compare
+        * request QR section and reply QR section
+        */
+       req->pos = sizeof (struct dns_header);
+       pos = in + sizeof (struct dns_header);
+       t = r - sizeof (struct dns_header);
+       for (i = 0; i < (int)qdcount; i ++) {
+               if ((npos = rdns_request_reply_cmp (req, pos,t)) == NULL) {
+                       rdns_info ("DNS request with id %d is for different query, ignoring", (int)req->id);
+                       return false;
+               }
+               t -= npos - pos;
+               pos = npos;
+       }
+       /*
+        * Now pos is in answer section, so we should extract data and form reply
+        */
+       rep = rdns_make_reply (req, header->rcode);
+
+       if (rep == NULL) {
+               rdns_warn ("Cannot allocate memory for reply");
+               return false;
+       }
+
+       type = req->requested_names[0].type;
+
+       if (rep->code == RDNS_RC_NOERROR) {
+               r -= pos - in;
+               /* Extract RR records */
+               for (i = 0; i < ntohs (header->ancount); i ++) {
+                       elt = malloc (sizeof (struct rdns_reply_entry));
+                       t = rdns_parse_rr (resolver, in, elt, &pos, rep, &r);
+                       if (t == -1) {
+                               free (elt);
+                               rdns_debug ("incomplete reply");
+                               break;
+                       }
+                       else if (t == 1) {
+                               DL_APPEND (rep->entries, elt);
+                               if (elt->type == type) {
+                                       found = true;
+                               }
+                       }
+                       else {
+                               rdns_debug ("no matching reply for %s",
+                                               req->requested_names[0].name);
+                               free (elt);
+                       }
+               }
+       }
+       
+       if (!found && type != RDNS_REQUEST_ANY) {
+               /* We have not found the requested RR type */
+               rep->code = RDNS_RC_NOREC;
+       }
+
+       *_rep = rep;
+       return true;
+}
+
+static void
+rdns_request_unschedule (struct rdns_request *req)
+{
+       req->async->del_timer (req->async->data,
+                       req->async_event);
+       /* Remove from id hashes */
+       HASH_DEL (req->io->requests, req);
+}
+
+void
+rdns_process_read (int fd, void *arg)
+{
+       struct rdns_io_channel *ioc = arg;
+       struct rdns_resolver *resolver;
+       struct rdns_request *req = NULL;
+       ssize_t r;
+       struct rdns_reply *rep;
+       uint8_t in[UDP_PACKET_SIZE];
+
+       resolver = ioc->resolver;
+       
+       /* First read packet from socket */
+       if (resolver->curve_plugin == NULL) {
+               r = read (fd, in, sizeof (in));
+               if (r > (int)(sizeof (struct dns_header) + sizeof (struct dns_query))) {
+                       req = rdns_find_dns_request (in, ioc);
+               }
+       }
+       else {
+               r = resolver->curve_plugin->cb.curve_plugin.recv_cb (ioc, in,
+                               sizeof (in), resolver->curve_plugin->data, &req);
+               if (req == NULL &&
+                               r > (int)(sizeof (struct dns_header) + sizeof (struct dns_query))) {
+                       req = rdns_find_dns_request (in, ioc);
+               }
+       }
+
+       if (req != NULL) {
+               if (rdns_parse_reply (in, r, req, &rep)) {
+                       UPSTREAM_OK (req->io->srv);
+                       req->state = RDNS_REQUEST_REPLIED;
+                       rdns_request_unschedule (req);
+                       req->func (rep, req->arg);
+                       REF_RELEASE (req);
+               }
+       }
+       else {
+               /* Still want to increase uses */
+               ioc->uses ++;
+       }
+}
+
+void
+rdns_process_timer (void *arg)
+{
+       struct rdns_request *req = (struct rdns_request *)arg;
+       struct rdns_reply *rep;
+       int r;
+       bool renew = false;
+       struct rdns_resolver *resolver;
+       struct rdns_server *serv = NULL;
+
+       req->retransmits --;
+       resolver = req->resolver;
+
+       if (req->retransmits == 0) {
+               UPSTREAM_FAIL (req->io->srv, time (NULL));
+               rep = rdns_make_reply (req, RDNS_RC_TIMEOUT);
+               req->state = RDNS_REQUEST_REPLIED;
+               rdns_request_unschedule (req);
+               req->func (rep, req->arg);
+               REF_RELEASE (req);
+
+               return;
+       }
+
+       if (!req->io->active) {
+               /* Do not reschedule IO requests on inactive sockets */
+               rdns_debug ("reschedule request with id: %d", (int)req->id);
+               rdns_request_unschedule (req);
+               REF_RELEASE (req->io);
+
+               UPSTREAM_SELECT_ROUND_ROBIN (resolver->servers, serv);
+
+               if (serv == NULL) {
+                       rdns_warn ("cannot find suitable server for request");
+                       rep = rdns_make_reply (req, RDNS_RC_SERVFAIL);
+                       req->state = RDNS_REQUEST_REPLIED;
+                       req->func (rep, req->arg);
+                       REF_RELEASE (req);
+               }
+
+               /* Select random IO channel */
+               req->io = serv->io_channels[ottery_rand_uint32 () % serv->io_cnt];
+               req->io->uses ++;
+               REF_RETAIN (req->io);
+               renew = true;
+       }
+
+       r = rdns_send_request (req, req->io->sock, renew);
+       if (r == 0) {
+               /* Retransmit one more time */
+               req->async->del_timer (req->async->data,
+                                       req->async_event);
+               req->async_event = req->async->add_write (req->async->data,
+                               req->io->sock, req);
+               req->state = RDNS_REQUEST_REGISTERED;
+       }
+       else if (r == -1) {
+               UPSTREAM_FAIL (req->io->srv, time (NULL));
+               rep = rdns_make_reply (req, RDNS_RC_NETERR);
+               req->state = RDNS_REQUEST_REPLIED;
+               rdns_request_unschedule (req);
+               req->func (rep, req->arg);
+               REF_RELEASE (req);
+       }
+       else {
+               req->async->repeat_timer (req->async->data, req->async_event);
+       }
+}
+
+static void
+rdns_process_periodic (void *arg)
+{
+       struct rdns_resolver *resolver = (struct rdns_resolver*)arg;
+
+       UPSTREAM_RESCAN (resolver->servers, time (NULL));
+}
+
+static void
+rdns_process_ioc_refresh (void *arg)
+{
+       struct rdns_resolver *resolver = (struct rdns_resolver*)arg;
+       struct rdns_server *serv;
+       struct rdns_io_channel *ioc, *nioc;
+       unsigned int i;
+
+       if (resolver->max_ioc_uses > 0) {
+               UPSTREAM_FOREACH (resolver->servers, serv) {
+                       for (i = 0; i < serv->io_cnt; i ++) {
+                               ioc = serv->io_channels[i];
+                               if (ioc->uses > resolver->max_ioc_uses) {
+                                       /* Schedule IOC removing */
+                                       nioc = calloc (1, sizeof (struct rdns_io_channel));
+                                       if (nioc == NULL) {
+                                               rdns_err ("calloc fails to allocate rdns_io_channel");
+                                               continue;
+                                       }
+                                       nioc->sock = rdns_make_client_socket (serv->name, serv->port,
+                                                       SOCK_DGRAM);
+                                       if (nioc->sock == -1) {
+                                               rdns_err ("cannot open socket to %s: %s", serv->name,
+                                                               strerror (errno));
+                                               free (nioc);
+                                               continue;
+                                       }
+                                       nioc->srv = serv;
+                                       nioc->active = true;
+                                       nioc->resolver = resolver;
+                                       nioc->async_io = resolver->async->add_read (resolver->async->data,
+                                                       nioc->sock, nioc);
+                                       REF_INIT_RETAIN (nioc, rdns_ioc_free);
+                                       serv->io_channels[i] = nioc;
+                                       rdns_debug ("scheduled io channel for server %s to be refreshed after "
+                                                       "%lu usages", serv->name, (unsigned long)ioc->uses);
+                                       ioc->active = false;
+                                       REF_RELEASE (ioc);
+                               }
+                       }
+               }
+       }
+}
+
+void
+rdns_process_retransmit (int fd, void *arg)
+{
+       struct rdns_request *req = (struct rdns_request *)arg;
+       struct rdns_resolver *resolver;
+       struct rdns_reply *rep;
+       int r;
+
+       resolver = req->resolver;
+
+       resolver->async->del_write (resolver->async->data,
+                       req->async_event);
+
+       r = rdns_send_request (req, fd, false);
+
+       if (r == 0) {
+               /* Retransmit one more time */
+               req->async_event = req->async->add_write (req->async->data,
+                                               fd, req);
+               req->state = RDNS_REQUEST_REGISTERED;
+       }
+       else if (r == -1) {
+               UPSTREAM_FAIL (req->io->srv, time (NULL));
+               rep = rdns_make_reply (req, RDNS_RC_NETERR);
+               req->state = RDNS_REQUEST_REPLIED;
+               req->func (rep, req->arg);
+               REF_RELEASE (req);
+       }
+       else {
+               req->async_event = req->async->add_timer (req->async->data,
+                       req->timeout, req);
+               req->state = RDNS_REQUEST_SENT;
+       }
+}
+
+struct rdns_request*
+rdns_make_request_full (
+               struct rdns_resolver *resolver,
+               dns_callback_type cb,
+               void *cbdata,
+               double timeout,
+               unsigned int repeats,
+               unsigned int queries,
+               ...
+               )
+{
+       va_list args;
+       struct rdns_request *req;
+       struct rdns_server *serv;
+       int r, type;
+       unsigned int i, tlen = 0, clen = 0, cur;
+       size_t olen;
+       const char *cur_name, *last_name = NULL;
+       struct rdns_compression_entry *comp = NULL;
+
+       if (!resolver->initialized) {
+               return NULL;
+       }
+
+       req = malloc (sizeof (struct rdns_request));
+       if (req == NULL) {
+               return NULL;
+       }
+
+       req->resolver = resolver;
+       req->func = cb;
+       req->arg = cbdata;
+       req->reply = NULL;
+       req->qcount = queries;
+       req->io = NULL;
+       req->state = RDNS_REQUEST_NEW;
+       req->packet = NULL;
+       req->requested_names = calloc (queries, sizeof (struct rdns_request_name));
+       if (req->requested_names == NULL) {
+               free (req);
+               return NULL;
+       }
+
+       req->type = 0;
+#ifdef TWEETNACL
+       req->curve_plugin_data = NULL;
+#endif
+       REF_INIT_RETAIN (req, rdns_request_free);
+       
+       /* Calculate packet's total length based on records count */
+       va_start (args, queries);
+       for (i = 0; i < queries * 2; i += 2) {
+               cur = i / 2;
+               cur_name = va_arg (args, const char *);
+               if (cur_name != NULL) {
+                       last_name = cur_name;
+                       clen = strlen (cur_name);
+                       if (clen == 0) {
+                               rdns_info ("got empty name to resolve");
+                               rdns_request_free (req);
+                               return NULL;
+                       }
+                       tlen += clen;
+               }
+               else if (last_name == NULL) {
+                       rdns_info ("got NULL as the first name to resolve");
+                       rdns_request_free (req);
+                       return NULL;
+               }
+
+               if (!rdns_format_dns_name (resolver, last_name, clen,
+                               &req->requested_names[cur].name, &olen)) {
+                       rdns_request_free (req);
+                       return NULL;
+               }
+
+               type = va_arg (args, int);
+               req->requested_names[cur].type = type;
+               req->requested_names[cur].len = olen;
+       }
+       va_end (args);
+
+       rdns_allocate_packet (req, tlen);
+       rdns_make_dns_header (req, queries);
+
+       for (i = 0; i < queries; i ++) {
+               cur_name = req->requested_names[i].name;
+               clen = req->requested_names[i].len;
+               type = req->requested_names[i].type;
+               if (queries > 1) {
+                       if (!rdns_add_rr (req, cur_name, clen, type, &comp)) {
+                               REF_RELEASE (req);
+                               rnds_compression_free (comp);
+                               return NULL;
+                       }
+               }
+               else {
+                       if (!rdns_add_rr (req, cur_name, clen, type, NULL)) {
+                               REF_RELEASE (req);
+                               rnds_compression_free (comp);
+                               return NULL;
+                       }
+               }
+       }
+
+       rnds_compression_free (comp);
+
+       /* Add EDNS RR */
+       rdns_add_edns0 (req);
+
+       req->retransmits = repeats;
+       req->timeout = timeout;
+       req->state = RDNS_REQUEST_NEW;
+       req->async = resolver->async;
+
+       UPSTREAM_SELECT_ROUND_ROBIN (resolver->servers, serv);
+
+       if (serv == NULL) {
+               rdns_warn ("cannot find suitable server for request");
+               REF_RELEASE (req);
+               return NULL;
+       }
+       
+       /* Select random IO channel */
+       req->io = serv->io_channels[ottery_rand_uint32 () % serv->io_cnt];
+       req->io->uses ++;
+       
+       /* Now send request to server */
+       r = rdns_send_request (req, req->io->sock, true);
+
+       if (r == -1) {
+               REF_RELEASE (req);
+               return NULL;
+       }
+
+       REF_RETAIN (req->io);
+       REF_RETAIN (req->resolver);
+
+       return req;
+}
+
+bool
+rdns_resolver_init (struct rdns_resolver *resolver)
+{
+       unsigned int i;
+       struct rdns_server *serv;
+       struct rdns_io_channel *ioc;
+
+       if (!resolver->async_binded) {
+               return false;
+       }
+       
+       if (resolver->servers == NULL) {
+               return false;
+       }
+
+       /* Now init io channels to all servers */
+       UPSTREAM_FOREACH (resolver->servers, serv) {
+               serv->io_channels = calloc (serv->io_cnt, sizeof (struct rdns_io_channel *));
+               for (i = 0; i < serv->io_cnt; i ++) {
+                       ioc = calloc (1, sizeof (struct rdns_io_channel));
+                       if (ioc == NULL) {
+                               rdns_err ("cannot allocate memory for the resolver");
+                               return false;
+                       }
+                       ioc->sock = rdns_make_client_socket (serv->name, serv->port, SOCK_DGRAM);
+                       ioc->active = true;
+                       if (ioc->sock == -1) {
+                               rdns_err ("cannot open socket to %s:%d %s", serv->name, serv->port, strerror (errno));
+                               free (ioc);
+                               return false;
+                       }
+                       else {
+                               ioc->srv = serv;
+                               ioc->resolver = resolver;
+                               ioc->async_io = resolver->async->add_read (resolver->async->data,
+                                               ioc->sock, ioc);
+                               REF_INIT_RETAIN (ioc, rdns_ioc_free);
+                               serv->io_channels[i] = ioc;
+                       }
+               }
+       }
+
+       if (resolver->async->add_periodic) {
+               resolver->periodic = resolver->async->add_periodic (resolver->async->data,
+                               UPSTREAM_REVIVE_TIME, rdns_process_periodic, resolver);
+       }
+
+       resolver->initialized = true;
+
+       return true;
+}
+
+void
+rdns_resolver_register_plugin (struct rdns_resolver *resolver,
+               struct rdns_plugin *plugin)
+{
+       if (resolver != NULL && plugin != NULL) {
+               /* XXX: support only network plugin now, and only a single one */
+               if (plugin->type == RDNS_PLUGIN_CURVE) {
+                       resolver->curve_plugin = plugin;
+               }
+       }
+}
+
+bool
+rdns_resolver_add_server (struct rdns_resolver *resolver,
+               const char *name, unsigned int port,
+               int priority, unsigned int io_cnt)
+{
+       struct rdns_server *serv;
+       union {
+               struct in_addr v4;
+               struct in6_addr v6;
+       } addr;
+
+       if (inet_pton (AF_INET, name, &addr) == 0 &&
+               inet_pton (AF_INET6, name, &addr) == 0) {
+               /* Invalid IP */
+               return false;
+       }
+
+       if (io_cnt == 0) {
+               return false;
+       }
+       if (port == 0 || port > UINT16_MAX) {
+               return false;
+       }
+
+       serv = calloc (1, sizeof (struct rdns_server));
+       if (serv == NULL) {
+               return false;
+       }
+       serv->name = strdup (name);
+       if (serv->name == NULL) {
+               free (serv);
+               return false;
+       }
+
+       serv->io_cnt = io_cnt;
+       serv->port = port;
+
+       UPSTREAM_ADD (resolver->servers, serv, priority);
+
+       return true;
+}
+
+void
+rdns_resolver_set_logger (struct rdns_resolver *resolver,
+               rdns_log_function logger, void *log_data)
+{
+       resolver->logger = logger;
+       resolver->log_data = log_data;
+}
+
+void
+rdns_resolver_set_log_level (struct rdns_resolver *resolver,
+               enum rdns_log_level level)
+{
+       resolver->log_level = level;
+}
+
+
+void
+rdns_resolver_set_max_io_uses (struct rdns_resolver *resolver,
+               uint64_t max_ioc_uses, double check_time)
+{
+       if (resolver->refresh_ioc_periodic != NULL) {
+               resolver->async->del_periodic (resolver->async->data,
+                               resolver->refresh_ioc_periodic);
+               resolver->refresh_ioc_periodic = NULL;
+       }
+
+       resolver->max_ioc_uses = max_ioc_uses;
+       if (check_time > 0.0 && resolver->async->add_periodic) {
+               resolver->refresh_ioc_periodic =
+                               resolver->async->add_periodic (resolver->async->data,
+                               check_time, rdns_process_ioc_refresh, resolver);
+       }
+}
+
+static void
+rdns_resolver_free (struct rdns_resolver *resolver)
+{
+       struct rdns_server *serv, *stmp;
+       struct rdns_io_channel *ioc;
+       unsigned int i;
+
+       if (resolver->initialized) {
+               if (resolver->periodic != NULL) {
+                       resolver->async->del_periodic (resolver->async->data, resolver->periodic);
+               }
+               if (resolver->refresh_ioc_periodic != NULL) {
+                       resolver->async->del_periodic (resolver->async->data,
+                                       resolver->refresh_ioc_periodic);
+               }
+               if (resolver->curve_plugin != NULL && resolver->curve_plugin->dtor != NULL) {
+                       resolver->curve_plugin->dtor (resolver, resolver->curve_plugin->data);
+               }
+               /* Stop IO watch on all IO channels */
+               UPSTREAM_FOREACH_SAFE (resolver->servers, serv, stmp) {
+                       for (i = 0; i < serv->io_cnt; i ++) {
+                               ioc = serv->io_channels[i];
+                               REF_RELEASE (ioc);
+                       }
+                       serv->io_cnt = 0;
+                       UPSTREAM_DEL (resolver->servers, serv);
+                       free (serv->io_channels);
+                       free (serv->name);
+                       free (serv);
+               }
+       }
+       free (resolver->async);
+       free (resolver);
+}
+
+
+struct rdns_resolver *
+rdns_resolver_new (void)
+{
+       struct rdns_resolver     *new;
+
+       new = calloc (1, sizeof (struct rdns_resolver));
+
+       REF_INIT_RETAIN (new, rdns_resolver_free);
+
+       new->logger = rdns_logger_internal;
+       new->log_data = new;
+
+       return new;
+}
+
+void
+rdns_resolver_async_bind (struct rdns_resolver *resolver,
+               struct rdns_async_context *ctx)
+{
+       if (resolver != NULL && ctx != NULL) {
+               resolver->async = ctx;
+               resolver->async_binded = true;
+       }
+}
diff --git a/contrib/librdns/upstream.h b/contrib/librdns/upstream.h
new file mode 100644 (file)
index 0000000..9646f89
--- /dev/null
@@ -0,0 +1,257 @@
+/*
+ * 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.
+ */
+
+#ifndef UPSTREAM_H_
+#define UPSTREAM_H_
+
+#include <time.h>
+#include <stdio.h>
+
+/**
+ * @file upstream.h
+ * The basic macros to define upstream objects
+ */
+
+#ifndef upstream_fatal
+#define upstream_fatal(msg) do { perror (msg); exit (-1); } while (0)
+#endif
+
+#ifndef upstream_malloc
+#define upstream_malloc(size) malloc (size)
+#endif
+
+#ifndef upstream_free
+#define upstream_free(size, ptr) free (ptr)
+#endif
+
+struct upstream_entry_s;
+struct upstream_common_data {
+       void **upstreams;
+       unsigned int allocated_nelts;
+       unsigned int nelts;
+       unsigned int alive;
+};
+
+typedef struct upstream_entry_s {
+       unsigned short errors;                                          /**< errors for this upstream   */
+       unsigned short dead;
+       unsigned short priority;
+       unsigned short weight;
+       time_t time;                                                            /**< time of marking                    */
+       void *parent;                                                           /**< parent object                              */
+       struct upstream_common_data *common;            /**< common data                                */
+       void *next;                                                                     /**< link to the next                   */
+} upstream_entry_t;
+
+/*
+ * Here we define some reasonable defaults:
+ * if an upstream has more than `UPSTREAM_MAX_ERRORS` in the period of time
+ * of `UPSTREAM_ERROR_TIME` then we shut it down for `UPSTREAM_REVIVE_TIME`.
+ * In this particular case times are 10 seconds for 10 errors and revive in
+ * 30 seconds.
+ */
+#ifndef UPSTREAM_REVIVE_TIME
+#define UPSTREAM_REVIVE_TIME 30
+#endif
+#ifndef UPSTREAM_ERROR_TIME
+#define UPSTREAM_ERROR_TIME 10
+#endif
+#ifndef UPSTREAM_MAX_ERRORS
+#define UPSTREAM_MAX_ERRORS 10
+#endif
+
+#define UPSTREAM_FAIL(u, now) do {                                                                                     \
+    if ((u)->up.time != 0) {                                                                                           \
+      if ((now) - (u)->up.time >= UPSTREAM_ERROR_TIME) {                                       \
+        if ((u)->up.errors >= UPSTREAM_MAX_ERRORS) {                                           \
+          (u)->up.dead = 1;                                                                                                    \
+          (u)->up.time = now;                                                                                          \
+          (u)->up.common->alive --;                                                                                    \
+        }                                                                                                                                      \
+        else {                                                                                                                         \
+          (u)->up.errors = 1;                                                                                          \
+          (u)->up.time = (now);                                                                                                \
+        }                                                                                                                                      \
+      }                                                                                                                                                \
+      else {                                                                                                                           \
+        (u)->up.errors ++;                                                                                                     \
+      }                                                                                                                                                \
+    }                                                                                                                                          \
+    else {                                                                                                                                     \
+      (u)->up.errors ++;                                                                                                       \
+      (u)->up.time = (now);                                                                                                    \
+    }                                                                                                                                          \
+} while (0)
+
+#define UPSTREAM_OK(u) do {                                                                                                    \
+    (u)->up.errors = 0;                                                                                                                \
+    (u)->up.time = 0;                                                                                                          \
+} while (0)
+
+#define UPSTREAM_ADD(head, u, priority) do {                                                           \
+    if (head == NULL) {                                                                                                                \
+      struct upstream_common_data *cd;                                                                         \
+      cd = upstream_malloc (sizeof (struct upstream_common_data));                     \
+      if (cd == NULL) {                                                                                                                \
+        upstream_fatal ("malloc failed");                                                                      \
+      }                                                                                                                                                \
+      cd->upstreams = upstream_malloc (sizeof (void *) * 8);                           \
+      if (cd == NULL) {                                                                                                                \
+        upstream_fatal ("malloc failed");                                                                      \
+      }                                                                                                                                                \
+      cd->allocated_nelts = 8;                                                                                         \
+      cd->nelts = 1;                                                                                                           \
+      cd->alive = 1;                                                                                                           \
+      cd->upstreams[0] = (u);                                                                                          \
+      (u)->up.common = cd;                                                                                                     \
+    }                                                                                                                                          \
+    else {                                                                                                                                     \
+      struct upstream_common_data *cd = (head)->up.common;                                     \
+      (u)->up.common = cd;                                                                                                     \
+      if (cd->nelts == cd->allocated_nelts) {                                                          \
+        void **nup;                                                                                                                    \
+        nup = upstream_malloc (sizeof (void *) * cd->nelts * 2);                       \
+        if (nup == NULL) {                                                                                                     \
+          upstream_fatal ("malloc failed");                                                                    \
+        }                                                                                                                                      \
+        memcpy (nup, cd->upstreams, cd->nelts * sizeof (void *));                      \
+        upstream_free (cd->nelts * sizeof (void *), cd->upstreams);            \
+        cd->upstreams = nup;                                                                                           \
+        cd->allocated_nelts *= 2;                                                                                      \
+      }                                                                                                                                                \
+      cd->upstreams[cd->nelts++] = (u);                                                                                \
+      cd->alive ++;                                                                                                                    \
+    }                                                                                                                                          \
+    (u)->up.next = (head);                                                                                                     \
+    (head) = (u);                                                                                                                      \
+    if (priority > 0) {                                                                                                                \
+      (u)->up.priority = (u)->up.weight = (priority);                                          \
+    }                                                                                                                                          \
+    else {                                                                                                                                     \
+      (u)->up.priority = (u)->up.weight = 65535;                                                       \
+    }                                                                                                                                          \
+    (u)->up.time = 0;                                                                                                          \
+    (u)->up.errors = 0;                                                                                                                \
+    (u)->up.dead = 0;                                                                                                          \
+    (u)->up.parent = (u);                                                                                                      \
+} while (0)
+
+#define UPSTREAM_DEL(head, u) do {                                                                                     \
+    if (head != NULL) {                                                                                                                \
+        struct upstream_common_data *cd = (head)->up.common;                           \
+        if ((u)->up.next != NULL) {                                                                                    \
+            (head) = (u)->up.next;                                                                                     \
+            cd->nelts --;                                                                                                      \
+            cd->alive --;                                                                                                      \
+        }                                                                                                                                      \
+        else {                                                                                                                         \
+            upstream_free (cd->allocated_nelts * sizeof (void *),                      \
+                cd->upstreams);                                                                                                \
+            upstream_free (sizeof (struct upstream_common_data), cd);          \
+            (head) = NULL;                                                                                                     \
+        }                                                                                                                                      \
+    }                                                                                                                                          \
+} while (0)
+
+#define UPSTREAM_FOREACH(head, u) for ((u) = (head); (u) != NULL; (u) = (u)->up.next)
+#define UPSTREAM_FOREACH_SAFE(head, u, tmp)                                                            \
+    for ((u) = (head);                                                                                                         \
+    (u) != NULL && ((tmp = (u)->up.next) || true);                                                     \
+    (u) = (tmp))
+
+#define UPSTREAM_REVIVE_ALL(head) do {                                                                         \
+    __typeof(head) elt = (head);                                                                                       \
+    while (elt != NULL) {                                                                                                      \
+      elt->up.dead = 0;                                                                                                                \
+      elt->up.errors = 0;                                                                                                      \
+      elt->up.time = 0;                                                                                                                \
+      elt = elt->up.next;                                                                                                      \
+    }                                                                                                                                          \
+    (head)->up.common->alive = (head)->up.common->nelts;                                       \
+} while (0)
+
+#define UPSTREAM_RESCAN(head, now) do {                                                                                \
+    __typeof(head) elt = (head);                                                                                       \
+    if ((head)->up.common->alive == 0) {                                                                       \
+      UPSTREAM_REVIVE_ALL((head));                                                                                     \
+    }                                                                                                                                          \
+    else {                                                                                                                                     \
+      while (elt != NULL) {                                                                                                    \
+        if (elt->up.dead) {                                                                                                    \
+          if ((now) - elt->up.time >= UPSTREAM_REVIVE_TIME) {                          \
+            elt->up.dead = 0;                                                                                          \
+            elt->up.errors = 0;                                                                                                \
+            elt->up.weight = elt->up.priority;                                                         \
+            (head)->up.common->alive ++;                                                                       \
+          }                                                                                                                                    \
+        }                                                                                                                                      \
+        else {                                                                                                                         \
+          if ((now) - elt->up.time >= UPSTREAM_ERROR_TIME &&                           \
+              elt->up.errors >= UPSTREAM_MAX_ERRORS) {                                         \
+            elt->up.dead = 1;                                                                                          \
+            elt->up.time = now;                                                                                                \
+            (head)->up.common->alive --;                                                                       \
+          }                                                                                                                                    \
+        }                                                                                                                                      \
+        elt = elt->up.next;                                                                                                    \
+      }                                                                                                                                                \
+    }                                                                                                                                          \
+} while (0)
+
+#define UPSTREAM_SELECT_ROUND_ROBIN(head, selected) do {                                       \
+    __typeof(head) elt = (head);                                                                                       \
+    (selected) = NULL;                                                                                                         \
+    int alive = 0;                                                                                                                     \
+    unsigned max_weight = 0;                                                                                           \
+    if ((head)->up.common->alive == 0){                                                                        \
+      UPSTREAM_REVIVE_ALL(head);                                                                                       \
+    }                                                                                                                                          \
+    while (elt != NULL) {                                                                                                      \
+      if (!elt->up.dead) {                                                                                                     \
+        if (elt->up.weight > max_weight) {                                                                     \
+          max_weight = elt->up.weight;                                                                         \
+          (selected) = elt;                                                                                                    \
+        }                                                                                                                                      \
+        alive ++;                                                                                                                      \
+      }                                                                                                                                                \
+      elt = elt->up.next;                                                                                                      \
+    }                                                                                                                                          \
+    if (max_weight == 0) {                                                                                                     \
+      elt = (head);                                                                                                                    \
+      while (elt != NULL) {                                                                                                    \
+        elt->up.weight = elt->up.priority;                                                                     \
+        if (!elt->up.dead) {                                                                                           \
+          if (elt->up.priority > max_weight) {                                                         \
+            max_weight = elt->up.priority;                                                                     \
+            (selected) = elt;                                                                                          \
+          }                                                                                                                                    \
+        }                                                                                                                                      \
+        elt = elt->up.next;                                                                                                    \
+      }                                                                                                                                                \
+    }                                                                                                                                          \
+    (selected)->up.weight --;                                                                                          \
+} while (0)
+
+#endif /* UPSTREAM_H_ */
diff --git a/contrib/librdns/util.c b/contrib/librdns/util.c
new file mode 100644 (file)
index 0000000..7d9dc98
--- /dev/null
@@ -0,0 +1,523 @@
+/* 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.
+ */
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/un.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <netdb.h>
+#include <fcntl.h>
+#include <ctype.h>
+
+#include "ottery.h"
+#include "util.h"
+#include "logger.h"
+
+static int
+rdns_make_socket_nonblocking (int fd)
+{
+       int                            ofl;
+
+       ofl = fcntl (fd, F_GETFL, 0);
+
+       if (fcntl (fd, F_SETFL, ofl | O_NONBLOCK) == -1) {
+               return -1;
+       }
+       return 0;
+}
+
+static int
+rdns_make_inet_socket (int type, struct addrinfo *addr)
+{
+       int fd, r, s_error;
+       socklen_t optlen;
+       struct addrinfo *cur;
+
+       cur = addr;
+       while (cur) {
+               /* Create socket */
+               fd = socket (cur->ai_family, type, 0);
+               if (fd == -1) {
+                       goto out;
+               }
+
+               if (rdns_make_socket_nonblocking (fd) < 0) {
+                       goto out;
+               }
+
+               /* Set close on exec */
+               if (fcntl (fd, F_SETFD, FD_CLOEXEC) == -1) {
+                       goto out;
+               }
+
+               r = connect (fd, cur->ai_addr, cur->ai_addrlen);
+
+               if (r == -1) {
+                       if (errno != EINPROGRESS) {
+                               goto out;
+                       }
+               }
+               else {
+                       /* Still need to check SO_ERROR on socket */
+                       optlen = sizeof (s_error);
+                       getsockopt (fd, SOL_SOCKET, SO_ERROR, (void *)&s_error, &optlen);
+                       if (s_error) {
+                               errno = s_error;
+                               goto out;
+                       }
+               }
+               break;
+out:
+               if (fd != -1) {
+                       close (fd);
+               }
+               fd = -1;
+               cur = cur->ai_next;
+       }
+       return (fd);
+}
+
+static int
+rdns_make_unix_socket (const char *path, struct sockaddr_un *addr, int type)
+{
+       int fd = -1, s_error, r, serrno;
+       socklen_t optlen;
+
+       if (path == NULL) {
+               return -1;
+       }
+
+       addr->sun_family = AF_UNIX;
+
+       memset (addr->sun_path, 0, sizeof (addr->sun_path));
+       memccpy (addr->sun_path, path, 0, sizeof (addr->sun_path) - 1);
+#ifdef FREEBSD
+       addr->sun_len = SUN_LEN (addr);
+#endif
+
+       fd = socket (PF_LOCAL, type, 0);
+
+       if (fd == -1) {
+               return -1;
+       }
+
+       if (rdns_make_socket_nonblocking (fd) < 0) {
+               goto out;
+       }
+
+       /* Set close on exec */
+       if (fcntl (fd, F_SETFD, FD_CLOEXEC) == -1) {
+               goto out;
+       }
+
+       r = connect (fd, (struct sockaddr *)addr, SUN_LEN (addr));
+
+       if (r == -1) {
+               if (errno != EINPROGRESS) {
+                       goto out;
+               }
+       }
+       else {
+               /* Still need to check SO_ERROR on socket */
+               optlen = sizeof (s_error);
+               getsockopt (fd, SOL_SOCKET, SO_ERROR, (void *)&s_error, &optlen);
+               if (s_error) {
+                       errno = s_error;
+                       goto out;
+               }
+       }
+
+       return (fd);
+
+  out:
+       serrno = errno;
+       if (fd != -1) {
+               close (fd);
+       }
+       errno = serrno;
+       return (-1);
+}
+
+/**
+ * Make a universal socket
+ * @param credits host, ip or path to unix socket
+ * @param port port (used for network sockets)
+ * @param async make this socket asynced
+ * @param is_server make this socket as server socket
+ * @param try_resolve try name resolution for a socket (BLOCKING)
+ */
+int
+rdns_make_client_socket (const char *credits, uint16_t port,
+               int type)
+{
+       struct sockaddr_un              un;
+       struct stat                     st;
+       struct addrinfo                 hints, *res;
+       int                             r;
+       char                            portbuf[8];
+
+       if (*credits == '/') {
+               r = stat (credits, &st);
+               if (r == -1) {
+                       /* Unix socket doesn't exists it must be created first */
+                       errno = ENOENT;
+                       return -1;
+               }
+               else {
+                       if ((st.st_mode & S_IFSOCK) == 0) {
+                               /* Path is not valid socket */
+                               errno = EINVAL;
+                               return -1;
+                       }
+                       else {
+                               return rdns_make_unix_socket (credits, &un, type);
+                       }
+               }
+       }
+       else {
+               /* TCP related part */
+               memset (&hints, 0, sizeof (hints));
+               hints.ai_family = AF_UNSPEC;     /* Allow IPv4 or IPv6 */
+               hints.ai_socktype = type; /* Type of the socket */
+               hints.ai_flags = 0;
+               hints.ai_protocol = 0;           /* Any protocol */
+               hints.ai_canonname = NULL;
+               hints.ai_addr = NULL;
+               hints.ai_next = NULL;
+
+               hints.ai_flags |= AI_NUMERICHOST | AI_NUMERICSERV;
+
+               snprintf (portbuf, sizeof (portbuf), "%d", (int)port);
+               if ((r = getaddrinfo (credits, portbuf, &hints, &res)) == 0) {
+                       r = rdns_make_inet_socket (type, res);
+                       freeaddrinfo (res);
+                       return r;
+               }
+               else {
+                       return -1;
+               }
+       }
+
+       /* Not reached */
+       return -1;
+}
+
+const char *
+rdns_strerror (enum dns_rcode rcode)
+{
+       rcode &= 0xf;
+       static char numbuf[16];
+
+       if ('\0' == dns_rcodes[rcode][0]) {
+               snprintf (numbuf, sizeof (numbuf), "UNKNOWN: %d", (int)rcode);
+               return numbuf;
+       }
+       return dns_rcodes[rcode];
+}
+
+const char *
+rdns_strtype (enum rdns_request_type type)
+{
+       return dns_types[type];
+}
+
+uint16_t
+rdns_permutor_generate_id (void)
+{
+       uint16_t id;
+
+       id = ottery_rand_unsigned ();
+
+       return id;
+}
+
+
+void
+rdns_reply_free (struct rdns_reply *rep)
+{
+       struct rdns_reply_entry *entry, *tmp;
+
+       LL_FOREACH_SAFE (rep->entries, entry, tmp) {
+               switch (entry->type) {
+               case RDNS_REQUEST_PTR:
+                       free (entry->content.ptr.name);
+                       break;
+               case RDNS_REQUEST_NS:
+                       free (entry->content.ns.name);
+                       break;
+               case RDNS_REQUEST_MX:
+                       free (entry->content.mx.name);
+                       break;
+               case RDNS_REQUEST_TXT:
+               case RDNS_REQUEST_SPF:
+                       free (entry->content.txt.data);
+                       break;
+               case RDNS_REQUEST_SRV:
+                       free (entry->content.srv.target);
+                       break;
+               case RDNS_REQUEST_TLSA:
+                       free (entry->content.tlsa.data);
+                       break;
+               case RDNS_REQUEST_SOA:
+                       free (entry->content.soa.mname);
+                       free (entry->content.soa.admin);
+                       break;
+               }
+               free (entry);
+       }
+       free (rep);
+}
+
+void
+rdns_request_free (struct rdns_request *req)
+{
+       unsigned int i;
+
+       if (req != NULL) {
+               if (req->packet != NULL) {
+                       free (req->packet);
+               }
+               for (i = 0; i < req->qcount; i ++) {
+                       free (req->requested_names[i].name);
+               }
+               if (req->requested_names != NULL) {
+                       free (req->requested_names);
+               }
+               if (req->reply != NULL) {
+                       rdns_reply_free (req->reply);
+               }
+               if (req->state >= RDNS_REQUEST_SENT &&
+                               req->state < RDNS_REQUEST_REPLIED) {
+                       /* Remove timer */
+                       req->async->del_timer (req->async->data,
+                                       req->async_event);
+                       /* Remove from id hashes */
+                       HASH_DEL (req->io->requests, req);
+               }
+               else if (req->state == RDNS_REQUEST_REGISTERED) {
+                       /* Remove retransmit event */
+                       req->async->del_write (req->async->data,
+                                       req->async_event);
+               }
+#ifdef TWEETNACL
+               if (req->curve_plugin_data != NULL) {
+                       req->resolver->curve_plugin->cb.curve_plugin.finish_cb (
+                                       req, req->resolver->curve_plugin->data);
+               }
+#endif
+               if (req->io != NULL && req->state > RDNS_REQUEST_NEW) {
+                       REF_RELEASE (req->io);
+                       REF_RELEASE (req->resolver);
+               }
+
+               free (req);
+       }
+}
+
+void
+rdns_ioc_free (struct rdns_io_channel *ioc)
+{
+       struct rdns_request *req, *rtmp;
+
+       HASH_ITER (hh, ioc->requests, req, rtmp) {
+               REF_RELEASE (req);
+       }
+       ioc->resolver->async->del_read (ioc->resolver->async->data,
+                       ioc->async_io);
+       close (ioc->sock);
+       free (ioc);
+}
+
+void
+rdns_resolver_release (struct rdns_resolver *resolver)
+{
+       REF_RELEASE (resolver);
+}
+
+struct rdns_request*
+rdns_request_retain (struct rdns_request *req)
+{
+       REF_RETAIN (req);
+       return req;
+}
+
+void
+rdns_request_release (struct rdns_request *req)
+{
+       REF_RELEASE (req);
+}
+
+static bool
+rdns_resolver_conf_process_line (struct rdns_resolver *resolver, char *line)
+{
+       char *p, *c;
+       bool has_obrace = false;
+       unsigned int port = dns_port;
+
+       if (strncmp (line, "nameserver", sizeof ("nameserver") - 1) == 0) {
+               p = line + sizeof ("nameserver") - 1;
+               /* Skip spaces */
+               while (*p == ' ' || *p == '\t') {
+                       p ++;
+               }
+               if (*p == '[') {
+                       has_obrace = true;
+                       p ++;
+               }
+               if (isxdigit (*p) || *p == ':') {
+                       c = p;
+                       while (isxdigit (*p) || *p == ':' || *p == '.') {
+                               p ++;
+                       }
+                       if (has_obrace && *p != ']') {
+                               return false;
+                       }
+                       else if (*p != '\0' && *p != '\n') {
+                               return false;
+                       }
+                       *p = '\0';
+                       if (has_obrace) {
+                               p ++;
+                               if (*p == ':') {
+                                       /* Maybe we have a port definition */
+                                       port = strtoul (p + 1, NULL, 10);
+                                       if (port == 0 || port > UINT16_MAX) {
+                                               return false;
+                                       }
+                               }
+                       }
+
+                       return rdns_resolver_add_server (resolver, c, port, 0, default_io_cnt);
+               }
+               else {
+                       return false;
+               }
+       }
+       /* XXX: skip unknown resolv.conf lines */
+
+       return true;
+}
+
+bool
+rdns_resolver_parse_resolv_conf (struct rdns_resolver *resolver, const char *path)
+{
+       FILE *in;
+       char buf[BUFSIZ];
+
+       in = fopen (path, "r");
+
+       if (in == NULL) {
+               return false;
+       }
+
+       while (!feof (in)) {
+               if (fgets (buf, sizeof (buf), in) == NULL) {
+                       break;
+               }
+               if (!rdns_resolver_conf_process_line (resolver, buf)) {
+                       rdns_warn ("rdns_resolver_parse_resolv_conf: cannot parse line: %s", buf);
+                       fclose (in);
+                       return false;
+               }
+       }
+
+       fclose (in);
+       return true;
+}
+
+bool
+rdns_request_has_type (struct rdns_request *req, enum rdns_request_type type)
+{
+       unsigned int i;
+
+       for (i = 0; i < req->qcount; i ++) {
+               if (req->requested_names[i].type == type) {
+                       return true;
+               }
+       }
+
+       return false;
+}
+
+const struct rdns_request_name *
+rdns_request_get_name (struct rdns_request *req, unsigned int *count)
+{
+
+       if (count != NULL) {
+               *count = req->qcount;
+       }
+       return req->requested_names;
+}
+
+char *
+rdns_generate_ptr_from_str (const char *str)
+{
+       union {
+               struct in_addr v4;
+               struct in6_addr v6;
+       } addr;
+       char *res = NULL;
+       unsigned char *bytes;
+       size_t len;
+
+       if (inet_pton (AF_INET, str, &addr.v4) == 1) {
+               bytes = (unsigned char *)&addr.v4;
+
+               len = 4 * 4 + sizeof ("in-addr.arpa");
+               res = malloc (len);
+               if (res) {
+                       snprintf (res, len, "%u.%u.%u.%u.in-addr.arpa",
+                                       (unsigned)bytes[3]&0xFF,
+                                       (unsigned)bytes[2]&0xFF,
+                                       (unsigned)bytes[1]&0xFF,
+                                       (unsigned)bytes[0]&0xFF);
+               }
+       }
+       else if (inet_pton (AF_INET6, str, &addr.v6) == 1) {
+               bytes = (unsigned char *)&addr.v6;
+
+               len = 2*32 + sizeof ("ip6.arpa");
+               res = malloc (len);
+               if (res) {
+                       snprintf(res, len,
+                                       "%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x."
+                                       "%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.ip6.arpa",
+                                       bytes[15]&0xF, bytes[15] >> 4, bytes[14]&0xF, bytes[14] >> 4,
+                                       bytes[13]&0xF, bytes[13] >> 4, bytes[12]&0xF, bytes[12] >> 4,
+                                       bytes[11]&0xF, bytes[11] >> 4, bytes[10]&0xF, bytes[10] >> 4,
+                                       bytes[9]&0xF, bytes[9] >> 4, bytes[8]&0xF, bytes[8] >> 4,
+                                       bytes[7]&0xF, bytes[7] >> 4, bytes[6]&0xF, bytes[6] >> 4,
+                                       bytes[5]&0xF, bytes[5] >> 4, bytes[4]&0xF, bytes[4] >> 4,
+                                       bytes[3]&0xF, bytes[3] >> 4, bytes[2]&0xF, bytes[2] >> 4,
+                                       bytes[1]&0xF, bytes[1] >> 4, bytes[0]&0xF, bytes[0] >> 4);
+               }
+       }
+
+       return res;
+}
diff --git a/contrib/librdns/util.h b/contrib/librdns/util.h
new file mode 100644 (file)
index 0000000..035f49a
--- /dev/null
@@ -0,0 +1,62 @@
+/* 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.
+ */
+#ifndef UTIL_H_
+#define UTIL_H_
+
+#include "dns_private.h"
+
+/**
+ * Make a universal socket
+ * @param credits host, ip or path to unix socket
+ * @param port port (used for network sockets)
+ * @param type of socket (SOCK_STREAM or SOCK_DGRAM)
+ */
+int
+rdns_make_client_socket (const char *credits, uint16_t port,
+               int type);
+
+/**
+ * Generate new random DNS id
+ * @return dns id
+ */
+uint16_t rdns_permutor_generate_id (void);
+
+
+/**
+ * Free IO channel
+ */
+void rdns_ioc_free (struct rdns_io_channel *ioc);
+
+/**
+ * Free request
+ * @param req
+ */
+void rdns_request_free (struct rdns_request *req);
+
+/**
+ * Free reply
+ * @param rep
+ */
+void rdns_reply_free (struct rdns_reply *rep);
+
+#endif /* UTIL_H_ */
diff --git a/contrib/libucl/CMakeLists.txt b/contrib/libucl/CMakeLists.txt
new file mode 100644 (file)
index 0000000..80f5015
--- /dev/null
@@ -0,0 +1,18 @@
+SET(UCLSRC            ucl_util.c
+                      ucl_parser.c
+                      ucl_emitter.c
+                      ucl_emitter_streamline.c
+                      ucl_emitter_utils.c
+                      ucl_hash.c
+                      ucl_schema.c
+                      lua_ucl.c)
+
+
+SET (LIB_TYPE STATIC)
+ADD_LIBRARY(ucl ${LIB_TYPE} ${UCLSRC})
+
+IF(ENABLE_URL_SIGN MATCHES "ON")
+       IF(OPENSSL_FOUND)
+               TARGET_LINK_LIBRARIES(ucl ${OPENSSL_LIBRARIES})
+       ENDIF(OPENSSL_FOUND)
+ENDIF(ENABLE_URL_SIGN MATCHES "ON")
diff --git a/contrib/libucl/khash.h b/contrib/libucl/khash.h
new file mode 100644 (file)
index 0000000..afc3ce3
--- /dev/null
@@ -0,0 +1,627 @@
+/* The MIT License
+
+   Copyright (c) 2008, 2009, 2011 by Attractive Chaos <attractor@live.co.uk>
+
+   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, sublicense, and/or sell copies of the Software, and to
+   permit persons to whom the Software is furnished to do so, subject to
+   the following conditions:
+
+   The above copyright notice and this permission notice shall be
+   included in all copies or substantial portions of the Software.
+
+   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+   NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+   BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+   ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+   CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+   SOFTWARE.
+*/
+
+/*
+  An example:
+
+#include "khash.h"
+KHASH_MAP_INIT_INT(32, char)
+int main() {
+       int ret, is_missing;
+       khiter_t k;
+       khash_t(32) *h = kh_init(32);
+       k = kh_put(32, h, 5, &ret);
+       kh_value(h, k) = 10;
+       k = kh_get(32, h, 10);
+       is_missing = (k == kh_end(h));
+       k = kh_get(32, h, 5);
+       kh_del(32, h, k);
+       for (k = kh_begin(h); k != kh_end(h); ++k)
+               if (kh_exist(h, k)) kh_value(h, k) = 1;
+       kh_destroy(32, h);
+       return 0;
+}
+*/
+
+/*
+  2013-05-02 (0.2.8):
+
+       * Use quadratic probing. When the capacity is power of 2, stepping function
+         i*(i+1)/2 guarantees to traverse each bucket. It is better than double
+         hashing on cache performance and is more robust than linear probing.
+
+         In theory, double hashing should be more robust than quadratic probing.
+         However, my implementation is probably not for large hash tables, because
+         the second hash function is closely tied to the first hash function,
+         which reduce the effectiveness of double hashing.
+
+       Reference: http://research.cs.vt.edu/AVresearch/hashing/quadratic.php
+
+  2011-12-29 (0.2.7):
+
+    * Minor code clean up; no actual effect.
+
+  2011-09-16 (0.2.6):
+
+       * The capacity is a power of 2. This seems to dramatically improve the
+         speed for simple keys. Thank Zilong Tan for the suggestion. Reference:
+
+          - http://code.google.com/p/ulib/
+          - http://nothings.org/computer/judy/
+
+       * Allow to optionally use linear probing which usually has better
+         performance for random input. Double hashing is still the default as it
+         is more robust to certain non-random input.
+
+       * Added Wang's integer hash function (not used by default). This hash
+         function is more robust to certain non-random input.
+
+  2011-02-14 (0.2.5):
+
+    * Allow to declare global functions.
+
+  2009-09-26 (0.2.4):
+
+    * Improve portability
+
+  2008-09-19 (0.2.3):
+
+       * Corrected the example
+       * Improved interfaces
+
+  2008-09-11 (0.2.2):
+
+       * Improved speed a little in kh_put()
+
+  2008-09-10 (0.2.1):
+
+       * Added kh_clear()
+       * Fixed a compiling error
+
+  2008-09-02 (0.2.0):
+
+       * Changed to token concatenation which increases flexibility.
+
+  2008-08-31 (0.1.2):
+
+       * Fixed a bug in kh_get(), which has not been tested previously.
+
+  2008-08-31 (0.1.1):
+
+       * Added destructor
+*/
+
+
+#ifndef __AC_KHASH_H
+#define __AC_KHASH_H
+
+/*!
+  @header
+
+  Generic hash table library.
+ */
+
+#define AC_VERSION_KHASH_H "0.2.8"
+
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+
+/* compiler specific configuration */
+
+#if UINT_MAX == 0xffffffffu
+typedef unsigned int khint32_t;
+#elif ULONG_MAX == 0xffffffffu
+typedef unsigned long khint32_t;
+#endif
+
+#if ULONG_MAX == ULLONG_MAX
+typedef unsigned long khint64_t;
+#else
+typedef unsigned long long khint64_t;
+#endif
+
+#ifndef kh_inline
+#ifdef _MSC_VER
+#define kh_inline __inline
+#else
+#define kh_inline inline
+#endif
+#endif /* kh_inline */
+
+#ifndef kh_unused
+# ifdef __GNUC__
+#   define kh_unused(x) __attribute__((__unused__)) x
+# else
+#   define kh_unused(x) x
+# endif
+#endif
+
+typedef khint32_t khint_t;
+typedef khint_t khiter_t;
+
+#define __ac_isempty(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&2)
+#define __ac_isdel(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&1)
+#define __ac_iseither(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&3)
+#define __ac_set_isdel_false(flag, i) (flag[i>>4]&=~(1ul<<((i&0xfU)<<1)))
+#define __ac_set_isempty_false(flag, i) (flag[i>>4]&=~(2ul<<((i&0xfU)<<1)))
+#define __ac_set_isboth_false(flag, i) (flag[i>>4]&=~(3ul<<((i&0xfU)<<1)))
+#define __ac_set_isdel_true(flag, i) (flag[i>>4]|=1ul<<((i&0xfU)<<1))
+
+#define __ac_fsize(m) ((m) < 16? 1 : (m)>>4)
+
+#ifndef kroundup32
+#define kroundup32(x) (--(x), (x)|=(x)>>1, (x)|=(x)>>2, (x)|=(x)>>4, (x)|=(x)>>8, (x)|=(x)>>16, ++(x))
+#endif
+
+#ifndef kcalloc
+#define kcalloc(N,Z) calloc(N,Z)
+#endif
+#ifndef kmalloc
+#define kmalloc(Z) malloc(Z)
+#endif
+#ifndef krealloc
+#define krealloc(P,Z) realloc(P,Z)
+#endif
+#ifndef kfree
+#define kfree(P) free(P)
+#endif
+
+static const double __ac_HASH_UPPER = 0.77;
+
+#define __KHASH_TYPE(name, khkey_t, khval_t) \
+       typedef struct kh_##name##_s { \
+               khint_t n_buckets, size, n_occupied, upper_bound; \
+               khint32_t *flags; \
+               khkey_t *keys; \
+               khval_t *vals; \
+       } kh_##name##_t;
+
+#define __KHASH_PROTOTYPES(name, khkey_t, khval_t)                                                     \
+       extern kh_##name##_t * kh_init_##name(void);                                                    \
+       extern void kh_destroy_##name(kh_##name##_t *h);                                                \
+       extern void kh_clear_##name(kh_##name##_t *h);                                                  \
+       extern khint_t kh_get_##name(const kh_##name##_t *h, khkey_t key);              \
+       extern int kh_resize_##name(kh_##name##_t *h, khint_t new_n_buckets);   \
+       extern khint_t kh_put_##name(kh_##name##_t *h, khkey_t key, int *ret);  \
+       extern void kh_del_##name(kh_##name##_t *h, khint_t x);
+
+#define __KHASH_IMPL(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \
+       SCOPE kh_##name##_t *kh_init_##name(void) {                                                     \
+               return (kh_##name##_t*)kcalloc(1, sizeof(kh_##name##_t));               \
+       }                                                                                                                                       \
+       SCOPE void kh_destroy_##name(kh_##name##_t *h)                                          \
+       {                                                                                                                                       \
+               if (h) {                                                                                                                \
+                       kfree((void *)h->keys); kfree(h->flags);                                        \
+                       kfree((void *)h->vals);                                                                         \
+                       kfree(h);                                                                                                       \
+               }                                                                                                                               \
+       }                                                                                                                                       \
+       SCOPE void kh_unused(kh_clear_##name)(kh_##name##_t *h)                         \
+       {                                                                                                                                       \
+               if (h && h->flags) {                                                                                    \
+                       memset(h->flags, 0xaa, __ac_fsize(h->n_buckets) * sizeof(khint32_t)); \
+                       h->size = h->n_occupied = 0;                                                            \
+               }                                                                                                                               \
+       }                                                                                                                                       \
+       SCOPE khint_t kh_get_##name(const kh_##name##_t *h, khkey_t key)        \
+       {                                                                                                                                       \
+               if (h->n_buckets) {                                                                                             \
+                       khint_t k, i, last, mask, step = 0; \
+                       mask = h->n_buckets - 1;                                                                        \
+                       k = __hash_func(key); i = k & mask;                                                     \
+                       last = i; \
+                       while (!__ac_isempty(h->flags, i) && (__ac_isdel(h->flags, i) || !__hash_equal(h->keys[i], key))) { \
+                               i = (i + (++step)) & mask; \
+                               if (i == last) return h->n_buckets;                                             \
+                       }                                                                                                                       \
+                       return __ac_iseither(h->flags, i)? h->n_buckets : i;            \
+               } else return 0;                                                                                                \
+       }                                                                                                                                       \
+       SCOPE int kh_resize_##name(kh_##name##_t *h, khint_t new_n_buckets) \
+       { /* This function uses 0.25*n_buckets bytes of working space instead of [sizeof(key_t+val_t)+.25]*n_buckets. */ \
+               khint32_t *new_flags = 0;                                                                               \
+               khint_t j = 1;                                                                                                  \
+               {                                                                                                                               \
+                       kroundup32(new_n_buckets);                                                                      \
+                       if (new_n_buckets < 4) new_n_buckets = 4;                                       \
+                       if (h->size >= (khint_t)(new_n_buckets * __ac_HASH_UPPER + 0.5)) j = 0; /* requested size is too small */ \
+                       else { /* hash table size to be changed (shrink or expand); rehash */ \
+                               new_flags = (khint32_t*)kmalloc(__ac_fsize(new_n_buckets) * sizeof(khint32_t)); \
+                               if (!new_flags) return -1;                                                              \
+                               memset(new_flags, 0xaa, __ac_fsize(new_n_buckets) * sizeof(khint32_t)); \
+                               if (h->n_buckets < new_n_buckets) {     /* expand */            \
+                                       khkey_t *new_keys = (khkey_t*)krealloc((void *)h->keys, new_n_buckets * sizeof(khkey_t)); \
+                                       if (!new_keys) { kfree(new_flags); return -1; }         \
+                                       h->keys = new_keys;                                                                     \
+                                       if (kh_is_map) {                                                                        \
+                                               khval_t *new_vals = (khval_t*)krealloc((void *)h->vals, new_n_buckets * sizeof(khval_t)); \
+                                               if (!new_vals) { kfree(new_flags); return -1; } \
+                                               h->vals = new_vals;                                                             \
+                                       }                                                                                                       \
+                               } /* otherwise shrink */                                                                \
+                       }                                                                                                                       \
+               }                                                                                                                               \
+               if (j) { /* rehashing is needed */                                                              \
+                       for (j = 0; j != h->n_buckets; ++j) {                                           \
+                               if (__ac_iseither(h->flags, j) == 0) {                                  \
+                                       khkey_t key = h->keys[j];                                                       \
+                                       khval_t val;                                                                            \
+                                       khint_t new_mask;                                                                       \
+                                       new_mask = new_n_buckets - 1;                                           \
+                                       if (kh_is_map) val = h->vals[j];                                        \
+                                       __ac_set_isdel_true(h->flags, j);                                       \
+                                       while (1) { /* kick-out process; sort of like in Cuckoo hashing */ \
+                                               khint_t k, i, step = 0; \
+                                               k = __hash_func(key);                                                   \
+                                               i = k & new_mask;                                                               \
+                                               while (!__ac_isempty(new_flags, i)) i = (i + (++step)) & new_mask; \
+                                               __ac_set_isempty_false(new_flags, i);                   \
+                                               if (i < h->n_buckets && __ac_iseither(h->flags, i) == 0) { /* kick out the existing element */ \
+                                                       { khkey_t tmp = h->keys[i]; h->keys[i] = key; key = tmp; } \
+                                                       if (kh_is_map) { khval_t tmp = h->vals[i]; h->vals[i] = val; val = tmp; } \
+                                                       __ac_set_isdel_true(h->flags, i); /* mark it as deleted in the old hash table */ \
+                                               } else { /* write the element and jump out of the loop */ \
+                                                       h->keys[i] = key;                                                       \
+                                                       if (kh_is_map) h->vals[i] = val;                        \
+                                                       break;                                                                          \
+                                               }                                                                                               \
+                                       }                                                                                                       \
+                               }                                                                                                               \
+                       }                                                                                                                       \
+                       if (h->n_buckets > new_n_buckets) { /* shrink the hash table */ \
+                               h->keys = (khkey_t*)krealloc((void *)h->keys, new_n_buckets * sizeof(khkey_t)); \
+                               if (kh_is_map) h->vals = (khval_t*)krealloc((void *)h->vals, new_n_buckets * sizeof(khval_t)); \
+                       }                                                                                                                       \
+                       kfree(h->flags); /* free the working space */                           \
+                       h->flags = new_flags;                                                                           \
+                       h->n_buckets = new_n_buckets;                                                           \
+                       h->n_occupied = h->size;                                                                        \
+                       h->upper_bound = (khint_t)(h->n_buckets * __ac_HASH_UPPER + 0.5); \
+               }                                                                                                                               \
+               return 0;                                                                                                               \
+       }                                                                                                                                       \
+       SCOPE khint_t kh_put_##name(kh_##name##_t *h, khkey_t key, int *ret) \
+       {                                                                                                                                       \
+               khint_t x;                                                                                                              \
+               if (h->n_occupied >= h->upper_bound) { /* update the hash table */ \
+                       if (h->n_buckets > (h->size<<1)) {                                                      \
+                               if (kh_resize_##name(h, h->n_buckets - 1) < 0) { /* clear "deleted" elements */ \
+                                       *ret = -1; return h->n_buckets;                                         \
+                               }                                                                                                               \
+                       } else if (kh_resize_##name(h, h->n_buckets + 1) < 0) { /* expand the hash table */ \
+                               *ret = -1; return h->n_buckets;                                                 \
+                       }                                                                                                                       \
+               } /* TODO: to implement automatically shrinking; resize() already support shrinking */ \
+               {                                                                                                                               \
+                       khint_t k, i, site, last, mask = h->n_buckets - 1, step = 0; \
+                       x = site = h->n_buckets; k = __hash_func(key); i = k & mask; \
+                       if (__ac_isempty(h->flags, i)) x = i; /* for speed up */        \
+                       else {                                                                                                          \
+                               last = i; \
+                               while (!__ac_isempty(h->flags, i) && (__ac_isdel(h->flags, i) || !__hash_equal(h->keys[i], key))) { \
+                                       if (__ac_isdel(h->flags, i)) site = i;                          \
+                                       i = (i + (++step)) & mask; \
+                                       if (i == last) { x = site; break; }                                     \
+                               }                                                                                                               \
+                               if (x == h->n_buckets) {                                                                \
+                                       if (__ac_isempty(h->flags, i) && site != h->n_buckets) x = site; \
+                                       else x = i;                                                                                     \
+                               }                                                                                                               \
+                       }                                                                                                                       \
+               }                                                                                                                               \
+               if (__ac_isempty(h->flags, x)) { /* not present at all */               \
+                       h->keys[x] = key;                                                                                       \
+                       __ac_set_isboth_false(h->flags, x);                                                     \
+                       ++h->size; ++h->n_occupied;                                                                     \
+                       *ret = 1;                                                                                                       \
+               } else if (__ac_isdel(h->flags, x)) { /* deleted */                             \
+                       h->keys[x] = key;                                                                                       \
+                       __ac_set_isboth_false(h->flags, x);                                                     \
+                       ++h->size;                                                                                                      \
+                       *ret = 2;                                                                                                       \
+               } else *ret = 0; /* Don't touch h->keys[x] if present and not deleted */ \
+               return x;                                                                                                               \
+       }                                                                                                                                       \
+       SCOPE void kh_del_##name(kh_##name##_t *h, khint_t x)                           \
+       {                                                                                                                                       \
+               if (x != h->n_buckets && !__ac_iseither(h->flags, x)) {                 \
+                       __ac_set_isdel_true(h->flags, x);                                                       \
+                       --h->size;                                                                                                      \
+               }                                                                                                                               \
+       }
+
+#define KHASH_DECLARE(name, khkey_t, khval_t)                                                  \
+       __KHASH_TYPE(name, khkey_t, khval_t)                                                            \
+       __KHASH_PROTOTYPES(name, khkey_t, khval_t)
+
+#define KHASH_INIT2(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \
+       __KHASH_TYPE(name, khkey_t, khval_t)                                                            \
+       __KHASH_IMPL(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal)
+
+#define KHASH_INIT(name, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \
+       KHASH_INIT2(name, static kh_inline, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal)
+
+/* --- BEGIN OF HASH FUNCTIONS --- */
+
+/*! @function
+  @abstract     Integer hash function
+  @param  key   The integer [khint32_t]
+  @return       The hash value [khint_t]
+ */
+#define kh_int_hash_func(key) (khint32_t)(key)
+/*! @function
+  @abstract     Integer comparison function
+ */
+#define kh_int_hash_equal(a, b) ((a) == (b))
+/*! @function
+  @abstract     64-bit integer hash function
+  @param  key   The integer [khint64_t]
+  @return       The hash value [khint_t]
+ */
+#define kh_int64_hash_func(key) (khint32_t)((key)>>33^(key)^(key)<<11)
+/*! @function
+  @abstract     64-bit integer comparison function
+ */
+#define kh_int64_hash_equal(a, b) ((a) == (b))
+/*! @function
+  @abstract     const char* hash function
+  @param  s     Pointer to a null terminated string
+  @return       The hash value
+ */
+static kh_inline khint_t __ac_X31_hash_string(const char *s)
+{
+       khint_t h = (khint_t)*s;
+       if (h) for (++s ; *s; ++s) h = (h << 5) - h + (khint_t)*s;
+       return h;
+}
+/*! @function
+  @abstract     Another interface to const char* hash function
+  @param  key   Pointer to a null terminated string [const char*]
+  @return       The hash value [khint_t]
+ */
+#define kh_str_hash_func(key) __ac_X31_hash_string(key)
+/*! @function
+  @abstract     Const char* comparison function
+ */
+#define kh_str_hash_equal(a, b) (strcmp(a, b) == 0)
+
+static kh_inline khint_t __ac_Wang_hash(khint_t key)
+{
+    key += ~(key << 15);
+    key ^=  (key >> 10);
+    key +=  (key << 3);
+    key ^=  (key >> 6);
+    key += ~(key << 11);
+    key ^=  (key >> 16);
+    return key;
+}
+#define kh_int_hash_func2(k) __ac_Wang_hash((khint_t)key)
+
+/* --- END OF HASH FUNCTIONS --- */
+
+/* Other convenient macros... */
+
+/*!
+  @abstract Type of the hash table.
+  @param  name  Name of the hash table [symbol]
+ */
+#define khash_t(name) kh_##name##_t
+
+/*! @function
+  @abstract     Initiate a hash table.
+  @param  name  Name of the hash table [symbol]
+  @return       Pointer to the hash table [khash_t(name)*]
+ */
+#define kh_init(name) kh_init_##name()
+
+/*! @function
+  @abstract     Destroy a hash table.
+  @param  name  Name of the hash table [symbol]
+  @param  h     Pointer to the hash table [khash_t(name)*]
+ */
+#define kh_destroy(name, h) kh_destroy_##name(h)
+
+/*! @function
+  @abstract     Reset a hash table without deallocating memory.
+  @param  name  Name of the hash table [symbol]
+  @param  h     Pointer to the hash table [khash_t(name)*]
+ */
+#define kh_clear(name, h) kh_clear_##name(h)
+
+/*! @function
+  @abstract     Resize a hash table.
+  @param  name  Name of the hash table [symbol]
+  @param  h     Pointer to the hash table [khash_t(name)*]
+  @param  s     New size [khint_t]
+ */
+#define kh_resize(name, h, s) kh_resize_##name(h, s)
+
+/*! @function
+  @abstract     Insert a key to the hash table.
+  @param  name  Name of the hash table [symbol]
+  @param  h     Pointer to the hash table [khash_t(name)*]
+  @param  k     Key [type of keys]
+  @param  r     Extra return code: -1 if the operation failed;
+                0 if the key is present in the hash table;
+                1 if the bucket is empty (never used); 2 if the element in
+                               the bucket has been deleted [int*]
+  @return       Iterator to the inserted element [khint_t]
+ */
+#define kh_put(name, h, k, r) kh_put_##name(h, k, r)
+
+/*! @function
+  @abstract     Retrieve a key from the hash table.
+  @param  name  Name of the hash table [symbol]
+  @param  h     Pointer to the hash table [khash_t(name)*]
+  @param  k     Key [type of keys]
+  @return       Iterator to the found element, or kh_end(h) if the element is absent [khint_t]
+ */
+#define kh_get(name, h, k) kh_get_##name(h, k)
+
+/*! @function
+  @abstract     Remove a key from the hash table.
+  @param  name  Name of the hash table [symbol]
+  @param  h     Pointer to the hash table [khash_t(name)*]
+  @param  k     Iterator to the element to be deleted [khint_t]
+ */
+#define kh_del(name, h, k) kh_del_##name(h, k)
+
+/*! @function
+  @abstract     Test whether a bucket contains data.
+  @param  h     Pointer to the hash table [khash_t(name)*]
+  @param  x     Iterator to the bucket [khint_t]
+  @return       1 if containing data; 0 otherwise [int]
+ */
+#define kh_exist(h, x) (!__ac_iseither((h)->flags, (x)))
+
+/*! @function
+  @abstract     Get key given an iterator
+  @param  h     Pointer to the hash table [khash_t(name)*]
+  @param  x     Iterator to the bucket [khint_t]
+  @return       Key [type of keys]
+ */
+#define kh_key(h, x) ((h)->keys[x])
+
+/*! @function
+  @abstract     Get value given an iterator
+  @param  h     Pointer to the hash table [khash_t(name)*]
+  @param  x     Iterator to the bucket [khint_t]
+  @return       Value [type of values]
+  @discussion   For hash sets, calling this results in segfault.
+ */
+#define kh_val(h, x) ((h)->vals[x])
+
+/*! @function
+  @abstract     Alias of kh_val()
+ */
+#define kh_value(h, x) ((h)->vals[x])
+
+/*! @function
+  @abstract     Get the start iterator
+  @param  h     Pointer to the hash table [khash_t(name)*]
+  @return       The start iterator [khint_t]
+ */
+#define kh_begin(h) (khint_t)(0)
+
+/*! @function
+  @abstract     Get the end iterator
+  @param  h     Pointer to the hash table [khash_t(name)*]
+  @return       The end iterator [khint_t]
+ */
+#define kh_end(h) ((h)->n_buckets)
+
+/*! @function
+  @abstract     Get the number of elements in the hash table
+  @param  h     Pointer to the hash table [khash_t(name)*]
+  @return       Number of elements in the hash table [khint_t]
+ */
+#define kh_size(h) ((h)->size)
+
+/*! @function
+  @abstract     Get the number of buckets in the hash table
+  @param  h     Pointer to the hash table [khash_t(name)*]
+  @return       Number of buckets in the hash table [khint_t]
+ */
+#define kh_n_buckets(h) ((h)->n_buckets)
+
+/*! @function
+  @abstract     Iterate over the entries in the hash table
+  @param  h     Pointer to the hash table [khash_t(name)*]
+  @param  kvar  Variable to which key will be assigned
+  @param  vvar  Variable to which value will be assigned
+  @param  code  Block of code to execute
+ */
+#define kh_foreach(h, kvar, vvar, code) { khint_t __i;         \
+       for (__i = kh_begin(h); __i != kh_end(h); ++__i) {              \
+               if (!kh_exist(h,__i)) continue;                                         \
+               (kvar) = kh_key(h,__i);                                                         \
+               (vvar) = kh_val(h,__i);                                                         \
+               code;                                                                                           \
+       } }
+
+/*! @function
+  @abstract     Iterate over the values in the hash table
+  @param  h     Pointer to the hash table [khash_t(name)*]
+  @param  vvar  Variable to which value will be assigned
+  @param  code  Block of code to execute
+ */
+#define kh_foreach_value(h, vvar, code) { khint_t __i;         \
+       for (__i = kh_begin(h); __i != kh_end(h); ++__i) {              \
+               if (!kh_exist(h,__i)) continue;                                         \
+               (vvar) = kh_val(h,__i);                                                         \
+               code;                                                                                           \
+       } }
+
+/* More conenient interfaces */
+
+/*! @function
+  @abstract     Instantiate a hash set containing integer keys
+  @param  name  Name of the hash table [symbol]
+ */
+#define KHASH_SET_INIT_INT(name)                                                                               \
+       KHASH_INIT(name, khint32_t, char, 0, kh_int_hash_func, kh_int_hash_equal)
+
+/*! @function
+  @abstract     Instantiate a hash map containing integer keys
+  @param  name  Name of the hash table [symbol]
+  @param  khval_t  Type of values [type]
+ */
+#define KHASH_MAP_INIT_INT(name, khval_t)                                                              \
+       KHASH_INIT(name, khint32_t, khval_t, 1, kh_int_hash_func, kh_int_hash_equal)
+
+/*! @function
+  @abstract     Instantiate a hash map containing 64-bit integer keys
+  @param  name  Name of the hash table [symbol]
+ */
+#define KHASH_SET_INIT_INT64(name)                                                                             \
+       KHASH_INIT(name, khint64_t, char, 0, kh_int64_hash_func, kh_int64_hash_equal)
+
+/*! @function
+  @abstract     Instantiate a hash map containing 64-bit integer keys
+  @param  name  Name of the hash table [symbol]
+  @param  khval_t  Type of values [type]
+ */
+#define KHASH_MAP_INIT_INT64(name, khval_t)                                                            \
+       KHASH_INIT(name, khint64_t, khval_t, 1, kh_int64_hash_func, kh_int64_hash_equal)
+
+typedef const char *kh_cstr_t;
+/*! @function
+  @abstract     Instantiate a hash map containing const char* keys
+  @param  name  Name of the hash table [symbol]
+ */
+#define KHASH_SET_INIT_STR(name)                                                                               \
+       KHASH_INIT(name, kh_cstr_t, char, 0, kh_str_hash_func, kh_str_hash_equal)
+
+/*! @function
+  @abstract     Instantiate a hash map containing const char* keys
+  @param  name  Name of the hash table [symbol]
+  @param  khval_t  Type of values [type]
+ */
+#define KHASH_MAP_INIT_STR(name, khval_t)                                                              \
+       KHASH_INIT(name, kh_cstr_t, khval_t, 1, kh_str_hash_func, kh_str_hash_equal)
+
+#endif /* __AC_KHASH_H */
diff --git a/contrib/libucl/kvec.h b/contrib/libucl/kvec.h
new file mode 100644 (file)
index 0000000..b5cce85
--- /dev/null
@@ -0,0 +1,103 @@
+/* The MIT License
+
+   Copyright (c) 2008, by Attractive Chaos <attractor@live.co.uk>
+
+   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, sublicense, and/or sell copies of the Software, and to
+   permit persons to whom the Software is furnished to do so, subject to
+   the following conditions:
+
+   The above copyright notice and this permission notice shall be
+   included in all copies or substantial portions of the Software.
+
+   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+   NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+   BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+   ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+   CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+   SOFTWARE.
+*/
+
+/*
+  An example:
+
+#include "kvec.h"
+int main() {
+       kvec_t(int) array;
+       kv_init(array);
+       kv_push(int, array, 10); // append
+       kv_a(int, array, 20) = 5; // dynamic
+       kv_A(array, 20) = 4; // static
+       kv_destroy(array);
+       return 0;
+}
+*/
+
+/*
+  2008-09-22 (0.1.0):
+
+       * The initial version.
+
+*/
+
+#ifndef AC_KVEC_H
+#define AC_KVEC_H
+
+#include <stdlib.h>
+
+#define kv_roundup32(x) (--(x), (x)|=(x)>>1, (x)|=(x)>>2, (x)|=(x)>>4, (x)|=(x)>>8, (x)|=(x)>>16, ++(x))
+
+#define kvec_t(type) struct { size_t n, m; type *a; }
+#define kv_init(v) ((v).n = (v).m = 0, (v).a = 0)
+#define kv_destroy(v) free((v).a)
+#define kv_A(v, i) ((v).a[(i)])
+#define kv_pop(v) ((v).a[--(v).n])
+#define kv_size(v) ((v).n)
+#define kv_max(v) ((v).m)
+
+#define kv_resize(type, v, s)  ((v).m = (s), (v).a = (type*)realloc((v).a, sizeof(type) * (v).m))
+#define kv_grow_factor 1.5
+#define kv_grow(type, v)  ((v).m = ((v).m > 1 ? (v).m * kv_grow_factor : 2), \
+               (v).a = (type*)realloc((v).a, sizeof(type) * (v).m))
+
+#define kv_copy(type, v1, v0) do {                                                                                     \
+               if ((v1).m < (v0).n) kv_resize(type, v1, (v0).n);                                       \
+               (v1).n = (v0).n;                                                                                                        \
+               memcpy((v1).a, (v0).a, sizeof(type) * (v0).n);                                          \
+       } while (0)                                                                                                                             \
+
+#define kv_push(type, v, x) do {                                                                                       \
+               if ((v).n == (v).m) {                                                                                           \
+                       kv_grow(type, v);                                                                                               \
+               }                                                                                                                                       \
+               (v).a[(v).n++] = (x);                                                                                           \
+       } while (0)
+
+#define kv_prepend(type, v, x) do {                                                                                    \
+       if ((v).n == (v).m) {                                                                                                   \
+               kv_grow(type, v);                                                                                                       \
+       }                                                                                                                                               \
+       memmove((v).a + 1, (v).a, sizeof(type) * (v).n);                                                        \
+       (v).a[0] = (x);                                                                                                                 \
+       (v).n ++;                                                                                                                               \
+} while (0)
+
+#define kv_concat(type, v1, v0) do {                                                                           \
+       if ((v1).m < (v0).n + (v1).n) kv_resize(type, v1, (v0).n + (v1).n);             \
+               memcpy((v1).a + (v1).n, (v0).a, sizeof(type) * ((v0).n + (v1).n));      \
+               (v1).n = (v0).n + (v1).n;                                                                                       \
+       } while (0)
+
+#define kv_del(type, v, i) do {                                                                                                \
+       if ((i) < (v).n) {                                                                                                              \
+               memmove((v).a + (i), (v).a + ((i) + 1), sizeof(type) * ((v).n - (i) - 1)); \
+               (v).n --;                                                                                                                       \
+       }                                                                                                                                               \
+} while (0)
+
+#endif
diff --git a/contrib/libucl/lua_ucl.c b/contrib/libucl/lua_ucl.c
new file mode 100644 (file)
index 0000000..682b0b5
--- /dev/null
@@ -0,0 +1,820 @@
+/* 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.
+ */
+
+/**
+ * @file lua ucl bindings
+ */
+
+#include "ucl.h"
+#include "ucl_internal.h"
+#include "lua_ucl.h"
+#include <strings.h>
+
+/***
+ * @module ucl
+ * This lua module allows to parse objects from strings and to store data into
+ * ucl objects. It uses `libucl` C library to parse and manipulate with ucl objects.
+ * @example
+local ucl = require("ucl")
+
+local parser = ucl.parser()
+local res,err = parser:parse_string('{key=value}')
+
+if not res then
+       print('parser error: ' .. err)
+else
+       local obj = parser:get_object()
+       local got = ucl.to_format(obj, 'json')
+endif
+
+local table = {
+  str = 'value',
+  num = 100500,
+  null = ucl.null,
+  func = function ()
+    return 'huh'
+  end
+}
+
+print(ucl.to_format(table, 'ucl'))
+-- Output:
+--[[
+num = 100500;
+str = "value";
+null = null;
+func = "huh";
+--]]
+ */
+
+#define PARSER_META "ucl.parser.meta"
+#define EMITTER_META "ucl.emitter.meta"
+#define NULL_META "null.emitter.meta"
+
+static int ucl_object_lua_push_array (lua_State *L, const ucl_object_t *obj);
+static int ucl_object_lua_push_scalar (lua_State *L, const ucl_object_t *obj, bool allow_array);
+static ucl_object_t* ucl_object_lua_fromtable (lua_State *L, int idx);
+static ucl_object_t* ucl_object_lua_fromelt (lua_State *L, int idx);
+
+static void *ucl_null;
+
+/**
+ * Push a single element of an object to lua
+ * @param L
+ * @param key
+ * @param obj
+ */
+static void
+ucl_object_lua_push_element (lua_State *L, const char *key,
+               const ucl_object_t *obj)
+{
+       lua_pushstring (L, key);
+       ucl_object_push_lua (L, obj, true);
+       lua_settable (L, -3);
+}
+
+static void
+lua_ucl_userdata_dtor (void *ud)
+{
+       struct ucl_lua_funcdata *fd = (struct ucl_lua_funcdata *)ud;
+
+       luaL_unref (fd->L, LUA_REGISTRYINDEX, fd->idx);
+       if (fd->ret != NULL) {
+               free (fd->ret);
+       }
+       free (fd);
+}
+
+static const char *
+lua_ucl_userdata_emitter (void *ud)
+{
+       struct ucl_lua_funcdata *fd = (struct ucl_lua_funcdata *)ud;
+       const char *out = "";
+
+       lua_rawgeti (fd->L, LUA_REGISTRYINDEX, fd->idx);
+
+       lua_pcall (fd->L, 0, 1, 0);
+       out = lua_tostring (fd->L, -1);
+
+       if (out != NULL) {
+               /* We need to store temporary string in a more appropriate place */
+               if (fd->ret) {
+                       free (fd->ret);
+               }
+               fd->ret = strdup (out);
+       }
+
+       lua_settop (fd->L, 0);
+
+       return fd->ret;
+}
+
+/**
+ * Push a single object to lua
+ * @param L
+ * @param obj
+ * @return
+ */
+static int
+ucl_object_lua_push_object (lua_State *L, const ucl_object_t *obj,
+               bool allow_array)
+{
+       const ucl_object_t *cur;
+       ucl_object_iter_t it = NULL;
+       int nelt = 0;
+
+       if (allow_array && obj->next != NULL) {
+               /* Actually we need to push this as an array */
+               return ucl_object_lua_push_array (L, obj);
+       }
+
+       /* Optimize allocation by preallocation of table */
+       while (ucl_iterate_object (obj, &it, true) != NULL) {
+               nelt ++;
+       }
+
+       lua_createtable (L, 0, nelt);
+       it = NULL;
+
+       while ((cur = ucl_iterate_object (obj, &it, true)) != NULL) {
+               ucl_object_lua_push_element (L, ucl_object_key (cur), cur);
+       }
+
+       return 1;
+}
+
+/**
+ * Push an array to lua as table indexed by integers
+ * @param L
+ * @param obj
+ * @return
+ */
+static int
+ucl_object_lua_push_array (lua_State *L, const ucl_object_t *obj)
+{
+       const ucl_object_t *cur;
+       int i = 1, nelt = 0;
+
+       /* Optimize allocation by preallocation of table */
+       LL_FOREACH (obj, cur) {
+               nelt ++;
+       }
+
+       lua_createtable (L, nelt, 0);
+
+       LL_FOREACH (obj, cur) {
+               ucl_object_push_lua (L, cur, false);
+               lua_rawseti (L, -2, i);
+               i ++;
+       }
+
+       return 1;
+}
+
+/**
+ * Push a simple object to lua depending on its actual type
+ */
+static int
+ucl_object_lua_push_scalar (lua_State *L, const ucl_object_t *obj,
+               bool allow_array)
+{
+       struct ucl_lua_funcdata *fd;
+
+       if (allow_array && obj->next != NULL) {
+               /* Actually we need to push this as an array */
+               return ucl_object_lua_push_array (L, obj);
+       }
+
+       switch (obj->type) {
+       case UCL_BOOLEAN:
+               lua_pushboolean (L, ucl_obj_toboolean (obj));
+               break;
+       case UCL_STRING:
+               lua_pushstring (L, ucl_obj_tostring (obj));
+               break;
+       case UCL_INT:
+#if LUA_VERSION_NUM >= 501
+               lua_pushinteger (L, ucl_obj_toint (obj));
+#else
+               lua_pushnumber (L, ucl_obj_toint (obj));
+#endif
+               break;
+       case UCL_FLOAT:
+       case UCL_TIME:
+               lua_pushnumber (L, ucl_obj_todouble (obj));
+               break;
+       case UCL_NULL:
+               lua_getfield (L, LUA_REGISTRYINDEX, "ucl.null");
+               break;
+       case UCL_USERDATA:
+               fd = (struct ucl_lua_funcdata *)obj->value.ud;
+               lua_rawgeti (L, LUA_REGISTRYINDEX, fd->idx);
+               break;
+       default:
+               lua_pushnil (L);
+               break;
+       }
+
+       return 1;
+}
+
+/***
+ * @function ucl_object_push_lua(L, obj, allow_array)
+ * This is a `C` function to push `UCL` object as lua variable. This function
+ * converts `obj` to lua representation using the following conversions:
+ *
+ * - *scalar* values are directly presented by lua objects
+ * - *userdata* values are converted to lua function objects using `LUA_REGISTRYINDEX`,
+ * this can be used to pass functions from lua to c and vice-versa
+ * - *arrays* are converted to lua tables with numeric indicies suitable for `ipairs` iterations
+ * - *objects* are converted to lua tables with string indicies
+ * @param {lua_State} L lua state pointer
+ * @param {ucl_object_t} obj object to push
+ * @param {bool} allow_array expand implicit arrays (should be true for all but partial arrays)
+ * @return {int} `1` if an object is pushed to lua
+ */
+int
+ucl_object_push_lua (lua_State *L, const ucl_object_t *obj, bool allow_array)
+{
+       switch (obj->type) {
+       case UCL_OBJECT:
+               return ucl_object_lua_push_object (L, obj, allow_array);
+       case UCL_ARRAY:
+               return ucl_object_lua_push_array (L, obj->value.av);
+       default:
+               return ucl_object_lua_push_scalar (L, obj, allow_array);
+       }
+}
+
+/**
+ * Parse lua table into object top
+ * @param L
+ * @param top
+ * @param idx
+ */
+static ucl_object_t *
+ucl_object_lua_fromtable (lua_State *L, int idx)
+{
+       ucl_object_t *obj, *top = NULL;
+       size_t keylen;
+       const char *k;
+       bool is_array = true;
+       int max = INT_MIN;
+
+       if (idx < 0) {
+               /* For negative indicies we want to invert them */
+               idx = lua_gettop (L) + idx + 1;
+       }
+       /* Check for array */
+       lua_pushnil (L);
+       while (lua_next (L, idx) != 0) {
+               if (lua_type (L, -2) == LUA_TNUMBER) {
+                       double num = lua_tonumber (L, -2);
+                       if (num == (int)num) {
+                               if (num > max) {
+                                       max = num;
+                               }
+                       }
+                       else {
+                               /* Keys are not integer */
+                               lua_pop (L, 2);
+                               is_array = false;
+                               break;
+                       }
+               }
+               else {
+                       /* Keys are not numeric */
+                       lua_pop (L, 2);
+                       is_array = false;
+                       break;
+               }
+               lua_pop (L, 1);
+       }
+
+       /* Table iterate */
+       if (is_array) {
+               int i;
+
+               top = ucl_object_typed_new (UCL_ARRAY);
+               for (i = 1; i <= max; i ++) {
+                       lua_pushinteger (L, i);
+                       lua_gettable (L, idx);
+                       obj = ucl_object_lua_fromelt (L, lua_gettop (L));
+                       if (obj != NULL) {
+                               ucl_array_append (top, obj);
+                       }
+               }
+       }
+       else {
+               lua_pushnil (L);
+               top = ucl_object_typed_new (UCL_OBJECT);
+               while (lua_next (L, idx) != 0) {
+                       /* copy key to avoid modifications */
+                       k = lua_tolstring (L, -2, &keylen);
+                       obj = ucl_object_lua_fromelt (L, lua_gettop (L));
+
+                       if (obj != NULL) {
+                               ucl_object_insert_key (top, obj, k, keylen, true);
+                       }
+                       lua_pop (L, 1);
+               }
+       }
+
+       return top;
+}
+
+/**
+ * Get a single element from lua to object obj
+ * @param L
+ * @param obj
+ * @param idx
+ */
+static ucl_object_t *
+ucl_object_lua_fromelt (lua_State *L, int idx)
+{
+       int type;
+       double num;
+       ucl_object_t *obj = NULL;
+       struct ucl_lua_funcdata *fd;
+
+       type = lua_type (L, idx);
+
+       switch (type) {
+       case LUA_TSTRING:
+               obj = ucl_object_fromstring_common (lua_tostring (L, idx), 0, 0);
+               break;
+       case LUA_TNUMBER:
+               num = lua_tonumber (L, idx);
+               if (num == (int64_t)num) {
+                       obj = ucl_object_fromint (num);
+               }
+               else {
+                       obj = ucl_object_fromdouble (num);
+               }
+               break;
+       case LUA_TBOOLEAN:
+               obj = ucl_object_frombool (lua_toboolean (L, idx));
+               break;
+       case LUA_TUSERDATA:
+               if (lua_topointer (L, idx) == ucl_null) {
+                       obj = ucl_object_typed_new (UCL_NULL);
+               }
+               break;
+       case LUA_TTABLE:
+       case LUA_TFUNCTION:
+       case LUA_TTHREAD:
+               if (luaL_getmetafield (L, idx, "__gen_ucl")) {
+                       if (lua_isfunction (L, -1)) {
+                               lua_settop (L, 3); /* gen, obj, func */
+                               lua_insert (L, 1); /* func, gen, obj */
+                               lua_insert (L, 2); /* func, obj, gen */
+                               lua_call(L, 2, 1);
+                               obj = ucl_object_lua_fromelt (L, 1);
+                       }
+                       lua_pop (L, 2);
+               }
+               else {
+                       if (type == LUA_TTABLE) {
+                               obj = ucl_object_lua_fromtable (L, idx);
+                       }
+                       else if (type == LUA_TFUNCTION) {
+                               fd = malloc (sizeof (*fd));
+                               if (fd != NULL) {
+                                       lua_pushvalue (L, idx);
+                                       fd->L = L;
+                                       fd->ret = NULL;
+                                       fd->idx = luaL_ref (L, LUA_REGISTRYINDEX);
+
+                                       obj = ucl_object_new_userdata (lua_ucl_userdata_dtor,
+                                                       lua_ucl_userdata_emitter);
+                                       obj->type = UCL_USERDATA;
+                                       obj->value.ud = (void *)fd;
+                               }
+                       }
+               }
+               break;
+       }
+
+       return obj;
+}
+
+/**
+ * @function ucl_object_lua_import(L, idx)
+ * Extracts ucl object from lua variable at `idx` position,
+ * @see ucl_object_push_lua for conversion definitions
+ * @param {lua_state} L lua state machine pointer
+ * @param {int} idx index where the source variable is placed
+ * @return {ucl_object_t} new ucl object extracted from lua variable. Reference count of this object is 1,
+ * this object thus needs to be unref'ed after usage.
+ */
+ucl_object_t *
+ucl_object_lua_import (lua_State *L, int idx)
+{
+       ucl_object_t *obj;
+       int t;
+
+       t = lua_type (L, idx);
+       switch (t) {
+       case LUA_TTABLE:
+               obj = ucl_object_lua_fromtable (L, idx);
+               break;
+       default:
+               obj = ucl_object_lua_fromelt (L, idx);
+               break;
+       }
+
+       return obj;
+}
+
+static int
+lua_ucl_parser_init (lua_State *L)
+{
+       struct ucl_parser *parser, **pparser;
+       int flags = 0;
+
+       if (lua_gettop (L) >= 1) {
+               flags = lua_tonumber (L, 1);
+       }
+
+       parser = ucl_parser_new (flags);
+       if (parser == NULL) {
+               lua_pushnil (L);
+       }
+
+       pparser = lua_newuserdata (L, sizeof (parser));
+       *pparser = parser;
+       luaL_getmetatable (L, PARSER_META);
+       lua_setmetatable (L, -2);
+
+       return 1;
+}
+
+static struct ucl_parser *
+lua_ucl_parser_get (lua_State *L, int index)
+{
+       return *((struct ucl_parser **) luaL_checkudata(L, index, PARSER_META));
+}
+
+/***
+ * @method parser:parse_file(name)
+ * Parse UCL object from file.
+ * @param {string} name filename to parse
+ * @return {bool[, string]} if res is `true` then file has been parsed successfully, otherwise an error string is also returned
+@example
+local parser = ucl.parser()
+local res,err = parser:parse_file('/some/file.conf')
+
+if not res then
+       print('parser error: ' .. err)
+else
+       -- Do something with object
+end
+ */
+static int
+lua_ucl_parser_parse_file (lua_State *L)
+{
+       struct ucl_parser *parser;
+       const char *file;
+       int ret = 2;
+
+       parser = lua_ucl_parser_get (L, 1);
+       file = luaL_checkstring (L, 2);
+
+       if (parser != NULL && file != NULL) {
+               if (ucl_parser_add_file (parser, file)) {
+                       lua_pushboolean (L, true);
+                       ret = 1;
+               }
+               else {
+                       lua_pushboolean (L, false);
+                       lua_pushstring (L, ucl_parser_get_error (parser));
+               }
+       }
+       else {
+               lua_pushboolean (L, false);
+               lua_pushstring (L, "invalid arguments");
+       }
+
+       return ret;
+}
+
+/***
+ * @method parser:parse_string(input)
+ * Parse UCL object from file.
+ * @param {string} input string to parse
+ * @return {bool[, string]} if res is `true` then file has been parsed successfully, otherwise an error string is also returned
+ */
+static int
+lua_ucl_parser_parse_string (lua_State *L)
+{
+       struct ucl_parser *parser;
+       const char *string;
+       size_t llen;
+       int ret = 2;
+
+       parser = lua_ucl_parser_get (L, 1);
+       string = luaL_checklstring (L, 2, &llen);
+
+       if (parser != NULL && string != NULL) {
+               if (ucl_parser_add_chunk (parser, (const unsigned char *)string, llen)) {
+                       lua_pushboolean (L, true);
+                       ret = 1;
+               }
+               else {
+                       lua_pushboolean (L, false);
+                       lua_pushstring (L, ucl_parser_get_error (parser));
+               }
+       }
+       else {
+               lua_pushboolean (L, false);
+               lua_pushstring (L, "invalid arguments");
+       }
+
+       return ret;
+}
+
+/***
+ * @method parser:get_object()
+ * Get top object from parser and export it to lua representation.
+ * @return {variant or nil} ucl object as lua native variable
+ */
+static int
+lua_ucl_parser_get_object (lua_State *L)
+{
+       struct ucl_parser *parser;
+       ucl_object_t *obj;
+       int ret = 1;
+
+       parser = lua_ucl_parser_get (L, 1);
+       obj = ucl_parser_get_object (parser);
+
+       if (obj != NULL) {
+               ret = ucl_object_push_lua (L, obj, false);
+               /* no need to keep reference */
+               ucl_object_unref (obj);
+       }
+       else {
+               lua_pushnil (L);
+       }
+
+       return ret;
+}
+
+static int
+lua_ucl_parser_gc (lua_State *L)
+{
+       struct ucl_parser *parser;
+
+       parser = lua_ucl_parser_get (L, 1);
+       ucl_parser_free (parser);
+
+       return 0;
+}
+
+static void
+lua_ucl_parser_mt (lua_State *L)
+{
+       luaL_newmetatable (L, PARSER_META);
+
+       lua_pushvalue(L, -1);
+       lua_setfield(L, -2, "__index");
+
+       lua_pushcfunction (L, lua_ucl_parser_gc);
+       lua_setfield (L, -2, "__gc");
+
+       lua_pushcfunction (L, lua_ucl_parser_parse_file);
+       lua_setfield (L, -2, "parse_file");
+
+       lua_pushcfunction (L, lua_ucl_parser_parse_string);
+       lua_setfield (L, -2, "parse_string");
+
+       lua_pushcfunction (L, lua_ucl_parser_get_object);
+       lua_setfield (L, -2, "get_object");
+
+       lua_pop (L, 1);
+}
+
+static int
+lua_ucl_to_string (lua_State *L, const ucl_object_t *obj, enum ucl_emitter type)
+{
+       unsigned char *result;
+
+       result = ucl_object_emit (obj, type);
+
+       if (result != NULL) {
+               lua_pushstring (L, (const char *)result);
+               free (result);
+       }
+       else {
+               lua_pushnil (L);
+       }
+
+       return 1;
+}
+
+static int
+lua_ucl_to_json (lua_State *L)
+{
+       ucl_object_t *obj;
+       int format = UCL_EMIT_JSON;
+
+       if (lua_gettop (L) > 1) {
+               if (lua_toboolean (L, 2)) {
+                       format = UCL_EMIT_JSON_COMPACT;
+               }
+       }
+
+       obj = ucl_object_lua_import (L, 1);
+       if (obj != NULL) {
+               lua_ucl_to_string (L, obj, format);
+               ucl_object_unref (obj);
+       }
+       else {
+               lua_pushnil (L);
+       }
+
+       return 1;
+}
+
+static int
+lua_ucl_to_config (lua_State *L)
+{
+       ucl_object_t *obj;
+
+       obj = ucl_object_lua_import (L, 1);
+       if (obj != NULL) {
+               lua_ucl_to_string (L, obj, UCL_EMIT_CONFIG);
+               ucl_object_unref (obj);
+       }
+       else {
+               lua_pushnil (L);
+       }
+
+       return 1;
+}
+
+/***
+ * @function ucl.to_format(var, format)
+ * Converts lua variable `var` to the specified `format`. Formats supported are:
+ *
+ * - `json` - fine printed json
+ * - `json-compact` - compacted json
+ * - `config` - fine printed configuration
+ * - `ucl` - same as `config`
+ * - `yaml` - embedded yaml
+ *
+ * If `var` contains function, they are called during output formatting and if
+ * they return string value, then this value is used for ouptut.
+ * @param {variant} var any sort of lua variable (if userdata then metafield `__to_ucl` is searched for output)
+ * @param {string} format any available format
+ * @return {string} string representation of `var` in the specific `format`.
+ * @example
+local table = {
+  str = 'value',
+  num = 100500,
+  null = ucl.null,
+  func = function ()
+    return 'huh'
+  end
+}
+
+print(ucl.to_format(table, 'ucl'))
+-- Output:
+--[[
+num = 100500;
+str = "value";
+null = null;
+func = "huh";
+--]]
+ */
+static int
+lua_ucl_to_format (lua_State *L)
+{
+       ucl_object_t *obj;
+       int format = UCL_EMIT_JSON;
+
+       if (lua_gettop (L) > 1) {
+               if (lua_type (L, 2) == LUA_TNUMBER) {
+                       format = lua_tonumber (L, 2);
+                       if (format < 0 || format >= UCL_EMIT_YAML) {
+                               lua_pushnil (L);
+                               return 1;
+                       }
+               }
+               else if (lua_type (L, 2) == LUA_TSTRING) {
+                       const char *strtype = lua_tostring (L, 2);
+
+                       if (strcasecmp (strtype, "json") == 0) {
+                               format = UCL_EMIT_JSON;
+                       }
+                       else if (strcasecmp (strtype, "json-compact") == 0) {
+                               format = UCL_EMIT_JSON_COMPACT;
+                       }
+                       else if (strcasecmp (strtype, "yaml") == 0) {
+                               format = UCL_EMIT_YAML;
+                       }
+                       else if (strcasecmp (strtype, "config") == 0 ||
+                               strcasecmp (strtype, "ucl") == 0) {
+                               format = UCL_EMIT_CONFIG;
+                       }
+               }
+       }
+
+       obj = ucl_object_lua_import (L, 1);
+       if (obj != NULL) {
+               lua_ucl_to_string (L, obj, format);
+               ucl_object_unref (obj);
+       }
+       else {
+               lua_pushnil (L);
+       }
+
+       return 1;
+}
+
+static int
+lua_ucl_null_tostring (lua_State* L)
+{
+       lua_pushstring (L, "null");
+       return 1;
+}
+
+static void
+lua_ucl_null_mt (lua_State *L)
+{
+       luaL_newmetatable (L, NULL_META);
+
+       lua_pushcfunction (L, lua_ucl_null_tostring);
+       lua_setfield (L, -2, "__tostring");
+
+       lua_pop (L, 1);
+}
+
+int
+luaopen_ucl (lua_State *L)
+{
+       lua_ucl_parser_mt (L);
+       lua_ucl_null_mt (L);
+
+       /* Create the refs weak table: */
+       lua_createtable (L, 0, 2);
+       lua_pushliteral (L, "v"); /* tbl, "v" */
+       lua_setfield (L, -2, "__mode");
+       lua_pushvalue (L, -1); /* tbl, tbl */
+       lua_setmetatable (L, -2); /* tbl */
+       lua_setfield (L, LUA_REGISTRYINDEX, "ucl.refs");
+
+       lua_newtable (L);
+
+       lua_pushcfunction (L, lua_ucl_parser_init);
+       lua_setfield (L, -2, "parser");
+
+       lua_pushcfunction (L, lua_ucl_to_json);
+       lua_setfield (L, -2, "to_json");
+
+       lua_pushcfunction (L, lua_ucl_to_config);
+       lua_setfield (L, -2, "to_config");
+
+       lua_pushcfunction (L, lua_ucl_to_format);
+       lua_setfield (L, -2, "to_format");
+
+       ucl_null = lua_newuserdata (L, 0);
+       luaL_getmetatable (L, NULL_META);
+       lua_setmetatable (L, -2);
+
+       lua_pushvalue (L, -1);
+       lua_setfield (L, LUA_REGISTRYINDEX, "ucl.null");
+
+       lua_setfield (L, -2, "null");
+
+       return 1;
+}
+
+struct ucl_lua_funcdata*
+ucl_object_toclosure (const ucl_object_t *obj)
+{
+       if (obj == NULL || obj->type != UCL_USERDATA) {
+               return NULL;
+       }
+
+       return (struct ucl_lua_funcdata*)obj->value.ud;
+}
diff --git a/contrib/libucl/lua_ucl.h b/contrib/libucl/lua_ucl.h
new file mode 100644 (file)
index 0000000..38e74d3
--- /dev/null
@@ -0,0 +1,69 @@
+/* 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.
+ */
+#ifndef LUA_UCL_H_
+#define LUA_UCL_H_
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <lua.h>
+#include <lauxlib.h>
+#include <lualib.h>
+#include "ucl.h"
+
+/**
+ * Closure structure for lua function storing inside UCL
+ */
+struct ucl_lua_funcdata {
+       lua_State *L;
+       int idx;
+       char *ret;
+};
+
+/**
+ * Initialize lua UCL API
+ */
+UCL_EXTERN int luaopen_ucl (lua_State *L);
+
+/**
+ * Import UCL object from lua state
+ * @param L lua state
+ * @param idx index of object at the lua stack to convert to UCL
+ * @return new UCL object or NULL, the caller should unref object after using
+ */
+UCL_EXTERN ucl_object_t* ucl_object_lua_import (lua_State *L, int idx);
+
+/**
+ * Push an object to lua
+ * @param L lua state
+ * @param obj object to push
+ * @param allow_array traverse over implicit arrays
+ */
+UCL_EXTERN int ucl_object_push_lua (lua_State *L,
+               const ucl_object_t *obj, bool allow_array);
+
+UCL_EXTERN struct ucl_lua_funcdata* ucl_object_toclosure (
+               const ucl_object_t *obj);
+
+#endif /* LUA_UCL_H_ */
diff --git a/contrib/libucl/tree.h b/contrib/libucl/tree.h
new file mode 100644 (file)
index 0000000..cee9373
--- /dev/null
@@ -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/ucl.h b/contrib/libucl/ucl.h
new file mode 100644 (file)
index 0000000..823ac8d
--- /dev/null
@@ -0,0 +1,1144 @@
+/* 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_H_
+#define UCL_H_
+
+#include <string.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdarg.h>
+#include <stdio.h>
+
+#ifdef _WIN32
+# define UCL_EXTERN __declspec(dllexport)
+#else
+# define UCL_EXTERN
+#endif
+
+/**
+ * @mainpage
+ * This is a reference manual for UCL API. You may find the description of UCL format by following this
+ * [github repository](https://github.com/vstakhov/libucl).
+ *
+ * This manual has several main sections:
+ *  - @ref structures
+ *  - @ref utils
+ *  - @ref parser
+ *  - @ref emitter
+ */
+
+/**
+ * @file ucl.h
+ * @brief UCL parsing and emitting functions
+ *
+ * UCL is universal configuration language, which is a form of
+ * JSON with less strict rules that make it more comfortable for
+ * using as a configuration language
+ */
+#ifdef  __cplusplus
+extern "C" {
+#endif
+/*
+ * Memory allocation utilities
+ * UCL_ALLOC(size) - allocate memory for UCL
+ * UCL_FREE(size, ptr) - free memory of specified size at ptr
+ * Default: malloc and free
+ */
+#ifndef UCL_ALLOC
+#define UCL_ALLOC(size) malloc(size)
+#endif
+#ifndef UCL_FREE
+#define UCL_FREE(size, ptr) free(ptr)
+#endif
+
+#if    __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)
+#define UCL_WARN_UNUSED_RESULT               \
+  __attribute__((warn_unused_result))
+#else
+#define UCL_WARN_UNUSED_RESULT
+#endif
+
+#ifdef __GNUC__
+#define UCL_DEPRECATED(func) func __attribute__ ((deprecated))
+#elif defined(_MSC_VER)
+#define UCL_DEPRECATED(func) __declspec(deprecated) func
+#else
+#define UCL_DEPRECATED(func) func
+#endif
+
+/**
+ * @defgroup structures Structures and types
+ * UCL defines several enumeration types used for error reporting or specifying flags and attributes.
+ *
+ * @{
+ */
+
+/**
+ * The common error codes returned by ucl parser
+ */
+typedef enum ucl_error {
+       UCL_EOK = 0, /**< No error */
+       UCL_ESYNTAX, /**< Syntax error occurred during parsing */
+       UCL_EIO, /**< IO error occurred during parsing */
+       UCL_ESTATE, /**< Invalid state machine state */
+       UCL_ENESTED, /**< Input has too many recursion levels */
+       UCL_EMACRO, /**< Error processing a macro */
+       UCL_EINTERNAL, /**< Internal unclassified error */
+       UCL_ESSL /**< SSL error */
+} ucl_error_t;
+
+/**
+ * #ucl_object_t may have one of specified types, some types are compatible with each other and some are not.
+ * For example, you can always convert #UCL_TIME to #UCL_FLOAT. Also you can convert #UCL_FLOAT to #UCL_INTEGER
+ * by loosing floating point. Every object may be converted to a string by #ucl_object_tostring_forced() function.
+ *
+ */
+typedef enum ucl_type {
+       UCL_OBJECT = 0, /**< UCL object - key/value pairs */
+       UCL_ARRAY, /**< UCL array */
+       UCL_INT, /**< Integer number */
+       UCL_FLOAT, /**< Floating point number */
+       UCL_STRING, /**< Null terminated string */
+       UCL_BOOLEAN, /**< Boolean value */
+       UCL_TIME, /**< Time value (floating point number of seconds) */
+       UCL_USERDATA, /**< Opaque userdata pointer (may be used in macros) */
+       UCL_NULL /**< Null value */
+} ucl_type_t;
+
+/**
+ * You can use one of these types to serialise #ucl_object_t by using ucl_object_emit().
+ */
+typedef enum ucl_emitter {
+       UCL_EMIT_JSON = 0, /**< Emit fine formatted JSON */
+       UCL_EMIT_JSON_COMPACT, /**< Emit compacted JSON */
+       UCL_EMIT_CONFIG, /**< Emit human readable config format */
+       UCL_EMIT_YAML /**< Emit embedded YAML format */
+} ucl_emitter_t;
+
+/**
+ * These flags defines parser behaviour. If you specify #UCL_PARSER_ZEROCOPY you must ensure
+ * that the input memory is not freed if an object is in use. Moreover, if you want to use
+ * zero-terminated keys and string values then you should not use zero-copy mode, as in this case
+ * UCL still has to perform copying implicitly.
+ */
+typedef enum ucl_parser_flags {
+       UCL_PARSER_KEY_LOWERCASE = 0x1, /**< Convert all keys to lower case */
+       UCL_PARSER_ZEROCOPY = 0x2, /**< Parse input in zero-copy mode if possible */
+       UCL_PARSER_NO_TIME = 0x4, /**< Do not parse time and treat time values as strings */
+       UCL_PARSER_NO_IMPLICIT_ARRAYS = 0x8 /** Create explicit arrays instead of implicit ones */
+} ucl_parser_flags_t;
+
+/**
+ * String conversion flags, that are used in #ucl_object_fromstring_common function.
+ */
+typedef enum ucl_string_flags {
+       UCL_STRING_ESCAPE = 0x1,  /**< Perform JSON escape */
+       UCL_STRING_TRIM = 0x2,    /**< Trim leading and trailing whitespaces */
+       UCL_STRING_PARSE_BOOLEAN = 0x4,    /**< Parse passed string and detect boolean */
+       UCL_STRING_PARSE_INT = 0x8,    /**< Parse passed string and detect integer number */
+       UCL_STRING_PARSE_DOUBLE = 0x10,    /**< Parse passed string and detect integer or float number */
+       UCL_STRING_PARSE_TIME = 0x20, /**< Parse time strings */
+       UCL_STRING_PARSE_NUMBER =  UCL_STRING_PARSE_INT|UCL_STRING_PARSE_DOUBLE|UCL_STRING_PARSE_TIME,  /**<
+                                                                       Parse passed string and detect number */
+       UCL_STRING_PARSE =  UCL_STRING_PARSE_BOOLEAN|UCL_STRING_PARSE_NUMBER,   /**<
+                                                                       Parse passed string (and detect booleans and numbers) */
+       UCL_STRING_PARSE_BYTES = 0x40  /**< Treat numbers as bytes */
+} ucl_string_flags_t;
+
+/**
+ * Basic flags for an object
+ */
+typedef enum ucl_object_flags {
+       UCL_OBJECT_ALLOCATED_KEY = 0x1, /**< An object has key allocated internally */
+       UCL_OBJECT_ALLOCATED_VALUE = 0x2, /**< An object has a string value allocated internally */
+       UCL_OBJECT_NEED_KEY_ESCAPE = 0x4, /**< The key of an object need to be escaped on output */
+       UCL_OBJECT_EPHEMERAL = 0x8, /**< Temporary object that does not need to be freed really */
+       UCL_OBJECT_MULTILINE = 0x10, /**< String should be displayed as multiline string */
+       UCL_OBJECT_MULTIVALUE = 0x20 /**< Object is a key with multiple values */
+} ucl_object_flags_t;
+
+/**
+ * UCL object structure. Please mention that the most of fields should not be touched by
+ * UCL users. In future, this structure may be converted to private one.
+ */
+typedef struct ucl_object_s {
+       /**
+        * Variant value type
+        */
+       union {
+               int64_t iv;                                                     /**< Int value of an object */
+               const char *sv;                                 /**< String value of an object */
+               double dv;                                                      /**< Double value of an object */
+               void *av;                                                       /**< Array                                      */
+               void *ov;                                                       /**< Object                                     */
+               void* ud;                                                       /**< Opaque user data           */
+       } value;
+       const char *key;                                                /**< Key of an object           */
+       struct ucl_object_s *next;                              /**< Array handle                       */
+       struct ucl_object_s *prev;                              /**< Array handle                       */
+       uint32_t keylen;                                                /**< Lenght of a key            */
+       uint32_t len;                                                   /**< Size of an object          */
+       uint32_t ref;                                                   /**< Reference count            */
+       uint16_t flags;                                                 /**< Object flags                       */
+       uint16_t type;                                                  /**< Real type                          */
+       unsigned char* trash_stack[2];                  /**< Pointer to allocated chunks */
+} ucl_object_t;
+
+/**
+ * Destructor type for userdata objects
+ * @param ud user specified data pointer
+ */
+typedef void (*ucl_userdata_dtor)(void *ud);
+typedef const char* (*ucl_userdata_emitter)(void *ud);
+
+/** @} */
+
+/**
+ * @defgroup utils Utility functions
+ * A number of utility functions simplify handling of UCL objects
+ *
+ * @{
+ */
+/**
+ * Copy and return a key of an object, returned key is zero-terminated
+ * @param obj CL object
+ * @return zero terminated key
+ */
+UCL_EXTERN char* ucl_copy_key_trash (const ucl_object_t *obj);
+
+/**
+ * Copy and return a string value of an object, returned key is zero-terminated
+ * @param obj CL object
+ * @return zero terminated string representation of object value
+ */
+UCL_EXTERN char* ucl_copy_value_trash (const ucl_object_t *obj);
+
+/**
+ * Creates a new object
+ * @return new object
+ */
+UCL_EXTERN ucl_object_t* ucl_object_new (void) UCL_WARN_UNUSED_RESULT;
+
+/**
+ * Create new object with type specified
+ * @param type type of a new object
+ * @return new object
+ */
+UCL_EXTERN ucl_object_t* ucl_object_typed_new (ucl_type_t type) UCL_WARN_UNUSED_RESULT;
+
+/**
+ * Create new object with type and priority specified
+ * @param type type of a new object
+ * @param priority priority of an object
+ * @return new object
+ */
+UCL_EXTERN ucl_object_t* ucl_object_new_full (ucl_type_t type, unsigned priority)
+       UCL_WARN_UNUSED_RESULT;
+
+/**
+ * Create new object with userdata dtor
+ * @param dtor destructor function
+ * @return new object
+ */
+UCL_EXTERN ucl_object_t* ucl_object_new_userdata (ucl_userdata_dtor dtor,
+               ucl_userdata_emitter emitter) UCL_WARN_UNUSED_RESULT;
+
+/**
+ * Perform deep copy of an object copying everything
+ * @param other object to copy
+ * @return new object with refcount equal to 1
+ */
+UCL_EXTERN ucl_object_t * ucl_object_copy (const ucl_object_t *other)
+       UCL_WARN_UNUSED_RESULT;
+
+/**
+ * Return the type of an object
+ * @return the object type
+ */
+UCL_EXTERN ucl_type_t ucl_object_type (const ucl_object_t *obj);
+
+/**
+ * Convert any string to an ucl object making the specified transformations
+ * @param str fixed size or NULL terminated string
+ * @param len length (if len is zero, than str is treated as NULL terminated)
+ * @param flags conversion flags
+ * @return new object
+ */
+UCL_EXTERN ucl_object_t * ucl_object_fromstring_common (const char *str, size_t len,
+               enum ucl_string_flags flags) UCL_WARN_UNUSED_RESULT;
+
+/**
+ * Create a UCL object from the specified string
+ * @param str NULL terminated string, will be json escaped
+ * @return new object
+ */
+UCL_EXTERN ucl_object_t *ucl_object_fromstring (const char *str) UCL_WARN_UNUSED_RESULT;
+
+/**
+ * Create a UCL object from the specified string
+ * @param str fixed size string, will be json escaped
+ * @param len length of a string
+ * @return new object
+ */
+UCL_EXTERN ucl_object_t *ucl_object_fromlstring (const char *str,
+               size_t len) UCL_WARN_UNUSED_RESULT;
+
+/**
+ * Create an object from an integer number
+ * @param iv number
+ * @return new object
+ */
+UCL_EXTERN ucl_object_t* ucl_object_fromint (int64_t iv) UCL_WARN_UNUSED_RESULT;
+
+/**
+ * Create an object from a float number
+ * @param dv number
+ * @return new object
+ */
+UCL_EXTERN ucl_object_t* ucl_object_fromdouble (double dv) UCL_WARN_UNUSED_RESULT;
+
+/**
+ * Create an object from a boolean
+ * @param bv bool value
+ * @return new object
+ */
+UCL_EXTERN ucl_object_t* ucl_object_frombool (bool bv) UCL_WARN_UNUSED_RESULT;
+
+/**
+ * Insert a object 'elt' to the hash 'top' and associate it with key 'key'
+ * @param top destination object (must be of type UCL_OBJECT)
+ * @param elt element to insert (must NOT be NULL)
+ * @param key key to associate with this object (either const or preallocated)
+ * @param keylen length of the key (or 0 for NULL terminated keys)
+ * @param copy_key make an internal copy of key
+ * @return true if key has been inserted
+ */
+UCL_EXTERN bool ucl_object_insert_key (ucl_object_t *top, ucl_object_t *elt,
+               const char *key, size_t keylen, bool copy_key);
+
+/**
+ * Replace a object 'elt' to the hash 'top' and associate it with key 'key', old object will be unrefed,
+ * if no object has been found this function works like ucl_object_insert_key()
+ * @param top destination object (must be of type UCL_OBJECT)
+ * @param elt element to insert (must NOT be NULL)
+ * @param key key to associate with this object (either const or preallocated)
+ * @param keylen length of the key (or 0 for NULL terminated keys)
+ * @param copy_key make an internal copy of key
+ * @return true if key has been inserted
+ */
+UCL_EXTERN bool ucl_object_replace_key (ucl_object_t *top, ucl_object_t *elt,
+               const char *key, size_t keylen, bool copy_key);
+
+/**
+ * Merge the keys from one object to another object. Overwrite on conflict
+ * @param top destination object (must be of type UCL_OBJECT)
+ * @param elt element to insert (must be of type UCL_OBJECT)
+ * @param copy copy rather than reference the elements
+ * @return true if all keys have been merged
+ */
+UCL_EXTERN bool ucl_object_merge (ucl_object_t *top, ucl_object_t *elt, bool copy);
+
+/**
+ * Delete a object associated with key 'key', old object will be unrefered,
+ * @param top object
+ * @param key key associated to the object to remove
+ * @param keylen length of the key (or 0 for NULL terminated keys)
+ */
+UCL_EXTERN bool ucl_object_delete_keyl (ucl_object_t *top,
+               const char *key, size_t keylen);
+
+/**
+ * Delete a object associated with key 'key', old object will be unrefered,
+ * @param top object
+ * @param key key associated to the object to remove
+ */
+UCL_EXTERN bool ucl_object_delete_key (ucl_object_t *top,
+               const char *key);
+
+
+/**
+ * Removes `key` from `top` object, returning the object that was removed. This
+ * object is not released, caller must unref the returned object when it is no
+ * longer needed.
+ * @param top object
+ * @param key key to remove
+ * @param keylen length of the key (or 0 for NULL terminated keys)
+ * @return removed object or NULL if object has not been found
+ */
+UCL_EXTERN ucl_object_t* ucl_object_pop_keyl (ucl_object_t *top, const char *key,
+               size_t keylen) UCL_WARN_UNUSED_RESULT;
+
+/**
+ * Removes `key` from `top` object returning the object that was removed. This
+ * object is not released, caller must unref the returned object when it is no
+ * longer needed.
+ * @param top object
+ * @param key key to remove
+ * @return removed object or NULL if object has not been found
+ */
+UCL_EXTERN ucl_object_t* ucl_object_pop_key (ucl_object_t *top, const char *key)
+       UCL_WARN_UNUSED_RESULT;
+
+/**
+ * Insert a object 'elt' to the hash 'top' and associate it with key 'key', if
+ * the specified key exist, try to merge its content
+ * @param top destination object (must be of type UCL_OBJECT)
+ * @param elt element to insert (must NOT be NULL)
+ * @param key key to associate with this object (either const or preallocated)
+ * @param keylen length of the key (or 0 for NULL terminated keys)
+ * @param copy_key make an internal copy of key
+ * @return true if key has been inserted
+ */
+UCL_EXTERN bool ucl_object_insert_key_merged (ucl_object_t *top, ucl_object_t *elt,
+               const char *key, size_t keylen, bool copy_key);
+
+/**
+ * Append an element to the end of array object
+ * @param top destination object (must NOT be NULL)
+ * @param elt element to append (must NOT be NULL)
+ * @return true if value has been inserted
+ */
+UCL_EXTERN bool ucl_array_append (ucl_object_t *top,
+               ucl_object_t *elt);
+
+/**
+ * Append an element to the start of array object
+ * @param top destination object (must NOT be NULL)
+ * @param elt element to append (must NOT be NULL)
+ * @return true if value has been inserted
+ */
+UCL_EXTERN bool ucl_array_prepend (ucl_object_t *top,
+               ucl_object_t *elt);
+
+/**
+ * Merge all elements of second array into the first array
+ * @param top destination array (must be of type UCL_ARRAY)
+ * @param elt array to copy elements from (must be of type UCL_ARRAY)
+ * @param copy copy elements instead of referencing them
+ * @return true if arrays were merged
+ */
+UCL_EXTERN bool ucl_array_merge (ucl_object_t *top, ucl_object_t *elt,
+               bool copy);
+
+/**
+ * Removes an element `elt` from the array `top`, returning the object that was
+ * removed. This object is not released, caller must unref the returned object
+ * when it is no longer needed.
+ * @param top array ucl object
+ * @param elt element to remove
+ * @return removed element or NULL if `top` is NULL or not an array
+ */
+UCL_EXTERN ucl_object_t* ucl_array_delete (ucl_object_t *top,
+               ucl_object_t *elt);
+
+/**
+ * Returns the first element of the array `top`
+ * @param top array ucl object
+ * @return element or NULL if `top` is NULL or not an array
+ */
+UCL_EXTERN const ucl_object_t* ucl_array_head (const ucl_object_t *top);
+
+/**
+ * Returns the last element of the array `top`
+ * @param top array ucl object
+ * @return element or NULL if `top` is NULL or not an array
+ */
+UCL_EXTERN const ucl_object_t* ucl_array_tail (const ucl_object_t *top);
+
+/**
+ * Removes the last element from the array `top`, returning the object that was
+ * removed. This object is not released, caller must unref the returned object
+ * when it is no longer needed.
+ * @param top array ucl object
+ * @return removed element or NULL if `top` is NULL or not an array
+ */
+UCL_EXTERN ucl_object_t* ucl_array_pop_last (ucl_object_t *top);
+
+/**
+ * Removes the first element from the array `top`, returning the object that was
+ * removed. This object is not released, caller must unref the returned object
+ * when it is no longer needed.
+ * @param top array ucl object
+ * @return removed element or NULL if `top` is NULL or not an array
+ */
+UCL_EXTERN ucl_object_t* ucl_array_pop_first (ucl_object_t *top);
+
+/**
+ * Return object identified by index of the array `top`
+ * @param top object to get a key from (must be of type UCL_ARRAY)
+ * @param index array index to return
+ * @return object at the specified index or NULL if index is not found
+ */
+UCL_EXTERN const ucl_object_t* ucl_array_find_index (const ucl_object_t *top,
+               unsigned int index);
+
+/**
+ * Replace an element in an array with a different element, returning the object
+ * that was replaced. This object is not released, caller must unref the
+ * returned object when it is no longer needed.
+ * @param top destination object (must be of type UCL_ARRAY)
+ * @param elt element to append (must NOT be NULL)
+ * @param index array index in destination to overwrite with elt
+ * @return object that was replaced or NULL if index is not found
+ */
+ucl_object_t *
+ucl_array_replace_index (ucl_object_t *top, ucl_object_t *elt,
+       unsigned int index);
+
+/**
+ * Append a element to another element forming an implicit array
+ * @param head head to append (may be NULL)
+ * @param elt new element
+ * @return the new implicit array
+ */
+UCL_EXTERN ucl_object_t * ucl_elt_append (ucl_object_t *head,
+               ucl_object_t *elt);
+
+/**
+ * Converts an object to double value
+ * @param obj CL object
+ * @param target target double variable
+ * @return true if conversion was successful
+ */
+UCL_EXTERN bool ucl_object_todouble_safe (const ucl_object_t *obj, double *target);
+
+/**
+ * Unsafe version of \ref ucl_obj_todouble_safe
+ * @param obj CL object
+ * @return double value
+ */
+UCL_EXTERN double ucl_object_todouble (const ucl_object_t *obj);
+
+/**
+ * Converts an object to integer value
+ * @param obj CL object
+ * @param target target integer variable
+ * @return true if conversion was successful
+ */
+UCL_EXTERN bool ucl_object_toint_safe (const ucl_object_t *obj, int64_t *target);
+
+/**
+ * Unsafe version of \ref ucl_obj_toint_safe
+ * @param obj CL object
+ * @return int value
+ */
+UCL_EXTERN int64_t ucl_object_toint (const ucl_object_t *obj);
+
+/**
+ * Converts an object to boolean value
+ * @param obj CL object
+ * @param target target boolean variable
+ * @return true if conversion was successful
+ */
+UCL_EXTERN bool ucl_object_toboolean_safe (const ucl_object_t *obj, bool *target);
+
+/**
+ * Unsafe version of \ref ucl_obj_toboolean_safe
+ * @param obj CL object
+ * @return boolean value
+ */
+UCL_EXTERN bool ucl_object_toboolean (const ucl_object_t *obj);
+
+/**
+ * Converts an object to string value
+ * @param obj CL object
+ * @param target target string variable, no need to free value
+ * @return true if conversion was successful
+ */
+UCL_EXTERN bool ucl_object_tostring_safe (const ucl_object_t *obj, const char **target);
+
+/**
+ * Unsafe version of \ref ucl_obj_tostring_safe
+ * @param obj CL object
+ * @return string value
+ */
+UCL_EXTERN const char* ucl_object_tostring (const ucl_object_t *obj);
+
+/**
+ * Convert any object to a string in JSON notation if needed
+ * @param obj CL object
+ * @return string value
+ */
+UCL_EXTERN const char* ucl_object_tostring_forced (const ucl_object_t *obj);
+
+/**
+ * Return string as char * and len, string may be not zero terminated, more efficient that \ref ucl_obj_tostring as it
+ * allows zero-copy (if #UCL_PARSER_ZEROCOPY has been used during parsing)
+ * @param obj CL object
+ * @param target target string variable, no need to free value
+ * @param tlen target length
+ * @return true if conversion was successful
+ */
+UCL_EXTERN bool ucl_object_tolstring_safe (const ucl_object_t *obj,
+               const char **target, size_t *tlen);
+
+/**
+ * Unsafe version of \ref ucl_obj_tolstring_safe
+ * @param obj CL object
+ * @return string value
+ */
+UCL_EXTERN const char* ucl_object_tolstring (const ucl_object_t *obj, size_t *tlen);
+
+/**
+ * Return object identified by a key in the specified object
+ * @param obj object to get a key from (must be of type UCL_OBJECT)
+ * @param key key to search
+ * @return object matching the specified key or NULL if key was not found
+ */
+UCL_EXTERN const ucl_object_t* ucl_object_find_key (const ucl_object_t *obj,
+               const char *key);
+
+/**
+ * Return object identified by a fixed size key in the specified object
+ * @param obj object to get a key from (must be of type UCL_OBJECT)
+ * @param key key to search
+ * @param klen length of a key
+ * @return object matching the specified key or NULL if key was not found
+ */
+UCL_EXTERN const ucl_object_t* ucl_object_find_keyl (const ucl_object_t *obj,
+               const char *key, size_t klen);
+
+/**
+ * Return object identified by dot notation string
+ * @param obj object to search in
+ * @param path dot.notation.path to the path to lookup. May use numeric .index on arrays
+ * @return object matched the specified path or NULL if path is not found
+ */
+UCL_EXTERN const ucl_object_t *ucl_lookup_path (const ucl_object_t *obj,
+               const char *path);
+
+/**
+ * Returns a key of an object as a NULL terminated string
+ * @param obj CL object
+ * @return key or NULL if there is no key
+ */
+UCL_EXTERN const char* ucl_object_key (const ucl_object_t *obj);
+
+/**
+ * Returns a key of an object as a fixed size string (may be more efficient)
+ * @param obj CL object
+ * @param len target key length
+ * @return key pointer
+ */
+UCL_EXTERN const char* ucl_object_keyl (const ucl_object_t *obj, size_t *len);
+
+/**
+ * Increase reference count for an object
+ * @param obj object to ref
+ * @return the referenced object
+ */
+UCL_EXTERN ucl_object_t* ucl_object_ref (const ucl_object_t *obj);
+
+/**
+ * Free ucl object
+ * @param obj ucl object to free
+ */
+UCL_DEPRECATED(UCL_EXTERN void ucl_object_free (ucl_object_t *obj));
+
+/**
+ * Decrease reference count for an object
+ * @param obj object to unref
+ */
+UCL_EXTERN void ucl_object_unref (ucl_object_t *obj);
+
+/**
+ * Compare objects `o1` and `o2`
+ * @param o1 the first object
+ * @param o2 the second object
+ * @return values >0, 0 and <0 if `o1` is more than, equal and less than `o2`.
+ * The order of comparison:
+ * 1) Type of objects
+ * 2) Size of objects
+ * 3) Content of objects
+ */
+UCL_EXTERN int ucl_object_compare (const ucl_object_t *o1,
+               const ucl_object_t *o2);
+
+/**
+ * Sort UCL array using `cmp` compare function
+ * @param ar
+ * @param cmp
+ */
+UCL_EXTERN void ucl_object_array_sort (ucl_object_t *ar,
+               int (*cmp)(const ucl_object_t *o1, const ucl_object_t *o2));
+
+/**
+ * Get the priority for specific UCL object
+ * @param obj any ucl object
+ * @return priority of an object
+ */
+UCL_EXTERN unsigned int ucl_object_get_priority (const ucl_object_t *obj);
+
+/**
+ * Set explicit priority of an object.
+ * @param obj any ucl object
+ * @param priority new priroity value (only 4 least significant bits are considred)
+ */
+UCL_EXTERN void ucl_object_set_priority (ucl_object_t *obj,
+               unsigned int priority);
+
+/**
+ * Opaque iterator object
+ */
+typedef void* ucl_object_iter_t;
+
+/**
+ * Get next key from an object
+ * @param obj object to iterate
+ * @param iter opaque iterator, must be set to NULL on the first call:
+ * ucl_object_iter_t it = NULL;
+ * while ((cur = ucl_iterate_object (obj, &it)) != NULL) ...
+ * @return the next object or NULL
+ */
+UCL_EXTERN const ucl_object_t* ucl_iterate_object (const ucl_object_t *obj,
+               ucl_object_iter_t *iter, bool expand_values);
+
+/**
+ * Create new safe iterator for the specified object
+ * @param obj object to iterate
+ * @return new iterator object that should be used with safe iterators API only
+ */
+UCL_EXTERN ucl_object_iter_t ucl_object_iterate_new (const ucl_object_t *obj)
+       UCL_WARN_UNUSED_RESULT;
+/**
+ * Reset initialized iterator to a new object
+ * @param obj new object to iterate
+ * @return modified iterator object
+ */
+UCL_EXTERN ucl_object_iter_t ucl_object_iterate_reset (ucl_object_iter_t it,
+               const ucl_object_t *obj);
+
+/**
+ * Get the next object from the `obj`. This fucntion iterates over arrays, objects
+ * and implicit arrays
+ * @param iter safe iterator
+ * @return the next object in sequence
+ */
+UCL_EXTERN const ucl_object_t* ucl_object_iterate_safe (ucl_object_iter_t iter,
+               bool expand_values);
+
+/**
+ * Free memory associated with the safe iterator
+ * @param it safe iterator object
+ */
+UCL_EXTERN void ucl_object_iterate_free (ucl_object_iter_t it);
+
+/** @} */
+
+
+/**
+ * @defgroup parser Parsing functions
+ * These functions are used to parse UCL objects
+ *
+ * @{
+ */
+
+/**
+ * Macro handler for a parser
+ * @param data the content of macro
+ * @param len the length of content
+ * @param arguments arguments object
+ * @param ud opaque user data
+ * @param err error pointer
+ * @return true if macro has been parsed
+ */
+typedef bool (*ucl_macro_handler) (const unsigned char *data, size_t len,
+               const ucl_object_t *arguments,
+               void* ud);
+
+/* Opaque parser */
+struct ucl_parser;
+
+/**
+ * Creates new parser object
+ * @param pool pool to allocate memory from
+ * @return new parser object
+ */
+UCL_EXTERN struct ucl_parser* ucl_parser_new (int flags);
+
+/**
+ * Register new handler for a macro
+ * @param parser parser object
+ * @param macro macro name (without leading dot)
+ * @param handler handler (it is called immediately after macro is parsed)
+ * @param ud opaque user data for a handler
+ */
+UCL_EXTERN void ucl_parser_register_macro (struct ucl_parser *parser, const char *macro,
+               ucl_macro_handler handler, void* ud);
+
+/**
+ * Handler to detect unregistered variables
+ * @param data variable data
+ * @param len length of variable
+ * @param replace (out) replace value for variable
+ * @param replace_len (out) replace length for variable
+ * @param need_free (out) UCL will free `dest` after usage
+ * @param ud opaque userdata
+ * @return true if variable
+ */
+typedef bool (*ucl_variable_handler) (const unsigned char *data, size_t len,
+               unsigned char **replace, size_t *replace_len, bool *need_free, void* ud);
+
+/**
+ * Register new parser variable
+ * @param parser parser object
+ * @param var variable name
+ * @param value variable value
+ */
+UCL_EXTERN void ucl_parser_register_variable (struct ucl_parser *parser, const char *var,
+               const char *value);
+
+/**
+ * Set handler for unknown variables
+ * @param parser parser structure
+ * @param handler desired handler
+ * @param ud opaque data for the handler
+ */
+UCL_EXTERN void ucl_parser_set_variables_handler (struct ucl_parser *parser,
+               ucl_variable_handler handler, void *ud);
+
+/**
+ * Load new chunk to a 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 added and false in case of error
+ */
+UCL_EXTERN bool ucl_parser_add_chunk (struct ucl_parser *parser,
+               const unsigned char *data, size_t len);
+
+/**
+ * Load new chunk to a parser with the specified priority
+ * @param parser parser structure
+ * @param data the pointer to the beginning of a chunk
+ * @param len the length of a chunk
+ * @param priority the desired priority of a chunk (only 4 least significant bits
+ * are considered for this parameter)
+ * @return true if chunk has been added and false in case of error
+ */
+UCL_EXTERN bool ucl_parser_add_chunk_priority (struct ucl_parser *parser,
+               const unsigned char *data, size_t len, unsigned priority);
+
+/**
+ * Load ucl object from a string
+ * @param parser parser structure
+ * @param data the pointer to the string
+ * @param len the length of the string, if `len` is 0 then `data` must be zero-terminated string
+ * @return true if string has been added and false in case of error
+ */
+UCL_EXTERN bool ucl_parser_add_string (struct ucl_parser *parser,
+               const char *data,size_t len);
+
+/**
+ * Load and add data from a file
+ * @param parser parser structure
+ * @param filename the name of file
+ * @param err if *err is NULL it is set to parser error
+ * @return true if chunk has been added and false in case of error
+ */
+UCL_EXTERN bool ucl_parser_add_file (struct ucl_parser *parser,
+               const char *filename);
+
+/**
+ * Load and add data from a file descriptor
+ * @param parser parser structure
+ * @param filename the name of file
+ * @param err if *err is NULL it is set to parser error
+ * @return true if chunk has been added and false in case of error
+ */
+UCL_EXTERN bool ucl_parser_add_fd (struct ucl_parser *parser,
+               int fd);
+
+/**
+ * Get a top object for a parser (refcount is increased)
+ * @param parser parser structure
+ * @param err if *err is NULL it is set to parser error
+ * @return top parser object or NULL
+ */
+UCL_EXTERN ucl_object_t* ucl_parser_get_object (struct ucl_parser *parser);
+
+/**
+ * Get the error string if failing
+ * @param parser parser object
+ */
+UCL_EXTERN const char *ucl_parser_get_error(struct ucl_parser *parser);
+
+/**
+ * Clear the error in the parser
+ * @param parser parser object
+ */
+UCL_EXTERN void ucl_parser_clear_error(struct ucl_parser *parser);
+
+/**
+ * Free ucl parser object
+ * @param parser parser object
+ */
+UCL_EXTERN void ucl_parser_free (struct ucl_parser *parser);
+
+/**
+ * Add new public key to parser for signatures check
+ * @param parser parser object
+ * @param key PEM representation of a key
+ * @param len length of the key
+ * @param err if *err is NULL it is set to parser error
+ * @return true if a key has been successfully added
+ */
+UCL_EXTERN bool ucl_pubkey_add (struct ucl_parser *parser, const unsigned char *key, size_t len);
+
+/**
+ * Set FILENAME and CURDIR variables in parser
+ * @param parser parser object
+ * @param filename filename to set or NULL to set FILENAME to "undef" and CURDIR to getcwd()
+ * @param need_expand perform realpath() if this variable is true and filename is not NULL
+ * @return true if variables has been set
+ */
+UCL_EXTERN bool ucl_parser_set_filevars (struct ucl_parser *parser, const char *filename,
+               bool need_expand);
+
+/** @} */
+
+/**
+ * @defgroup emitter Emitting functions
+ * These functions are used to serialise UCL objects to some string representation.
+ *
+ * @{
+ */
+
+struct ucl_emitter_context;
+/**
+ * Structure using for emitter callbacks
+ */
+struct ucl_emitter_functions {
+       /** Append a single character */
+       int (*ucl_emitter_append_character) (unsigned char c, size_t nchars, void *ud);
+       /** Append a string of a specified length */
+       int (*ucl_emitter_append_len) (unsigned const char *str, size_t len, void *ud);
+       /** Append a 64 bit integer */
+       int (*ucl_emitter_append_int) (int64_t elt, void *ud);
+       /** Append floating point element */
+       int (*ucl_emitter_append_double) (double elt, void *ud);
+       /** Free userdata */
+       void (*ucl_emitter_free_func)(void *ud);
+       /** Opaque userdata pointer */
+       void *ud;
+};
+
+struct ucl_emitter_operations {
+       /** Write a primitive element */
+       void (*ucl_emitter_write_elt) (struct ucl_emitter_context *ctx,
+               const ucl_object_t *obj, bool first, bool print_key);
+       /** Start ucl object */
+       void (*ucl_emitter_start_object) (struct ucl_emitter_context *ctx,
+               const ucl_object_t *obj, bool print_key);
+       /** End ucl object */
+       void (*ucl_emitter_end_object) (struct ucl_emitter_context *ctx,
+               const ucl_object_t *obj);
+       /** Start ucl array */
+       void (*ucl_emitter_start_array) (struct ucl_emitter_context *ctx,
+               const ucl_object_t *obj, bool print_key);
+       void (*ucl_emitter_end_array) (struct ucl_emitter_context *ctx,
+               const ucl_object_t *obj);
+};
+
+/**
+ * Structure that defines emitter functions
+ */
+struct ucl_emitter_context {
+       /** Name of emitter (e.g. json, compact_json) */
+       const char *name;
+       /** Unique id (e.g. UCL_EMIT_JSON for standard emitters */
+       int id;
+       /** A set of output functions */
+       const struct ucl_emitter_functions *func;
+       /** A set of output operations */
+       const struct ucl_emitter_operations *ops;
+       /** Current amount of indent tabs */
+       unsigned int indent;
+       /** Top level object */
+       const ucl_object_t *top;
+       /** The rest of context */
+       unsigned char data[1];
+};
+
+/**
+ * Emit object to a string
+ * @param obj object
+ * @param emit_type if type is #UCL_EMIT_JSON then emit json, if type is
+ * #UCL_EMIT_CONFIG then emit config like object
+ * @return dump of an object (must be freed after using) or NULL in case of error
+ */
+UCL_EXTERN unsigned char *ucl_object_emit (const ucl_object_t *obj,
+               enum ucl_emitter emit_type);
+
+/**
+ * Emit object to a string
+ * @param obj object
+ * @param emit_type if type is #UCL_EMIT_JSON then emit json, if type is
+ * #UCL_EMIT_CONFIG then emit config like object
+ * @param emitter a set of emitter functions
+ * @return dump of an object (must be freed after using) or NULL in case of error
+ */
+UCL_EXTERN bool ucl_object_emit_full (const ucl_object_t *obj,
+               enum ucl_emitter emit_type,
+               struct ucl_emitter_functions *emitter);
+
+/**
+ * Start streamlined UCL object emitter
+ * @param obj top UCL object
+ * @param emit_type emit type
+ * @param emitter a set of emitter functions
+ * @return new streamlined context that should be freed by
+ * `ucl_object_emit_streamline_finish`
+ */
+UCL_EXTERN struct ucl_emitter_context* ucl_object_emit_streamline_new (
+               const ucl_object_t *obj, enum ucl_emitter emit_type,
+               struct ucl_emitter_functions *emitter);
+
+/**
+ * Start object or array container for the streamlined output
+ * @param ctx streamlined context
+ * @param obj container object
+ */
+UCL_EXTERN void ucl_object_emit_streamline_start_container (
+               struct ucl_emitter_context *ctx, const ucl_object_t *obj);
+/**
+ * Add a complete UCL object to streamlined output
+ * @param ctx streamlined context
+ * @param obj object to output
+ */
+UCL_EXTERN void ucl_object_emit_streamline_add_object (
+               struct ucl_emitter_context *ctx, const ucl_object_t *obj);
+/**
+ * End previously added container
+ * @param ctx streamlined context
+ */
+UCL_EXTERN void ucl_object_emit_streamline_end_container (
+               struct ucl_emitter_context *ctx);
+/**
+ * Terminate streamlined container finishing all containers in it
+ * @param ctx streamlined context
+ */
+UCL_EXTERN void ucl_object_emit_streamline_finish (
+               struct ucl_emitter_context *ctx);
+
+/**
+ * Returns functions to emit object to memory
+ * @param pmem target pointer (should be freed by caller)
+ * @return emitter functions structure
+ */
+UCL_EXTERN struct ucl_emitter_functions* ucl_object_emit_memory_funcs (
+               void **pmem);
+
+/**
+ * Returns functions to emit object to FILE *
+ * @param fp FILE * object
+ * @return emitter functions structure
+ */
+UCL_EXTERN struct ucl_emitter_functions* ucl_object_emit_file_funcs (
+               FILE *fp);
+/**
+ * Returns functions to emit object to a file descriptor
+ * @param fd file descriptor
+ * @return emitter functions structure
+ */
+UCL_EXTERN struct ucl_emitter_functions* ucl_object_emit_fd_funcs (
+               int fd);
+
+/**
+ * Free emitter functions
+ * @param f pointer to functions
+ */
+UCL_EXTERN void ucl_object_emit_funcs_free (struct ucl_emitter_functions *f);
+
+/** @} */
+
+/**
+ * @defgroup schema Schema functions
+ * These functions are used to validate UCL objects using json schema format
+ *
+ * @{
+ */
+
+/**
+ * Used to define UCL schema error
+ */
+enum ucl_schema_error_code {
+       UCL_SCHEMA_OK = 0,          /**< no error */
+       UCL_SCHEMA_TYPE_MISMATCH,   /**< type of object is incorrect */
+       UCL_SCHEMA_INVALID_SCHEMA,  /**< schema is invalid */
+       UCL_SCHEMA_MISSING_PROPERTY,/**< one or more missing properties */
+       UCL_SCHEMA_CONSTRAINT,      /**< constraint found */
+       UCL_SCHEMA_MISSING_DEPENDENCY, /**< missing dependency */
+       UCL_SCHEMA_UNKNOWN          /**< generic error */
+};
+
+/**
+ * Generic ucl schema error
+ */
+struct ucl_schema_error {
+       enum ucl_schema_error_code code;        /**< error code */
+       char msg[128];                                          /**< error message */
+       const ucl_object_t *obj;                        /**< object where error occured */
+};
+
+/**
+ * Validate object `obj` using schema object `schema`.
+ * @param schema schema object
+ * @param obj object to validate
+ * @param err error pointer, if this parameter is not NULL and error has been
+ * occured, then `err` is filled with the exact error definition.
+ * @return true if `obj` is valid using `schema`
+ */
+UCL_EXTERN bool ucl_object_validate (const ucl_object_t *schema,
+               const ucl_object_t *obj, struct ucl_schema_error *err);
+
+/** @} */
+
+#ifdef  __cplusplus
+}
+#endif
+/*
+ * XXX: Poorly named API functions, need to replace them with the appropriate
+ * named function. All API functions *must* use naming ucl_object_*. Usage of
+ * ucl_obj* should be avoided.
+ */
+#define ucl_obj_todouble_safe ucl_object_todouble_safe
+#define ucl_obj_todouble ucl_object_todouble
+#define ucl_obj_tostring ucl_object_tostring
+#define ucl_obj_tostring_safe ucl_object_tostring_safe
+#define ucl_obj_tolstring ucl_object_tolstring
+#define ucl_obj_tolstring_safe ucl_object_tolstring_safe
+#define ucl_obj_toint ucl_object_toint
+#define ucl_obj_toint_safe ucl_object_toint_safe
+#define ucl_obj_toboolean ucl_object_toboolean
+#define ucl_obj_toboolean_safe ucl_object_toboolean_safe
+#define ucl_obj_get_key ucl_object_find_key
+#define ucl_obj_get_keyl ucl_object_find_keyl
+#define ucl_obj_unref ucl_object_unref
+#define ucl_obj_ref ucl_object_ref
+#define ucl_obj_free ucl_object_free
+
+#endif /* UCL_H_ */
diff --git a/contrib/libucl/ucl_chartable.h b/contrib/libucl/ucl_chartable.h
new file mode 100644 (file)
index 0000000..5248e11
--- /dev/null
@@ -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/ucl_emitter.c b/contrib/libucl/ucl_emitter.c
new file mode 100644 (file)
index 0000000..9ddf358
--- /dev/null
@@ -0,0 +1,511 @@
+/* Copyright (c) 2013, Vsevolod Stakhov
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *       * Redistributions of source code must retain the above copyright
+ *         notice, this list of conditions and the following disclaimer.
+ *       * Redistributions in binary form must reproduce the above copyright
+ *         notice, this list of conditions and the following disclaimer in the
+ *         documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "ucl.h"
+#include "ucl_internal.h"
+#include "ucl_chartable.h"
+#ifdef HAVE_FLOAT_H
+#include <float.h>
+#endif
+#ifdef HAVE_MATH_H
+#include <math.h>
+#endif
+
+/**
+ * @file ucl_emitter.c
+ * Serialise UCL object to various of output formats
+ */
+
+static void ucl_emitter_common_elt (struct ucl_emitter_context *ctx,
+               const ucl_object_t *obj, bool first, bool print_key, bool compact);
+
+#define UCL_EMIT_TYPE_OPS(type)                \
+       static void ucl_emit_ ## type ## _elt (struct ucl_emitter_context *ctx, \
+               const ucl_object_t *obj, bool first, bool print_key);   \
+       static void ucl_emit_ ## type ## _start_obj (struct ucl_emitter_context *ctx,   \
+               const ucl_object_t *obj, bool print_key);       \
+       static void ucl_emit_ ## type## _start_array (struct ucl_emitter_context *ctx,  \
+               const ucl_object_t *obj, bool print_key);       \
+       static void ucl_emit_ ##type## _end_object (struct ucl_emitter_context *ctx,    \
+               const ucl_object_t *obj);       \
+       static void ucl_emit_ ##type## _end_array (struct ucl_emitter_context *ctx,     \
+               const ucl_object_t *obj)
+
+/*
+ * JSON format operations
+ */
+UCL_EMIT_TYPE_OPS(json);
+UCL_EMIT_TYPE_OPS(json_compact);
+UCL_EMIT_TYPE_OPS(config);
+UCL_EMIT_TYPE_OPS(yaml);
+
+#define UCL_EMIT_TYPE_CONTENT(type) {  \
+       .ucl_emitter_write_elt = ucl_emit_ ## type ## _elt,     \
+       .ucl_emitter_start_object = ucl_emit_ ## type ##_start_obj,     \
+       .ucl_emitter_start_array = ucl_emit_ ## type ##_start_array,    \
+       .ucl_emitter_end_object = ucl_emit_ ## type ##_end_object,      \
+       .ucl_emitter_end_array = ucl_emit_ ## type ##_end_array \
+}
+
+
+const struct ucl_emitter_operations ucl_standartd_emitter_ops[] = {
+       [UCL_EMIT_JSON] = UCL_EMIT_TYPE_CONTENT(json),
+       [UCL_EMIT_JSON_COMPACT] = UCL_EMIT_TYPE_CONTENT(json_compact),
+       [UCL_EMIT_CONFIG] = UCL_EMIT_TYPE_CONTENT(config),
+       [UCL_EMIT_YAML] = UCL_EMIT_TYPE_CONTENT(yaml)
+};
+
+/*
+ * Utility to check whether we need a top object
+ */
+#define UCL_EMIT_IDENT_TOP_OBJ(ctx, obj) ((ctx)->top != (obj) || \
+               ((ctx)->id == UCL_EMIT_JSON_COMPACT || (ctx)->id == UCL_EMIT_JSON))
+
+
+/**
+ * Add tabulation to the output buffer
+ * @param buf target buffer
+ * @param tabs number of tabs to add
+ */
+static inline void
+ucl_add_tabs (const struct ucl_emitter_functions *func, unsigned int tabs,
+               bool compact)
+{
+       if (!compact && tabs > 0) {
+               func->ucl_emitter_append_character (' ', tabs * 4, func->ud);
+       }
+}
+
+/**
+ * Print key for the element
+ * @param ctx
+ * @param obj
+ */
+static void
+ucl_emitter_print_key (bool print_key, struct ucl_emitter_context *ctx,
+               const ucl_object_t *obj, bool compact)
+{
+       const struct ucl_emitter_functions *func = ctx->func;
+
+       if (!print_key) {
+               return;
+       }
+
+       if (ctx->id == UCL_EMIT_CONFIG) {
+               if (obj->flags & UCL_OBJECT_NEED_KEY_ESCAPE) {
+                       ucl_elt_string_write_json (obj->key, obj->keylen, ctx);
+               }
+               else {
+                       func->ucl_emitter_append_len (obj->key, obj->keylen, func->ud);
+               }
+
+               if (obj->type != UCL_OBJECT && obj->type != UCL_ARRAY) {
+                       func->ucl_emitter_append_len (" = ", 3, func->ud);
+               }
+               else {
+                       func->ucl_emitter_append_character (' ', 1, func->ud);
+               }
+       }
+       else if (ctx->id == UCL_EMIT_YAML) {
+               if (obj->keylen > 0 && (obj->flags & UCL_OBJECT_NEED_KEY_ESCAPE)) {
+                       ucl_elt_string_write_json (obj->key, obj->keylen, ctx);
+               }
+               else if (obj->keylen > 0) {
+                       func->ucl_emitter_append_len (obj->key, obj->keylen, func->ud);
+               }
+               else {
+                       func->ucl_emitter_append_len ("null", 4, func->ud);
+               }
+
+               func->ucl_emitter_append_len (": ", 2, func->ud);
+       }
+       else {
+               if (obj->keylen > 0) {
+                       ucl_elt_string_write_json (obj->key, obj->keylen, ctx);
+               }
+               else {
+                       func->ucl_emitter_append_len ("null", 4, func->ud);
+               }
+
+               if (compact) {
+                       func->ucl_emitter_append_character (':', 1, func->ud);
+               }
+               else {
+                       func->ucl_emitter_append_len (": ", 2, func->ud);
+               }
+       }
+}
+
+static void
+ucl_emitter_finish_object (struct ucl_emitter_context *ctx,
+               const ucl_object_t *obj, bool compact, bool is_array)
+{
+       const struct ucl_emitter_functions *func = ctx->func;
+
+       if (ctx->id == UCL_EMIT_CONFIG && obj != ctx->top) {
+               if (obj->type != UCL_OBJECT && obj->type != UCL_ARRAY) {
+                       if (!is_array) {
+                               /* Objects are split by ';' */
+                               func->ucl_emitter_append_len (";\n", 2, func->ud);
+                       }
+                       else {
+                               /* Use commas for arrays */
+                               func->ucl_emitter_append_len (",\n", 2, func->ud);
+                       }
+               }
+               else {
+                       func->ucl_emitter_append_character ('\n', 1, func->ud);
+               }
+       }
+}
+
+/**
+ * End standard ucl object
+ * @param ctx emitter context
+ * @param compact compact flag
+ */
+static void
+ucl_emitter_common_end_object (struct ucl_emitter_context *ctx,
+               const ucl_object_t *obj, bool compact)
+{
+       const struct ucl_emitter_functions *func = ctx->func;
+
+       if (UCL_EMIT_IDENT_TOP_OBJ(ctx, obj)) {
+               ctx->indent --;
+               if (compact) {
+                       func->ucl_emitter_append_character ('}', 1, func->ud);
+               }
+               else {
+                       if (ctx->id != UCL_EMIT_CONFIG) {
+                               /* newline is already added for this format */
+                               func->ucl_emitter_append_character ('\n', 1, func->ud);
+                       }
+                       ucl_add_tabs (func, ctx->indent, compact);
+                       func->ucl_emitter_append_character ('}', 1, func->ud);
+               }
+       }
+
+       ucl_emitter_finish_object (ctx, obj, compact, false);
+}
+
+/**
+ * End standard ucl array
+ * @param ctx emitter context
+ * @param compact compact flag
+ */
+static void
+ucl_emitter_common_end_array (struct ucl_emitter_context *ctx,
+               const ucl_object_t *obj, bool compact)
+{
+       const struct ucl_emitter_functions *func = ctx->func;
+
+       ctx->indent --;
+       if (compact) {
+               func->ucl_emitter_append_character (']', 1, func->ud);
+       }
+       else {
+               if (ctx->id != UCL_EMIT_CONFIG) {
+                       /* newline is already added for this format */
+                       func->ucl_emitter_append_character ('\n', 1, func->ud);
+               }
+               ucl_add_tabs (func, ctx->indent, compact);
+               func->ucl_emitter_append_character (']', 1, func->ud);
+       }
+
+       ucl_emitter_finish_object (ctx, obj, compact, true);
+}
+
+/**
+ * Start emit standard UCL array
+ * @param ctx emitter context
+ * @param obj object to write
+ * @param compact compact flag
+ */
+static void
+ucl_emitter_common_start_array (struct ucl_emitter_context *ctx,
+               const ucl_object_t *obj, bool print_key, bool compact)
+{
+       const ucl_object_t *cur;
+       ucl_object_iter_t iter = NULL;
+       const struct ucl_emitter_functions *func = ctx->func;
+       bool first = true;
+
+       ucl_emitter_print_key (print_key, ctx, obj, compact);
+
+       if (compact) {
+               func->ucl_emitter_append_character ('[', 1, func->ud);
+       }
+       else {
+               func->ucl_emitter_append_len ("[\n", 2, func->ud);
+       }
+
+       ctx->indent ++;
+
+       if (obj->type == UCL_ARRAY) {
+               /* explicit array */
+               while ((cur = ucl_iterate_object (obj, &iter, true)) != NULL) {
+                       ucl_emitter_common_elt (ctx, cur, first, false, compact);
+                       first = false;
+               }
+       }
+       else {
+               /* implicit array */
+               cur = obj;
+               while (cur) {
+                       ucl_emitter_common_elt (ctx, cur, first, false, compact);
+                       first = false;
+                       cur = cur->next;
+               }
+       }
+
+
+}
+
+/**
+ * Start emit standard UCL object
+ * @param ctx emitter context
+ * @param obj object to write
+ * @param compact compact flag
+ */
+static void
+ucl_emitter_common_start_object (struct ucl_emitter_context *ctx,
+               const ucl_object_t *obj, bool print_key, bool compact)
+{
+       ucl_hash_iter_t it = NULL;
+       const ucl_object_t *cur, *elt;
+       const struct ucl_emitter_functions *func = ctx->func;
+       bool first = true;
+
+       ucl_emitter_print_key (print_key, ctx, obj, compact);
+       /*
+        * Print <ident_level>{
+        * <ident_level + 1><object content>
+        */
+       if (UCL_EMIT_IDENT_TOP_OBJ(ctx, obj)) {
+               if (compact) {
+                       func->ucl_emitter_append_character ('{', 1, func->ud);
+               }
+               else {
+                       func->ucl_emitter_append_len ("{\n", 2, func->ud);
+               }
+               ctx->indent ++;
+       }
+
+       while ((cur = ucl_hash_iterate (obj->value.ov, &it))) {
+
+               if (ctx->id == UCL_EMIT_CONFIG) {
+                       LL_FOREACH (cur, elt) {
+                               ucl_emitter_common_elt (ctx, elt, first, true, compact);
+                       }
+               }
+               else {
+                       /* Expand implicit arrays */
+                       if (cur->next != NULL) {
+                               if (!first) {
+                                       if (compact) {
+                                               func->ucl_emitter_append_character (',', 1, func->ud);
+                                       }
+                                       else {
+                                               func->ucl_emitter_append_len (",\n", 2, func->ud);
+                                       }
+                               }
+                               ucl_add_tabs (func, ctx->indent, compact);
+                               ucl_emitter_common_start_array (ctx, cur, true, compact);
+                               ucl_emitter_common_end_array (ctx, cur, compact);
+                       }
+                       else {
+                               ucl_emitter_common_elt (ctx, cur, first, true, compact);
+                       }
+               }
+
+               first = false;
+       }
+}
+
+/**
+ * Common choice of object emitting
+ * @param ctx emitter context
+ * @param obj object to print
+ * @param first flag to mark the first element
+ * @param print_key print key of an object
+ * @param compact compact output
+ */
+static void
+ucl_emitter_common_elt (struct ucl_emitter_context *ctx,
+               const ucl_object_t *obj, bool first, bool print_key, bool compact)
+{
+       const struct ucl_emitter_functions *func = ctx->func;
+       bool flag;
+       struct ucl_object_userdata *ud;
+       const char *ud_out = "";
+
+       if (ctx->id != UCL_EMIT_CONFIG && !first) {
+               if (compact) {
+                       func->ucl_emitter_append_character (',', 1, func->ud);
+               }
+               else {
+                       if (ctx->id == UCL_EMIT_YAML && ctx->indent == 0) {
+                               func->ucl_emitter_append_len ("\n", 1, func->ud);
+                       } else {
+                               func->ucl_emitter_append_len (",\n", 2, func->ud);
+                       }
+               }
+       }
+
+       ucl_add_tabs (func, ctx->indent, compact);
+
+       switch (obj->type) {
+       case UCL_INT:
+               ucl_emitter_print_key (print_key, ctx, obj, compact);
+               func->ucl_emitter_append_int (ucl_object_toint (obj), func->ud);
+               ucl_emitter_finish_object (ctx, obj, compact, !print_key);
+               break;
+       case UCL_FLOAT:
+       case UCL_TIME:
+               ucl_emitter_print_key (print_key, ctx, obj, compact);
+               func->ucl_emitter_append_double (ucl_object_todouble (obj), func->ud);
+               ucl_emitter_finish_object (ctx, obj, compact, !print_key);
+               break;
+       case UCL_BOOLEAN:
+               ucl_emitter_print_key (print_key, ctx, obj, compact);
+               flag = ucl_object_toboolean (obj);
+               if (flag) {
+                       func->ucl_emitter_append_len ("true", 4, func->ud);
+               }
+               else {
+                       func->ucl_emitter_append_len ("false", 5, func->ud);
+               }
+               ucl_emitter_finish_object (ctx, obj, compact, !print_key);
+               break;
+       case UCL_STRING:
+               ucl_emitter_print_key (print_key, ctx, obj, compact);
+               if (ctx->id == UCL_EMIT_CONFIG && ucl_maybe_long_string (obj)) {
+                       ucl_elt_string_write_multiline (obj->value.sv, obj->len, ctx);
+               }
+               else {
+                       ucl_elt_string_write_json (obj->value.sv, obj->len, ctx);
+               }
+               ucl_emitter_finish_object (ctx, obj, compact, !print_key);
+               break;
+       case UCL_NULL:
+               ucl_emitter_print_key (print_key, ctx, obj, compact);
+               func->ucl_emitter_append_len ("null", 4, func->ud);
+               ucl_emitter_finish_object (ctx, obj, compact, !print_key);
+               break;
+       case UCL_OBJECT:
+               ucl_emitter_common_start_object (ctx, obj, print_key, compact);
+               ucl_emitter_common_end_object (ctx, obj, compact);
+               break;
+       case UCL_ARRAY:
+               ucl_emitter_common_start_array (ctx, obj, print_key, compact);
+               ucl_emitter_common_end_array (ctx, obj, compact);
+               break;
+       case UCL_USERDATA:
+               ud = (struct ucl_object_userdata *)obj;
+               ucl_emitter_print_key (print_key, ctx, obj, compact);
+               if (ud->emitter) {
+                       ud_out = ud->emitter (obj->value.ud);
+                       if (ud_out == NULL) {
+                               ud_out = "null";
+                       }
+               }
+               ucl_elt_string_write_json (ud_out, strlen (ud_out), ctx);
+               ucl_emitter_finish_object (ctx, obj, compact, !print_key);
+               break;
+       }
+}
+
+/*
+ * Specific standard implementations of the emitter functions
+ */
+#define UCL_EMIT_TYPE_IMPL(type, compact)              \
+       static void ucl_emit_ ## type ## _elt (struct ucl_emitter_context *ctx, \
+               const ucl_object_t *obj, bool first, bool print_key) {  \
+               ucl_emitter_common_elt (ctx, obj, first, print_key, (compact)); \
+       }       \
+       static void ucl_emit_ ## type ## _start_obj (struct ucl_emitter_context *ctx,   \
+               const ucl_object_t *obj, bool print_key) {      \
+               ucl_emitter_common_start_object (ctx, obj, print_key, (compact));       \
+       }       \
+       static void ucl_emit_ ## type## _start_array (struct ucl_emitter_context *ctx,  \
+               const ucl_object_t *obj, bool print_key) {      \
+               ucl_emitter_common_start_array (ctx, obj, print_key, (compact));        \
+       }       \
+       static void ucl_emit_ ##type## _end_object (struct ucl_emitter_context *ctx,    \
+               const ucl_object_t *obj) {      \
+               ucl_emitter_common_end_object (ctx, obj, (compact));    \
+       }       \
+       static void ucl_emit_ ##type## _end_array (struct ucl_emitter_context *ctx,     \
+               const ucl_object_t *obj) {      \
+               ucl_emitter_common_end_array (ctx, obj, (compact));     \
+       }
+
+UCL_EMIT_TYPE_IMPL(json, false)
+UCL_EMIT_TYPE_IMPL(json_compact, true)
+UCL_EMIT_TYPE_IMPL(config, false)
+UCL_EMIT_TYPE_IMPL(yaml, false)
+
+unsigned char *
+ucl_object_emit (const ucl_object_t *obj, enum ucl_emitter emit_type)
+{
+       unsigned char *res = NULL;
+       struct ucl_emitter_functions *func;
+       if (obj == NULL) {
+               return NULL;
+       }
+
+       func = ucl_object_emit_memory_funcs ((void **)&res);
+
+       if (func != NULL) {
+               ucl_object_emit_full (obj, emit_type, func);
+               ucl_object_emit_funcs_free (func);
+       }
+
+       return res;
+}
+
+bool
+ucl_object_emit_full (const ucl_object_t *obj, enum ucl_emitter emit_type,
+               struct ucl_emitter_functions *emitter)
+{
+       const struct ucl_emitter_context *ctx;
+       struct ucl_emitter_context my_ctx;
+       bool res = false;
+
+       ctx = ucl_emit_get_standard_context (emit_type);
+       if (ctx != NULL) {
+               memcpy (&my_ctx, ctx, sizeof (my_ctx));
+               my_ctx.func = emitter;
+               my_ctx.indent = 0;
+               my_ctx.top = obj;
+
+               my_ctx.ops->ucl_emitter_write_elt (&my_ctx, obj, true, false);
+               res = true;
+       }
+
+       return res;
+}
diff --git a/contrib/libucl/ucl_emitter_streamline.c b/contrib/libucl/ucl_emitter_streamline.c
new file mode 100644 (file)
index 0000000..ff27c88
--- /dev/null
@@ -0,0 +1,165 @@
+/* Copyright (c) 2014, Vsevolod Stakhov
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *       * Redistributions of source code must retain the above copyright
+ *         notice, this list of conditions and the following disclaimer.
+ *       * Redistributions in binary form must reproduce the above copyright
+ *         notice, this list of conditions and the following disclaimer in the
+ *         documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "ucl.h"
+#include "ucl_internal.h"
+#include "ucl_chartable.h"
+
+struct ucl_emitter_streamline_stack {
+       bool is_array;
+       bool empty;
+       const ucl_object_t *obj;
+       struct ucl_emitter_streamline_stack *next;
+};
+
+struct ucl_emitter_context_streamline {
+       /* Inherited from the main context */
+       const char *name;
+       int id;
+       const struct ucl_emitter_functions *func;
+       const struct ucl_emitter_operations *ops;
+       unsigned int ident;
+       const ucl_object_t *top;
+
+       /* Streamline specific fields */
+       struct ucl_emitter_streamline_stack *containers;
+};
+
+#define TO_STREAMLINE(ctx) (struct ucl_emitter_context_streamline *)(ctx)
+
+struct ucl_emitter_context*
+ucl_object_emit_streamline_new (const ucl_object_t *obj,
+               enum ucl_emitter emit_type,
+               struct ucl_emitter_functions *emitter)
+{
+       const struct ucl_emitter_context *ctx;
+       struct ucl_emitter_context_streamline *sctx;
+
+       ctx = ucl_emit_get_standard_context (emit_type);
+       if (ctx == NULL) {
+               return NULL;
+       }
+
+       sctx = calloc (1, sizeof (*sctx));
+       if (sctx == NULL) {
+               return NULL;
+       }
+
+       memcpy (sctx, ctx, sizeof (*ctx));
+       sctx->func = emitter;
+       sctx->top = obj;
+
+       ucl_object_emit_streamline_start_container ((struct ucl_emitter_context *)sctx,
+                       obj);
+
+       return (struct ucl_emitter_context *)sctx;
+}
+
+void
+ucl_object_emit_streamline_start_container (struct ucl_emitter_context *ctx,
+               const ucl_object_t *obj)
+{
+       struct ucl_emitter_context_streamline *sctx = TO_STREAMLINE(ctx);
+       struct ucl_emitter_streamline_stack *st, *top;
+       bool print_key = false;
+
+       /* Check top object presence */
+       if (sctx->top == NULL) {
+               sctx->top = obj;
+       }
+
+       top = sctx->containers;
+       st = malloc (sizeof (*st));
+       if (st != NULL) {
+               if (top != NULL && !top->is_array) {
+                       print_key = true;
+               }
+               st->empty = true;
+               st->obj = obj;
+               if (obj != NULL && obj->type == UCL_ARRAY) {
+                       st->is_array = true;
+                       sctx->ops->ucl_emitter_start_array (ctx, obj, print_key);
+               }
+               else {
+                       st->is_array = false;
+                       sctx->ops->ucl_emitter_start_object (ctx, obj, print_key);
+               }
+               LL_PREPEND (sctx->containers, st);
+       }
+}
+
+void
+ucl_object_emit_streamline_add_object (
+               struct ucl_emitter_context *ctx, const ucl_object_t *obj)
+{
+       struct ucl_emitter_context_streamline *sctx = TO_STREAMLINE(ctx);
+       bool is_array = false, is_first = false;
+
+       if (sctx->containers != NULL) {
+               if (sctx->containers->is_array) {
+                       is_array = true;
+               }
+               if (sctx->containers->empty) {
+                       is_first = true;
+                       sctx->containers->empty = false;
+               }
+       }
+
+       sctx->ops->ucl_emitter_write_elt (ctx, obj, is_first, !is_array);
+}
+
+void
+ucl_object_emit_streamline_end_container (struct ucl_emitter_context *ctx)
+{
+       struct ucl_emitter_context_streamline *sctx = TO_STREAMLINE(ctx);
+       struct ucl_emitter_streamline_stack *st;
+
+       if (sctx->containers != NULL) {
+               st = sctx->containers;
+
+               if (st->is_array) {
+                       sctx->ops->ucl_emitter_end_array (ctx, st->obj);
+               }
+               else {
+                       sctx->ops->ucl_emitter_end_object (ctx, st->obj);
+               }
+               sctx->containers = st->next;
+               free (st);
+       }
+}
+
+void
+ucl_object_emit_streamline_finish (struct ucl_emitter_context *ctx)
+{
+       struct ucl_emitter_context_streamline *sctx = TO_STREAMLINE(ctx);
+
+       while (sctx->containers != NULL) {
+               ucl_object_emit_streamline_end_container (ctx);
+       }
+
+       free (sctx);
+}
diff --git a/contrib/libucl/ucl_emitter_utils.c b/contrib/libucl/ucl_emitter_utils.c
new file mode 100644 (file)
index 0000000..91cad78
--- /dev/null
@@ -0,0 +1,487 @@
+/* Copyright (c) 2014, Vsevolod Stakhov
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *       * Redistributions of source code must retain the above copyright
+ *         notice, this list of conditions and the following disclaimer.
+ *       * Redistributions in binary form must reproduce the above copyright
+ *         notice, this list of conditions and the following disclaimer in the
+ *         documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "ucl.h"
+#include "ucl_internal.h"
+#include "ucl_chartable.h"
+
+#ifdef HAVE_FLOAT_H
+#include <float.h>
+#endif
+#ifdef HAVE_MATH_H
+#include <math.h>
+#endif
+
+extern const struct ucl_emitter_operations ucl_standartd_emitter_ops[];
+
+static const struct ucl_emitter_context ucl_standard_emitters[] = {
+       [UCL_EMIT_JSON] = {
+               .name = "json",
+               .id = UCL_EMIT_JSON,
+               .func = NULL,
+               .ops = &ucl_standartd_emitter_ops[UCL_EMIT_JSON]
+       },
+       [UCL_EMIT_JSON_COMPACT] = {
+               .name = "json_compact",
+               .id = UCL_EMIT_JSON_COMPACT,
+               .func = NULL,
+               .ops = &ucl_standartd_emitter_ops[UCL_EMIT_JSON_COMPACT]
+       },
+       [UCL_EMIT_CONFIG] = {
+               .name = "config",
+               .id = UCL_EMIT_CONFIG,
+               .func = NULL,
+               .ops = &ucl_standartd_emitter_ops[UCL_EMIT_CONFIG]
+       },
+       [UCL_EMIT_YAML] = {
+               .name = "yaml",
+               .id = UCL_EMIT_YAML,
+               .func = NULL,
+               .ops = &ucl_standartd_emitter_ops[UCL_EMIT_YAML]
+       }
+};
+
+/**
+ * Get standard emitter context for a specified emit_type
+ * @param emit_type type of emitter
+ * @return context or NULL if input is invalid
+ */
+const struct ucl_emitter_context *
+ucl_emit_get_standard_context (enum ucl_emitter emit_type)
+{
+       if (emit_type >= UCL_EMIT_JSON && emit_type <= UCL_EMIT_YAML) {
+               return &ucl_standard_emitters[emit_type];
+       }
+
+       return NULL;
+}
+
+/**
+ * Serialise string
+ * @param str string to emit
+ * @param buf target buffer
+ */
+void
+ucl_elt_string_write_json (const char *str, size_t size,
+               struct ucl_emitter_context *ctx)
+{
+       const char *p = str, *c = str;
+       size_t len = 0;
+       const struct ucl_emitter_functions *func = ctx->func;
+
+       func->ucl_emitter_append_character ('"', 1, func->ud);
+
+       while (size) {
+               if (ucl_test_character (*p, UCL_CHARACTER_JSON_UNSAFE)) {
+                       if (len > 0) {
+                               func->ucl_emitter_append_len (c, len, func->ud);
+                       }
+                       switch (*p) {
+                       case '\n':
+                               func->ucl_emitter_append_len ("\\n", 2, func->ud);
+                               break;
+                       case '\r':
+                               func->ucl_emitter_append_len ("\\r", 2, func->ud);
+                               break;
+                       case '\b':
+                               func->ucl_emitter_append_len ("\\b", 2, func->ud);
+                               break;
+                       case '\t':
+                               func->ucl_emitter_append_len ("\\t", 2, func->ud);
+                               break;
+                       case '\f':
+                               func->ucl_emitter_append_len ("\\f", 2, func->ud);
+                               break;
+                       case '\\':
+                               func->ucl_emitter_append_len ("\\\\", 2, func->ud);
+                               break;
+                       case '"':
+                               func->ucl_emitter_append_len ("\\\"", 2, func->ud);
+                               break;
+                       }
+                       len = 0;
+                       c = ++p;
+               }
+               else {
+                       p ++;
+                       len ++;
+               }
+               size --;
+       }
+       if (len > 0) {
+               func->ucl_emitter_append_len (c, len, func->ud);
+       }
+       func->ucl_emitter_append_character ('"', 1, func->ud);
+}
+
+void
+ucl_elt_string_write_multiline (const char *str, size_t size,
+               struct ucl_emitter_context *ctx)
+{
+       const struct ucl_emitter_functions *func = ctx->func;
+
+       func->ucl_emitter_append_len ("<<EOD\n", sizeof ("<<EOD\n") - 1, func->ud);
+       func->ucl_emitter_append_len (str, size, func->ud);
+       func->ucl_emitter_append_len ("\nEOD", sizeof ("\nEOD") - 1, func->ud);
+}
+
+/*
+ * Generic utstring output
+ */
+static int
+ucl_utstring_append_character (unsigned char c, size_t len, void *ud)
+{
+       UT_string *buf = ud;
+
+       if (len == 1) {
+               utstring_append_c (buf, c);
+       }
+       else {
+               utstring_reserve (buf, len + 1);
+               memset (&buf->d[buf->i], c, len);
+               buf->i += len;
+               buf->d[buf->i] = '\0';
+       }
+
+       return 0;
+}
+
+static int
+ucl_utstring_append_len (const unsigned char *str, size_t len, void *ud)
+{
+       UT_string *buf = ud;
+
+       utstring_append_len (buf, str, len);
+
+       return 0;
+}
+
+static int
+ucl_utstring_append_int (int64_t val, void *ud)
+{
+       UT_string *buf = ud;
+
+       utstring_printf (buf, "%jd", (intmax_t)val);
+       return 0;
+}
+
+static int
+ucl_utstring_append_double (double val, void *ud)
+{
+       UT_string *buf = ud;
+       const double delta = 0.0000001;
+
+       if (val == (double)(int)val) {
+               utstring_printf (buf, "%.1lf", val);
+       }
+       else if (fabs (val - (double)(int)val) < delta) {
+               /* Write at maximum precision */
+               utstring_printf (buf, "%.*lg", DBL_DIG, val);
+       }
+       else {
+               utstring_printf (buf, "%lf", val);
+       }
+
+       return 0;
+}
+
+/*
+ * Generic file output
+ */
+static int
+ucl_file_append_character (unsigned char c, size_t len, void *ud)
+{
+       FILE *fp = ud;
+
+       while (len --) {
+               fputc (c, fp);
+       }
+
+       return 0;
+}
+
+static int
+ucl_file_append_len (const unsigned char *str, size_t len, void *ud)
+{
+       FILE *fp = ud;
+
+       fwrite (str, len, 1, fp);
+
+       return 0;
+}
+
+static int
+ucl_file_append_int (int64_t val, void *ud)
+{
+       FILE *fp = ud;
+
+       fprintf (fp, "%jd", (intmax_t)val);
+
+       return 0;
+}
+
+static int
+ucl_file_append_double (double val, void *ud)
+{
+       FILE *fp = ud;
+       const double delta = 0.0000001;
+
+       if (val == (double)(int)val) {
+               fprintf (fp, "%.1lf", val);
+       }
+       else if (fabs (val - (double)(int)val) < delta) {
+               /* Write at maximum precision */
+               fprintf (fp, "%.*lg", DBL_DIG, val);
+       }
+       else {
+               fprintf (fp, "%lf", val);
+       }
+
+       return 0;
+}
+
+/*
+ * Generic file descriptor writing functions
+ */
+static int
+ucl_fd_append_character (unsigned char c, size_t len, void *ud)
+{
+       int fd = *(int *)ud;
+       unsigned char *buf;
+
+       if (len == 1) {
+               return write (fd, &c, 1);
+       }
+       else {
+               buf = malloc (len);
+               if (buf == NULL) {
+                       /* Fallback */
+                       while (len --) {
+                               if (write (fd, &c, 1) == -1) {
+                                       return -1;
+                               }
+                       }
+               }
+               else {
+                       memset (buf, c, len);
+                       if (write (fd, buf, len) == -1) {
+                               free(buf);
+                               return -1;
+                       }
+                       free (buf);
+               }
+       }
+
+       return 0;
+}
+
+static int
+ucl_fd_append_len (const unsigned char *str, size_t len, void *ud)
+{
+       int fd = *(int *)ud;
+
+       return write (fd, str, len);
+}
+
+static int
+ucl_fd_append_int (int64_t val, void *ud)
+{
+       int fd = *(int *)ud;
+       char intbuf[64];
+
+       snprintf (intbuf, sizeof (intbuf), "%jd", (intmax_t)val);
+       return write (fd, intbuf, strlen (intbuf));
+}
+
+static int
+ucl_fd_append_double (double val, void *ud)
+{
+       int fd = *(int *)ud;
+       const double delta = 0.0000001;
+       char nbuf[64];
+
+       if (val == (double)(int)val) {
+               snprintf (nbuf, sizeof (nbuf), "%.1lf", val);
+       }
+       else if (fabs (val - (double)(int)val) < delta) {
+               /* Write at maximum precision */
+               snprintf (nbuf, sizeof (nbuf), "%.*lg", DBL_DIG, val);
+       }
+       else {
+               snprintf (nbuf, sizeof (nbuf), "%lf", val);
+       }
+
+       return write (fd, nbuf, strlen (nbuf));
+}
+
+struct ucl_emitter_functions*
+ucl_object_emit_memory_funcs (void **pmem)
+{
+       struct ucl_emitter_functions *f;
+       UT_string *s;
+
+       f = calloc (1, sizeof (*f));
+
+       if (f != NULL) {
+               f->ucl_emitter_append_character = ucl_utstring_append_character;
+               f->ucl_emitter_append_double = ucl_utstring_append_double;
+               f->ucl_emitter_append_int = ucl_utstring_append_int;
+               f->ucl_emitter_append_len = ucl_utstring_append_len;
+               f->ucl_emitter_free_func = free;
+               utstring_new (s);
+               f->ud = s;
+               *pmem = s->d;
+               s->pd = pmem;
+       }
+
+       return f;
+}
+
+struct ucl_emitter_functions*
+ucl_object_emit_file_funcs (FILE *fp)
+{
+       struct ucl_emitter_functions *f;
+
+       f = calloc (1, sizeof (*f));
+
+       if (f != NULL) {
+               f->ucl_emitter_append_character = ucl_file_append_character;
+               f->ucl_emitter_append_double = ucl_file_append_double;
+               f->ucl_emitter_append_int = ucl_file_append_int;
+               f->ucl_emitter_append_len = ucl_file_append_len;
+               f->ucl_emitter_free_func = NULL;
+               f->ud = fp;
+       }
+
+       return f;
+}
+
+struct ucl_emitter_functions*
+ucl_object_emit_fd_funcs (int fd)
+{
+       struct ucl_emitter_functions *f;
+       int *ip;
+
+       f = calloc (1, sizeof (*f));
+
+       if (f != NULL) {
+               ip = malloc (sizeof (fd));
+               if (ip == NULL) {
+                       free (f);
+                       return NULL;
+               }
+
+               memcpy (ip, &fd, sizeof (fd));
+               f->ucl_emitter_append_character = ucl_fd_append_character;
+               f->ucl_emitter_append_double = ucl_fd_append_double;
+               f->ucl_emitter_append_int = ucl_fd_append_int;
+               f->ucl_emitter_append_len = ucl_fd_append_len;
+               f->ucl_emitter_free_func = free;
+               f->ud = ip;
+       }
+
+       return f;
+}
+
+void
+ucl_object_emit_funcs_free (struct ucl_emitter_functions *f)
+{
+       if (f != NULL) {
+               if (f->ucl_emitter_free_func != NULL) {
+                       f->ucl_emitter_free_func (f->ud);
+               }
+               free (f);
+       }
+}
+
+
+unsigned char *
+ucl_object_emit_single_json (const ucl_object_t *obj)
+{
+       UT_string *buf = NULL;
+       unsigned char *res = NULL;
+
+       if (obj == NULL) {
+               return NULL;
+       }
+
+       utstring_new (buf);
+
+       if (buf != NULL) {
+               switch (obj->type) {
+               case UCL_OBJECT:
+                       ucl_utstring_append_len ("object", 6, buf);
+                       break;
+               case UCL_ARRAY:
+                       ucl_utstring_append_len ("array", 5, buf);
+                       break;
+               case UCL_INT:
+                       ucl_utstring_append_int (obj->value.iv, buf);
+                       break;
+               case UCL_FLOAT:
+               case UCL_TIME:
+                       ucl_utstring_append_double (obj->value.dv, buf);
+                       break;
+               case UCL_NULL:
+                       ucl_utstring_append_len ("null", 4, buf);
+                       break;
+               case UCL_BOOLEAN:
+                       if (obj->value.iv) {
+                               ucl_utstring_append_len ("true", 4, buf);
+                       }
+                       else {
+                               ucl_utstring_append_len ("false", 5, buf);
+                       }
+                       break;
+               case UCL_STRING:
+                       ucl_utstring_append_len (obj->value.sv, obj->len, buf);
+                       break;
+               case UCL_USERDATA:
+                       ucl_utstring_append_len ("userdata", 8, buf);
+                       break;
+               }
+               res = utstring_body (buf);
+               free (buf);
+       }
+
+       return res;
+}
+
+#define LONG_STRING_LIMIT 80
+
+bool
+ucl_maybe_long_string (const ucl_object_t *obj)
+{
+       if (obj->len > LONG_STRING_LIMIT || (obj->flags & UCL_OBJECT_MULTILINE)) {
+               /* String is long enough, so search for newline characters in it */
+               if (memchr (obj->value.sv, '\n', obj->len) != NULL) {
+                       return true;
+               }
+       }
+
+       return false;
+}
diff --git a/contrib/libucl/ucl_hash.c b/contrib/libucl/ucl_hash.c
new file mode 100644 (file)
index 0000000..275e84d
--- /dev/null
@@ -0,0 +1,353 @@
+/* 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 "khash.h"
+#include "kvec.h"
+
+struct ucl_hash_elt {
+       const ucl_object_t *obj;
+       size_t ar_idx;
+};
+
+struct ucl_hash_struct {
+       void *hash;
+       kvec_t(const ucl_object_t *) ar;
+       bool caseless;
+};
+
+static inline uint32_t
+ucl_hash_func (const ucl_object_t *o)
+{
+       return XXH32 (o->key, o->keylen, 0xdeadbeef);
+}
+
+static inline int
+ucl_hash_equal (const ucl_object_t *k1, const ucl_object_t *k2)
+{
+       if (k1->keylen == k2->keylen) {
+               return strncmp (k1->key, k2->key, k1->keylen) == 0;
+       }
+
+       return 0;
+}
+
+KHASH_INIT (ucl_hash_node, const ucl_object_t *, struct ucl_hash_elt, 1,
+               ucl_hash_func, ucl_hash_equal)
+
+static inline uint32_t
+ucl_hash_caseless_func (const ucl_object_t *o)
+{
+       void *xxh = XXH32_init (0xdeadbeef);
+       char hash_buf[64], *c;
+       const char *p;
+       ssize_t remain = o->keylen;
+
+       p = o->key;
+       c = &hash_buf[0];
+
+       while (remain > 0) {
+               *c++ = tolower (*p++);
+
+               if (c - &hash_buf[0] == sizeof (hash_buf)) {
+                       XXH32_update (xxh, hash_buf, sizeof (hash_buf));
+                       c = &hash_buf[0];
+               }
+               remain --;
+       }
+
+       if (c - &hash_buf[0] != 0) {
+               XXH32_update (xxh, hash_buf, c - &hash_buf[0]);
+       }
+
+       return XXH32_digest (xxh);
+}
+
+static inline int
+ucl_hash_caseless_equal (const ucl_object_t *k1, const ucl_object_t *k2)
+{
+       if (k1->keylen == k2->keylen) {
+               return strncasecmp (k1->key, k2->key, k1->keylen) == 0;
+       }
+
+       return 0;
+}
+
+KHASH_INIT (ucl_hash_caseless_node, const ucl_object_t *, struct ucl_hash_elt, 1,
+               ucl_hash_caseless_func, ucl_hash_caseless_equal)
+
+ucl_hash_t*
+ucl_hash_create (bool ignore_case)
+{
+       ucl_hash_t *new;
+
+       new = UCL_ALLOC (sizeof (ucl_hash_t));
+       if (new != NULL) {
+               kv_init (new->ar);
+
+               new->caseless = ignore_case;
+               if (ignore_case) {
+                       khash_t(ucl_hash_caseless_node) *h = kh_init (ucl_hash_caseless_node);
+                       new->hash = (void *)h;
+               }
+               else {
+                       khash_t(ucl_hash_node) *h = kh_init (ucl_hash_node);
+                       new->hash = (void *)h;
+               }
+       }
+       return new;
+}
+
+void ucl_hash_destroy (ucl_hash_t* hashlin, ucl_hash_free_func *func)
+{
+       const ucl_object_t *cur, *tmp;
+
+       if (hashlin == NULL) {
+               return;
+       }
+
+       if (func != NULL) {
+               /* Iterate over the hash first */
+               khash_t(ucl_hash_node) *h = (khash_t(ucl_hash_node) *)
+                               hashlin->hash;
+               khiter_t k;
+
+               for (k = kh_begin (h); k != kh_end (h); ++k) {
+                       if (kh_exist (h, k)) {
+                               cur = (kh_value (h, k)).obj;
+                               while (cur != NULL) {
+                                       tmp = cur->next;
+                                       func (__DECONST (ucl_object_t *, cur));
+                                       cur = tmp;
+                               }
+                       }
+               }
+       }
+
+       if (hashlin->caseless) {
+               khash_t(ucl_hash_caseless_node) *h = (khash_t(ucl_hash_caseless_node) *)
+                       hashlin->hash;
+               kh_destroy (ucl_hash_caseless_node, h);
+       }
+       else {
+               khash_t(ucl_hash_node) *h = (khash_t(ucl_hash_node) *)
+                       hashlin->hash;
+               kh_destroy (ucl_hash_node, h);
+       }
+
+       kv_destroy (hashlin->ar);
+       UCL_FREE (sizeof (*hashlin), hashlin);
+}
+
+void
+ucl_hash_insert (ucl_hash_t* hashlin, const ucl_object_t *obj,
+               const char *key, unsigned keylen)
+{
+       khiter_t k;
+       int ret;
+       struct ucl_hash_elt *elt;
+
+       if (hashlin == NULL) {
+               return;
+       }
+
+       if (hashlin->caseless) {
+               khash_t(ucl_hash_caseless_node) *h = (khash_t(ucl_hash_caseless_node) *)
+                               hashlin->hash;
+               k = kh_put (ucl_hash_caseless_node, h, obj, &ret);
+               if (ret > 0) {
+                       elt = &kh_value (h, k);
+                       kv_push (const ucl_object_t *, hashlin->ar, obj);
+                       elt->obj = obj;
+                       elt->ar_idx = kv_size (hashlin->ar) - 1;
+               }
+       }
+       else {
+               khash_t(ucl_hash_node) *h = (khash_t(ucl_hash_node) *)
+                               hashlin->hash;
+               k = kh_put (ucl_hash_node, h, obj, &ret);
+               if (ret > 0) {
+                       elt = &kh_value (h, k);
+                       kv_push (const ucl_object_t *, hashlin->ar, obj);
+                       elt->obj = obj;
+                       elt->ar_idx = kv_size (hashlin->ar) - 1;
+               }
+       }
+}
+
+void ucl_hash_replace (ucl_hash_t* hashlin, const ucl_object_t *old,
+               const ucl_object_t *new)
+{
+       khiter_t k;
+       int ret;
+       struct ucl_hash_elt elt, *pelt;
+
+       if (hashlin == NULL) {
+               return;
+       }
+
+       if (hashlin->caseless) {
+               khash_t(ucl_hash_caseless_node) *h = (khash_t(ucl_hash_caseless_node) *)
+                               hashlin->hash;
+               k = kh_put (ucl_hash_caseless_node, h, old, &ret);
+               if (ret == 0) {
+                       elt = kh_value (h, k);
+                       kh_del (ucl_hash_caseless_node, h, k);
+                       k = kh_put (ucl_hash_caseless_node, h, new, &ret);
+                       pelt = &kh_value (h, k);
+                       pelt->obj = new;
+                       pelt->ar_idx = elt.ar_idx;
+                       kv_A (hashlin->ar, elt.ar_idx) = new;
+               }
+       }
+       else {
+               khash_t(ucl_hash_node) *h = (khash_t(ucl_hash_node) *)
+                               hashlin->hash;
+               k = kh_put (ucl_hash_node, h, old, &ret);
+               if (ret == 0) {
+                       elt = kh_value (h, k);
+                       kh_del (ucl_hash_node, h, k);
+                       k = kh_put (ucl_hash_node, h, new, &ret);
+                       pelt = &kh_value (h, k);
+                       pelt->obj = new;
+                       pelt->ar_idx = elt.ar_idx;
+                       kv_A (hashlin->ar, elt.ar_idx) = new;
+               }
+       }
+}
+
+struct ucl_hash_real_iter {
+       const ucl_object_t **cur;
+       const ucl_object_t **end;
+};
+
+const void*
+ucl_hash_iterate (ucl_hash_t *hashlin, ucl_hash_iter_t *iter)
+{
+       struct ucl_hash_real_iter *it = (struct ucl_hash_real_iter *)(*iter);
+       const ucl_object_t *ret = NULL;
+
+       if (hashlin == NULL) {
+               return NULL;
+       }
+
+       if (it == NULL) {
+               it = UCL_ALLOC (sizeof (*it));
+               it->cur = &hashlin->ar.a[0];
+               it->end = it->cur + hashlin->ar.n;
+       }
+
+       if (it->cur < it->end) {
+               ret = *it->cur++;
+       }
+       else {
+               UCL_FREE (sizeof (*it), it);
+               *iter = NULL;
+               return NULL;
+       }
+
+       *iter = it;
+
+       return ret;
+}
+
+bool
+ucl_hash_iter_has_next (ucl_hash_t *hashlin, ucl_hash_iter_t iter)
+{
+       struct ucl_hash_real_iter *it = (struct ucl_hash_real_iter *)(iter);
+
+       return it->cur < it->end - 1;
+}
+
+
+const ucl_object_t*
+ucl_hash_search (ucl_hash_t* hashlin, const char *key, unsigned keylen)
+{
+       khiter_t k;
+       const ucl_object_t *ret = NULL;
+       ucl_object_t search;
+       struct ucl_hash_elt *elt;
+
+       search.key = key;
+       search.keylen = keylen;
+
+       if (hashlin == NULL) {
+               return NULL;
+       }
+
+       if (hashlin->caseless) {
+               khash_t(ucl_hash_caseless_node) *h = (khash_t(ucl_hash_caseless_node) *)
+                                               hashlin->hash;
+
+               k = kh_get (ucl_hash_caseless_node, h, &search);
+               if (k != kh_end (h)) {
+                       elt = &kh_value (h, k);
+                       ret = elt->obj;
+               }
+       }
+       else {
+               khash_t(ucl_hash_node) *h = (khash_t(ucl_hash_node) *)
+                                               hashlin->hash;
+               k = kh_get (ucl_hash_node, h, &search);
+               if (k != kh_end (h)) {
+                       elt = &kh_value (h, k);
+                       ret = elt->obj;
+               }
+       }
+
+       return ret;
+}
+
+void
+ucl_hash_delete (ucl_hash_t* hashlin, const ucl_object_t *obj)
+{
+       khiter_t k;
+       struct ucl_hash_elt *elt;
+
+       if (hashlin == NULL) {
+               return;
+       }
+
+       if (hashlin->caseless) {
+               khash_t(ucl_hash_caseless_node) *h = (khash_t(ucl_hash_caseless_node) *)
+                       hashlin->hash;
+
+               k = kh_get (ucl_hash_caseless_node, h, obj);
+               if (k != kh_end (h)) {
+                       elt = &kh_value (h, k);
+                       kv_A (hashlin->ar, elt->ar_idx) = NULL;
+                       kh_del (ucl_hash_caseless_node, h, k);
+               }
+       }
+       else {
+               khash_t(ucl_hash_node) *h = (khash_t(ucl_hash_node) *)
+                       hashlin->hash;
+               k = kh_get (ucl_hash_node, h, obj);
+               if (k != kh_end (h)) {
+                       elt = &kh_value (h, k);
+                       kv_A (hashlin->ar, elt->ar_idx) = NULL;
+                       kh_del (ucl_hash_node, h, k);
+               }
+       }
+}
diff --git a/contrib/libucl/ucl_hash.h b/contrib/libucl/ucl_hash.h
new file mode 100644 (file)
index 0000000..64c83ea
--- /dev/null
@@ -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"
+
+/******************************************************************************/
+
+struct ucl_hash_node_s;
+typedef struct ucl_hash_node_s 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.
+ */
+struct ucl_hash_struct;
+typedef struct ucl_hash_struct ucl_hash_t;
+
+
+/**
+ * Initializes the hashtable.
+ */
+ucl_hash_t* ucl_hash_create (bool ignore_case);
+
+/**
+ * Deinitializes the hashtable.
+ */
+void ucl_hash_destroy (ucl_hash_t* hashlin, ucl_hash_free_func *func);
+
+/**
+ * Inserts an element in the the hashtable.
+ */
+void ucl_hash_insert (ucl_hash_t* hashlin, const ucl_object_t *obj, const char *key,
+               unsigned keylen);
+
+/**
+ * Replace element in the hash
+ */
+void ucl_hash_replace (ucl_hash_t* hashlin, const ucl_object_t *old,
+               const ucl_object_t *new);
+
+/**
+ * Delete an element from the the hashtable.
+ */
+void ucl_hash_delete (ucl_hash_t* hashlin, const ucl_object_t *obj);
+
+/**
+ * Searches an element in the hashtable.
+ */
+const ucl_object_t* ucl_hash_search (ucl_hash_t* hashlin, const char *key,
+               unsigned keylen);
+
+
+/**
+ * Iterate over hash table
+ * @param hashlin hash
+ * @param iter iterator (must be NULL on first iteration)
+ * @return the next object
+ */
+const void* ucl_hash_iterate (ucl_hash_t *hashlin, ucl_hash_iter_t *iter);
+
+/**
+ * Check whether an iterator has next element
+ */
+bool ucl_hash_iter_has_next (ucl_hash_t *hashlin, ucl_hash_iter_t iter);
+
+#endif
diff --git a/contrib/libucl/ucl_internal.h b/contrib/libucl/ucl_internal.h
new file mode 100644 (file)
index 0000000..bdbe691
--- /dev/null
@@ -0,0 +1,399 @@
+/* Copyright (c) 2013, Vsevolod Stakhov
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *       * Redistributions of source code must retain the above copyright
+ *         notice, this list of conditions and the following disclaimer.
+ *       * Redistributions in binary form must reproduce the above copyright
+ *         notice, this list of conditions and the following disclaimer in the
+ *         documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef UCL_INTERNAL_H_
+#define UCL_INTERNAL_H_
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#else
+/* Help embedded builds */
+#define HAVE_SYS_TYPES_H
+#define HAVE_SYS_MMAN_H
+#define HAVE_SYS_STAT_H
+#define HAVE_SYS_PARAM_H
+#define HAVE_LIMITS_H
+#define HAVE_FCNTL_H
+#define HAVE_ERRNO_H
+#define HAVE_UNISTD_H
+#define HAVE_CTYPE_H
+#define HAVE_STDIO_H
+#define HAVE_STRING_H
+#define HAVE_FLOAT_H
+#define HAVE_LIBGEN_H
+#define HAVE_MATH_H
+#define HAVE_STDBOOL_H
+#define HAVE_STDINT_H
+#define HAVE_STDARG_H
+#ifndef _WIN32
+# define HAVE_REGEX_H
+#endif
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#ifdef HAVE_SYS_MMAN_H
+# ifndef _WIN32
+#  include <sys/mman.h>
+# endif
+#endif
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_CTYPE_H
+#include <ctype.h>
+#endif
+#ifdef HAVE_STDIO_H
+#include <stdio.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "utlist.h"
+#include "utstring.h"
+#include "uthash.h"
+#include "ucl.h"
+#include "ucl_hash.h"
+#include "xxhash.h"
+
+#ifdef HAVE_OPENSSL
+#include <openssl/evp.h>
+#endif
+
+#ifndef __DECONST
+#define __DECONST(type, var)    ((type)(uintptr_t)(const void *)(var))
+#endif
+
+/**
+ * @file rcl_internal.h
+ * Internal structures and functions of UCL library
+ */
+
+#define UCL_MAX_RECURSION 16
+#define UCL_TRASH_KEY 0
+#define UCL_TRASH_VALUE 1
+
+enum ucl_parser_state {
+       UCL_STATE_INIT = 0,
+       UCL_STATE_OBJECT,
+       UCL_STATE_ARRAY,
+       UCL_STATE_KEY,
+       UCL_STATE_VALUE,
+       UCL_STATE_AFTER_VALUE,
+       UCL_STATE_ARRAY_VALUE,
+       UCL_STATE_SCOMMENT,
+       UCL_STATE_MCOMMENT,
+       UCL_STATE_MACRO_NAME,
+       UCL_STATE_MACRO,
+       UCL_STATE_ERROR
+};
+
+enum ucl_character_type {
+       UCL_CHARACTER_DENIED = 0,
+       UCL_CHARACTER_KEY = 1,
+       UCL_CHARACTER_KEY_START = 1 << 1,
+       UCL_CHARACTER_WHITESPACE = 1 << 2,
+       UCL_CHARACTER_WHITESPACE_UNSAFE = 1 << 3,
+       UCL_CHARACTER_VALUE_END = 1 << 4,
+       UCL_CHARACTER_VALUE_STR = 1 << 5,
+       UCL_CHARACTER_VALUE_DIGIT = 1 << 6,
+       UCL_CHARACTER_VALUE_DIGIT_START = 1 << 7,
+       UCL_CHARACTER_ESCAPE = 1 << 8,
+       UCL_CHARACTER_KEY_SEP = 1 << 9,
+       UCL_CHARACTER_JSON_UNSAFE = 1 << 10,
+       UCL_CHARACTER_UCL_UNSAFE = 1 << 11
+};
+
+struct ucl_macro {
+       char *name;
+       ucl_macro_handler handler;
+       void* ud;
+       UT_hash_handle hh;
+};
+
+struct ucl_stack {
+       ucl_object_t *obj;
+       struct ucl_stack *next;
+       int level;
+};
+
+struct ucl_chunk {
+       const unsigned char *begin;
+       const unsigned char *end;
+       const unsigned char *pos;
+       size_t remain;
+       unsigned int line;
+       unsigned int column;
+       unsigned priority;
+       struct ucl_chunk *next;
+};
+
+#ifdef HAVE_OPENSSL
+struct ucl_pubkey {
+       EVP_PKEY *key;
+       struct ucl_pubkey *next;
+};
+#else
+struct ucl_pubkey {
+       struct ucl_pubkey *next;
+};
+#endif
+
+struct ucl_variable {
+       char *var;
+       char *value;
+       size_t var_len;
+       size_t value_len;
+       struct ucl_variable *prev, *next;
+};
+
+struct ucl_parser {
+       enum ucl_parser_state state;
+       enum ucl_parser_state prev_state;
+       unsigned int recursion;
+       int flags;
+       ucl_object_t *top_obj;
+       ucl_object_t *cur_obj;
+       char *cur_file;
+       struct ucl_macro *macroes;
+       struct ucl_stack *stack;
+       struct ucl_chunk *chunks;
+       struct ucl_pubkey *keys;
+       struct ucl_variable *variables;
+       ucl_variable_handler var_handler;
+       void *var_data;
+       UT_string *err;
+};
+
+struct ucl_object_userdata {
+       ucl_object_t obj;
+       ucl_userdata_dtor dtor;
+       ucl_userdata_emitter emitter;
+};
+
+/**
+ * Unescape json string inplace
+ * @param str
+ */
+size_t ucl_unescape_json_string (char *str, size_t len);
+
+/**
+ * Handle include macro
+ * @param data include data
+ * @param len length of data
+ * @param ud user data
+ * @param err error ptr
+ * @return
+ */
+bool ucl_include_handler (const unsigned char *data, size_t len,
+               const ucl_object_t *args, void* ud);
+
+bool ucl_try_include_handler (const unsigned char *data, size_t len,
+               const ucl_object_t *args, void* ud);
+
+/**
+ * Handle includes macro
+ * @param data include data
+ * @param len length of data
+ * @param ud user data
+ * @param err error ptr
+ * @return
+ */
+bool ucl_includes_handler (const unsigned char *data, size_t len,
+               const ucl_object_t *args, void* ud);
+
+size_t ucl_strlcpy (char *dst, const char *src, size_t siz);
+size_t ucl_strlcpy_unsafe (char *dst, const char *src, size_t siz);
+size_t ucl_strlcpy_tolower (char *dst, const char *src, size_t siz);
+
+
+#ifdef __GNUC__
+static inline void
+ucl_create_err (UT_string **err, const char *fmt, ...)
+__attribute__ (( format( printf, 2, 3) ));
+#endif
+
+static inline void
+ucl_create_err (UT_string **err, const char *fmt, ...)
+
+{
+       if (*err == NULL) {
+               utstring_new (*err);
+               va_list ap;
+               va_start (ap, fmt);
+               utstring_printf_va (*err, fmt, ap);
+               va_end (ap);
+       }
+}
+
+/**
+ * Check whether a given string contains a boolean value
+ * @param obj object to set
+ * @param start start of a string
+ * @param len length of a string
+ * @return true if a string is a boolean value
+ */
+static inline bool
+ucl_maybe_parse_boolean (ucl_object_t *obj, const unsigned char *start, size_t len)
+{
+       const char *p = (const char *)start;
+       bool ret = false, val = false;
+
+       if (len == 5) {
+               if ((p[0] == 'f' || p[0] == 'F') && strncasecmp (p, "false", 5) == 0) {
+                       ret = true;
+                       val = false;
+               }
+       }
+       else if (len == 4) {
+               if ((p[0] == 't' || p[0] == 'T') && strncasecmp (p, "true", 4) == 0) {
+                       ret = true;
+                       val = true;
+               }
+       }
+       else if (len == 3) {
+               if ((p[0] == 'y' || p[0] == 'Y') && strncasecmp (p, "yes", 3) == 0) {
+                       ret = true;
+                       val = true;
+               }
+               else if ((p[0] == 'o' || p[0] == 'O') && strncasecmp (p, "off", 3) == 0) {
+                       ret = true;
+                       val = false;
+               }
+       }
+       else if (len == 2) {
+               if ((p[0] == 'n' || p[0] == 'N') && strncasecmp (p, "no", 2) == 0) {
+                       ret = true;
+                       val = false;
+               }
+               else if ((p[0] == 'o' || p[0] == 'O') && strncasecmp (p, "on", 2) == 0) {
+                       ret = true;
+                       val = true;
+               }
+       }
+
+       if (ret) {
+               obj->type = UCL_BOOLEAN;
+               obj->value.iv = val;
+       }
+
+       return ret;
+}
+
+/**
+ * Check numeric string
+ * @param obj object to set if a string is numeric
+ * @param start start of string
+ * @param end end of string
+ * @param pos position where parsing has stopped
+ * @param allow_double allow parsing of floating point values
+ * @return 0 if string is numeric and error code (EINVAL or ERANGE) in case of conversion error
+ */
+int ucl_maybe_parse_number (ucl_object_t *obj,
+               const char *start, const char *end, const char **pos,
+               bool allow_double, bool number_bytes, bool allow_time);
+
+
+static inline const ucl_object_t *
+ucl_hash_search_obj (ucl_hash_t* hashlin, ucl_object_t *obj)
+{
+       return (const ucl_object_t *)ucl_hash_search (hashlin, obj->key, obj->keylen);
+}
+
+static inline ucl_hash_t * ucl_hash_insert_object (ucl_hash_t *hashlin,
+               const ucl_object_t *obj,
+               bool ignore_case) UCL_WARN_UNUSED_RESULT;
+
+static inline ucl_hash_t *
+ucl_hash_insert_object (ucl_hash_t *hashlin,
+               const ucl_object_t *obj,
+               bool ignore_case)
+{
+       if (hashlin == NULL) {
+               hashlin = ucl_hash_create (ignore_case);
+       }
+       ucl_hash_insert (hashlin, obj, obj->key, obj->keylen);
+
+       return hashlin;
+}
+
+/**
+ * Get standard emitter context for a specified emit_type
+ * @param emit_type type of emitter
+ * @return context or NULL if input is invalid
+ */
+const struct ucl_emitter_context *
+ucl_emit_get_standard_context (enum ucl_emitter emit_type);
+
+/**
+ * Serialize string as JSON string
+ * @param str string to emit
+ * @param buf target buffer
+ */
+void ucl_elt_string_write_json (const char *str, size_t size,
+               struct ucl_emitter_context *ctx);
+
+/**
+ * Write multiline string using `EOD` as string terminator
+ * @param str
+ * @param size
+ * @param ctx
+ */
+void ucl_elt_string_write_multiline (const char *str, size_t size,
+               struct ucl_emitter_context *ctx);
+
+/**
+ * Emit a single object to string
+ * @param obj
+ * @return
+ */
+unsigned char * ucl_object_emit_single_json (const ucl_object_t *obj);
+
+/**
+ * Check whether a specified string is long and should be likely printed in
+ * multiline mode
+ * @param obj
+ * @return
+ */
+bool ucl_maybe_long_string (const ucl_object_t *obj);
+
+#endif /* UCL_INTERNAL_H_ */
diff --git a/contrib/libucl/ucl_parser.c b/contrib/libucl/ucl_parser.c
new file mode 100644 (file)
index 0000000..704eea1
--- /dev/null
@@ -0,0 +1,2222 @@
+/* Copyright (c) 2013, Vsevolod Stakhov
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *       * Redistributions of source code must retain the above copyright
+ *         notice, this list of conditions and the following disclaimer.
+ *       * Redistributions in binary form must reproduce the above copyright
+ *         notice, this list of conditions and the following disclaimer in the
+ *         documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "ucl.h"
+#include "ucl_internal.h"
+#include "ucl_chartable.h"
+
+/**
+ * @file ucl_parser.c
+ * The implementation of ucl parser
+ */
+
+struct ucl_parser_saved_state {
+       unsigned int line;
+       unsigned int column;
+       size_t remain;
+       const unsigned char *pos;
+};
+
+/**
+ * Move up to len characters
+ * @param parser
+ * @param begin
+ * @param len
+ * @return new position in chunk
+ */
+#define ucl_chunk_skipc(chunk, p)    do{                                       \
+    if (*(p) == '\n') {                                                                                \
+        (chunk)->line ++;                                                                      \
+        (chunk)->column = 0;                                                           \
+    }                                                                                                          \
+    else (chunk)->column ++;                                                           \
+    (p++);                                                                                                     \
+    (chunk)->pos ++;                                                                           \
+    (chunk)->remain --;                                                                                \
+    } while (0)
+
+static inline void
+ucl_set_err (struct ucl_parser *parser, int code, const char *str, UT_string **err)
+{
+       const char *fmt_string, *filename;
+       struct ucl_chunk *chunk = parser->chunks;
+
+       if (parser->cur_file) {
+               filename = parser->cur_file;
+       }
+       else {
+               filename = "<unknown>";
+       }
+       if (chunk->pos < chunk->end) {
+               if (isgraph (*chunk->pos)) {
+                       fmt_string = "error while parsing %s: "
+                                       "line: %d, column: %d - '%s', character: '%c'";
+               }
+               else {
+                       fmt_string = "error while parsing %s: "
+                                       "line: %d, column: %d - '%s', character: '0x%02x'";
+               }
+               ucl_create_err (err, fmt_string,
+                       filename, chunk->line, chunk->column,
+                       str, *chunk->pos);
+       }
+       else {
+               ucl_create_err (err, "error while parsing %s: at the end of chunk: %s",
+                       filename, str);
+       }
+}
+
+/**
+ * Skip all comments from the current pos resolving nested and multiline comments
+ * @param parser
+ * @return
+ */
+static bool
+ucl_skip_comments (struct ucl_parser *parser)
+{
+       struct ucl_chunk *chunk = parser->chunks;
+       const unsigned char *p;
+       int comments_nested = 0;
+       bool quoted = false;
+
+       p = chunk->pos;
+
+start:
+       if (chunk->remain > 0 && *p == '#') {
+               if (parser->state != UCL_STATE_SCOMMENT &&
+                               parser->state != UCL_STATE_MCOMMENT) {
+                       while (p < chunk->end) {
+                               if (*p == '\n') {
+                                       ucl_chunk_skipc (chunk, p);
+                                       goto start;
+                               }
+                               ucl_chunk_skipc (chunk, p);
+                       }
+               }
+       }
+       else if (chunk->remain >= 2 && *p == '/') {
+               if (p[1] == '*') {
+                       ucl_chunk_skipc (chunk, p);
+                       comments_nested ++;
+                       ucl_chunk_skipc (chunk, p);
+
+                       while (p < chunk->end) {
+                               if (*p == '"' && *(p - 1) != '\\') {
+                                       quoted = !quoted;
+                               }
+
+                               if (!quoted) {
+                                       if (*p == '*') {
+                                               ucl_chunk_skipc (chunk, p);
+                                               if (*p == '/') {
+                                                       comments_nested --;
+                                                       if (comments_nested == 0) {
+                                                               ucl_chunk_skipc (chunk, p);
+                                                               goto start;
+                                                       }
+                                               }
+                                               ucl_chunk_skipc (chunk, p);
+                                       }
+                                       else if (p[0] == '/' && chunk->remain >= 2 && p[1] == '*') {
+                                               comments_nested ++;
+                                               ucl_chunk_skipc (chunk, p);
+                                               ucl_chunk_skipc (chunk, p);
+                                               continue;
+                                       }
+                               }
+                               ucl_chunk_skipc (chunk, p);
+                       }
+                       if (comments_nested != 0) {
+                               ucl_set_err (parser, UCL_ENESTED,
+                                               "unfinished multiline comment", &parser->err);
+                               return false;
+                       }
+               }
+       }
+
+       return true;
+}
+
+/**
+ * Return multiplier for a character
+ * @param c multiplier character
+ * @param is_bytes if true use 1024 multiplier
+ * @return multiplier
+ */
+static inline unsigned long
+ucl_lex_num_multiplier (const unsigned char c, bool is_bytes) {
+       const struct {
+               char c;
+               long mult_normal;
+               long mult_bytes;
+       } multipliers[] = {
+                       {'m', 1000 * 1000, 1024 * 1024},
+                       {'k', 1000, 1024},
+                       {'g', 1000 * 1000 * 1000, 1024 * 1024 * 1024}
+       };
+       int i;
+
+       for (i = 0; i < 3; i ++) {
+               if (tolower (c) == multipliers[i].c) {
+                       if (is_bytes) {
+                               return multipliers[i].mult_bytes;
+                       }
+                       return multipliers[i].mult_normal;
+               }
+       }
+
+       return 1;
+}
+
+
+/**
+ * Return multiplier for time scaling
+ * @param c
+ * @return
+ */
+static inline double
+ucl_lex_time_multiplier (const unsigned char c) {
+       const struct {
+               char c;
+               double mult;
+       } multipliers[] = {
+                       {'m', 60},
+                       {'h', 60 * 60},
+                       {'d', 60 * 60 * 24},
+                       {'w', 60 * 60 * 24 * 7},
+                       {'y', 60 * 60 * 24 * 7 * 365}
+       };
+       int i;
+
+       for (i = 0; i < 5; i ++) {
+               if (tolower (c) == multipliers[i].c) {
+                       return multipliers[i].mult;
+               }
+       }
+
+       return 1;
+}
+
+/**
+ * Return true if a character is a end of an atom
+ * @param c
+ * @return
+ */
+static inline bool
+ucl_lex_is_atom_end (const unsigned char c)
+{
+       return ucl_test_character (c, UCL_CHARACTER_VALUE_END);
+}
+
+static inline bool
+ucl_lex_is_comment (const unsigned char c1, const unsigned char c2)
+{
+       if (c1 == '/') {
+               if (c2 == '*') {
+                       return true;
+               }
+       }
+       else if (c1 == '#') {
+               return true;
+       }
+       return false;
+}
+
+/**
+ * Check variable found
+ * @param parser
+ * @param ptr
+ * @param remain
+ * @param out_len
+ * @param strict
+ * @param found
+ * @return
+ */
+static inline const char *
+ucl_check_variable_safe (struct ucl_parser *parser, const char *ptr, size_t remain,
+               size_t *out_len, bool strict, bool *found)
+{
+       struct ucl_variable *var;
+       unsigned char *dst;
+       size_t dstlen;
+       bool need_free = false;
+
+       LL_FOREACH (parser->variables, var) {
+               if (strict) {
+                       if (remain == var->var_len) {
+                               if (memcmp (ptr, var->var, var->var_len) == 0) {
+                                       *out_len += var->value_len;
+                                       *found = true;
+                                       return (ptr + var->var_len);
+                               }
+                       }
+               }
+               else {
+                       if (remain >= var->var_len) {
+                               if (memcmp (ptr, var->var, var->var_len) == 0) {
+                                       *out_len += var->value_len;
+                                       *found = true;
+                                       return (ptr + var->var_len);
+                               }
+                       }
+               }
+       }
+
+       /* XXX: can only handle ${VAR} */
+       if (!(*found) && parser->var_handler != NULL && strict) {
+               /* Call generic handler */
+               if (parser->var_handler (ptr, remain, &dst, &dstlen, &need_free,
+                               parser->var_data)) {
+                       *found = true;
+                       if (need_free) {
+                               free (dst);
+                       }
+                       return (ptr + remain);
+               }
+       }
+
+       return ptr;
+}
+
+/**
+ * Check for a variable in a given string
+ * @param parser
+ * @param ptr
+ * @param remain
+ * @param out_len
+ * @param vars_found
+ * @return
+ */
+static const char *
+ucl_check_variable (struct ucl_parser *parser, const char *ptr,
+               size_t remain, size_t *out_len, bool *vars_found)
+{
+       const char *p, *end, *ret = ptr;
+       bool found = false;
+
+       if (*ptr == '{') {
+               /* We need to match the variable enclosed in braces */
+               p = ptr + 1;
+               end = ptr + remain;
+               while (p < end) {
+                       if (*p == '}') {
+                               ret = ucl_check_variable_safe (parser, ptr + 1, p - ptr - 1,
+                                               out_len, true, &found);
+                               if (found) {
+                                       /* {} must be excluded actually */
+                                       ret ++;
+                                       if (!*vars_found) {
+                                               *vars_found = true;
+                                       }
+                               }
+                               else {
+                                       *out_len += 2;
+                               }
+                               break;
+                       }
+                       p ++;
+               }
+       }
+       else if (*ptr != '$') {
+               /* Not count escaped dollar sign */
+               ret = ucl_check_variable_safe (parser, ptr, remain, out_len, false, &found);
+               if (found && !*vars_found) {
+                       *vars_found = true;
+               }
+               if (!found) {
+                       (*out_len) ++;
+               }
+       }
+       else {
+               ret ++;
+               (*out_len) ++;
+       }
+
+       return ret;
+}
+
+/**
+ * Expand a single variable
+ * @param parser
+ * @param ptr
+ * @param remain
+ * @param dest
+ * @return
+ */
+static const char *
+ucl_expand_single_variable (struct ucl_parser *parser, const char *ptr,
+               size_t remain, unsigned char **dest)
+{
+       unsigned char *d = *dest, *dst;
+       const char *p = ptr + 1, *ret;
+       struct ucl_variable *var;
+       size_t dstlen;
+       bool need_free = false;
+       bool found = false;
+       bool strict = false;
+
+       ret = ptr + 1;
+       remain --;
+
+       if (*p == '$') {
+               *d++ = *p++;
+               *dest = d;
+               return p;
+       }
+       else if (*p == '{') {
+               p ++;
+               strict = true;
+               ret += 2;
+               remain -= 2;
+       }
+
+       LL_FOREACH (parser->variables, var) {
+               if (remain >= var->var_len) {
+                       if (memcmp (p, var->var, var->var_len) == 0) {
+                               memcpy (d, var->value, var->value_len);
+                               ret += var->var_len;
+                               d += var->value_len;
+                               found = true;
+                               break;
+                       }
+               }
+       }
+       if (!found) {
+               if (strict && parser->var_handler != NULL) {
+                       if (parser->var_handler (ptr, remain, &dst, &dstlen, &need_free,
+                                                       parser->var_data)) {
+                               memcpy (d, dst, dstlen);
+                               ret += dstlen;
+                               d += remain;
+                               found = true;
+                       }
+               }
+
+               /* Leave variable as is */
+               if (!found) {
+                       if (strict) {
+                               /* Copy '${' */
+                               memcpy (d, ptr, 2);
+                               d += 2;
+                               ret --;
+                       }
+                       else {
+                               memcpy (d, ptr, 1);
+                               d ++;
+                       }
+               }
+       }
+
+       *dest = d;
+       return ret;
+}
+
+/**
+ * Expand variables in string
+ * @param parser
+ * @param dst
+ * @param src
+ * @param in_len
+ * @return
+ */
+static ssize_t
+ucl_expand_variable (struct ucl_parser *parser, unsigned char **dst,
+               const char *src, size_t in_len)
+{
+       const char *p, *end = src + in_len;
+       unsigned char *d;
+       size_t out_len = 0;
+       bool vars_found = false;
+
+       p = src;
+       while (p != end) {
+               if (*p == '$') {
+                       p = ucl_check_variable (parser, p + 1, end - p - 1, &out_len, &vars_found);
+               }
+               else {
+                       p ++;
+                       out_len ++;
+               }
+       }
+
+       if (!vars_found) {
+               /* Trivial case */
+               *dst = NULL;
+               return in_len;
+       }
+
+       *dst = UCL_ALLOC (out_len + 1);
+       if (*dst == NULL) {
+               return in_len;
+       }
+
+       d = *dst;
+       p = src;
+       while (p != end) {
+               if (*p == '$') {
+                       p = ucl_expand_single_variable (parser, p, end - p, &d);
+               }
+               else {
+                       *d++ = *p++;
+               }
+       }
+
+       *d = '\0';
+
+       return out_len;
+}
+
+/**
+ * Store or copy pointer to the trash stack
+ * @param parser parser object
+ * @param src src string
+ * @param dst destination buffer (trash stack pointer)
+ * @param dst_const const destination pointer (e.g. value of object)
+ * @param in_len input length
+ * @param need_unescape need to unescape source (and copy it)
+ * @param need_lowercase need to lowercase value (and copy)
+ * @param need_expand need to expand variables (and copy as well)
+ * @return output length (excluding \0 symbol)
+ */
+static inline ssize_t
+ucl_copy_or_store_ptr (struct ucl_parser *parser,
+               const unsigned char *src, unsigned char **dst,
+               const char **dst_const, size_t in_len,
+               bool need_unescape, bool need_lowercase, bool need_expand)
+{
+       ssize_t ret = -1, tret;
+       unsigned char *tmp;
+
+       if (need_unescape || need_lowercase ||
+                       (need_expand && parser->variables != NULL) ||
+                       !(parser->flags & UCL_PARSER_ZEROCOPY)) {
+               /* Copy string */
+               *dst = UCL_ALLOC (in_len + 1);
+               if (*dst == NULL) {
+                       ucl_set_err (parser, 0, "cannot allocate memory for a string",
+                                       &parser->err);
+                       return false;
+               }
+               if (need_lowercase) {
+                       ret = ucl_strlcpy_tolower (*dst, src, in_len + 1);
+               }
+               else {
+                       ret = ucl_strlcpy_unsafe (*dst, src, in_len + 1);
+               }
+
+               if (need_unescape) {
+                       ret = ucl_unescape_json_string (*dst, ret);
+               }
+               if (need_expand) {
+                       tmp = *dst;
+                       tret = ret;
+                       ret = ucl_expand_variable (parser, dst, tmp, ret);
+                       if (*dst == NULL) {
+                               /* Nothing to expand */
+                               *dst = tmp;
+                               ret = tret;
+                       }
+                       else {
+                               /* Free unexpanded value */
+                               UCL_FREE (in_len + 1, tmp);
+                       }
+               }
+               *dst_const = *dst;
+       }
+       else {
+               *dst_const = src;
+               ret = in_len;
+       }
+
+       return ret;
+}
+
+/**
+ * Create and append an object at the specified level
+ * @param parser
+ * @param is_array
+ * @param level
+ * @return
+ */
+static inline ucl_object_t *
+ucl_add_parser_stack (ucl_object_t *obj, struct ucl_parser *parser, bool is_array, int level)
+{
+       struct ucl_stack *st;
+
+       if (!is_array) {
+               if (obj == NULL) {
+                       obj = ucl_object_new_full (UCL_OBJECT, parser->chunks->priority);
+               }
+               else {
+                       obj->type = UCL_OBJECT;
+               }
+               obj->value.ov = ucl_hash_create (parser->flags & UCL_PARSER_KEY_LOWERCASE);
+               parser->state = UCL_STATE_KEY;
+       }
+       else {
+               if (obj == NULL) {
+                       obj = ucl_object_new_full (UCL_ARRAY, parser->chunks->priority);
+               }
+               else {
+                       obj->type = UCL_ARRAY;
+               }
+               parser->state = UCL_STATE_VALUE;
+       }
+
+       st = UCL_ALLOC (sizeof (struct ucl_stack));
+       if (st == NULL) {
+               ucl_set_err (parser, 0, "cannot allocate memory for an object",
+                               &parser->err);
+               ucl_object_unref (obj);
+               return NULL;
+       }
+       st->obj = obj;
+       st->level = level;
+       LL_PREPEND (parser->stack, st);
+       parser->cur_obj = obj;
+
+       return obj;
+}
+
+int
+ucl_maybe_parse_number (ucl_object_t *obj,
+               const char *start, const char *end, const char **pos,
+               bool allow_double, bool number_bytes, bool allow_time)
+{
+       const char *p = start, *c = start;
+       char *endptr;
+       bool got_dot = false, got_exp = false, need_double = false,
+                       is_time = false, valid_start = false, is_hex = false,
+                       is_neg = false;
+       double dv = 0;
+       int64_t lv = 0;
+
+       if (*p == '-') {
+               is_neg = true;
+               c ++;
+               p ++;
+       }
+       while (p < end) {
+               if (is_hex && isxdigit (*p)) {
+                       p ++;
+               }
+               else if (isdigit (*p)) {
+                       valid_start = true;
+                       p ++;
+               }
+               else if (!is_hex && (*p == 'x' || *p == 'X')) {
+                       is_hex = true;
+                       allow_double = false;
+                       c = p + 1;
+               }
+               else if (allow_double) {
+                       if (p == c) {
+                               /* Empty digits sequence, not a number */
+                               *pos = start;
+                               return EINVAL;
+                       }
+                       else if (*p == '.') {
+                               if (got_dot) {
+                                       /* Double dots, not a number */
+                                       *pos = start;
+                                       return EINVAL;
+                               }
+                               else {
+                                       got_dot = true;
+                                       need_double = true;
+                                       p ++;
+                               }
+                       }
+                       else if (*p == 'e' || *p == 'E') {
+                               if (got_exp) {
+                                       /* Double exp, not a number */
+                                       *pos = start;
+                                       return EINVAL;
+                               }
+                               else {
+                                       got_exp = true;
+                                       need_double = true;
+                                       p ++;
+                                       if (p >= end) {
+                                               *pos = start;
+                                               return EINVAL;
+                                       }
+                                       if (!isdigit (*p) && *p != '+' && *p != '-') {
+                                               /* Wrong exponent sign */
+                                               *pos = start;
+                                               return EINVAL;
+                                       }
+                                       else {
+                                               p ++;
+                                       }
+                               }
+                       }
+                       else {
+                               /* Got the end of the number, need to check */
+                               break;
+                       }
+               }
+               else {
+                       break;
+               }
+       }
+
+       if (!valid_start) {
+               *pos = start;
+               return EINVAL;
+       }
+
+       errno = 0;
+       if (need_double) {
+               dv = strtod (c, &endptr);
+       }
+       else {
+               if (is_hex) {
+                       lv = strtoimax (c, &endptr, 16);
+               }
+               else {
+                       lv = strtoimax (c, &endptr, 10);
+               }
+       }
+       if (errno == ERANGE) {
+               *pos = start;
+               return ERANGE;
+       }
+
+       /* Now check endptr */
+       if (endptr == NULL || ucl_lex_is_atom_end (*endptr) || *endptr == '\0') {
+               p = endptr;
+               goto set_obj;
+       }
+
+       if (endptr < end && endptr != start) {
+               p = endptr;
+               switch (*p) {
+               case 'm':
+               case 'M':
+               case 'g':
+               case 'G':
+               case 'k':
+               case 'K':
+                       if (end - p >= 2) {
+                               if (p[1] == 's' || p[1] == 'S') {
+                                       /* Milliseconds */
+                                       if (!need_double) {
+                                               need_double = true;
+                                               dv = lv;
+                                       }
+                                       is_time = true;
+                                       if (p[0] == 'm' || p[0] == 'M') {
+                                               dv /= 1000.;
+                                       }
+                                       else {
+                                               dv *= ucl_lex_num_multiplier (*p, false);
+                                       }
+                                       p += 2;
+                                       goto set_obj;
+                               }
+                               else if (number_bytes || (p[1] == 'b' || p[1] == 'B')) {
+                                       /* Bytes */
+                                       if (need_double) {
+                                               need_double = false;
+                                               lv = dv;
+                                       }
+                                       lv *= ucl_lex_num_multiplier (*p, true);
+                                       p += 2;
+                                       goto set_obj;
+                               }
+                               else if (ucl_lex_is_atom_end (p[1])) {
+                                       if (need_double) {
+                                               dv *= ucl_lex_num_multiplier (*p, false);
+                                       }
+                                       else {
+                                               lv *= ucl_lex_num_multiplier (*p, number_bytes);
+                                       }
+                                       p ++;
+                                       goto set_obj;
+                               }
+                               else if (allow_time && end - p >= 3) {
+                                       if (tolower (p[0]) == 'm' &&
+                                                       tolower (p[1]) == 'i' &&
+                                                       tolower (p[2]) == 'n') {
+                                               /* Minutes */
+                                               if (!need_double) {
+                                                       need_double = true;
+                                                       dv = lv;
+                                               }
+                                               is_time = true;
+                                               dv *= 60.;
+                                               p += 3;
+                                               goto set_obj;
+                                       }
+                               }
+                       }
+                       else {
+                               if (need_double) {
+                                       dv *= ucl_lex_num_multiplier (*p, false);
+                               }
+                               else {
+                                       lv *= ucl_lex_num_multiplier (*p, number_bytes);
+                               }
+                               p ++;
+                               goto set_obj;
+                       }
+                       break;
+               case 'S':
+               case 's':
+                       if (allow_time &&
+                                       (p == end - 1 || ucl_lex_is_atom_end (p[1]))) {
+                               if (!need_double) {
+                                       need_double = true;
+                                       dv = lv;
+                               }
+                               p ++;
+                               is_time = true;
+                               goto set_obj;
+                       }
+                       break;
+               case 'h':
+               case 'H':
+               case 'd':
+               case 'D':
+               case 'w':
+               case 'W':
+               case 'Y':
+               case 'y':
+                       if (allow_time &&
+                                       (p == end - 1 || ucl_lex_is_atom_end (p[1]))) {
+                               if (!need_double) {
+                                       need_double = true;
+                                       dv = lv;
+                               }
+                               is_time = true;
+                               dv *= ucl_lex_time_multiplier (*p);
+                               p ++;
+                               goto set_obj;
+                       }
+                       break;
+               case '\t':
+               case ' ':
+                       while (p < end && ucl_test_character(*p, UCL_CHARACTER_WHITESPACE)) {
+                               p++;
+                       }
+                       if (ucl_lex_is_atom_end(*p))
+                               goto set_obj;
+                       break;
+               }
+       }
+       else if (endptr == end) {
+               /* Just a number at the end of chunk */
+               p = endptr;
+               goto set_obj;
+       }
+
+       *pos = c;
+       return EINVAL;
+
+       set_obj:
+       if (allow_double && (need_double || is_time)) {
+               if (!is_time) {
+                       obj->type = UCL_FLOAT;
+               }
+               else {
+                       obj->type = UCL_TIME;
+               }
+               obj->value.dv = is_neg ? (-dv) : dv;
+       }
+       else {
+               obj->type = UCL_INT;
+               obj->value.iv = is_neg ? (-lv) : lv;
+       }
+       *pos = p;
+       return 0;
+}
+
+/**
+ * Parse possible number
+ * @param parser
+ * @param chunk
+ * @return true if a number has been parsed
+ */
+static bool
+ucl_lex_number (struct ucl_parser *parser,
+               struct ucl_chunk *chunk, ucl_object_t *obj)
+{
+       const unsigned char *pos;
+       int ret;
+
+       ret = ucl_maybe_parse_number (obj, chunk->pos, chunk->end, (const char **)&pos,
+                       true, false, ((parser->flags & UCL_PARSER_NO_TIME) == 0));
+
+       if (ret == 0) {
+               chunk->remain -= pos - chunk->pos;
+               chunk->column += pos - chunk->pos;
+               chunk->pos = pos;
+               return true;
+       }
+       else if (ret == ERANGE) {
+               ucl_set_err (parser, ERANGE, "numeric value out of range", &parser->err);
+       }
+
+       return false;
+}
+
+/**
+ * Parse quoted string with possible escapes
+ * @param parser
+ * @param chunk
+ * @return true if a string has been parsed
+ */
+static bool
+ucl_lex_json_string (struct ucl_parser *parser,
+               struct ucl_chunk *chunk, bool *need_unescape, bool *ucl_escape, bool *var_expand)
+{
+       const unsigned char *p = chunk->pos;
+       unsigned char c;
+       int i;
+
+       while (p < chunk->end) {
+               c = *p;
+               if (c < 0x1F) {
+                       /* Unmasked control character */
+                       if (c == '\n') {
+                               ucl_set_err (parser, UCL_ESYNTAX, "unexpected newline",
+                                               &parser->err);
+                       }
+                       else {
+                               ucl_set_err (parser, UCL_ESYNTAX, "unexpected control character",
+                                               &parser->err);
+                       }
+                       return false;
+               }
+               else if (c == '\\') {
+                       ucl_chunk_skipc (chunk, p);
+                       c = *p;
+                       if (p >= chunk->end) {
+                               ucl_set_err (parser, UCL_ESYNTAX, "unfinished escape character",
+                                               &parser->err);
+                               return false;
+                       }
+                       else if (ucl_test_character (c, UCL_CHARACTER_ESCAPE)) {
+                               if (c == 'u') {
+                                       ucl_chunk_skipc (chunk, p);
+                                       for (i = 0; i < 4 && p < chunk->end; i ++) {
+                                               if (!isxdigit (*p)) {
+                                                       ucl_set_err (parser, UCL_ESYNTAX, "invalid utf escape",
+                                                                       &parser->err);
+                                                       return false;
+                                               }
+                                               ucl_chunk_skipc (chunk, p);
+                                       }
+                                       if (p >= chunk->end) {
+                                               ucl_set_err (parser, UCL_ESYNTAX, "unfinished escape character",
+                                                               &parser->err);
+                                               return false;
+                                       }
+                               }
+                               else {
+                                       ucl_chunk_skipc (chunk, p);
+                               }
+                       }
+                       *need_unescape = true;
+                       *ucl_escape = true;
+                       continue;
+               }
+               else if (c == '"') {
+                       ucl_chunk_skipc (chunk, p);
+                       return true;
+               }
+               else if (ucl_test_character (c, UCL_CHARACTER_UCL_UNSAFE)) {
+                       *ucl_escape = true;
+               }
+               else if (c == '$') {
+                       *var_expand = true;
+               }
+               ucl_chunk_skipc (chunk, p);
+       }
+
+       ucl_set_err (parser, UCL_ESYNTAX, "no quote at the end of json string",
+                       &parser->err);
+       return false;
+}
+
+static void
+ucl_parser_append_elt (struct ucl_parser *parser, ucl_hash_t *cont,
+               ucl_object_t *top,
+               ucl_object_t *elt)
+{
+       ucl_object_t *nobj;
+
+       if ((parser->flags & UCL_PARSER_NO_IMPLICIT_ARRAYS) == 0) {
+               /* Implicit array */
+               top->flags |= UCL_OBJECT_MULTIVALUE;
+               DL_APPEND (top, elt);
+       }
+       else {
+               if ((top->flags & UCL_OBJECT_MULTIVALUE) != 0) {
+                       /* Just add to the explicit array */
+                       ucl_array_append (top, elt);
+               }
+               else {
+                       /* Convert to an array */
+                       ucl_hash_delete (cont, top);
+                       nobj = ucl_object_typed_new (UCL_ARRAY);
+                       nobj->key = top->key;
+                       nobj->keylen = top->keylen;
+                       nobj->flags |= UCL_OBJECT_MULTIVALUE;
+                       ucl_array_append (nobj, top);
+                       ucl_array_append (nobj, elt);
+                       ucl_hash_insert (cont, nobj, nobj->key, nobj->keylen);
+               }
+       }
+}
+
+/**
+ * Parse a key in an object
+ * @param parser
+ * @param chunk
+ * @return true if a key has been parsed
+ */
+static bool
+ucl_parse_key (struct ucl_parser *parser, struct ucl_chunk *chunk, bool *next_key, bool *end_of_object)
+{
+       const unsigned char *p, *c = NULL, *end, *t;
+       const char *key = NULL;
+       bool got_quote = false, got_eq = false, got_semicolon = false,
+                       need_unescape = false, ucl_escape = false, var_expand = false,
+                       got_content = false, got_sep = false;
+       ucl_object_t *nobj, *tobj;
+       ucl_hash_t *container;
+       ssize_t keylen;
+
+       p = chunk->pos;
+
+       if (*p == '.') {
+               /* It is macro actually */
+               ucl_chunk_skipc (chunk, p);
+               parser->prev_state = parser->state;
+               parser->state = UCL_STATE_MACRO_NAME;
+               return true;
+       }
+       while (p < chunk->end) {
+               /*
+                * A key must start with alpha, number, '/' or '_' and end with space character
+                */
+               if (c == NULL) {
+                       if (chunk->remain >= 2 && ucl_lex_is_comment (p[0], p[1])) {
+                               if (!ucl_skip_comments (parser)) {
+                                       return false;
+                               }
+                               p = chunk->pos;
+                       }
+                       else if (ucl_test_character (*p, UCL_CHARACTER_WHITESPACE_UNSAFE)) {
+                               ucl_chunk_skipc (chunk, p);
+                       }
+                       else if (ucl_test_character (*p, UCL_CHARACTER_KEY_START)) {
+                               /* The first symbol */
+                               c = p;
+                               ucl_chunk_skipc (chunk, p);
+                               got_content = true;
+                       }
+                       else if (*p == '"') {
+                               /* JSON style key */
+                               c = p + 1;
+                               got_quote = true;
+                               got_content = true;
+                               ucl_chunk_skipc (chunk, p);
+                       }
+                       else if (*p == '}') {
+                               /* We have actually end of an object */
+                               *end_of_object = true;
+                               return true;
+                       }
+                       else if (*p == '.') {
+                               ucl_chunk_skipc (chunk, p);
+                               parser->prev_state = parser->state;
+                               parser->state = UCL_STATE_MACRO_NAME;
+                               return true;
+                       }
+                       else {
+                               /* Invalid identifier */
+                               ucl_set_err (parser, UCL_ESYNTAX, "key must begin with a letter",
+                                               &parser->err);
+                               return false;
+                       }
+               }
+               else {
+                       /* Parse the body of a key */
+                       if (!got_quote) {
+                               if (ucl_test_character (*p, UCL_CHARACTER_KEY)) {
+                                       got_content = true;
+                                       ucl_chunk_skipc (chunk, p);
+                               }
+                               else if (ucl_test_character (*p, UCL_CHARACTER_KEY_SEP)) {
+                                       end = p;
+                                       break;
+                               }
+                               else {
+                                       ucl_set_err (parser, UCL_ESYNTAX, "invalid character in a key",
+                                                       &parser->err);
+                                       return false;
+                               }
+                       }
+                       else {
+                               /* We need to parse json like quoted string */
+                               if (!ucl_lex_json_string (parser, chunk, &need_unescape, &ucl_escape, &var_expand)) {
+                                       return false;
+                               }
+                               /* Always escape keys obtained via json */
+                               end = chunk->pos - 1;
+                               p = chunk->pos;
+                               break;
+                       }
+               }
+       }
+
+       if (p >= chunk->end && got_content) {
+               ucl_set_err (parser, UCL_ESYNTAX, "unfinished key", &parser->err);
+               return false;
+       }
+       else if (!got_content) {
+               return true;
+       }
+       *end_of_object = false;
+       /* We are now at the end of the key, need to parse the rest */
+       while (p < chunk->end) {
+               if (ucl_test_character (*p, UCL_CHARACTER_WHITESPACE)) {
+                       ucl_chunk_skipc (chunk, p);
+               }
+               else if (*p == '=') {
+                       if (!got_eq && !got_semicolon) {
+                               ucl_chunk_skipc (chunk, p);
+                               got_eq = true;
+                       }
+                       else {
+                               ucl_set_err (parser, UCL_ESYNTAX, "unexpected '=' character",
+                                               &parser->err);
+                               return false;
+                       }
+               }
+               else if (*p == ':') {
+                       if (!got_eq && !got_semicolon) {
+                               ucl_chunk_skipc (chunk, p);
+                               got_semicolon = true;
+                       }
+                       else {
+                               ucl_set_err (parser, UCL_ESYNTAX, "unexpected ':' character",
+                                               &parser->err);
+                               return false;
+                       }
+               }
+               else if (chunk->remain >= 2 && ucl_lex_is_comment (p[0], p[1])) {
+                       /* Check for comment */
+                       if (!ucl_skip_comments (parser)) {
+                               return false;
+                       }
+                       p = chunk->pos;
+               }
+               else {
+                       /* Start value */
+                       break;
+               }
+       }
+
+       if (p >= chunk->end && got_content) {
+               ucl_set_err (parser, UCL_ESYNTAX, "unfinished key", &parser->err);
+               return false;
+       }
+
+       got_sep = got_semicolon || got_eq;
+
+       if (!got_sep) {
+               /*
+                * Maybe we have more keys nested, so search for termination character.
+                * Possible choices:
+                * 1) key1 key2 ... keyN [:=] value <- we treat that as error
+                * 2) key1 ... keyN {} or [] <- we treat that as nested objects
+                * 3) key1 value[;,\n] <- we treat that as linear object
+                */
+               t = p;
+               *next_key = false;
+               while (ucl_test_character (*t, UCL_CHARACTER_WHITESPACE)) {
+                       t ++;
+               }
+               /* Check first non-space character after a key */
+               if (*t != '{' && *t != '[') {
+                       while (t < chunk->end) {
+                               if (*t == ',' || *t == ';' || *t == '\n' || *t == '\r') {
+                                       break;
+                               }
+                               else if (*t == '{' || *t == '[') {
+                                       *next_key = true;
+                                       break;
+                               }
+                               t ++;
+                       }
+               }
+       }
+
+       /* Create a new object */
+       nobj = ucl_object_new_full (UCL_NULL, parser->chunks->priority);
+       keylen = ucl_copy_or_store_ptr (parser, c, &nobj->trash_stack[UCL_TRASH_KEY],
+                       &key, end - c, need_unescape, parser->flags & UCL_PARSER_KEY_LOWERCASE, false);
+       if (keylen == -1) {
+               ucl_object_unref (nobj);
+               return false;
+       }
+       else if (keylen == 0) {
+               ucl_set_err (parser, UCL_ESYNTAX, "empty keys are not allowed", &parser->err);
+               ucl_object_unref (nobj);
+               return false;
+       }
+
+       container = parser->stack->obj->value.ov;
+       nobj->key = key;
+       nobj->keylen = keylen;
+       tobj = __DECONST (ucl_object_t *, ucl_hash_search_obj (container, nobj));
+       if (tobj == NULL) {
+               container = ucl_hash_insert_object (container, nobj,
+                               parser->flags & UCL_PARSER_KEY_LOWERCASE);
+               nobj->prev = nobj;
+               nobj->next = NULL;
+               parser->stack->obj->len ++;
+       }
+       else {
+               /*
+                * The logic here is the following:
+                *
+                * - if we have two objects with the same priority, then we form an
+                * implicit or explicit array
+                * - if a new object has bigger priority, then we overwrite an old one
+                * - if a new object has lower priority, then we ignore it
+                */
+               unsigned priold = ucl_object_get_priority (tobj),
+                               prinew = ucl_object_get_priority (nobj);
+               if (priold == prinew) {
+                       ucl_parser_append_elt (parser, container, tobj, nobj);
+               }
+               else if (priold > prinew) {
+                       ucl_object_unref (nobj);
+                       return true;
+               }
+               else {
+                       ucl_hash_replace (container, tobj, nobj);
+                       ucl_object_unref (tobj);
+               }
+       }
+
+       if (ucl_escape) {
+               nobj->flags |= UCL_OBJECT_NEED_KEY_ESCAPE;
+       }
+       parser->stack->obj->value.ov = container;
+
+       parser->cur_obj = nobj;
+
+       return true;
+}
+
+/**
+ * Parse a cl string
+ * @param parser
+ * @param chunk
+ * @return true if a key has been parsed
+ */
+static bool
+ucl_parse_string_value (struct ucl_parser *parser,
+               struct ucl_chunk *chunk, bool *var_expand, bool *need_unescape)
+{
+       const unsigned char *p;
+       enum {
+               UCL_BRACE_ROUND = 0,
+               UCL_BRACE_SQUARE,
+               UCL_BRACE_FIGURE
+       };
+       int braces[3][2] = {{0, 0}, {0, 0}, {0, 0}};
+
+       p = chunk->pos;
+
+       while (p < chunk->end) {
+
+               /* Skip pairs of figure braces */
+               if (*p == '{') {
+                       braces[UCL_BRACE_FIGURE][0] ++;
+               }
+               else if (*p == '}') {
+                       braces[UCL_BRACE_FIGURE][1] ++;
+                       if (braces[UCL_BRACE_FIGURE][1] <= braces[UCL_BRACE_FIGURE][0]) {
+                               /* This is not a termination symbol, continue */
+                               ucl_chunk_skipc (chunk, p);
+                               continue;
+                       }
+               }
+               /* Skip pairs of square braces */
+               else if (*p == '[') {
+                       braces[UCL_BRACE_SQUARE][0] ++;
+               }
+               else if (*p == ']') {
+                       braces[UCL_BRACE_SQUARE][1] ++;
+                       if (braces[UCL_BRACE_SQUARE][1] <= braces[UCL_BRACE_SQUARE][0]) {
+                               /* This is not a termination symbol, continue */
+                               ucl_chunk_skipc (chunk, p);
+                               continue;
+                       }
+               }
+               else if (*p == '$') {
+                       *var_expand = true;
+               }
+               else if (*p == '\\') {
+                       *need_unescape = true;
+                       ucl_chunk_skipc (chunk, p);
+                       if (p < chunk->end) {
+                               ucl_chunk_skipc (chunk, p);
+                       }
+                       continue;
+               }
+
+               if (ucl_lex_is_atom_end (*p) || (chunk->remain >= 2 && ucl_lex_is_comment (p[0], p[1]))) {
+                       break;
+               }
+               ucl_chunk_skipc (chunk, p);
+       }
+
+       return true;
+}
+
+/**
+ * Parse multiline string ending with \n{term}\n
+ * @param parser
+ * @param chunk
+ * @param term
+ * @param term_len
+ * @return size of multiline string or 0 in case of error
+ */
+static int
+ucl_parse_multiline_string (struct ucl_parser *parser,
+               struct ucl_chunk *chunk, const unsigned char *term,
+               int term_len, unsigned char const **beg,
+               bool *var_expand)
+{
+       const unsigned char *p, *c, *tend;
+       bool newline = false;
+       int len = 0;
+
+       p = chunk->pos;
+
+       c = p;
+
+       while (p < chunk->end) {
+               if (newline) {
+                       if (chunk->end - p < term_len) {
+                               return 0;
+                       }
+                       else if (memcmp (p, term, term_len) == 0) {
+                               tend = p + term_len;
+                               if (*tend != '\n' && *tend != ';' && *tend != ',') {
+                                       /* Incomplete terminator */
+                                       ucl_chunk_skipc (chunk, p);
+                                       continue;
+                               }
+                               len = p - c;
+                               chunk->remain -= term_len;
+                               chunk->pos = p + term_len;
+                               chunk->column = term_len;
+                               *beg = c;
+                               break;
+                       }
+               }
+               if (*p == '\n') {
+                       newline = true;
+               }
+               else {
+                       if (*p == '$') {
+                               *var_expand = true;
+                       }
+                       newline = false;
+               }
+               ucl_chunk_skipc (chunk, p);
+       }
+
+       return len;
+}
+
+static ucl_object_t*
+ucl_get_value_object (struct ucl_parser *parser)
+{
+       ucl_object_t *t, *obj = NULL;
+
+       if (parser == NULL || parser->stack == NULL || parser->stack->obj == NULL) {
+               return NULL;
+       }
+
+       if (parser->stack->obj->type == UCL_ARRAY) {
+               /* Object must be allocated */
+               obj = ucl_object_new_full (UCL_NULL, parser->chunks->priority);
+               t = parser->stack->obj;
+               ucl_array_append (t, obj);
+               parser->cur_obj = obj;
+       }
+       else {
+               /* Object has been already allocated */
+               obj = parser->cur_obj;
+       }
+
+       return obj;
+}
+
+/**
+ * Handle value data
+ * @param parser
+ * @param chunk
+ * @return
+ */
+static bool
+ucl_parse_value (struct ucl_parser *parser, struct ucl_chunk *chunk)
+{
+       const unsigned char *p, *c;
+       ucl_object_t *obj = NULL;
+       unsigned int stripped_spaces;
+       int str_len;
+       bool need_unescape = false, ucl_escape = false, var_expand = false;
+
+       p = chunk->pos;
+
+       /* Skip any spaces and comments */
+       if (ucl_test_character (*p, UCL_CHARACTER_WHITESPACE_UNSAFE) ||
+                       (chunk->remain >= 2 && ucl_lex_is_comment (p[0], p[1]))) {
+               while (p < chunk->end && ucl_test_character (*p, UCL_CHARACTER_WHITESPACE_UNSAFE)) {
+                       ucl_chunk_skipc (chunk, p);
+               }
+               if (!ucl_skip_comments (parser)) {
+                       return false;
+               }
+               p = chunk->pos;
+       }
+
+       while (p < chunk->end) {
+               c = p;
+               switch (*p) {
+               case '"':
+                       obj = ucl_get_value_object (parser);
+                       ucl_chunk_skipc (chunk, p);
+                       if (!ucl_lex_json_string (parser, chunk, &need_unescape, &ucl_escape, &var_expand)) {
+                               return false;
+                       }
+                       str_len = chunk->pos - c - 2;
+                       obj->type = UCL_STRING;
+                       if ((str_len = ucl_copy_or_store_ptr (parser, c + 1, &obj->trash_stack[UCL_TRASH_VALUE],
+                                       &obj->value.sv, str_len, need_unescape, false, var_expand)) == -1) {
+                               return false;
+                       }
+                       obj->len = str_len;
+                       parser->state = UCL_STATE_AFTER_VALUE;
+                       p = chunk->pos;
+                       return true;
+                       break;
+               case '{':
+                       obj = ucl_get_value_object (parser);
+                       /* We have a new object */
+                       obj = ucl_add_parser_stack (obj, parser, false, parser->stack->level);
+                       if (obj == NULL) {
+                               return false;
+                       }
+
+                       ucl_chunk_skipc (chunk, p);
+                       return true;
+                       break;
+               case '[':
+                       obj = ucl_get_value_object (parser);
+                       /* We have a new array */
+                       obj = ucl_add_parser_stack (obj, parser, true, parser->stack->level);
+                       if (obj == NULL) {
+                               return false;
+                       }
+
+                       ucl_chunk_skipc (chunk, p);
+                       return true;
+                       break;
+               case ']':
+                       /* We have the array ending */
+                       if (parser->stack && parser->stack->obj->type == UCL_ARRAY) {
+                               parser->state = UCL_STATE_AFTER_VALUE;
+                               return true;
+                       }
+                       else {
+                               goto parse_string;
+                       }
+                       break;
+               case '<':
+                       obj = ucl_get_value_object (parser);
+                       /* We have something like multiline value, which must be <<[A-Z]+\n */
+                       if (chunk->end - p > 3) {
+                               if (memcmp (p, "<<", 2) == 0) {
+                                       p += 2;
+                                       /* We allow only uppercase characters in multiline definitions */
+                                       while (p < chunk->end && *p >= 'A' && *p <= 'Z') {
+                                               p ++;
+                                       }
+                                       if (*p =='\n') {
+                                               /* Set chunk positions and start multiline parsing */
+                                               c += 2;
+                                               chunk->remain -= p - c;
+                                               chunk->pos = p + 1;
+                                               chunk->column = 0;
+                                               chunk->line ++;
+                                               if ((str_len = ucl_parse_multiline_string (parser, chunk, c,
+                                                               p - c, &c, &var_expand)) == 0) {
+                                                       ucl_set_err (parser, UCL_ESYNTAX,
+                                                                       "unterminated multiline value", &parser->err);
+                                                       return false;
+                                               }
+                                               obj->type = UCL_STRING;
+                                               if ((str_len = ucl_copy_or_store_ptr (parser, c, &obj->trash_stack[UCL_TRASH_VALUE],
+                                                       &obj->value.sv, str_len - 1, false, false, var_expand)) == -1) {
+                                                       return false;
+                                               }
+                                               obj->len = str_len;
+                                               parser->state = UCL_STATE_AFTER_VALUE;
+                                               return true;
+                                       }
+                               }
+                       }
+                       /* Fallback to ordinary strings */
+               default:
+parse_string:
+                       if (obj == NULL) {
+                               obj = ucl_get_value_object (parser);
+                       }
+                       /* Parse atom */
+                       if (ucl_test_character (*p, UCL_CHARACTER_VALUE_DIGIT_START)) {
+                               if (!ucl_lex_number (parser, chunk, obj)) {
+                                       if (parser->state == UCL_STATE_ERROR) {
+                                               return false;
+                                       }
+                               }
+                               else {
+                                       parser->state = UCL_STATE_AFTER_VALUE;
+                                       return true;
+                               }
+                               /* Fallback to normal string */
+                       }
+
+                       if (!ucl_parse_string_value (parser, chunk, &var_expand, &need_unescape)) {
+                               return false;
+                       }
+                       /* Cut trailing spaces */
+                       stripped_spaces = 0;
+                       while (ucl_test_character (*(chunk->pos - 1 - stripped_spaces),
+                                       UCL_CHARACTER_WHITESPACE)) {
+                               stripped_spaces ++;
+                       }
+                       str_len = chunk->pos - c - stripped_spaces;
+                       if (str_len <= 0) {
+                               ucl_set_err (parser, 0, "string value must not be empty",
+                                               &parser->err);
+                               return false;
+                       }
+                       else if (str_len == 4 && memcmp (c, "null", 4) == 0) {
+                               obj->len = 0;
+                               obj->type = UCL_NULL;
+                       }
+                       else if (!ucl_maybe_parse_boolean (obj, c, str_len)) {
+                               obj->type = UCL_STRING;
+                               if ((str_len = ucl_copy_or_store_ptr (parser, c, &obj->trash_stack[UCL_TRASH_VALUE],
+                                               &obj->value.sv, str_len, need_unescape,
+                                               false, var_expand)) == -1) {
+                                       return false;
+                               }
+                               obj->len = str_len;
+                       }
+                       parser->state = UCL_STATE_AFTER_VALUE;
+                       p = chunk->pos;
+
+                       return true;
+                       break;
+               }
+       }
+
+       return true;
+}
+
+/**
+ * Handle after value data
+ * @param parser
+ * @param chunk
+ * @return
+ */
+static bool
+ucl_parse_after_value (struct ucl_parser *parser, struct ucl_chunk *chunk)
+{
+       const unsigned char *p;
+       bool got_sep = false;
+       struct ucl_stack *st;
+
+       p = chunk->pos;
+
+       while (p < chunk->end) {
+               if (ucl_test_character (*p, UCL_CHARACTER_WHITESPACE)) {
+                       /* Skip whitespaces */
+                       ucl_chunk_skipc (chunk, p);
+               }
+               else if (chunk->remain >= 2 && ucl_lex_is_comment (p[0], p[1])) {
+                       /* Skip comment */
+                       if (!ucl_skip_comments (parser)) {
+                               return false;
+                       }
+                       /* Treat comment as a separator */
+                       got_sep = true;
+                       p = chunk->pos;
+               }
+               else if (ucl_test_character (*p, UCL_CHARACTER_VALUE_END)) {
+                       if (*p == '}' || *p == ']') {
+                               if (parser->stack == NULL) {
+                                       ucl_set_err (parser, UCL_ESYNTAX,
+                                                       "end of array or object detected without corresponding start",
+                                                       &parser->err);
+                                       return false;
+                               }
+                               if ((*p == '}' && parser->stack->obj->type == UCL_OBJECT) ||
+                                               (*p == ']' && parser->stack->obj->type == UCL_ARRAY)) {
+
+                                       /* Pop all nested objects from a stack */
+                                       st = parser->stack;
+                                       parser->stack = st->next;
+                                       UCL_FREE (sizeof (struct ucl_stack), st);
+
+                                       while (parser->stack != NULL) {
+                                               st = parser->stack;
+                                               if (st->next == NULL || st->next->level == st->level) {
+                                                       break;
+                                               }
+                                               parser->stack = st->next;
+                                               UCL_FREE (sizeof (struct ucl_stack), st);
+                                       }
+                               }
+                               else {
+                                       ucl_set_err (parser, UCL_ESYNTAX,
+                                                       "unexpected terminating symbol detected",
+                                                       &parser->err);
+                                       return false;
+                               }
+
+                               if (parser->stack == NULL) {
+                                       /* Ignore everything after a top object */
+                                       return true;
+                               }
+                               else {
+                                       ucl_chunk_skipc (chunk, p);
+                               }
+                               got_sep = true;
+                       }
+                       else {
+                               /* Got a separator */
+                               got_sep = true;
+                               ucl_chunk_skipc (chunk, p);
+                       }
+               }
+               else {
+                       /* Anything else */
+                       if (!got_sep) {
+                               ucl_set_err (parser, UCL_ESYNTAX, "delimiter is missing",
+                                               &parser->err);
+                               return false;
+                       }
+                       return true;
+               }
+       }
+
+       return true;
+}
+
+/**
+ * Handle macro data
+ * @param parser
+ * @param chunk
+ * @return
+ */
+static bool
+ucl_parse_macro_value (struct ucl_parser *parser,
+               struct ucl_chunk *chunk, struct ucl_macro *macro,
+               unsigned char const **macro_start, size_t *macro_len)
+{
+       const unsigned char *p, *c;
+       bool need_unescape = false, ucl_escape = false, var_expand = false;
+
+       p = chunk->pos;
+
+       switch (*p) {
+       case '"':
+               /* We have macro value encoded in quotes */
+               c = p;
+               ucl_chunk_skipc (chunk, p);
+               if (!ucl_lex_json_string (parser, chunk, &need_unescape, &ucl_escape, &var_expand)) {
+                       return false;
+               }
+
+               *macro_start = c + 1;
+               *macro_len = chunk->pos - c - 2;
+               p = chunk->pos;
+               break;
+       case '{':
+               /* We got a multiline macro body */
+               ucl_chunk_skipc (chunk, p);
+               /* Skip spaces at the beginning */
+               while (p < chunk->end) {
+                       if (ucl_test_character (*p, UCL_CHARACTER_WHITESPACE_UNSAFE)) {
+                               ucl_chunk_skipc (chunk, p);
+                       }
+                       else {
+                               break;
+                       }
+               }
+               c = p;
+               while (p < chunk->end) {
+                       if (*p == '}') {
+                               break;
+                       }
+                       ucl_chunk_skipc (chunk, p);
+               }
+               *macro_start = c;
+               *macro_len = p - c;
+               ucl_chunk_skipc (chunk, p);
+               break;
+       default:
+               /* Macro is not enclosed in quotes or braces */
+               c = p;
+               while (p < chunk->end) {
+                       if (ucl_lex_is_atom_end (*p)) {
+                               break;
+                       }
+                       ucl_chunk_skipc (chunk, p);
+               }
+               *macro_start = c;
+               *macro_len = p - c;
+               break;
+       }
+
+       /* We are at the end of a macro */
+       /* Skip ';' and space characters and return to previous state */
+       while (p < chunk->end) {
+               if (!ucl_test_character (*p, UCL_CHARACTER_WHITESPACE_UNSAFE) && *p != ';') {
+                       break;
+               }
+               ucl_chunk_skipc (chunk, p);
+       }
+       return true;
+}
+
+/**
+ * Parse macro arguments as UCL object
+ * @param parser parser structure
+ * @param chunk the current data chunk
+ * @return
+ */
+static ucl_object_t *
+ucl_parse_macro_arguments (struct ucl_parser *parser,
+               struct ucl_chunk *chunk)
+{
+       ucl_object_t *res = NULL;
+       struct ucl_parser *params_parser;
+       int obraces = 1, ebraces = 0, state = 0;
+       const unsigned char *p, *c;
+       size_t args_len = 0;
+       struct ucl_parser_saved_state saved;
+
+       saved.column = chunk->column;
+       saved.line = chunk->line;
+       saved.pos = chunk->pos;
+       saved.remain = chunk->remain;
+       p = chunk->pos;
+
+       if (*p != '(' || chunk->remain < 2) {
+               return NULL;
+       }
+
+       /* Set begin and start */
+       ucl_chunk_skipc (chunk, p);
+       c = p;
+
+       while ((p) < (chunk)->end) {
+               switch (state) {
+               case 0:
+                       /* Parse symbols and check for '(', ')' and '"' */
+                       if (*p == '(') {
+                               obraces ++;
+                       }
+                       else if (*p == ')') {
+                               ebraces ++;
+                       }
+                       else if (*p == '"') {
+                               state = 1;
+                       }
+                       /* Check pairing */
+                       if (obraces == ebraces) {
+                               state = 99;
+                       }
+                       else {
+                               args_len ++;
+                       }
+                       /* Check overflow */
+                       if (chunk->remain == 0) {
+                               goto restore_chunk;
+                       }
+                       ucl_chunk_skipc (chunk, p);
+                       break;
+               case 1:
+                       /* We have quote character, so skip all but quotes */
+                       if (*p == '"' && *(p - 1) != '\\') {
+                               state = 0;
+                       }
+                       if (chunk->remain == 0) {
+                               goto restore_chunk;
+                       }
+                       ucl_chunk_skipc (chunk, p);
+                       break;
+               case 99:
+                       /*
+                        * We have read the full body of arguments, so we need to parse and set
+                        * object from that
+                        */
+                       params_parser = ucl_parser_new (parser->flags);
+                       if (!ucl_parser_add_chunk (params_parser, c, args_len)) {
+                               ucl_set_err (parser, UCL_ESYNTAX, "macro arguments parsing error",
+                                               &parser->err);
+                       }
+                       else {
+                               res = ucl_parser_get_object (params_parser);
+                       }
+                       ucl_parser_free (params_parser);
+
+                       return res;
+
+                       break;
+               }
+       }
+
+       return res;
+
+restore_chunk:
+       chunk->column = saved.column;
+       chunk->line = saved.line;
+       chunk->pos = saved.pos;
+       chunk->remain = saved.remain;
+
+       return NULL;
+}
+
+#define SKIP_SPACES_COMMENTS(parser, chunk, p) do {                                                            \
+       while ((p) < (chunk)->end) {                                                                                            \
+               if (!ucl_test_character (*(p), UCL_CHARACTER_WHITESPACE_UNSAFE)) {              \
+                       if ((chunk)->remain >= 2 && ucl_lex_is_comment ((p)[0], (p)[1])) {      \
+                               if (!ucl_skip_comments (parser)) {                                                              \
+                                       return false;                                                                                           \
+                               }                                                                                                                               \
+                               p = (chunk)->pos;                                                                                               \
+                       }                                                                                                                                       \
+                       break;                                                                                                                          \
+               }                                                                                                                                               \
+               ucl_chunk_skipc (chunk, p);                                                                                             \
+       }                                                                                                                                                       \
+} while(0)
+
+/**
+ * Handle the main states of rcl parser
+ * @param parser parser structure
+ * @param data the pointer to the beginning of a chunk
+ * @param len the length of a chunk
+ * @return true if chunk has been parsed and false in case of error
+ */
+static bool
+ucl_state_machine (struct ucl_parser *parser)
+{
+       ucl_object_t *obj, *macro_args;
+       struct ucl_chunk *chunk = parser->chunks;
+       const unsigned char *p, *c = NULL, *macro_start = NULL;
+       unsigned char *macro_escaped;
+       size_t macro_len = 0;
+       struct ucl_macro *macro = NULL;
+       bool next_key = false, end_of_object = false, ret;
+
+       if (parser->top_obj == NULL) {
+               if (*chunk->pos == '[') {
+                       obj = ucl_add_parser_stack (NULL, parser, true, 0);
+               }
+               else {
+                       obj = ucl_add_parser_stack (NULL, parser, false, 0);
+               }
+               if (obj == NULL) {
+                       return false;
+               }
+               parser->top_obj = obj;
+               parser->cur_obj = obj;
+               parser->state = UCL_STATE_INIT;
+       }
+
+       p = chunk->pos;
+       while (chunk->pos < chunk->end) {
+               switch (parser->state) {
+               case UCL_STATE_INIT:
+                       /*
+                        * At the init state we can either go to the parse array or object
+                        * if we got [ or { correspondingly or can just treat new data as
+                        * a key of newly created object
+                        */
+                       if (!ucl_skip_comments (parser)) {
+                               parser->prev_state = parser->state;
+                               parser->state = UCL_STATE_ERROR;
+                               return false;
+                       }
+                       else {
+                               /* Skip any spaces */
+                               while (p < chunk->end && ucl_test_character (*p,
+                                               UCL_CHARACTER_WHITESPACE_UNSAFE)) {
+                                       ucl_chunk_skipc (chunk, p);
+                               }
+                               p = chunk->pos;
+                               if (*p == '[') {
+                                       parser->state = UCL_STATE_VALUE;
+                                       ucl_chunk_skipc (chunk, p);
+                               }
+                               else {
+                                       parser->state = UCL_STATE_KEY;
+                                       if (*p == '{') {
+                                               ucl_chunk_skipc (chunk, p);
+                                       }
+                               }
+                       }
+                       break;
+               case UCL_STATE_KEY:
+                       /* Skip any spaces */
+                       while (p < chunk->end && ucl_test_character (*p, UCL_CHARACTER_WHITESPACE_UNSAFE)) {
+                               ucl_chunk_skipc (chunk, p);
+                       }
+                       if (*p == '}') {
+                               /* We have the end of an object */
+                               parser->state = UCL_STATE_AFTER_VALUE;
+                               continue;
+                       }
+                       if (parser->stack == NULL) {
+                               /* No objects are on stack, but we want to parse a key */
+                               ucl_set_err (parser, UCL_ESYNTAX, "top object is finished but the parser "
+                                               "expects a key", &parser->err);
+                               parser->prev_state = parser->state;
+                               parser->state = UCL_STATE_ERROR;
+                               return false;
+                       }
+                       if (!ucl_parse_key (parser, chunk, &next_key, &end_of_object)) {
+                               parser->prev_state = parser->state;
+                               parser->state = UCL_STATE_ERROR;
+                               return false;
+                       }
+                       if (end_of_object) {
+                               p = chunk->pos;
+                               parser->state = UCL_STATE_AFTER_VALUE;
+                               continue;
+                       }
+                       else if (parser->state != UCL_STATE_MACRO_NAME) {
+                               if (next_key && parser->stack->obj->type == UCL_OBJECT) {
+                                       /* Parse more keys and nest objects accordingly */
+                                       obj = ucl_add_parser_stack (parser->cur_obj, parser, false,
+                                                       parser->stack->level + 1);
+                                       if (obj == NULL) {
+                                               return false;
+                                       }
+                               }
+                               else {
+                                       parser->state = UCL_STATE_VALUE;
+                               }
+                       }
+                       else {
+                               c = chunk->pos;
+                       }
+                       p = chunk->pos;
+                       break;
+               case UCL_STATE_VALUE:
+                       /* We need to check what we do have */
+                       if (!ucl_parse_value (parser, chunk)) {
+                               parser->prev_state = parser->state;
+                               parser->state = UCL_STATE_ERROR;
+                               return false;
+                       }
+                       /* State is set in ucl_parse_value call */
+                       p = chunk->pos;
+                       break;
+               case UCL_STATE_AFTER_VALUE:
+                       if (!ucl_parse_after_value (parser, chunk)) {
+                               parser->prev_state = parser->state;
+                               parser->state = UCL_STATE_ERROR;
+                               return false;
+                       }
+                       if (parser->stack != NULL) {
+                               if (parser->stack->obj->type == UCL_OBJECT) {
+                                       parser->state = UCL_STATE_KEY;
+                               }
+                               else {
+                                       /* Array */
+                                       parser->state = UCL_STATE_VALUE;
+                               }
+                       }
+                       else {
+                               /* Skip everything at the end */
+                               return true;
+                       }
+                       p = chunk->pos;
+                       break;
+               case UCL_STATE_MACRO_NAME:
+                       if (!ucl_test_character (*p, UCL_CHARACTER_WHITESPACE_UNSAFE) &&
+                                       *p != '(') {
+                               ucl_chunk_skipc (chunk, p);
+                       }
+                       else if (p - c > 0) {
+                               /* We got macro name */
+                               macro_len = (size_t)(p - c);
+                               HASH_FIND (hh, parser->macroes, c, macro_len, macro);
+                               if (macro == NULL) {
+                                       ucl_create_err (&parser->err, "error on line %d at column %d: "
+                                                       "unknown macro: '%.*s', character: '%c'",
+                                                               chunk->line, chunk->column, (int)(p - c), c, *chunk->pos);
+                                       parser->state = UCL_STATE_ERROR;
+                                       return false;
+                               }
+                               /* Now we need to skip all spaces */
+                               SKIP_SPACES_COMMENTS(parser, chunk, p);
+                               parser->state = UCL_STATE_MACRO;
+                       }
+                       break;
+               case UCL_STATE_MACRO:
+                       if (*chunk->pos == '(') {
+                               macro_args = ucl_parse_macro_arguments (parser, chunk);
+                               p = chunk->pos;
+                               if (macro_args) {
+                                       SKIP_SPACES_COMMENTS(parser, chunk, p);
+                               }
+                       }
+                       else {
+                               macro_args = NULL;
+                       }
+                       if (!ucl_parse_macro_value (parser, chunk, macro,
+                                       &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) {
+                               ret = macro->handler (macro_start, macro_len, macro_args,
+                                               macro->ud);
+                       }
+                       else {
+                               ret = macro->handler (macro_escaped, macro_len, macro_args,
+                                               macro->ud);
+                               UCL_FREE (macro_len + 1, macro_escaped);
+                       }
+                       p = chunk->pos;
+                       if (macro_args) {
+                               ucl_object_unref (macro_args);
+                       }
+                       if (!ret) {
+                               return false;
+                       }
+                       break;
+               default:
+                       /* TODO: add all states */
+                       ucl_set_err (parser, UCL_EINTERNAL,
+                                       "internal error: parser is in an unknown state", &parser->err);
+                       parser->state = UCL_STATE_ERROR;
+                       return false;
+               }
+       }
+
+       return true;
+}
+
+struct ucl_parser*
+ucl_parser_new (int flags)
+{
+       struct ucl_parser *new;
+
+       new = UCL_ALLOC (sizeof (struct ucl_parser));
+       if (new == NULL) {
+               return NULL;
+       }
+       memset (new, 0, sizeof (struct ucl_parser));
+
+       ucl_parser_register_macro (new, "include", ucl_include_handler, new);
+       ucl_parser_register_macro (new, "try_include", ucl_try_include_handler, new);
+       ucl_parser_register_macro (new, "includes", ucl_includes_handler, new);
+
+       new->flags = flags;
+
+       /* Initial assumption about filevars */
+       ucl_parser_set_filevars (new, NULL, false);
+
+       return new;
+}
+
+
+void
+ucl_parser_register_macro (struct ucl_parser *parser, const char *macro,
+               ucl_macro_handler handler, void* ud)
+{
+       struct ucl_macro *new;
+
+       if (macro == NULL || handler == NULL) {
+               return;
+       }
+       new = UCL_ALLOC (sizeof (struct ucl_macro));
+       if (new == NULL) {
+               return;
+       }
+       memset (new, 0, sizeof (struct ucl_macro));
+       new->handler = handler;
+       new->name = strdup (macro);
+       new->ud = ud;
+       HASH_ADD_KEYPTR (hh, parser->macroes, new->name, strlen (new->name), new);
+}
+
+void
+ucl_parser_register_variable (struct ucl_parser *parser, const char *var,
+               const char *value)
+{
+       struct ucl_variable *new = NULL, *cur;
+
+       if (var == NULL) {
+               return;
+       }
+
+       /* Find whether a variable already exists */
+       LL_FOREACH (parser->variables, cur) {
+               if (strcmp (cur->var, var) == 0) {
+                       new = cur;
+                       break;
+               }
+       }
+
+       if (value == NULL) {
+
+               if (new != NULL) {
+                       /* Remove variable */
+                       DL_DELETE (parser->variables, new);
+                       free (new->var);
+                       free (new->value);
+                       UCL_FREE (sizeof (struct ucl_variable), new);
+               }
+               else {
+                       /* Do nothing */
+                       return;
+               }
+       }
+       else {
+               if (new == NULL) {
+                       new = UCL_ALLOC (sizeof (struct ucl_variable));
+                       if (new == NULL) {
+                               return;
+                       }
+                       memset (new, 0, sizeof (struct ucl_variable));
+                       new->var = strdup (var);
+                       new->var_len = strlen (var);
+                       new->value = strdup (value);
+                       new->value_len = strlen (value);
+
+                       DL_APPEND (parser->variables, new);
+               }
+               else {
+                       free (new->value);
+                       new->value = strdup (value);
+                       new->value_len = strlen (value);
+               }
+       }
+}
+
+void
+ucl_parser_set_variables_handler (struct ucl_parser *parser,
+               ucl_variable_handler handler, void *ud)
+{
+       parser->var_handler = handler;
+       parser->var_data = ud;
+}
+
+bool
+ucl_parser_add_chunk_priority (struct ucl_parser *parser, const unsigned char *data,
+               size_t len, unsigned priority)
+{
+       struct ucl_chunk *chunk;
+
+       if (data == NULL) {
+               ucl_create_err (&parser->err, "invalid chunk added");
+               return false;
+       }
+       if (len == 0) {
+               parser->top_obj = ucl_object_new_full (UCL_OBJECT, priority);
+               return true;
+       }
+       if (parser->state != UCL_STATE_ERROR) {
+               chunk = UCL_ALLOC (sizeof (struct ucl_chunk));
+               if (chunk == NULL) {
+                       ucl_create_err (&parser->err, "cannot allocate chunk structure");
+                       return false;
+               }
+               chunk->begin = data;
+               chunk->remain = len;
+               chunk->pos = chunk->begin;
+               chunk->end = chunk->begin + len;
+               chunk->line = 1;
+               chunk->column = 0;
+               chunk->priority = priority;
+               LL_PREPEND (parser->chunks, chunk);
+               parser->recursion ++;
+               if (parser->recursion > UCL_MAX_RECURSION) {
+                       ucl_create_err (&parser->err, "maximum include nesting limit is reached: %d",
+                                       parser->recursion);
+                       return false;
+               }
+               return ucl_state_machine (parser);
+       }
+
+       ucl_create_err (&parser->err, "a parser is in an invalid state");
+
+       return false;
+}
+
+bool
+ucl_parser_add_chunk (struct ucl_parser *parser, const unsigned char *data,
+               size_t len)
+{
+       return ucl_parser_add_chunk_priority (parser, data, len, 0);
+}
+
+bool
+ucl_parser_add_string (struct ucl_parser *parser, const char *data,
+               size_t len)
+{
+       if (data == NULL) {
+               ucl_create_err (&parser->err, "invalid string added");
+               return false;
+       }
+       if (len == 0) {
+               len = strlen (data);
+       }
+
+       return ucl_parser_add_chunk (parser, (const unsigned char *)data, len);
+}
diff --git a/contrib/libucl/ucl_schema.c b/contrib/libucl/ucl_schema.c
new file mode 100644 (file)
index 0000000..834b62a
--- /dev/null
@@ -0,0 +1,1015 @@
+/*
+ * 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;
+       unsigned int idx = 0;
+
+       while (ret && (elt = ucl_iterate_object (schema, &iter, true)) != NULL) {
+               if (strcmp (ucl_object_key (elt), "items") == 0) {
+                       if (elt->type == UCL_ARRAY) {
+                               found = ucl_array_head (obj);
+                               while (ret && (it = ucl_iterate_object (elt, &piter, true)) != NULL) {
+                                       if (found) {
+                                               ret = ucl_schema_validate (it, found, false, err, root);
+                                               found = ucl_array_find_index (obj, ++idx);
+                                       }
+                               }
+                               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 = ucl_array_find_index (obj, idx);
+                                       while (elt) {
+                                               if (!ucl_schema_validate (additional_schema, elt, false,
+                                                               err, root)) {
+                                                       ret = false;
+                                                       break;
+                                               }
+                                               elt = ucl_array_find_index (obj, idx ++);
+                                       }
+                               }
+                       }
+               }
+               /* 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 = ucl_array_head (cur);
+               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/ucl_util.c b/contrib/libucl/ucl_util.c
new file mode 100644 (file)
index 0000000..41e012b
--- /dev/null
@@ -0,0 +1,2568 @@
+/* Copyright (c) 2013, Vsevolod Stakhov
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *       * Redistributions of source code must retain the above copyright
+ *         notice, this list of conditions and the following disclaimer.
+ *       * Redistributions in binary form must reproduce the above copyright
+ *         notice, this list of conditions and the following disclaimer in the
+ *         documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "ucl.h"
+#include "ucl_internal.h"
+#include "ucl_chartable.h"
+#include "kvec.h"
+
+#ifndef _WIN32
+#include <glob.h>
+#endif
+
+#ifdef HAVE_LIBGEN_H
+#include <libgen.h> /* For dirname */
+#endif
+
+typedef kvec_t(ucl_object_t *) ucl_array_t;
+
+#define UCL_ARRAY_GET(ar, obj) ucl_array_t *ar = \
+       (ucl_array_t *)((obj) != NULL ? (obj)->value.av : NULL)
+
+#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
+
+#ifdef _WIN32
+#include <limits.h>
+#define NBBY CHAR_BIT
+#endif
+
+static void *ucl_mmap(char *addr, size_t length, int prot, int access, int fd, off_t offset)
+{
+       void *map = NULL;
+       HANDLE handle = INVALID_HANDLE_VALUE;
+
+       switch (prot) {
+       default:
+       case PROT_READ:
+               {
+                       handle = CreateFileMapping((HANDLE) _get_osfhandle(fd), 0, PAGE_READONLY, 0, length, 0);
+                       if (!handle) break;
+                       map = (void *) MapViewOfFile(handle, FILE_MAP_READ, 0, 0, length);
+                       CloseHandle(handle);
+                       break;
+               }
+       case PROT_WRITE:
+               {
+                       handle = CreateFileMapping((HANDLE) _get_osfhandle(fd), 0, PAGE_READWRITE, 0, length, 0);
+                       if (!handle) break;
+                       map = (void *) MapViewOfFile(handle, FILE_MAP_WRITE, 0, 0, length);
+                       CloseHandle(handle);
+                       break;
+               }
+       case PROT_READWRITE:
+               {
+                       handle = CreateFileMapping((HANDLE) _get_osfhandle(fd), 0, PAGE_READWRITE, 0, length, 0);
+                       if (!handle) break;
+                       map = (void *) MapViewOfFile(handle, FILE_MAP_ALL_ACCESS, 0, 0, length);
+                       CloseHandle(handle);
+                       break;
+               }
+       }
+       if (map == (void *) NULL) {
+               return (void *) MAP_FAILED;
+       }
+       return (void *) ((char *) map + offset);
+}
+
+static int ucl_munmap(void *map,size_t length)
+{
+       if (!UnmapViewOfFile(map)) {
+               return(-1);
+       }
+       return(0);
+}
+
+static char* ucl_realpath(const char *path, char *resolved_path) {
+    char *p;
+    char tmp[MAX_PATH + 1];
+    strncpy(tmp, path, sizeof(tmp)-1);
+    p = tmp;
+    while(*p) {
+        if (*p == '/') *p = '\\';
+        p++;
+    }
+    return _fullpath(resolved_path, tmp, MAX_PATH);
+}
+#else
+#define ucl_mmap mmap
+#define ucl_munmap munmap
+#define ucl_realpath realpath
+#endif
+
+typedef void (*ucl_object_dtor) (ucl_object_t *obj);
+static void ucl_object_free_internal (ucl_object_t *obj, bool allow_rec,
+               ucl_object_dtor dtor);
+static void ucl_object_dtor_unref (ucl_object_t *obj);
+
+static void
+ucl_object_dtor_free (ucl_object_t *obj)
+{
+       if (obj->trash_stack[UCL_TRASH_KEY] != NULL) {
+               UCL_FREE (obj->hh.keylen, obj->trash_stack[UCL_TRASH_KEY]);
+       }
+       if (obj->trash_stack[UCL_TRASH_VALUE] != NULL) {
+               UCL_FREE (obj->len, obj->trash_stack[UCL_TRASH_VALUE]);
+       }
+       /* Do not free ephemeral objects */
+       if ((obj->flags & UCL_OBJECT_EPHEMERAL) == 0) {
+               if (obj->type != UCL_USERDATA) {
+                       UCL_FREE (sizeof (ucl_object_t), obj);
+               }
+               else {
+                       struct ucl_object_userdata *ud = (struct ucl_object_userdata *)obj;
+                       if (ud->dtor) {
+                               ud->dtor (obj->value.ud);
+                       }
+                       UCL_FREE (sizeof (*ud), obj);
+               }
+       }
+}
+
+/*
+ * This is a helper function that performs exactly the same as
+ * `ucl_object_unref` but it doesn't iterate over elements allowing
+ * to use it for individual elements of arrays and multiple values
+ */
+static void
+ucl_object_dtor_unref_single (ucl_object_t *obj)
+{
+       if (obj != NULL) {
+#ifdef HAVE_ATOMIC_BUILTINS
+               unsigned int rc = __sync_sub_and_fetch (&obj->ref, 1);
+               if (rc == 0) {
+#else
+               if (--obj->ref == 0) {
+#endif
+                       ucl_object_free_internal (obj, false, ucl_object_dtor_unref);
+               }
+       }
+}
+
+static void
+ucl_object_dtor_unref (ucl_object_t *obj)
+{
+       if (obj->ref == 0) {
+               ucl_object_dtor_free (obj);
+       }
+       else {
+               /* This may cause dtor unref being called one more time */
+               ucl_object_dtor_unref_single (obj);
+       }
+}
+
+static void
+ucl_object_free_internal (ucl_object_t *obj, bool allow_rec, ucl_object_dtor dtor)
+{
+       ucl_object_t *tmp, *sub;
+
+       while (obj != NULL) {
+               if (obj->type == UCL_ARRAY) {
+                       UCL_ARRAY_GET (vec, obj);
+                       unsigned int i;
+
+                       if (vec != NULL) {
+                               for (i = 0; i < vec->n; i ++) {
+                                       sub = kv_A (*vec, i);
+                                       if (sub != NULL) {
+                                               tmp = sub;
+                                               while (sub) {
+                                                       tmp = sub->next;
+                                                       dtor (sub);
+                                                       sub = tmp;
+                                               }
+                                       }
+                               }
+                               kv_destroy (*vec);
+                               UCL_FREE (sizeof (*vec), vec);
+                       }
+               }
+               else if (obj->type == UCL_OBJECT) {
+                       if (obj->value.ov != NULL) {
+                               ucl_hash_destroy (obj->value.ov, (ucl_hash_free_func *)dtor);
+                       }
+               }
+               tmp = obj->next;
+               dtor (obj);
+               obj = tmp;
+
+               if (!allow_rec) {
+                       break;
+               }
+       }
+}
+
+void
+ucl_object_free (ucl_object_t *obj)
+{
+       ucl_object_free_internal (obj, true, ucl_object_dtor_free);
+}
+
+size_t
+ucl_unescape_json_string (char *str, size_t len)
+{
+       char *t = str, *h = str;
+       int i, uval;
+
+       if (len <= 1) {
+               return len;
+       }
+       /* t is target (tortoise), h is source (hare) */
+
+       while (len) {
+               if (*h == '\\') {
+                       h ++;
+                       switch (*h) {
+                       case 'n':
+                               *t++ = '\n';
+                               break;
+                       case 'r':
+                               *t++ = '\r';
+                               break;
+                       case 'b':
+                               *t++ = '\b';
+                               break;
+                       case 't':
+                               *t++ = '\t';
+                               break;
+                       case 'f':
+                               *t++ = '\f';
+                               break;
+                       case '\\':
+                               *t++ = '\\';
+                               break;
+                       case '"':
+                               *t++ = '"';
+                               break;
+                       case 'u':
+                               /* Unicode escape */
+                               uval = 0;
+                               if (len > 3) {
+                                       for (i = 0; i < 4; i++) {
+                                               uval <<= 4;
+                                               if (isdigit (h[i])) {
+                                                       uval += h[i] - '0';
+                                               }
+                                               else if (h[i] >= 'a' && h[i] <= 'f') {
+                                                       uval += h[i] - 'a' + 10;
+                                               }
+                                               else if (h[i] >= 'A' && h[i] <= 'F') {
+                                                       uval += h[i] - 'A' + 10;
+                                               }
+                                               else {
+                                                       break;
+                                               }
+                                       }
+                                       h += 3;
+                                       len -= 3;
+                                       /* Encode */
+                                       if(uval < 0x80) {
+                                               t[0] = (char)uval;
+                                               t ++;
+                                       }
+                                       else if(uval < 0x800) {
+                                               t[0] = 0xC0 + ((uval & 0x7C0) >> 6);
+                                               t[1] = 0x80 + ((uval & 0x03F));
+                                               t += 2;
+                                       }
+                                       else if(uval < 0x10000) {
+                                               t[0] = 0xE0 + ((uval & 0xF000) >> 12);
+                                               t[1] = 0x80 + ((uval & 0x0FC0) >> 6);
+                                               t[2] = 0x80 + ((uval & 0x003F));
+                                               t += 3;
+                                       }
+                                       else if(uval <= 0x10FFFF) {
+                                               t[0] = 0xF0 + ((uval & 0x1C0000) >> 18);
+                                               t[1] = 0x80 + ((uval & 0x03F000) >> 12);
+                                               t[2] = 0x80 + ((uval & 0x000FC0) >> 6);
+                                               t[3] = 0x80 + ((uval & 0x00003F));
+                                               t += 4;
+                                       }
+                                       else {
+                                               *t++ = '?';
+                                       }
+                               }
+                               else {
+                                       *t++ = 'u';
+                               }
+                               break;
+                       default:
+                               *t++ = *h;
+                               break;
+                       }
+                       h ++;
+                       len --;
+               }
+               else {
+                       *t++ = *h++;
+               }
+               len --;
+       }
+       *t = '\0';
+
+       return (t - str);
+}
+
+char *
+ucl_copy_key_trash (const ucl_object_t *obj)
+{
+       ucl_object_t *deconst;
+
+       if (obj == NULL) {
+               return NULL;
+       }
+       if (obj->trash_stack[UCL_TRASH_KEY] == NULL && obj->key != NULL) {
+               deconst = __DECONST (ucl_object_t *, obj);
+               deconst->trash_stack[UCL_TRASH_KEY] = malloc (obj->keylen + 1);
+               if (deconst->trash_stack[UCL_TRASH_KEY] != NULL) {
+                       memcpy (deconst->trash_stack[UCL_TRASH_KEY], obj->key, obj->keylen);
+                       deconst->trash_stack[UCL_TRASH_KEY][obj->keylen] = '\0';
+               }
+               deconst->key = obj->trash_stack[UCL_TRASH_KEY];
+               deconst->flags |= UCL_OBJECT_ALLOCATED_KEY;
+       }
+
+       return obj->trash_stack[UCL_TRASH_KEY];
+}
+
+char *
+ucl_copy_value_trash (const ucl_object_t *obj)
+{
+       ucl_object_t *deconst;
+
+       if (obj == NULL) {
+               return NULL;
+       }
+       if (obj->trash_stack[UCL_TRASH_VALUE] == NULL) {
+               deconst = __DECONST (ucl_object_t *, obj);
+               if (obj->type == UCL_STRING) {
+
+                       /* Special case for strings */
+                       deconst->trash_stack[UCL_TRASH_VALUE] = malloc (obj->len + 1);
+                       if (deconst->trash_stack[UCL_TRASH_VALUE] != NULL) {
+                               memcpy (deconst->trash_stack[UCL_TRASH_VALUE], obj->value.sv, obj->len);
+                               deconst->trash_stack[UCL_TRASH_VALUE][obj->len] = '\0';
+                               deconst->value.sv = obj->trash_stack[UCL_TRASH_VALUE];
+                       }
+               }
+               else {
+                       /* Just emit value in json notation */
+                       deconst->trash_stack[UCL_TRASH_VALUE] = ucl_object_emit_single_json (obj);
+                       deconst->len = strlen (obj->trash_stack[UCL_TRASH_VALUE]);
+               }
+               deconst->flags |= UCL_OBJECT_ALLOCATED_VALUE;
+       }
+       return obj->trash_stack[UCL_TRASH_VALUE];
+}
+
+UCL_EXTERN ucl_object_t*
+ucl_parser_get_object (struct ucl_parser *parser)
+{
+       if (parser->state != UCL_STATE_ERROR && parser->top_obj != NULL) {
+               return ucl_object_ref (parser->top_obj);
+       }
+
+       return NULL;
+}
+
+UCL_EXTERN void
+ucl_parser_free (struct ucl_parser *parser)
+{
+       struct ucl_stack *stack, *stmp;
+       struct ucl_macro *macro, *mtmp;
+       struct ucl_chunk *chunk, *ctmp;
+       struct ucl_pubkey *key, *ktmp;
+       struct ucl_variable *var, *vtmp;
+
+       if (parser == NULL) {
+               return;
+       }
+
+       if (parser->top_obj != NULL) {
+               ucl_object_unref (parser->top_obj);
+       }
+
+       LL_FOREACH_SAFE (parser->stack, stack, stmp) {
+               free (stack);
+       }
+       HASH_ITER (hh, parser->macroes, macro, mtmp) {
+               free (macro->name);
+               HASH_DEL (parser->macroes, macro);
+               UCL_FREE (sizeof (struct ucl_macro), macro);
+       }
+       LL_FOREACH_SAFE (parser->chunks, chunk, ctmp) {
+               UCL_FREE (sizeof (struct ucl_chunk), chunk);
+       }
+       LL_FOREACH_SAFE (parser->keys, key, ktmp) {
+               UCL_FREE (sizeof (struct ucl_pubkey), key);
+       }
+       LL_FOREACH_SAFE (parser->variables, var, vtmp) {
+               free (var->value);
+               free (var->var);
+               UCL_FREE (sizeof (struct ucl_variable), var);
+       }
+
+       if (parser->err != NULL) {
+               utstring_free (parser->err);
+       }
+
+       if (parser->cur_file) {
+               free (parser->cur_file);
+       }
+
+       UCL_FREE (sizeof (struct ucl_parser), parser);
+}
+
+UCL_EXTERN const char *
+ucl_parser_get_error(struct ucl_parser *parser)
+{
+       if (parser == NULL) {
+               return NULL;
+       }
+
+       if (parser->err == NULL)
+               return NULL;
+
+       return utstring_body(parser->err);
+}
+
+UCL_EXTERN void
+ucl_parser_clear_error(struct ucl_parser *parser)
+{
+       if (parser != NULL && parser->err != NULL) {
+               utstring_free(parser->err);
+               parser->err = NULL;
+       }
+}
+
+UCL_EXTERN bool
+ucl_pubkey_add (struct ucl_parser *parser, const unsigned char *key, size_t len)
+{
+#ifndef HAVE_OPENSSL
+       ucl_create_err (&parser->err, "cannot check signatures without openssl");
+       return false;
+#else
+# if (OPENSSL_VERSION_NUMBER < 0x10000000L)
+       ucl_create_err (&parser->err, "cannot check signatures, openssl version is unsupported");
+       return EXIT_FAILURE;
+# else
+       struct ucl_pubkey *nkey;
+       BIO *mem;
+
+       mem = BIO_new_mem_buf ((void *)key, len);
+       nkey = UCL_ALLOC (sizeof (struct ucl_pubkey));
+       if (nkey == NULL) {
+               ucl_create_err (&parser->err, "cannot allocate memory for key");
+               return false;
+       }
+       nkey->key = PEM_read_bio_PUBKEY (mem, &nkey->key, NULL, NULL);
+       BIO_free (mem);
+       if (nkey->key == NULL) {
+               UCL_FREE (sizeof (struct ucl_pubkey), nkey);
+               ucl_create_err (&parser->err, "%s",
+                               ERR_error_string (ERR_get_error (), NULL));
+               return false;
+       }
+       LL_PREPEND (parser->keys, nkey);
+# endif
+#endif
+       return true;
+}
+
+#ifdef CURL_FOUND
+struct ucl_curl_cbdata {
+       unsigned char *buf;
+       size_t buflen;
+};
+
+static size_t
+ucl_curl_write_callback (void* contents, size_t size, size_t nmemb, void* ud)
+{
+       struct ucl_curl_cbdata *cbdata = ud;
+       size_t realsize = size * nmemb;
+
+       cbdata->buf = realloc (cbdata->buf, cbdata->buflen + realsize + 1);
+       if (cbdata->buf == NULL) {
+               return 0;
+       }
+
+       memcpy (&(cbdata->buf[cbdata->buflen]), contents, realsize);
+       cbdata->buflen += realsize;
+       cbdata->buf[cbdata->buflen] = 0;
+
+       return realsize;
+}
+#endif
+
+/**
+ * Fetch a url and save results to the memory buffer
+ * @param url url to fetch
+ * @param len length of url
+ * @param buf target buffer
+ * @param buflen target length
+ * @return
+ */
+static bool
+ucl_fetch_url (const unsigned char *url, unsigned char **buf, size_t *buflen,
+               UT_string **err, bool must_exist)
+{
+
+#ifdef HAVE_FETCH_H
+       struct url *fetch_url;
+       struct url_stat us;
+       FILE *in;
+
+       fetch_url = fetchParseURL (url);
+       if (fetch_url == NULL) {
+               ucl_create_err (err, "invalid URL %s: %s",
+                               url, strerror (errno));
+               return false;
+       }
+       if ((in = fetchXGet (fetch_url, &us, "")) == NULL) {
+               if (!must_exist) {
+                       ucl_create_err (err, "cannot fetch URL %s: %s",
+                               url, strerror (errno));
+               }
+               fetchFreeURL (fetch_url);
+               return false;
+       }
+
+       *buflen = us.size;
+       *buf = malloc (*buflen);
+       if (*buf == NULL) {
+               ucl_create_err (err, "cannot allocate buffer for URL %s: %s",
+                               url, strerror (errno));
+               fclose (in);
+               fetchFreeURL (fetch_url);
+               return false;
+       }
+
+       if (fread (*buf, *buflen, 1, in) != 1) {
+               ucl_create_err (err, "cannot read URL %s: %s",
+                               url, strerror (errno));
+               fclose (in);
+               fetchFreeURL (fetch_url);
+               return false;
+       }
+
+       fetchFreeURL (fetch_url);
+       return true;
+#elif defined(CURL_FOUND)
+       CURL *curl;
+       int r;
+       struct ucl_curl_cbdata cbdata;
+
+       curl = curl_easy_init ();
+       if (curl == NULL) {
+               ucl_create_err (err, "CURL interface is broken");
+               return false;
+       }
+       if ((r = curl_easy_setopt (curl, CURLOPT_URL, url)) != CURLE_OK) {
+               ucl_create_err (err, "invalid URL %s: %s",
+                               url, curl_easy_strerror (r));
+               curl_easy_cleanup (curl);
+               return false;
+       }
+       curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, ucl_curl_write_callback);
+       cbdata.buf = *buf;
+       cbdata.buflen = *buflen;
+       curl_easy_setopt (curl, CURLOPT_WRITEDATA, &cbdata);
+
+       if ((r = curl_easy_perform (curl)) != CURLE_OK) {
+               if (!must_exist) {
+                       ucl_create_err (err, "error fetching URL %s: %s",
+                               url, curl_easy_strerror (r));
+               }
+               curl_easy_cleanup (curl);
+               if (cbdata.buf) {
+                       free (cbdata.buf);
+               }
+               return false;
+       }
+       *buf = cbdata.buf;
+       *buflen = cbdata.buflen;
+
+       return true;
+#else
+       ucl_create_err (err, "URL support is disabled");
+       return false;
+#endif
+}
+
+/**
+ * Fetch a file and save results to the memory buffer
+ * @param filename filename to fetch
+ * @param len length of filename
+ * @param buf target buffer
+ * @param buflen target length
+ * @return
+ */
+static bool
+ucl_fetch_file (const unsigned char *filename, unsigned char **buf, size_t *buflen,
+               UT_string **err, bool must_exist)
+{
+       int fd;
+       struct stat st;
+
+       if (stat (filename, &st) == -1 || !S_ISREG (st.st_mode)) {
+               if (must_exist) {
+                       ucl_create_err (err, "cannot stat file %s: %s",
+                                       filename, strerror (errno));
+               }
+               return false;
+       }
+       if (st.st_size == 0) {
+               /* Do not map empty files */
+               *buf = "";
+               *buflen = 0;
+       }
+       else {
+               if ((fd = open (filename, O_RDONLY)) == -1) {
+                       ucl_create_err (err, "cannot open file %s: %s",
+                                       filename, strerror (errno));
+                       return false;
+               }
+               if ((*buf = ucl_mmap (NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0)) == MAP_FAILED) {
+                       close (fd);
+                       ucl_create_err (err, "cannot mmap file %s: %s",
+                                       filename, strerror (errno));
+                       return false;
+               }
+               *buflen = st.st_size;
+               close (fd);
+       }
+
+       return true;
+}
+
+
+#if (defined(HAVE_OPENSSL) && OPENSSL_VERSION_NUMBER >= 0x10000000L)
+static inline bool
+ucl_sig_check (const unsigned char *data, size_t datalen,
+               const unsigned char *sig, size_t siglen, struct ucl_parser *parser)
+{
+       struct ucl_pubkey *key;
+       char dig[EVP_MAX_MD_SIZE];
+       unsigned int diglen;
+       EVP_PKEY_CTX *key_ctx;
+       EVP_MD_CTX *sign_ctx = NULL;
+
+       sign_ctx = EVP_MD_CTX_create ();
+
+       LL_FOREACH (parser->keys, key) {
+               key_ctx = EVP_PKEY_CTX_new (key->key, NULL);
+               if (key_ctx != NULL) {
+                       if (EVP_PKEY_verify_init (key_ctx) <= 0) {
+                               EVP_PKEY_CTX_free (key_ctx);
+                               continue;
+                       }
+                       if (EVP_PKEY_CTX_set_rsa_padding (key_ctx, RSA_PKCS1_PADDING) <= 0) {
+                               EVP_PKEY_CTX_free (key_ctx);
+                               continue;
+                       }
+                       if (EVP_PKEY_CTX_set_signature_md (key_ctx, EVP_sha256 ()) <= 0) {
+                               EVP_PKEY_CTX_free (key_ctx);
+                               continue;
+                       }
+                       EVP_DigestInit (sign_ctx, EVP_sha256 ());
+                       EVP_DigestUpdate (sign_ctx, data, datalen);
+                       EVP_DigestFinal (sign_ctx, dig, &diglen);
+
+                       if (EVP_PKEY_verify (key_ctx, sig, siglen, dig, diglen) == 1) {
+                               EVP_MD_CTX_destroy (sign_ctx);
+                               EVP_PKEY_CTX_free (key_ctx);
+                               return true;
+                       }
+
+                       EVP_PKEY_CTX_free (key_ctx);
+               }
+       }
+
+       EVP_MD_CTX_destroy (sign_ctx);
+
+       return false;
+}
+#endif
+
+/**
+ * Include an url to configuration
+ * @param data
+ * @param len
+ * @param parser
+ * @param err
+ * @return
+ */
+static bool
+ucl_include_url (const unsigned char *data, size_t len,
+               struct ucl_parser *parser, bool check_signature, bool must_exist,
+               unsigned priority)
+{
+
+       bool res;
+       unsigned char *buf = NULL;
+       size_t buflen = 0;
+       struct ucl_chunk *chunk;
+       char urlbuf[PATH_MAX];
+       int prev_state;
+
+       snprintf (urlbuf, sizeof (urlbuf), "%.*s", (int)len, data);
+
+       if (!ucl_fetch_url (urlbuf, &buf, &buflen, &parser->err, must_exist)) {
+               return (!must_exist || false);
+       }
+
+       if (check_signature) {
+#if (defined(HAVE_OPENSSL) && OPENSSL_VERSION_NUMBER >= 0x10000000L)
+               unsigned char *sigbuf = NULL;
+               size_t siglen = 0;
+               /* We need to check signature first */
+               snprintf (urlbuf, sizeof (urlbuf), "%.*s.sig", (int)len, data);
+               if (!ucl_fetch_url (urlbuf, &sigbuf, &siglen, &parser->err, true)) {
+                       return false;
+               }
+               if (!ucl_sig_check (buf, buflen, sigbuf, siglen, parser)) {
+                       ucl_create_err (&parser->err, "cannot verify url %s: %s",
+                                                       urlbuf,
+                                                       ERR_error_string (ERR_get_error (), NULL));
+                       if (siglen > 0) {
+                               ucl_munmap (sigbuf, siglen);
+                       }
+                       return false;
+               }
+               if (siglen > 0) {
+                       ucl_munmap (sigbuf, siglen);
+               }
+#endif
+       }
+
+       prev_state = parser->state;
+       parser->state = UCL_STATE_INIT;
+
+       res = ucl_parser_add_chunk_priority (parser, buf, buflen, priority);
+       if (res == true) {
+               /* Remove chunk from the stack */
+               chunk = parser->chunks;
+               if (chunk != NULL) {
+                       parser->chunks = chunk->next;
+                       UCL_FREE (sizeof (struct ucl_chunk), chunk);
+               }
+       }
+
+       parser->state = prev_state;
+       free (buf);
+
+       return res;
+}
+
+/**
+ * Include a single file to the parser
+ * @param data
+ * @param len
+ * @param parser
+ * @param check_signature
+ * @param must_exist
+ * @param allow_glob
+ * @param priority
+ * @return
+ */
+static bool
+ucl_include_file_single (const unsigned char *data, size_t len,
+               struct ucl_parser *parser, bool check_signature, bool must_exist,
+               unsigned priority)
+{
+       bool res;
+       struct ucl_chunk *chunk;
+       unsigned char *buf = NULL;
+       char *old_curfile;
+       size_t buflen;
+       char filebuf[PATH_MAX], realbuf[PATH_MAX];
+       int prev_state;
+       struct ucl_variable *cur_var, *tmp_var, *old_curdir = NULL,
+                       *old_filename = NULL;
+
+       snprintf (filebuf, sizeof (filebuf), "%.*s", (int)len, data);
+       if (ucl_realpath (filebuf, realbuf) == NULL) {
+               if (!must_exist) {
+                       return true;
+               }
+               ucl_create_err (&parser->err, "cannot open file %s: %s",
+                                                                       filebuf,
+                                                                       strerror (errno));
+               return false;
+       }
+
+       if (parser->cur_file && strcmp (realbuf, parser->cur_file) == 0) {
+               /* We are likely including the file itself */
+               ucl_create_err (&parser->err, "trying to include the file %s from itself",
+                               realbuf);
+               return false;
+       }
+
+       if (!ucl_fetch_file (realbuf, &buf, &buflen, &parser->err, must_exist)) {
+               return (!must_exist || false);
+       }
+
+       if (check_signature) {
+#if (defined(HAVE_OPENSSL) && OPENSSL_VERSION_NUMBER >= 0x10000000L)
+               unsigned char *sigbuf = NULL;
+               size_t siglen = 0;
+               /* We need to check signature first */
+               snprintf (filebuf, sizeof (filebuf), "%s.sig", realbuf);
+               if (!ucl_fetch_file (filebuf, &sigbuf, &siglen, &parser->err, true)) {
+                       return false;
+               }
+               if (!ucl_sig_check (buf, buflen, sigbuf, siglen, parser)) {
+                       ucl_create_err (&parser->err, "cannot verify file %s: %s",
+                                                       filebuf,
+                                                       ERR_error_string (ERR_get_error (), NULL));
+                       if (siglen > 0) {
+                               ucl_munmap (sigbuf, siglen);
+                       }
+                       return false;
+               }
+               if (siglen > 0) {
+                       ucl_munmap (sigbuf, siglen);
+               }
+#endif
+       }
+
+       old_curfile = parser->cur_file;
+       parser->cur_file = strdup (realbuf);
+
+       /* Store old file vars */
+       DL_FOREACH_SAFE (parser->variables, cur_var, tmp_var) {
+               if (strcmp (cur_var->var, "CURDIR") == 0) {
+                       old_curdir = cur_var;
+                       DL_DELETE (parser->variables, cur_var);
+               }
+               else if (strcmp (cur_var->var, "FILENAME") == 0) {
+                       old_filename = cur_var;
+                       DL_DELETE (parser->variables, cur_var);
+               }
+       }
+
+       ucl_parser_set_filevars (parser, realbuf, false);
+
+       prev_state = parser->state;
+       parser->state = UCL_STATE_INIT;
+
+       res = ucl_parser_add_chunk_priority (parser, buf, buflen, priority);
+       if (!res && !must_exist) {
+               /* Free error */
+               utstring_free (parser->err);
+               parser->err = NULL;
+               parser->state = UCL_STATE_AFTER_VALUE;
+       }
+
+       /* Remove chunk from the stack */
+       chunk = parser->chunks;
+       if (chunk != NULL) {
+               parser->chunks = chunk->next;
+               UCL_FREE (sizeof (struct ucl_chunk), chunk);
+               parser->recursion --;
+       }
+
+       /* Restore old file vars */
+       parser->cur_file = old_curfile;
+       DL_FOREACH_SAFE (parser->variables, cur_var, tmp_var) {
+               if (strcmp (cur_var->var, "CURDIR") == 0 && old_curdir) {
+                       DL_DELETE (parser->variables, cur_var);
+                       free (cur_var->var);
+                       free (cur_var->value);
+                       UCL_FREE (sizeof (struct ucl_variable), cur_var);
+               }
+               else if (strcmp (cur_var->var, "FILENAME") == 0 && old_filename) {
+                       DL_DELETE (parser->variables, cur_var);
+                       free (cur_var->var);
+                       free (cur_var->value);
+                       UCL_FREE (sizeof (struct ucl_variable), cur_var);
+               }
+       }
+       if (old_filename) {
+               DL_APPEND (parser->variables, old_filename);
+       }
+       if (old_curdir) {
+               DL_APPEND (parser->variables, old_curdir);
+       }
+       if (old_curfile) {
+               free (old_curfile);
+       }
+
+       parser->state = prev_state;
+
+       if (buflen > 0) {
+               ucl_munmap (buf, buflen);
+       }
+
+       return res;
+}
+
+/**
+ * Include a file to configuration
+ * @param data
+ * @param len
+ * @param parser
+ * @param err
+ * @return
+ */
+static bool
+ucl_include_file (const unsigned char *data, size_t len,
+               struct ucl_parser *parser, bool check_signature, bool must_exist,
+               bool allow_glob, unsigned priority)
+{
+       const unsigned char *p = data, *end = data + len;
+       bool need_glob = false;
+       int cnt = 0;
+       char glob_pattern[PATH_MAX];
+       size_t i;
+
+#ifndef _WIN32
+       if (!allow_glob) {
+               return ucl_include_file_single (data, len, parser, check_signature,
+                       must_exist, priority);
+       }
+       else {
+               /* Check for special symbols in a filename */
+               while (p != end) {
+                       if (*p == '*' || *p == '?') {
+                               need_glob = true;
+                               break;
+                       }
+                       p ++;
+               }
+               if (need_glob) {
+                       glob_t globbuf;
+                       memset (&globbuf, 0, sizeof (globbuf));
+                       ucl_strlcpy (glob_pattern, (const char *)data, sizeof (glob_pattern));
+                       if (glob (glob_pattern, 0, NULL, &globbuf) != 0) {
+                               return (!must_exist || false);
+                       }
+                       for (i = 0; i < globbuf.gl_pathc; i ++) {
+                               if (!ucl_include_file_single ((unsigned char *)globbuf.gl_pathv[i],
+                                               strlen (globbuf.gl_pathv[i]), parser, check_signature,
+                                               must_exist, priority)) {
+                                       globfree (&globbuf);
+                                       return false;
+                               }
+                               cnt ++;
+                       }
+                       globfree (&globbuf);
+
+                       if (cnt == 0 && must_exist) {
+                               ucl_create_err (&parser->err, "cannot match any files for pattern %s",
+                                       glob_pattern);
+                               return false;
+                       }
+               }
+               else {
+                       return ucl_include_file_single (data, len, parser, check_signature,
+                               must_exist, priority);
+               }
+       }
+#else
+       /* Win32 compilers do not support globbing. Therefore, for Win32,
+          treat allow_glob/need_glob as a NOOP and just return */
+       return ucl_include_file_single (data, len, parser, check_signature,
+               must_exist, priority);
+#endif
+       
+       return true;
+}
+
+/**
+ * Common function to handle .*include* macros
+ * @param data
+ * @param len
+ * @param args
+ * @param parser
+ * @param default_try
+ * @param default_sign
+ * @return
+ */
+static bool
+ucl_include_common (const unsigned char *data, size_t len,
+               const ucl_object_t *args, struct ucl_parser *parser,
+               bool default_try,
+               bool default_sign)
+{
+       bool try_load, allow_glob, allow_url, need_sign;
+       unsigned priority;
+       const ucl_object_t *param;
+       ucl_object_iter_t it = NULL;
+
+       /* Default values */
+       try_load = default_try;
+       allow_glob = false;
+       allow_url = true;
+       need_sign = default_sign;
+       priority = 0;
+
+       /* Process arguments */
+       if (args != NULL && args->type == UCL_OBJECT) {
+               while ((param = ucl_iterate_object (args, &it, true)) != NULL) {
+                       if (param->type == UCL_BOOLEAN) {
+                               if (strcmp (param->key, "try") == 0) {
+                                       try_load = ucl_object_toboolean (param);
+                               }
+                               else if (strcmp (param->key, "sign") == 0) {
+                                       need_sign = ucl_object_toboolean (param);
+                               }
+                               else if (strcmp (param->key, "glob") == 0) {
+                                       allow_glob =  ucl_object_toboolean (param);
+                               }
+                               else if (strcmp (param->key, "url") == 0) {
+                                       allow_url =  ucl_object_toboolean (param);
+                               }
+                       }
+                       else if (param->type == UCL_INT) {
+                               if (strcmp (param->key, "priority") == 0) {
+                                       priority = ucl_object_toint (param);
+                               }
+                       }
+               }
+       }
+
+       if (*data == '/' || *data == '.') {
+               /* Try to load a file */
+               return ucl_include_file (data, len, parser, need_sign, !try_load,
+                               allow_glob, priority);
+       }
+       else if (allow_url) {
+               /* Globbing is not used for URL's */
+               return ucl_include_url (data, len, parser, need_sign, !try_load,
+                               priority);
+       }
+
+       return false;
+}
+
+/**
+ * Handle include macro
+ * @param data include data
+ * @param len length of data
+ * @param ud user data
+ * @param err error ptr
+ * @return
+ */
+UCL_EXTERN bool
+ucl_include_handler (const unsigned char *data, size_t len,
+               const ucl_object_t *args, void* ud)
+{
+       struct ucl_parser *parser = ud;
+
+       return ucl_include_common (data, len, args, parser, false, false);
+}
+
+/**
+ * Handle includes macro
+ * @param data include data
+ * @param len length of data
+ * @param ud user data
+ * @param err error ptr
+ * @return
+ */
+UCL_EXTERN bool
+ucl_includes_handler (const unsigned char *data, size_t len,
+               const ucl_object_t *args, void* ud)
+{
+       struct ucl_parser *parser = ud;
+
+       return ucl_include_common (data, len, args, parser, false, true);
+}
+
+
+UCL_EXTERN bool
+ucl_try_include_handler (const unsigned char *data, size_t len,
+               const ucl_object_t *args, void* ud)
+{
+       struct ucl_parser *parser = ud;
+
+       return ucl_include_common (data, len, args, parser, true, false);
+}
+
+UCL_EXTERN bool
+ucl_parser_set_filevars (struct ucl_parser *parser, const char *filename, bool need_expand)
+{
+       char realbuf[PATH_MAX], *curdir;
+
+       if (filename != NULL) {
+               if (need_expand) {
+                       if (ucl_realpath (filename, realbuf) == NULL) {
+                               return false;
+                       }
+               }
+               else {
+                       ucl_strlcpy (realbuf, filename, sizeof (realbuf));
+               }
+
+               /* Define variables */
+               ucl_parser_register_variable (parser, "FILENAME", realbuf);
+               curdir = dirname (realbuf);
+               ucl_parser_register_variable (parser, "CURDIR", curdir);
+       }
+       else {
+               /* Set everything from the current dir */
+               curdir = getcwd (realbuf, sizeof (realbuf));
+               ucl_parser_register_variable (parser, "FILENAME", "undef");
+               ucl_parser_register_variable (parser, "CURDIR", curdir);
+       }
+
+       return true;
+}
+
+UCL_EXTERN bool
+ucl_parser_add_file (struct ucl_parser *parser, const char *filename)
+{
+       unsigned char *buf;
+       size_t len;
+       bool ret;
+       char realbuf[PATH_MAX];
+
+       if (ucl_realpath (filename, realbuf) == NULL) {
+               ucl_create_err (&parser->err, "cannot open file %s: %s",
+                               filename,
+                               strerror (errno));
+               return false;
+       }
+
+       if (!ucl_fetch_file (realbuf, &buf, &len, &parser->err, true)) {
+               return false;
+       }
+
+       if (parser->cur_file) {
+               free (parser->cur_file);
+       }
+       parser->cur_file = strdup (realbuf);
+       ucl_parser_set_filevars (parser, realbuf, false);
+       ret = ucl_parser_add_chunk (parser, buf, len);
+
+       if (len > 0) {
+               ucl_munmap (buf, len);
+       }
+
+       return ret;
+}
+
+UCL_EXTERN bool
+ucl_parser_add_fd (struct ucl_parser *parser, int fd)
+{
+       unsigned char *buf;
+       size_t len;
+       bool ret;
+       struct stat st;
+
+       if (fstat (fd, &st) == -1) {
+               ucl_create_err (&parser->err, "cannot stat fd %d: %s",
+                       fd, strerror (errno));
+               return false;
+       }
+       if ((buf = ucl_mmap (NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0)) == MAP_FAILED) {
+               ucl_create_err (&parser->err, "cannot mmap fd %d: %s",
+                       fd, strerror (errno));
+               return false;
+       }
+
+       if (parser->cur_file) {
+               free (parser->cur_file);
+       }
+       parser->cur_file = NULL;
+       len = st.st_size;
+       ret = ucl_parser_add_chunk (parser, buf, len);
+
+       if (len > 0) {
+               ucl_munmap (buf, len);
+       }
+
+       return ret;
+}
+
+size_t
+ucl_strlcpy (char *dst, const char *src, size_t siz)
+{
+       char *d = dst;
+       const char *s = src;
+       size_t n = siz;
+
+       /* Copy as many bytes as will fit */
+       if (n != 0) {
+               while (--n != 0) {
+                       if ((*d++ = *s++) == '\0') {
+                               break;
+                       }
+               }
+       }
+
+       if (n == 0 && siz != 0) {
+               *d = '\0';
+       }
+
+       return (s - src - 1);    /* count does not include NUL */
+}
+
+size_t
+ucl_strlcpy_unsafe (char *dst, const char *src, size_t siz)
+{
+       memcpy (dst, src, siz - 1);
+       dst[siz - 1] = '\0';
+
+       return siz - 1;
+}
+
+size_t
+ucl_strlcpy_tolower (char *dst, const char *src, size_t siz)
+{
+       char *d = dst;
+       const char *s = src;
+       size_t n = siz;
+
+       /* Copy as many bytes as will fit */
+       if (n != 0) {
+               while (--n != 0) {
+                       if ((*d++ = tolower (*s++)) == '\0') {
+                               break;
+                       }
+               }
+       }
+
+       if (n == 0 && siz != 0) {
+               *d = '\0';
+       }
+
+       return (s - src);    /* count does not include NUL */
+}
+
+ucl_object_t *
+ucl_object_fromstring_common (const char *str, size_t len, enum ucl_string_flags flags)
+{
+       ucl_object_t *obj;
+       const char *start, *end, *p, *pos;
+       char *dst, *d;
+       size_t escaped_len;
+
+       if (str == NULL) {
+               return NULL;
+       }
+
+       obj = ucl_object_new ();
+       if (obj) {
+               if (len == 0) {
+                       len = strlen (str);
+               }
+               if (flags & UCL_STRING_TRIM) {
+                       /* Skip leading spaces */
+                       for (start = str; (size_t)(start - str) < len; start ++) {
+                               if (!ucl_test_character (*start, UCL_CHARACTER_WHITESPACE_UNSAFE)) {
+                                       break;
+                               }
+                       }
+                       /* Skip trailing spaces */
+                       for (end = str + len - 1; end > start; end --) {
+                               if (!ucl_test_character (*end, UCL_CHARACTER_WHITESPACE_UNSAFE)) {
+                                       break;
+                               }
+                       }
+                       end ++;
+               }
+               else {
+                       start = str;
+                       end = str + len;
+               }
+
+               obj->type = UCL_STRING;
+               if (flags & UCL_STRING_ESCAPE) {
+                       for (p = start, escaped_len = 0; p < end; p ++, escaped_len ++) {
+                               if (ucl_test_character (*p, UCL_CHARACTER_JSON_UNSAFE)) {
+                                       escaped_len ++;
+                               }
+                       }
+                       dst = malloc (escaped_len + 1);
+                       if (dst != NULL) {
+                               for (p = start, d = dst; p < end; p ++, d ++) {
+                                       if (ucl_test_character (*p, UCL_CHARACTER_JSON_UNSAFE)) {
+                                               switch (*p) {
+                                               case '\n':
+                                                       *d++ = '\\';
+                                                       *d = 'n';
+                                                       break;
+                                               case '\r':
+                                                       *d++ = '\\';
+                                                       *d = 'r';
+                                                       break;
+                                               case '\b':
+                                                       *d++ = '\\';
+                                                       *d = 'b';
+                                                       break;
+                                               case '\t':
+                                                       *d++ = '\\';
+                                                       *d = 't';
+                                                       break;
+                                               case '\f':
+                                                       *d++ = '\\';
+                                                       *d = 'f';
+                                                       break;
+                                               case '\\':
+                                                       *d++ = '\\';
+                                                       *d = '\\';
+                                                       break;
+                                               case '"':
+                                                       *d++ = '\\';
+                                                       *d = '"';
+                                                       break;
+                                               }
+                                       }
+                                       else {
+                                               *d = *p;
+                                       }
+                               }
+                               *d = '\0';
+                               obj->value.sv = dst;
+                               obj->trash_stack[UCL_TRASH_VALUE] = dst;
+                               obj->len = escaped_len;
+                       }
+               }
+               else {
+                       dst = malloc (end - start + 1);
+                       if (dst != NULL) {
+                               ucl_strlcpy_unsafe (dst, start, end - start + 1);
+                               obj->value.sv = dst;
+                               obj->trash_stack[UCL_TRASH_VALUE] = dst;
+                               obj->len = end - start;
+                       }
+               }
+               if ((flags & UCL_STRING_PARSE) && dst != NULL) {
+                       /* Parse what we have */
+                       if (flags & UCL_STRING_PARSE_BOOLEAN) {
+                               if (!ucl_maybe_parse_boolean (obj, dst, obj->len) && (flags & UCL_STRING_PARSE_NUMBER)) {
+                                       ucl_maybe_parse_number (obj, dst, dst + obj->len, &pos,
+                                                       flags & UCL_STRING_PARSE_DOUBLE,
+                                                       flags & UCL_STRING_PARSE_BYTES,
+                                                       flags & UCL_STRING_PARSE_TIME);
+                               }
+                       }
+                       else {
+                               ucl_maybe_parse_number (obj, dst, dst + obj->len, &pos,
+                                               flags & UCL_STRING_PARSE_DOUBLE,
+                                               flags & UCL_STRING_PARSE_BYTES,
+                                               flags & UCL_STRING_PARSE_TIME);
+                       }
+               }
+       }
+
+       return obj;
+}
+
+static bool
+ucl_object_insert_key_common (ucl_object_t *top, ucl_object_t *elt,
+               const char *key, size_t keylen, bool copy_key, bool merge, bool replace)
+{
+       ucl_object_t *found, *tmp;
+       const ucl_object_t *cur;
+       ucl_object_iter_t it = NULL;
+       const char *p;
+       int ret = true;
+
+       if (elt == NULL || key == NULL) {
+               return false;
+       }
+
+       if (top == NULL) {
+               return false;
+       }
+
+       if (top->type != UCL_OBJECT) {
+               /* It is possible to convert NULL type to an object */
+               if (top->type == UCL_NULL) {
+                       top->type = UCL_OBJECT;
+               }
+               else {
+                       /* Refuse converting of other object types */
+                       return false;
+               }
+       }
+
+       if (top->value.ov == NULL) {
+               top->value.ov = ucl_hash_create (false);
+       }
+
+       if (keylen == 0) {
+               keylen = strlen (key);
+       }
+
+       for (p = key; p < key + keylen; p ++) {
+               if (ucl_test_character (*p, UCL_CHARACTER_UCL_UNSAFE)) {
+                       elt->flags |= UCL_OBJECT_NEED_KEY_ESCAPE;
+                       break;
+               }
+       }
+
+       /* workaround for some use cases */
+       if (elt->trash_stack[UCL_TRASH_KEY] != NULL &&
+                       key != (const char *)elt->trash_stack[UCL_TRASH_KEY]) {
+               /* Remove copied key */
+               free (elt->trash_stack[UCL_TRASH_KEY]);
+               elt->trash_stack[UCL_TRASH_KEY] = NULL;
+               elt->flags &= ~UCL_OBJECT_ALLOCATED_KEY;
+       }
+
+       elt->key = key;
+       elt->keylen = keylen;
+
+       if (copy_key) {
+               ucl_copy_key_trash (elt);
+       }
+
+       found = __DECONST (ucl_object_t *, ucl_hash_search_obj (top->value.ov, elt));
+
+       if (found == NULL) {
+               top->value.ov = ucl_hash_insert_object (top->value.ov, elt, false);
+               top->len ++;
+               if (replace) {
+                       ret = false;
+               }
+       }
+       else {
+               if (replace) {
+                       ucl_hash_replace (top->value.ov, found, elt);
+                       ucl_object_unref (found);
+               }
+               else if (merge) {
+                       if (found->type != UCL_OBJECT && elt->type == UCL_OBJECT) {
+                               /* Insert old elt to new one */
+                               ucl_object_insert_key_common (elt, found, found->key,
+                                               found->keylen, copy_key, false, false);
+                               ucl_hash_delete (top->value.ov, found);
+                               top->value.ov = ucl_hash_insert_object (top->value.ov, elt, false);
+                       }
+                       else if (found->type == UCL_OBJECT && elt->type != UCL_OBJECT) {
+                               /* Insert new to old */
+                               ucl_object_insert_key_common (found, elt, elt->key,
+                                               elt->keylen, copy_key, false, false);
+                       }
+                       else if (found->type == UCL_OBJECT && elt->type == UCL_OBJECT) {
+                               /* Mix two hashes */
+                               while ((cur = ucl_iterate_object (elt, &it, true)) != NULL) {
+                                       tmp = ucl_object_ref (cur);
+                                       ucl_object_insert_key_common (found, tmp, cur->key,
+                                                       cur->keylen, copy_key, false, false);
+                               }
+                               ucl_object_unref (elt);
+                       }
+                       else {
+                               /* Just make a list of scalars */
+                               DL_APPEND (found, elt);
+                       }
+               }
+               else {
+                       DL_APPEND (found, elt);
+               }
+       }
+
+       return ret;
+}
+
+bool
+ucl_object_delete_keyl (ucl_object_t *top, const char *key, size_t keylen)
+{
+       ucl_object_t *found;
+
+       if (top == NULL || key == NULL) {
+               return false;
+       }
+
+       found = __DECONST (ucl_object_t *, ucl_object_find_keyl (top, key, keylen));
+
+       if (found == NULL) {
+               return false;
+       }
+
+       ucl_hash_delete (top->value.ov, found);
+       ucl_object_unref (found);
+       top->len --;
+
+       return true;
+}
+
+bool
+ucl_object_delete_key (ucl_object_t *top, const char *key)
+{
+       return ucl_object_delete_keyl (top, key, strlen(key));
+}
+
+ucl_object_t*
+ucl_object_pop_keyl (ucl_object_t *top, const char *key, size_t keylen)
+{
+       const ucl_object_t *found;
+
+       if (top == NULL || key == NULL) {
+               return false;
+       }
+       found = ucl_object_find_keyl (top, key, keylen);
+
+       if (found == NULL) {
+               return NULL;
+       }
+       ucl_hash_delete (top->value.ov, found);
+       top->len --;
+
+       return __DECONST (ucl_object_t *, found);
+}
+
+ucl_object_t*
+ucl_object_pop_key (ucl_object_t *top, const char *key)
+{
+       return ucl_object_pop_keyl (top, key, strlen(key));
+}
+
+bool
+ucl_object_insert_key (ucl_object_t *top, ucl_object_t *elt,
+               const char *key, size_t keylen, bool copy_key)
+{
+       return ucl_object_insert_key_common (top, elt, key, keylen, copy_key, false, false);
+}
+
+bool
+ucl_object_insert_key_merged (ucl_object_t *top, ucl_object_t *elt,
+               const char *key, size_t keylen, bool copy_key)
+{
+       return ucl_object_insert_key_common (top, elt, key, keylen, copy_key, true, false);
+}
+
+bool
+ucl_object_replace_key (ucl_object_t *top, ucl_object_t *elt,
+               const char *key, size_t keylen, bool copy_key)
+{
+       return ucl_object_insert_key_common (top, elt, key, keylen, copy_key, false, true);
+}
+
+bool
+ucl_object_merge (ucl_object_t *top, ucl_object_t *elt, bool copy)
+{
+       ucl_object_t *cur = NULL, *cp = NULL, *found = NULL;
+       ucl_object_iter_t iter = NULL;
+
+       if (top == NULL || top->type != UCL_OBJECT || elt == NULL || elt->type != UCL_OBJECT) {
+               return false;
+       }
+
+       /* Mix two hashes */
+       while ((cur = (ucl_object_t*)ucl_hash_iterate (elt->value.ov, &iter))) {
+               if (copy) {
+                       cp = ucl_object_copy (cur);
+               }
+               else {
+                       cp = ucl_object_ref (cur);
+               }
+               found = __DECONST(ucl_object_t *, ucl_hash_search (top->value.ov, cp->key, cp->keylen));
+               if (found == NULL) {
+                       /* The key does not exist */
+                       top->value.ov = ucl_hash_insert_object (top->value.ov, cp, false);
+                       top->len ++;
+               }
+               else {
+                       /* The key already exists, replace it */
+                       ucl_hash_replace (top->value.ov, found, cp);
+                       ucl_object_unref (found);
+               }
+       }
+
+       return true;
+}
+
+const ucl_object_t *
+ucl_object_find_keyl (const ucl_object_t *obj, const char *key, size_t klen)
+{
+       const ucl_object_t *ret;
+       ucl_object_t srch;
+
+       if (obj == NULL || obj->type != UCL_OBJECT || key == NULL) {
+               return NULL;
+       }
+
+       srch.key = key;
+       srch.keylen = klen;
+       ret = ucl_hash_search_obj (obj->value.ov, &srch);
+
+       return ret;
+}
+
+const ucl_object_t *
+ucl_object_find_key (const ucl_object_t *obj, const char *key)
+{
+       if (key == NULL)
+               return NULL;
+
+       return ucl_object_find_keyl (obj, key, strlen(key));
+}
+
+const ucl_object_t*
+ucl_iterate_object (const ucl_object_t *obj, ucl_object_iter_t *iter, bool expand_values)
+{
+       const ucl_object_t *elt = NULL;
+
+       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: {
+                       unsigned int idx;
+                       UCL_ARRAY_GET (vec, obj);
+                       idx = (unsigned int)(uintptr_t)(*iter);
+
+                       if (vec != NULL) {
+                               while (idx < kv_size (*vec)) {
+                                       if ((elt = kv_A (*vec, idx)) != NULL) {
+                                               idx ++;
+                                               break;
+                                       }
+                                       idx ++;
+                               }
+                               *iter = (void *)(uintptr_t)idx;
+                       }
+
+                       return elt;
+                       break;
+               }
+               default:
+                       /* Go to linear iteration */
+                       break;
+               }
+       }
+       /* Treat everything as a linear list */
+       elt = *iter;
+       if (elt == NULL) {
+               elt = obj;
+       }
+       else if (elt == obj) {
+               return NULL;
+       }
+       *iter = __DECONST (void *, elt->next ? elt->next : obj);
+       return elt;
+
+       /* Not reached */
+       return NULL;
+}
+
+const char safe_iter_magic[4] = {'u', 'i', 't', 'e'};
+struct ucl_object_safe_iter {
+       char magic[4]; /* safety check */
+       const ucl_object_t *impl_it; /* implicit object iteration */
+       ucl_object_iter_t expl_it; /* explicit iteration */
+};
+
+#define UCL_SAFE_ITER(ptr) (struct ucl_object_safe_iter *)(ptr)
+#define UCL_SAFE_ITER_CHECK(it) do { \
+       assert (it != NULL); \
+       assert (memcmp (it->magic, safe_iter_magic, sizeof (it->magic)) == 0); \
+ } while (0)
+
+ucl_object_iter_t
+ucl_object_iterate_new (const ucl_object_t *obj)
+{
+       struct ucl_object_safe_iter *it;
+
+       it = UCL_ALLOC (sizeof (*it));
+       if (it != NULL) {
+               memcpy (it->magic, safe_iter_magic, sizeof (it->magic));
+               it->expl_it = NULL;
+               it->impl_it = obj;
+       }
+
+       return (ucl_object_iter_t)it;
+}
+
+
+ucl_object_iter_t
+ucl_object_iterate_reset (ucl_object_iter_t it, const ucl_object_t *obj)
+{
+       struct ucl_object_safe_iter *rit = UCL_SAFE_ITER (it);
+
+       UCL_SAFE_ITER_CHECK (rit);
+
+       rit->impl_it = obj;
+       rit->expl_it = NULL;
+
+       return it;
+}
+
+const ucl_object_t*
+ucl_object_iterate_safe (ucl_object_iter_t it, bool expand_values)
+{
+       struct ucl_object_safe_iter *rit = UCL_SAFE_ITER (it);
+       const ucl_object_t *ret = NULL;
+
+       UCL_SAFE_ITER_CHECK (rit);
+
+       if (rit->impl_it == NULL) {
+               return NULL;
+       }
+
+       if (rit->impl_it->type == UCL_OBJECT || rit->impl_it->type == UCL_ARRAY) {
+               ret = ucl_iterate_object (rit->impl_it, &rit->expl_it, true);
+
+               if (ret == NULL) {
+                       /* Need to switch to another implicit object in chain */
+                       rit->impl_it = rit->impl_it->next;
+                       rit->expl_it = NULL;
+                       return ucl_object_iterate_safe (it, expand_values);
+               }
+       }
+       else {
+               /* Just iterate over the implicit array */
+               ret = rit->impl_it;
+               rit->impl_it = rit->impl_it->next;
+               if (expand_values) {
+                       /* We flatten objects if need to expand values */
+                       if (ret->type == UCL_OBJECT || ret->type == UCL_ARRAY) {
+                               return ucl_object_iterate_safe (it, expand_values);
+                       }
+               }
+       }
+
+       return ret;
+}
+
+void
+ucl_object_iterate_free (ucl_object_iter_t it)
+{
+       struct ucl_object_safe_iter *rit = UCL_SAFE_ITER (it);
+
+       UCL_SAFE_ITER_CHECK (rit);
+
+       UCL_FREE (sizeof (*rit), it);
+}
+
+const ucl_object_t *
+ucl_lookup_path (const ucl_object_t *top, const char *path_in) {
+       const ucl_object_t *o = NULL, *found;
+       const char *p, *c;
+       char *err_str;
+       unsigned index;
+
+       if (path_in == NULL || top == NULL) {
+               return NULL;
+       }
+
+       found = NULL;
+       p = path_in;
+
+       /* Skip leading dots */
+       while (*p == '.') {
+               p ++;
+       }
+
+       c = p;
+       while (*p != '\0') {
+               p ++;
+               if (*p == '.' || *p == '\0') {
+                       if (p > c) {
+                               switch (top->type) {
+                               case UCL_ARRAY:
+                                       /* Key should be an int */
+                                       index = strtoul (c, &err_str, 10);
+                                       if (err_str != NULL && (*err_str != '.' && *err_str != '\0')) {
+                                               return NULL;
+                                       }
+                                       o = ucl_array_find_index (top, index);
+                                       break;
+                               default:
+                                       o = ucl_object_find_keyl (top, c, p - c);
+                                       break;
+                               }
+                               if (o == NULL) {
+                                       return NULL;
+                               }
+                               top = o;
+                       }
+                       if (*p != '\0') {
+                               c = p + 1;
+                       }
+               }
+       }
+       found = o;
+
+       return found;
+}
+
+
+ucl_object_t *
+ucl_object_new (void)
+{
+       return ucl_object_typed_new (UCL_NULL);
+}
+
+ucl_object_t *
+ucl_object_typed_new (ucl_type_t type)
+{
+       return ucl_object_new_full (type, 0);
+}
+
+ucl_object_t *
+ucl_object_new_full (ucl_type_t type, unsigned priority)
+{
+       ucl_object_t *new;
+
+       if (type != UCL_USERDATA) {
+               new = UCL_ALLOC (sizeof (ucl_object_t));
+               if (new != NULL) {
+                       memset (new, 0, sizeof (ucl_object_t));
+                       new->ref = 1;
+                       new->type = (type <= UCL_NULL ? type : UCL_NULL);
+                       new->next = NULL;
+                       new->prev = new;
+                       ucl_object_set_priority (new, priority);
+
+                       if (type == UCL_ARRAY) {
+                               new->value.av = UCL_ALLOC (sizeof (ucl_array_t));
+                               if (new->value.av) {
+                                       memset (new->value.av, 0, sizeof (ucl_array_t));
+                                       UCL_ARRAY_GET (vec, new);
+
+                                       /* Preallocate some space for arrays */
+                                       kv_resize (ucl_object_t *, *vec, 8);
+                               }
+                       }
+               }
+       }
+       else {
+               new = ucl_object_new_userdata (NULL, NULL);
+               ucl_object_set_priority (new, priority);
+       }
+
+       return new;
+}
+
+ucl_object_t*
+ucl_object_new_userdata (ucl_userdata_dtor dtor, ucl_userdata_emitter emitter)
+{
+       struct ucl_object_userdata *new;
+       size_t nsize = sizeof (*new);
+
+       new = UCL_ALLOC (nsize);
+       if (new != NULL) {
+               memset (new, 0, nsize);
+               new->obj.ref = 1;
+               new->obj.type = UCL_USERDATA;
+               new->obj.next = NULL;
+               new->obj.prev = (ucl_object_t *)new;
+               new->dtor = dtor;
+               new->emitter = emitter;
+       }
+
+       return (ucl_object_t *)new;
+}
+
+ucl_type_t
+ucl_object_type (const ucl_object_t *obj)
+{
+       return obj->type;
+}
+
+ucl_object_t*
+ucl_object_fromstring (const char *str)
+{
+       return ucl_object_fromstring_common (str, 0, UCL_STRING_ESCAPE);
+}
+
+ucl_object_t *
+ucl_object_fromlstring (const char *str, size_t len)
+{
+       return ucl_object_fromstring_common (str, len, UCL_STRING_ESCAPE);
+}
+
+ucl_object_t *
+ucl_object_fromint (int64_t iv)
+{
+       ucl_object_t *obj;
+
+       obj = ucl_object_new ();
+       if (obj != NULL) {
+               obj->type = UCL_INT;
+               obj->value.iv = iv;
+       }
+
+       return obj;
+}
+
+ucl_object_t *
+ucl_object_fromdouble (double dv)
+{
+       ucl_object_t *obj;
+
+       obj = ucl_object_new ();
+       if (obj != NULL) {
+               obj->type = UCL_FLOAT;
+               obj->value.dv = dv;
+       }
+
+       return obj;
+}
+
+ucl_object_t*
+ucl_object_frombool (bool bv)
+{
+       ucl_object_t *obj;
+
+       obj = ucl_object_new ();
+       if (obj != NULL) {
+               obj->type = UCL_BOOLEAN;
+               obj->value.iv = bv;
+       }
+
+       return obj;
+}
+
+bool
+ucl_array_append (ucl_object_t *top, ucl_object_t *elt)
+{
+       UCL_ARRAY_GET (vec, top);
+
+       if (elt == NULL || top == NULL) {
+               return false;
+       }
+
+       if (vec == NULL) {
+               vec = UCL_ALLOC (sizeof (*vec));
+               kv_init (*vec);
+               top->value.av = (void *)vec;
+       }
+
+       kv_push (ucl_object_t *, *vec, elt);
+
+       top->len ++;
+
+       return true;
+}
+
+bool
+ucl_array_prepend (ucl_object_t *top, ucl_object_t *elt)
+{
+       UCL_ARRAY_GET (vec, top);
+
+       if (elt == NULL || top == NULL) {
+               return false;
+       }
+
+       if (vec == NULL) {
+               vec = UCL_ALLOC (sizeof (*vec));
+               kv_init (*vec);
+               top->value.av = (void *)vec;
+               kv_push (ucl_object_t *, *vec, elt);
+       }
+       else {
+               /* Slow O(n) algorithm */
+               kv_prepend (ucl_object_t *, *vec, elt);
+       }
+
+       top->len ++;
+
+       return true;
+}
+
+bool
+ucl_array_merge (ucl_object_t *top, ucl_object_t *elt, bool copy)
+{
+       unsigned i;
+       ucl_object_t **obj;
+       UCL_ARRAY_GET (v1, top);
+       UCL_ARRAY_GET (v2, elt);
+
+       if (elt == NULL || top == NULL || top->type != UCL_ARRAY || elt->type != UCL_ARRAY) {
+               return false;
+       }
+
+       kv_concat (ucl_object_t *, *v1, *v2);
+
+       for (i = v2->n; i < v1->n; i ++) {
+               obj = &kv_A (*v1, i);
+               if (*obj == NULL) {
+                       continue;
+               }
+
+               top->len ++;
+               if (copy) {
+                       *obj = ucl_object_copy (*obj);
+               }
+               else {
+                       ucl_object_ref (*obj);
+               }
+       }
+
+       return true;
+}
+
+ucl_object_t *
+ucl_array_delete (ucl_object_t *top, ucl_object_t *elt)
+{
+       UCL_ARRAY_GET (vec, top);
+       ucl_object_t *ret = NULL;
+       unsigned i;
+
+       for (i = 0; i < vec->n; i ++) {
+               if (kv_A (*vec, i) == elt) {
+                       kv_del (ucl_object_t *, *vec, i);
+                       ret = elt;
+                       top->len --;
+                       break;
+               }
+       }
+
+       return ret;
+}
+
+const ucl_object_t *
+ucl_array_head (const ucl_object_t *top)
+{
+       UCL_ARRAY_GET (vec, top);
+
+       if (top == NULL || top->type != UCL_ARRAY || top->value.av == NULL) {
+               return NULL;
+       }
+
+       return (vec->n > 0 ? vec->a[0] : NULL);
+}
+
+const ucl_object_t *
+ucl_array_tail (const ucl_object_t *top)
+{
+       UCL_ARRAY_GET (vec, top);
+
+       if (top == NULL || top->type != UCL_ARRAY || top->value.av == NULL) {
+               return NULL;
+       }
+
+       return (vec->n > 0 ? vec->a[vec->n - 1] : NULL);
+}
+
+ucl_object_t *
+ucl_array_pop_last (ucl_object_t *top)
+{
+       UCL_ARRAY_GET (vec, top);
+       ucl_object_t **obj, *ret = NULL;
+
+       if (vec != NULL && vec->n > 0) {
+               obj = &kv_A (*vec, vec->n - 1);
+               ret = *obj;
+               kv_del (ucl_object_t *, *vec, vec->n - 1);
+               top->len --;
+       }
+
+       return ret;
+}
+
+ucl_object_t *
+ucl_array_pop_first (ucl_object_t *top)
+{
+       UCL_ARRAY_GET (vec, top);
+       ucl_object_t **obj, *ret = NULL;
+
+       if (vec != NULL && vec->n > 0) {
+               obj = &kv_A (*vec, 0);
+               ret = *obj;
+               kv_del (ucl_object_t *, *vec, 0);
+               top->len --;
+       }
+
+       return ret;
+}
+
+const ucl_object_t *
+ucl_array_find_index (const ucl_object_t *top, unsigned int index)
+{
+       UCL_ARRAY_GET (vec, top);
+
+       if (vec != NULL && vec->n > 0 && index < vec->n) {
+               return kv_A (*vec, index);
+       }
+
+       return NULL;
+}
+
+ucl_object_t *
+ucl_array_replace_index (ucl_object_t *top, ucl_object_t *elt,
+       unsigned int index)
+{
+       UCL_ARRAY_GET (vec, top);
+       ucl_object_t *ret = NULL;
+
+       if (vec != NULL && vec->n > 0 && index < vec->n) {
+               ret = kv_A (*vec, index);
+               kv_A (*vec, index) = elt;
+       }
+
+       return ret;
+}
+
+ucl_object_t *
+ucl_elt_append (ucl_object_t *head, ucl_object_t *elt)
+{
+
+       if (head == NULL) {
+               elt->next = NULL;
+               elt->prev = elt;
+               head = elt;
+       }
+       else {
+               elt->prev = head->prev;
+               head->prev->next = elt;
+               head->prev = elt;
+               elt->next = NULL;
+       }
+
+       return head;
+}
+
+bool
+ucl_object_todouble_safe (const ucl_object_t *obj, double *target)
+{
+       if (obj == NULL || target == NULL) {
+               return false;
+       }
+       switch (obj->type) {
+       case UCL_INT:
+               *target = obj->value.iv; /* Probaly could cause overflow */
+               break;
+       case UCL_FLOAT:
+       case UCL_TIME:
+               *target = obj->value.dv;
+               break;
+       default:
+               return false;
+       }
+
+       return true;
+}
+
+double
+ucl_object_todouble (const ucl_object_t *obj)
+{
+       double result = 0.;
+
+       ucl_object_todouble_safe (obj, &result);
+       return result;
+}
+
+bool
+ucl_object_toint_safe (const ucl_object_t *obj, int64_t *target)
+{
+       if (obj == NULL || target == NULL) {
+               return false;
+       }
+       switch (obj->type) {
+       case UCL_INT:
+               *target = obj->value.iv;
+               break;
+       case UCL_FLOAT:
+       case UCL_TIME:
+               *target = obj->value.dv; /* Loosing of decimal points */
+               break;
+       default:
+               return false;
+       }
+
+       return true;
+}
+
+int64_t
+ucl_object_toint (const ucl_object_t *obj)
+{
+       int64_t result = 0;
+
+       ucl_object_toint_safe (obj, &result);
+       return result;
+}
+
+bool
+ucl_object_toboolean_safe (const ucl_object_t *obj, bool *target)
+{
+       if (obj == NULL || target == NULL) {
+               return false;
+       }
+       switch (obj->type) {
+       case UCL_BOOLEAN:
+               *target = (obj->value.iv == true);
+               break;
+       default:
+               return false;
+       }
+
+       return true;
+}
+
+bool
+ucl_object_toboolean (const ucl_object_t *obj)
+{
+       bool result = false;
+
+       ucl_object_toboolean_safe (obj, &result);
+       return result;
+}
+
+bool
+ucl_object_tostring_safe (const ucl_object_t *obj, const char **target)
+{
+       if (obj == NULL || target == NULL) {
+               return false;
+       }
+
+       switch (obj->type) {
+       case UCL_STRING:
+               *target = ucl_copy_value_trash (obj);
+               break;
+       default:
+               return false;
+       }
+
+       return true;
+}
+
+const char *
+ucl_object_tostring (const ucl_object_t *obj)
+{
+       const char *result = NULL;
+
+       ucl_object_tostring_safe (obj, &result);
+       return result;
+}
+
+const char *
+ucl_object_tostring_forced (const ucl_object_t *obj)
+{
+       return ucl_copy_value_trash (obj);
+}
+
+bool
+ucl_object_tolstring_safe (const ucl_object_t *obj, const char **target, size_t *tlen)
+{
+       if (obj == NULL || target == NULL) {
+               return false;
+       }
+       switch (obj->type) {
+       case UCL_STRING:
+               *target = obj->value.sv;
+               if (tlen != NULL) {
+                       *tlen = obj->len;
+               }
+               break;
+       default:
+               return false;
+       }
+
+       return true;
+}
+
+const char *
+ucl_object_tolstring (const ucl_object_t *obj, size_t *tlen)
+{
+       const char *result = NULL;
+
+       ucl_object_tolstring_safe (obj, &result, tlen);
+       return result;
+}
+
+const char *
+ucl_object_key (const ucl_object_t *obj)
+{
+       return ucl_copy_key_trash (obj);
+}
+
+const char *
+ucl_object_keyl (const ucl_object_t *obj, size_t *len)
+{
+       if (len == NULL || obj == NULL) {
+               return NULL;
+       }
+       *len = obj->keylen;
+       return obj->key;
+}
+
+ucl_object_t *
+ucl_object_ref (const ucl_object_t *obj)
+{
+       ucl_object_t *res = NULL;
+
+       if (obj != NULL) {
+               if (obj->flags & UCL_OBJECT_EPHEMERAL) {
+                       /*
+                        * Use deep copy for ephemeral objects, note that its refcount
+                        * is NOT increased, since ephemeral objects does not need refcount
+                        * at all
+                        */
+                       res = ucl_object_copy (obj);
+               }
+               else {
+                       res = __DECONST (ucl_object_t *, obj);
+#ifdef HAVE_ATOMIC_BUILTINS
+                       (void)__sync_add_and_fetch (&res->ref, 1);
+#else
+                       res->ref ++;
+#endif
+               }
+       }
+       return res;
+}
+
+static ucl_object_t *
+ucl_object_copy_internal (const ucl_object_t *other, bool allow_array)
+{
+
+       ucl_object_t *new;
+       ucl_object_iter_t it = NULL;
+       const ucl_object_t *cur;
+
+       new = malloc (sizeof (*new));
+
+       if (new != NULL) {
+               memcpy (new, other, sizeof (*new));
+               if (other->flags & UCL_OBJECT_EPHEMERAL) {
+                       /* Copied object is always non ephemeral */
+                       new->flags &= ~UCL_OBJECT_EPHEMERAL;
+               }
+               new->ref = 1;
+               /* Unlink from others */
+               new->next = NULL;
+               new->prev = new;
+
+               /* deep copy of values stored */
+               if (other->trash_stack[UCL_TRASH_KEY] != NULL) {
+                       new->trash_stack[UCL_TRASH_KEY] =
+                                       strdup (other->trash_stack[UCL_TRASH_KEY]);
+                       if (other->key == (const char *)other->trash_stack[UCL_TRASH_KEY]) {
+                               new->key = new->trash_stack[UCL_TRASH_KEY];
+                       }
+               }
+               if (other->trash_stack[UCL_TRASH_VALUE] != NULL) {
+                       new->trash_stack[UCL_TRASH_VALUE] =
+                                       strdup (other->trash_stack[UCL_TRASH_VALUE]);
+                       if (new->type == UCL_STRING) {
+                               new->value.sv = new->trash_stack[UCL_TRASH_VALUE];
+                       }
+               }
+
+               if (other->type == UCL_ARRAY || other->type == UCL_OBJECT) {
+                       /* reset old value */
+                       memset (&new->value, 0, sizeof (new->value));
+
+                       while ((cur = ucl_iterate_object (other, &it, true)) != NULL) {
+                               if (other->type == UCL_ARRAY) {
+                                       ucl_array_append (new, ucl_object_copy_internal (cur, false));
+                               }
+                               else {
+                                       ucl_object_t *cp = ucl_object_copy_internal (cur, true);
+                                       if (cp != NULL) {
+                                               ucl_object_insert_key (new, cp, cp->key, cp->keylen,
+                                                               false);
+                                       }
+                               }
+                       }
+               }
+               else if (allow_array && other->next != NULL) {
+                       LL_FOREACH (other->next, cur) {
+                               ucl_object_t *cp = ucl_object_copy_internal (cur, false);
+                               if (cp != NULL) {
+                                       DL_APPEND (new, cp);
+                               }
+                       }
+               }
+       }
+
+       return new;
+}
+
+ucl_object_t *
+ucl_object_copy (const ucl_object_t *other)
+{
+       return ucl_object_copy_internal (other, true);
+}
+
+void
+ucl_object_unref (ucl_object_t *obj)
+{
+       if (obj != NULL) {
+#ifdef HAVE_ATOMIC_BUILTINS
+               unsigned int rc = __sync_sub_and_fetch (&obj->ref, 1);
+               if (rc == 0) {
+#else
+               if (--obj->ref == 0) {
+#endif
+                       ucl_object_free_internal (obj, true, ucl_object_dtor_unref);
+               }
+       }
+}
+
+int
+ucl_object_compare (const ucl_object_t *o1, const ucl_object_t *o2)
+{
+       const ucl_object_t *it1, *it2;
+       ucl_object_iter_t iter = NULL;
+       int ret = 0;
+
+       if (o1->type != o2->type) {
+               return (o1->type) - (o2->type);
+       }
+
+       switch (o1->type) {
+       case UCL_STRING:
+               if (o1->len == o2->len && o1->len > 0) {
+                       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 && o1->len > 0) {
+                       UCL_ARRAY_GET (vec1, o1);
+                       UCL_ARRAY_GET (vec2, o2);
+                       unsigned i;
+
+                       /* Compare all elements in both arrays */
+                       for (i = 0; i < vec1->n; i ++) {
+                               it1 = kv_A (*vec1, i);
+                               it2 = kv_A (*vec2, i);
+
+                               if (it1 == NULL && it2 != NULL) {
+                                       return -1;
+                               }
+                               else if (it2 == NULL && it1 != NULL) {
+                                       return 1;
+                               }
+                               else if (it1 != NULL && it2 != NULL) {
+                                       ret = ucl_object_compare (it1, it2);
+                                       if (ret != 0) {
+                                               break;
+                                       }
+                               }
+                       }
+               }
+               else {
+                       ret = o1->len - o2->len;
+               }
+               break;
+       case UCL_OBJECT:
+               if (o1->len == o2->len && o1->len > 0) {
+                       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))
+{
+       UCL_ARRAY_GET (vec, ar);
+
+       if (cmp == NULL || ar == NULL || ar->type != UCL_ARRAY) {
+               return;
+       }
+
+       qsort (vec->a, vec->n, sizeof (ucl_object_t *),
+                       (int (*)(const void *, const void *))cmp);
+}
+
+#define PRIOBITS 4
+
+unsigned int
+ucl_object_get_priority (const ucl_object_t *obj)
+{
+       if (obj == NULL) {
+               return 0;
+       }
+
+       return (obj->flags >> ((sizeof (obj->flags) * NBBY) - PRIOBITS));
+}
+
+void
+ucl_object_set_priority (ucl_object_t *obj,
+               unsigned int priority)
+{
+       if (obj != NULL) {
+               priority &= (0x1 << PRIOBITS) - 1;
+               obj->flags |= priority << ((sizeof (obj->flags) * NBBY) - PRIOBITS);
+       }
+}
diff --git a/contrib/libucl/xxhash.c b/contrib/libucl/xxhash.c
new file mode 100644 (file)
index 0000000..5869503
--- /dev/null
@@ -0,0 +1,475 @@
+/*\r
+xxHash - Fast Hash algorithm\r
+Copyright (C) 2012-2013, Yann Collet.\r
+BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)\r
+\r
+Redistribution and use in source and binary forms, with or without\r
+modification, are permitted provided that the following conditions are\r
+met:\r
+\r
+* Redistributions of source code must retain the above copyright\r
+notice, this list of conditions and the following disclaimer.\r
+* Redistributions in binary form must reproduce the above\r
+copyright notice, this list of conditions and the following disclaimer\r
+in the documentation and/or other materials provided with the\r
+distribution.\r
+\r
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\r
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\r
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\r
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\r
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\r
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\r
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\r
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\r
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\r
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\r
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
+\r
+You can contact the author at :\r
+- xxHash source repository : http://code.google.com/p/xxhash/\r
+*/\r
+\r
+\r
+//**************************************\r
+// Tuning parameters\r
+//**************************************\r
+// Unaligned memory access is automatically enabled for "common" CPU, such as x86.\r
+// For others CPU, the compiler will be more cautious, and insert extra code to ensure aligned access is respected.\r
+// If you know your target CPU supports unaligned memory access, you want to force this option manually to improve performance.\r
+// You can also enable this parameter if you know your input data will always be aligned (boundaries of 4, for U32).\r
+#if defined(__ARM_FEATURE_UNALIGNED) || defined(__i386) || defined(_M_IX86) || defined(__x86_64__) || defined(_M_X64)\r
+#  define XXH_USE_UNALIGNED_ACCESS 1\r
+#endif\r
+\r
+// XXH_ACCEPT_NULL_INPUT_POINTER :\r
+// If the input pointer is a null pointer, xxHash default behavior is to trigger a memory access error, since it is a bad pointer.\r
+// When this option is enabled, xxHash output for null input pointers will be the same as a null-length input.\r
+// This option has a very small performance cost (only measurable on small inputs).\r
+// By default, this option is disabled. To enable it, uncomment below define :\r
+//#define XXH_ACCEPT_NULL_INPUT_POINTER 1\r
+\r
+// XXH_FORCE_NATIVE_FORMAT :\r
+// By default, xxHash library provides endian-independant Hash values, based on little-endian convention.\r
+// Results are therefore identical for little-endian and big-endian CPU.\r
+// This comes at a performance cost for big-endian CPU, since some swapping is required to emulate little-endian format.\r
+// Should endian-independance be of no importance for your application, you may set the #define below to 1.\r
+// It will improve speed for Big-endian CPU.\r
+// This option has no impact on Little_Endian CPU.\r
+#define XXH_FORCE_NATIVE_FORMAT 0\r
+\r
+\r
+//**************************************\r
+// Compiler Specific Options\r
+//**************************************\r
+// Disable some Visual warning messages\r
+#ifdef _MSC_VER  // Visual Studio\r
+#  pragma warning(disable : 4127)      // disable: C4127: conditional expression is constant\r
+#endif\r
+\r
+#ifdef _MSC_VER    // Visual Studio\r
+#  define forceinline static __forceinline\r
+#else \r
+#  ifdef __GNUC__\r
+#    define forceinline static inline __attribute__((always_inline))\r
+#  else\r
+#    define forceinline static inline\r
+#  endif\r
+#endif\r
+\r
+\r
+//**************************************\r
+// Includes & Memory related functions\r
+//**************************************\r
+#include "xxhash.h"\r
+// Modify the local functions below should you wish to use some other memory related routines\r
+// for malloc(), free()\r
+#include <stdlib.h>\r
+forceinline void* XXH_malloc(size_t s) { return malloc(s); }\r
+forceinline void  XXH_free  (void* p)  { free(p); }\r
+// for memcpy()\r
+#include <string.h>\r
+forceinline void* XXH_memcpy(void* dest, const void* src, size_t size) { return memcpy(dest,src,size); }\r
+\r
+\r
+//**************************************\r
+// Basic Types\r
+//**************************************\r
+#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L   // C99\r
+# include <stdint.h>\r
+  typedef uint8_t  BYTE;\r
+  typedef uint16_t U16;\r
+  typedef uint32_t U32;\r
+  typedef  int32_t S32;\r
+  typedef uint64_t U64;\r
+#else\r
+  typedef unsigned char      BYTE;\r
+  typedef unsigned short     U16;\r
+  typedef unsigned int       U32;\r
+  typedef   signed int       S32;\r
+  typedef unsigned long long U64;\r
+#endif\r
+\r
+#if defined(__GNUC__)  && !defined(XXH_USE_UNALIGNED_ACCESS)\r
+#  define _PACKED __attribute__ ((packed))\r
+#else\r
+#  define _PACKED\r
+#endif\r
+\r
+#if !defined(XXH_USE_UNALIGNED_ACCESS) && !defined(__GNUC__)\r
+#  ifdef __IBMC__\r
+#    pragma pack(1)\r
+#  else\r
+#    pragma pack(push, 1)\r
+#  endif\r
+#endif\r
+\r
+typedef struct _U32_S { U32 v; } _PACKED U32_S;\r
+\r
+#if !defined(XXH_USE_UNALIGNED_ACCESS) && !defined(__GNUC__)\r
+#  pragma pack(pop)\r
+#endif\r
+\r
+#define A32(x) (((U32_S *)(x))->v)\r
+\r
+\r
+//***************************************\r
+// Compiler-specific Functions and Macros\r
+//***************************************\r
+#define GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__)\r
+\r
+// Note : although _rotl exists for minGW (GCC under windows), performance seems poor\r
+#if defined(_MSC_VER)\r
+#  define XXH_rotl32(x,r) _rotl(x,r)\r
+#else\r
+#  define XXH_rotl32(x,r) ((x << r) | (x >> (32 - r)))\r
+#endif\r
+\r
+#if defined(_MSC_VER)     // Visual Studio\r
+#  define XXH_swap32 _byteswap_ulong\r
+#elif GCC_VERSION >= 403\r
+#  define XXH_swap32 __builtin_bswap32\r
+#else\r
+static inline U32 XXH_swap32 (U32 x) {\r
+    return  ((x << 24) & 0xff000000 ) |\r
+        ((x <<  8) & 0x00ff0000 ) |\r
+        ((x >>  8) & 0x0000ff00 ) |\r
+        ((x >> 24) & 0x000000ff );}\r
+#endif\r
+\r
+\r
+//**************************************\r
+// Constants\r
+//**************************************\r
+#define PRIME32_1   2654435761U\r
+#define PRIME32_2   2246822519U\r
+#define PRIME32_3   3266489917U\r
+#define PRIME32_4    668265263U\r
+#define PRIME32_5    374761393U\r
+\r
+\r
+//**************************************\r
+// Architecture Macros\r
+//**************************************\r
+typedef enum { XXH_bigEndian=0, XXH_littleEndian=1 } XXH_endianess;\r
+#ifndef XXH_CPU_LITTLE_ENDIAN   // It is possible to define XXH_CPU_LITTLE_ENDIAN externally, for example using a compiler switch\r
+    static const int one = 1;\r
+#   define XXH_CPU_LITTLE_ENDIAN   (*(char*)(&one))\r
+#endif\r
+\r
+\r
+//**************************************\r
+// Macros\r
+//**************************************\r
+#define XXH_STATIC_ASSERT(c)   { enum { XXH_static_assert = 1/(!!(c)) }; }    // use only *after* variable declarations\r
+\r
+\r
+//****************************\r
+// Memory reads\r
+//****************************\r
+typedef enum { XXH_aligned, XXH_unaligned } XXH_alignment;\r
+\r
+forceinline U32 XXH_readLE32_align(const U32* ptr, XXH_endianess endian, XXH_alignment align)\r
+{ \r
+    if (align==XXH_unaligned)\r
+        return endian==XXH_littleEndian ? A32(ptr) : XXH_swap32(A32(ptr)); \r
+    else\r
+        return endian==XXH_littleEndian ? *ptr : XXH_swap32(*ptr); \r
+}\r
+\r
+forceinline U32 XXH_readLE32(const U32* ptr, XXH_endianess endian) { return XXH_readLE32_align(ptr, endian, XXH_unaligned); }\r
+\r
+\r
+//****************************\r
+// Simple Hash Functions\r
+//****************************\r
+forceinline U32 XXH32_endian_align(const void* input, int len, U32 seed, XXH_endianess endian, XXH_alignment align)\r
+{\r
+    const BYTE* p = (const BYTE*)input;\r
+    const BYTE* const bEnd = p + len;\r
+    U32 h32;\r
+\r
+#ifdef XXH_ACCEPT_NULL_INPUT_POINTER\r
+    if (p==NULL) { len=0; p=(const BYTE*)(size_t)16; }\r
+#endif\r
+\r
+    if (len>=16)\r
+    {\r
+        const BYTE* const limit = bEnd - 16;\r
+        U32 v1 = seed + PRIME32_1 + PRIME32_2;\r
+        U32 v2 = seed + PRIME32_2;\r
+        U32 v3 = seed + 0;\r
+        U32 v4 = seed - PRIME32_1;\r
+\r
+        do\r
+        {\r
+            v1 += XXH_readLE32_align((const U32*)p, endian, align) * PRIME32_2; v1 = XXH_rotl32(v1, 13); v1 *= PRIME32_1; p+=4;\r
+            v2 += XXH_readLE32_align((const U32*)p, endian, align) * PRIME32_2; v2 = XXH_rotl32(v2, 13); v2 *= PRIME32_1; p+=4;\r
+            v3 += XXH_readLE32_align((const U32*)p, endian, align) * PRIME32_2; v3 = XXH_rotl32(v3, 13); v3 *= PRIME32_1; p+=4;\r
+            v4 += XXH_readLE32_align((const U32*)p, endian, align) * PRIME32_2; v4 = XXH_rotl32(v4, 13); v4 *= PRIME32_1; p+=4;\r
+        } while (p<=limit);\r
+\r
+        h32 = XXH_rotl32(v1, 1) + XXH_rotl32(v2, 7) + XXH_rotl32(v3, 12) + XXH_rotl32(v4, 18);\r
+    }\r
+    else\r
+    {\r
+        h32  = seed + PRIME32_5;\r
+    }\r
+\r
+    h32 += (U32) len;\r
+\r
+    while (p<=bEnd-4)\r
+    {\r
+        h32 += XXH_readLE32_align((const U32*)p, endian, align) * PRIME32_3;\r
+        h32  = XXH_rotl32(h32, 17) * PRIME32_4 ;\r
+        p+=4;\r
+    }\r
+\r
+    while (p<bEnd)\r
+    {\r
+        h32 += (*p) * PRIME32_5;\r
+        h32 = XXH_rotl32(h32, 11) * PRIME32_1 ;\r
+        p++;\r
+    }\r
+\r
+    h32 ^= h32 >> 15;\r
+    h32 *= PRIME32_2;\r
+    h32 ^= h32 >> 13;\r
+    h32 *= PRIME32_3;\r
+    h32 ^= h32 >> 16;\r
+\r
+    return h32;\r
+}\r
+\r
+\r
+U32 XXH32(const void* input, int len, U32 seed)\r
+{\r
+#if 0\r
+    // Simple version, good for code maintenance, but unfortunately slow for small inputs\r
+    void* state = XXH32_init(seed);\r
+    XXH32_update(state, input, len);\r
+    return XXH32_digest(state);\r
+#else\r
+    XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN;\r
+\r
+#  if !defined(XXH_USE_UNALIGNED_ACCESS)\r
+    if (!(((size_t)input) & 3))   // Input is aligned, let's leverage the speed advantage\r
+    {\r
+        if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT)\r
+            return XXH32_endian_align(input, len, seed, XXH_littleEndian, XXH_aligned);\r
+        else\r
+            return XXH32_endian_align(input, len, seed, XXH_bigEndian, XXH_aligned);\r
+    }\r
+#  endif\r
+\r
+    if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT)\r
+        return XXH32_endian_align(input, len, seed, XXH_littleEndian, XXH_unaligned);\r
+    else\r
+        return XXH32_endian_align(input, len, seed, XXH_bigEndian, XXH_unaligned);\r
+#endif\r
+}\r
+\r
+\r
+//****************************\r
+// Advanced Hash Functions\r
+//****************************\r
+\r
+struct XXH_state32_t\r
+{\r
+    U64 total_len;\r
+    U32 seed;\r
+    U32 v1;\r
+    U32 v2;\r
+    U32 v3;\r
+    U32 v4;\r
+    int memsize;\r
+    char memory[16];\r
+};\r
+\r
+\r
+int XXH32_sizeofState(void)\r
+{\r
+    XXH_STATIC_ASSERT(XXH32_SIZEOFSTATE >= sizeof(struct XXH_state32_t));   // A compilation error here means XXH32_SIZEOFSTATE is not large enough\r
+    return sizeof(struct XXH_state32_t); \r
+}\r
+\r
+\r
+XXH_errorcode XXH32_resetState(void* state_in, U32 seed)\r
+{ \r
+    struct XXH_state32_t * state = (struct XXH_state32_t *) state_in;\r
+    state->seed = seed;\r
+    state->v1 = seed + PRIME32_1 + PRIME32_2;\r
+    state->v2 = seed + PRIME32_2;\r
+    state->v3 = seed + 0;\r
+    state->v4 = seed - PRIME32_1;\r
+    state->total_len = 0;\r
+    state->memsize = 0;\r
+    return XXH_OK;\r
+}\r
+\r
+\r
+void* XXH32_init (U32 seed)\r
+{\r
+    void* state = XXH_malloc (sizeof(struct XXH_state32_t));\r
+    XXH32_resetState(state, seed);\r
+    return state;\r
+}\r
+\r
+\r
+forceinline XXH_errorcode XXH32_update_endian (void* state_in, const void* input, int len, XXH_endianess endian)\r
+{\r
+    struct XXH_state32_t * state = (struct XXH_state32_t *) state_in;\r
+    const BYTE* p = (const BYTE*)input;\r
+    const BYTE* const bEnd = p + len;\r
+\r
+#ifdef XXH_ACCEPT_NULL_INPUT_POINTER\r
+    if (input==NULL) return XXH_ERROR;\r
+#endif\r
+\r
+    state->total_len += len;\r
+\r
+    if (state->memsize + len < 16)   // fill in tmp buffer\r
+    {\r
+        XXH_memcpy(state->memory + state->memsize, input, len);\r
+        state->memsize +=  len;\r
+        return XXH_OK;\r
+    }\r
+\r
+    if (state->memsize)   // some data left from previous update\r
+    {\r
+        XXH_memcpy(state->memory + state->memsize, input, 16-state->memsize);\r
+        {\r
+            const U32* p32 = (const U32*)state->memory;\r
+            state->v1 += XXH_readLE32(p32, endian) * PRIME32_2; state->v1 = XXH_rotl32(state->v1, 13); state->v1 *= PRIME32_1; p32++;\r
+            state->v2 += XXH_readLE32(p32, endian) * PRIME32_2; state->v2 = XXH_rotl32(state->v2, 13); state->v2 *= PRIME32_1; p32++; \r
+            state->v3 += XXH_readLE32(p32, endian) * PRIME32_2; state->v3 = XXH_rotl32(state->v3, 13); state->v3 *= PRIME32_1; p32++;\r
+            state->v4 += XXH_readLE32(p32, endian) * PRIME32_2; state->v4 = XXH_rotl32(state->v4, 13); state->v4 *= PRIME32_1; p32++;\r
+        }\r
+        p += 16-state->memsize;\r
+        state->memsize = 0;\r
+    }\r
+\r
+    if (p <= bEnd-16)\r
+    {\r
+        const BYTE* const limit = bEnd - 16;\r
+        U32 v1 = state->v1;\r
+        U32 v2 = state->v2;\r
+        U32 v3 = state->v3;\r
+        U32 v4 = state->v4;\r
+\r
+        do\r
+        {\r
+            v1 += XXH_readLE32((const U32*)p, endian) * PRIME32_2; v1 = XXH_rotl32(v1, 13); v1 *= PRIME32_1; p+=4;\r
+            v2 += XXH_readLE32((const U32*)p, endian) * PRIME32_2; v2 = XXH_rotl32(v2, 13); v2 *= PRIME32_1; p+=4;\r
+            v3 += XXH_readLE32((const U32*)p, endian) * PRIME32_2; v3 = XXH_rotl32(v3, 13); v3 *= PRIME32_1; p+=4;\r
+            v4 += XXH_readLE32((const U32*)p, endian) * PRIME32_2; v4 = XXH_rotl32(v4, 13); v4 *= PRIME32_1; p+=4;\r
+        } while (p<=limit);\r
+\r
+        state->v1 = v1;\r
+        state->v2 = v2;\r
+        state->v3 = v3;\r
+        state->v4 = v4;\r
+    }\r
+\r
+    if (p < bEnd)\r
+    {\r
+        XXH_memcpy(state->memory, p, bEnd-p);\r
+        state->memsize = (int)(bEnd-p);\r
+    }\r
+\r
+    return XXH_OK;\r
+}\r
+\r
+XXH_errorcode XXH32_update (void* state_in, const void* input, int len)\r
+{\r
+    XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN;\r
+    \r
+    if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT)\r
+        return XXH32_update_endian(state_in, input, len, XXH_littleEndian);\r
+    else\r
+        return XXH32_update_endian(state_in, input, len, XXH_bigEndian);\r
+}\r
+\r
+\r
+\r
+forceinline U32 XXH32_intermediateDigest_endian (void* state_in, XXH_endianess endian)\r
+{\r
+    struct XXH_state32_t * state = (struct XXH_state32_t *) state_in;\r
+    const BYTE * p = (const BYTE*)state->memory;\r
+    BYTE* bEnd = (BYTE*)state->memory + state->memsize;\r
+    U32 h32;\r
+\r
+    if (state->total_len >= 16)\r
+    {\r
+        h32 = XXH_rotl32(state->v1, 1) + XXH_rotl32(state->v2, 7) + XXH_rotl32(state->v3, 12) + XXH_rotl32(state->v4, 18);\r
+    }\r
+    else\r
+    {\r
+        h32  = state->seed + PRIME32_5;\r
+    }\r
+\r
+    h32 += (U32) state->total_len;\r
+\r
+    while (p<=bEnd-4)\r
+    {\r
+        h32 += XXH_readLE32((const U32*)p, endian) * PRIME32_3;\r
+        h32  = XXH_rotl32(h32, 17) * PRIME32_4;\r
+        p+=4;\r
+    }\r
+\r
+    while (p<bEnd)\r
+    {\r
+        h32 += (*p) * PRIME32_5;\r
+        h32 = XXH_rotl32(h32, 11) * PRIME32_1;\r
+        p++;\r
+    }\r
+\r
+    h32 ^= h32 >> 15;\r
+    h32 *= PRIME32_2;\r
+    h32 ^= h32 >> 13;\r
+    h32 *= PRIME32_3;\r
+    h32 ^= h32 >> 16;\r
+\r
+    return h32;\r
+}\r
+\r
+\r
+U32 XXH32_intermediateDigest (void* state_in)\r
+{\r
+    XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN;\r
+    \r
+    if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT)\r
+        return XXH32_intermediateDigest_endian(state_in, XXH_littleEndian);\r
+    else\r
+        return XXH32_intermediateDigest_endian(state_in, XXH_bigEndian);\r
+}\r
+\r
+\r
+U32 XXH32_digest (void* state_in)\r
+{\r
+    U32 h32 = XXH32_intermediateDigest(state_in);\r
+\r
+    XXH_free(state_in);\r
+\r
+    return h32;\r
+}\r
diff --git a/contrib/libucl/xxhash.h b/contrib/libucl/xxhash.h
new file mode 100644 (file)
index 0000000..b9f5288
--- /dev/null
@@ -0,0 +1,164 @@
+/*\r
+   xxHash - Fast Hash algorithm\r
+   Header File\r
+   Copyright (C) 2012-2013, Yann Collet.\r
+   BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)\r
+\r
+   Redistribution and use in source and binary forms, with or without\r
+   modification, are permitted provided that the following conditions are\r
+   met:\r
+  \r
+       * Redistributions of source code must retain the above copyright\r
+   notice, this list of conditions and the following disclaimer.\r
+       * Redistributions in binary form must reproduce the above\r
+   copyright notice, this list of conditions and the following disclaimer\r
+   in the documentation and/or other materials provided with the\r
+   distribution.\r
+  \r
+   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\r
+   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\r
+   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\r
+   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\r
+   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\r
+   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\r
+   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\r
+   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\r
+   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\r
+   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\r
+   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
+\r
+   You can contact the author at :\r
+   - xxHash source repository : http://code.google.com/p/xxhash/\r
+*/\r
+\r
+/* Notice extracted from xxHash homepage :\r
+\r
+xxHash is an extremely fast Hash algorithm, running at RAM speed limits.\r
+It also successfully passes all tests from the SMHasher suite.\r
+\r
+Comparison (single thread, Windows Seven 32 bits, using SMHasher on a Core 2 Duo @3GHz)\r
+\r
+Name            Speed       Q.Score   Author\r
+xxHash          5.4 GB/s     10\r
+CrapWow         3.2 GB/s      2       Andrew\r
+MumurHash 3a    2.7 GB/s     10       Austin Appleby\r
+SpookyHash      2.0 GB/s     10       Bob Jenkins\r
+SBox            1.4 GB/s      9       Bret Mulvey\r
+Lookup3         1.2 GB/s      9       Bob Jenkins\r
+SuperFastHash   1.2 GB/s      1       Paul Hsieh\r
+CityHash64      1.05 GB/s    10       Pike & Alakuijala\r
+FNV             0.55 GB/s     5       Fowler, Noll, Vo\r
+CRC32           0.43 GB/s     9\r
+MD5-32          0.33 GB/s    10       Ronald L. Rivest\r
+SHA1-32         0.28 GB/s    10\r
+\r
+Q.Score is a measure of quality of the hash function. \r
+It depends on successfully passing SMHasher test set. \r
+10 is a perfect score.\r
+*/\r
+\r
+#pragma once\r
+\r
+#if defined (__cplusplus)\r
+extern "C" {\r
+#endif\r
+\r
+\r
+//****************************\r
+// Type\r
+//****************************\r
+typedef enum { XXH_OK=0, XXH_ERROR } XXH_errorcode;\r
+\r
+\r
+\r
+//****************************\r
+// Simple Hash Functions\r
+//****************************\r
+\r
+unsigned int XXH32 (const void* input, int len, unsigned int seed);\r
+\r
+/*\r
+XXH32() :\r
+    Calculate the 32-bits hash of sequence of length "len" stored at memory address "input".\r
+    The memory between input & input+len must be valid (allocated and read-accessible).\r
+    "seed" can be used to alter the result predictably.\r
+    This function successfully passes all SMHasher tests.\r
+    Speed on Core 2 Duo @ 3 GHz (single thread, SMHasher benchmark) : 5.4 GB/s\r
+    Note that "len" is type "int", which means it is limited to 2^31-1.\r
+    If your data is larger, use the advanced functions below.\r
+*/\r
+\r
+\r
+\r
+//****************************\r
+// Advanced Hash Functions\r
+//****************************\r
+\r
+void*         XXH32_init   (unsigned int seed);\r
+XXH_errorcode XXH32_update (void* state, const void* input, int len);\r
+unsigned int  XXH32_digest (void* state);\r
+\r
+/*\r
+These functions calculate the xxhash of an input provided in several small packets,\r
+as opposed to an input provided as a single block.\r
+\r
+It must be started with :\r
+void* XXH32_init()\r
+The function returns a pointer which holds the state of calculation.\r
+\r
+This pointer must be provided as "void* state" parameter for XXH32_update().\r
+XXH32_update() can be called as many times as necessary.\r
+The user must provide a valid (allocated) input.\r
+The function returns an error code, with 0 meaning OK, and any other value meaning there is an error.\r
+Note that "len" is type "int", which means it is limited to 2^31-1. \r
+If your data is larger, it is recommended to chunk your data into blocks \r
+of size for example 2^30 (1GB) to avoid any "int" overflow issue.\r
+\r
+Finally, you can end the calculation anytime, by using XXH32_digest().\r
+This function returns the final 32-bits hash.\r
+You must provide the same "void* state" parameter created by XXH32_init().\r
+Memory will be freed by XXH32_digest().\r
+*/\r
+\r
+\r
+int           XXH32_sizeofState(void);\r
+XXH_errorcode XXH32_resetState(void* state, unsigned int seed);\r
+\r
+#define       XXH32_SIZEOFSTATE 48\r
+typedef struct { long long ll[(XXH32_SIZEOFSTATE+(sizeof(long long)-1))/sizeof(long long)]; } XXH32_stateSpace_t;\r
+/*\r
+These functions allow user application to make its own allocation for state.\r
+\r
+XXH32_sizeofState() is used to know how much space must be allocated for the xxHash 32-bits state.\r
+Note that the state must be aligned to access 'long long' fields. Memory must be allocated and referenced by a pointer.\r
+This pointer must then be provided as 'state' into XXH32_resetState(), which initializes the state.\r
+\r
+For static allocation purposes (such as allocation on stack, or freestanding systems without malloc()),\r
+use the structure XXH32_stateSpace_t, which will ensure that memory space is large enough and correctly aligned to access 'long long' fields.\r
+*/\r
+\r
+\r
+unsigned int XXH32_intermediateDigest (void* state);\r
+/*\r
+This function does the same as XXH32_digest(), generating a 32-bit hash,\r
+but preserve memory context.\r
+This way, it becomes possible to generate intermediate hashes, and then continue feeding data with XXH32_update().\r
+To free memory context, use XXH32_digest(), or free().\r
+*/\r
+\r
+\r
+\r
+//****************************\r
+// Deprecated function names\r
+//****************************\r
+// The following translations are provided to ease code transition\r
+// You are encouraged to no longer this function names\r
+#define XXH32_feed   XXH32_update\r
+#define XXH32_result XXH32_digest\r
+#define XXH32_getIntermediateResult XXH32_intermediateDigest\r
+\r
+\r
+\r
+#if defined (__cplusplus)\r
+}\r
+#endif\r
index fa2aa006e0cdfd8e683f92481e80ca91d95b8e75..1fcab9cb7d3b8dcb244d25e384ebf730f4688acf 100644 (file)
@@ -56,11 +56,6 @@ ENDMACRO(AddModules MLIST WLIST)
 
 # Contrib software
 ADD_SUBDIRECTORY(cdb)
-ADD_SUBDIRECTORY(ucl/cmake)
-SET(SLAVE_BUILD 1)
-ADD_SUBDIRECTORY(rdns)
-UNSET(SLAVE_BUILD)
-
 # Rspamd core components
 ADD_SUBDIRECTORY(lua)
 ADD_SUBDIRECTORY(libcryptobox)