[Midnightbsd-cvs] src [11074] vendor/libucl: add libucl

laffer1 at midnightbsd.org laffer1 at midnightbsd.org
Tue Jun 19 09:29:01 EDT 2018


Revision: 11074
          http://svnweb.midnightbsd.org/src/?rev=11074
Author:   laffer1
Date:     2018-06-19 09:29:00 -0400 (Tue, 19 Jun 2018)
Log Message:
-----------
add libucl

Added Paths:
-----------
    vendor/libucl/
    vendor/libucl/dist/
    vendor/libucl/dist/Makefile.am
    vendor/libucl/dist/Makefile.unix
    vendor/libucl/dist/Makefile.w32
    vendor/libucl/dist/README.md
    vendor/libucl/dist/autogen.sh
    vendor/libucl/dist/cmake/
    vendor/libucl/dist/cmake/CMakeLists.txt
    vendor/libucl/dist/configure.ac
    vendor/libucl/dist/doc/
    vendor/libucl/dist/doc/Makefile.am
    vendor/libucl/dist/doc/api.md
    vendor/libucl/dist/doc/libucl.3
    vendor/libucl/dist/doc/pandoc.template
    vendor/libucl/dist/include/
    vendor/libucl/dist/include/ucl.h
    vendor/libucl/dist/libucl.pc.in
    vendor/libucl/dist/src/
    vendor/libucl/dist/src/Makefile.am
    vendor/libucl/dist/src/tree.h
    vendor/libucl/dist/src/ucl_chartable.h
    vendor/libucl/dist/src/ucl_emitter.c
    vendor/libucl/dist/src/ucl_hash.c
    vendor/libucl/dist/src/ucl_hash.h
    vendor/libucl/dist/src/ucl_internal.h
    vendor/libucl/dist/src/ucl_parser.c
    vendor/libucl/dist/src/ucl_schema.c
    vendor/libucl/dist/src/ucl_util.c
    vendor/libucl/dist/src/xxhash.c
    vendor/libucl/dist/src/xxhash.h
    vendor/libucl/dist/stamp-h.in
    vendor/libucl/dist/tests/
    vendor/libucl/dist/tests/Makefile.am
    vendor/libucl/dist/tests/basic/
    vendor/libucl/dist/tests/basic/1.in
    vendor/libucl/dist/tests/basic/1.res
    vendor/libucl/dist/tests/basic/10.in
    vendor/libucl/dist/tests/basic/2.in
    vendor/libucl/dist/tests/basic/2.res
    vendor/libucl/dist/tests/basic/3.in
    vendor/libucl/dist/tests/basic/3.res
    vendor/libucl/dist/tests/basic/4.in
    vendor/libucl/dist/tests/basic/4.res
    vendor/libucl/dist/tests/basic/6.in
    vendor/libucl/dist/tests/basic/6.res
    vendor/libucl/dist/tests/basic/8.in
    vendor/libucl/dist/tests/basic/8.res
    vendor/libucl/dist/tests/basic/9-comment.inc
    vendor/libucl/dist/tests/basic/9-empty.inc
    vendor/libucl/dist/tests/basic/9.in
    vendor/libucl/dist/tests/basic/9.inc
    vendor/libucl/dist/tests/basic/9.res
    vendor/libucl/dist/tests/basic.test
    vendor/libucl/dist/tests/generate.res
    vendor/libucl/dist/tests/generate.test
    vendor/libucl/dist/tests/run_tests.sh
    vendor/libucl/dist/tests/schema/
    vendor/libucl/dist/tests/schema/additionalItems.json
    vendor/libucl/dist/tests/schema/additionalProperties.json
    vendor/libucl/dist/tests/schema/allOf.json
    vendor/libucl/dist/tests/schema/anyOf.json
    vendor/libucl/dist/tests/schema/definitions.json
    vendor/libucl/dist/tests/schema/dependencies.json
    vendor/libucl/dist/tests/schema/enum.json
    vendor/libucl/dist/tests/schema/items.json
    vendor/libucl/dist/tests/schema/maxItems.json
    vendor/libucl/dist/tests/schema/maxLength.json
    vendor/libucl/dist/tests/schema/maxProperties.json
    vendor/libucl/dist/tests/schema/maximum.json
    vendor/libucl/dist/tests/schema/minItems.json
    vendor/libucl/dist/tests/schema/minLength.json
    vendor/libucl/dist/tests/schema/minProperties.json
    vendor/libucl/dist/tests/schema/minimum.json
    vendor/libucl/dist/tests/schema/multipleOf.json
    vendor/libucl/dist/tests/schema/not.json
    vendor/libucl/dist/tests/schema/oneOf.json
    vendor/libucl/dist/tests/schema/pattern.json
    vendor/libucl/dist/tests/schema/patternProperties.json
    vendor/libucl/dist/tests/schema/properties.json
    vendor/libucl/dist/tests/schema/ref.json
    vendor/libucl/dist/tests/schema/refRemote.json
    vendor/libucl/dist/tests/schema/required.json
    vendor/libucl/dist/tests/schema/type.json
    vendor/libucl/dist/tests/schema/uniqueItems.json
    vendor/libucl/dist/tests/schema.test
    vendor/libucl/dist/tests/speed.test
    vendor/libucl/dist/tests/test_basic.c
    vendor/libucl/dist/tests/test_generate.c
    vendor/libucl/dist/tests/test_schema.c
    vendor/libucl/dist/tests/test_speed.c
    vendor/libucl/dist/uthash/
    vendor/libucl/dist/uthash/uthash.h
    vendor/libucl/dist/uthash/utlist.h
    vendor/libucl/dist/uthash/utstring.h
    vendor/libucl/dist/utils/
    vendor/libucl/dist/utils/Makefile.am
    vendor/libucl/dist/utils/chargen.c
    vendor/libucl/dist/utils/objdump.c

Added: vendor/libucl/dist/Makefile.am
===================================================================
--- vendor/libucl/dist/Makefile.am	                        (rev 0)
+++ vendor/libucl/dist/Makefile.am	2018-06-19 13:29:00 UTC (rev 11074)
@@ -0,0 +1,7 @@
+ACLOCAL_AMFLAGS = -I m4
+EXTRA_DIST = uthash README.md
+
+pkgconfigdir = $(libdir)/pkgconfig
+pkgconfig_DATA = libucl.pc
+
+SUBDIRS = src tests utils doc

Added: vendor/libucl/dist/Makefile.unix
===================================================================
--- vendor/libucl/dist/Makefile.unix	                        (rev 0)
+++ vendor/libucl/dist/Makefile.unix	2018-06-19 13:29:00 UTC (rev 11074)
@@ -0,0 +1,92 @@
+CC ?= gcc
+DESTDIR ?= /usr/local
+LD ?= gcc
+C_COMMON_FLAGS ?= -fPIC -Wall -W -Wno-unused-parameter -Wno-pointer-sign -I./include -I./uthash -I./src
+MAJOR_VERSION = 0
+MINOR_VERSION = 2
+PATCH_VERSION = 9
+VERSION = "$(MAJOR_VERSION).$(MINOR_VERSION).$(PATCH_VERSION)"
+SONAME = libucl.so
+SONAME_FULL = $(SONAME).$(MAJOR_VERSION)
+OBJDIR ?= .obj
+TESTDIR ?= tests
+SRCDIR ?= src
+INCLUDEDIR ?= include
+MKDIR ?= mkdir
+INSTALL ?= install
+RM ?= rm
+RMDIR ?= rmdir
+LN ?= ln
+LD_SHARED_FLAGS ?= -Wl,-soname,$(SONAME) -shared -lm
+LD_UCL_FLAGS ?= -L$(OBJDIR) -Wl,-rpath,$(OBJDIR) -lucl
+LD_ADD ?= -lrt
+COPT_FLAGS ?= -O2
+HDEPS = $(SRCDIR)/ucl_hash.h \
+		$(SRCDIR)/ucl_chartable.h \
+		$(SRCDIR)/ucl_internal.h \
+		$(INCLUDEDIR)/ucl.h \
+		$(SRCDIR)/xxhash.h
+OBJECTS = $(OBJDIR)/ucl_hash.o \
+		$(OBJDIR)/ucl_util.o \
+		$(OBJDIR)/ucl_parser.o \
+		$(OBJDIR)/ucl_emitter.o \
+		$(OBJDIR)/ucl_schema.o \
+		$(OBJDIR)/xxhash.o
+
+all: $(OBJDIR) $(OBJDIR)/$(SONAME)
+
+$(OBJDIR)/$(SONAME): $(OBJDIR)/$(SONAME_FULL)
+	$(LN) -sf $(SONAME_FULL) $(OBJDIR)/$(SONAME)
+
+$(OBJDIR)/$(SONAME_FULL): $(OBJECTS)
+	$(CC) -o $(OBJDIR)/$(SONAME_FULL) $(OBJECTS) $(LD_SHARED_FLAGS) $(LDFLAGS) $(SSL_LIBS) $(FETCH_LIBS)
+
+$(OBJDIR):
+	@$(MKDIR) -p $(OBJDIR)
+
+# Compile rules
+$(OBJDIR)/ucl_util.o: $(SRCDIR)/ucl_util.c $(HDEPS)
+	$(CC) -o $(OBJDIR)/ucl_util.o $(CPPFLAGS) $(COPT_FLAGS) $(CFLAGS) $(C_COMMON_FLAGS) $(SSL_CFLAGS) $(FETCH_FLAGS) -c $(SRCDIR)/ucl_util.c
+$(OBJDIR)/ucl_parser.o: $(SRCDIR)/ucl_parser.c $(HDEPS)
+	$(CC) -o $(OBJDIR)/ucl_parser.o $(CPPFLAGS) $(COPT_FLAGS) $(CFLAGS) $(C_COMMON_FLAGS) $(SSL_CFLAGS) $(FETCH_FLAGS) -c $(SRCDIR)/ucl_parser.c
+$(OBJDIR)/ucl_emitter.o: $(SRCDIR)/ucl_emitter.c $(HDEPS)
+	$(CC) -o $(OBJDIR)/ucl_emitter.o $(CPPFLAGS) $(COPT_FLAGS) $(CFLAGS) $(C_COMMON_FLAGS) $(SSL_CFLAGS) $(FETCH_FLAGS) -c $(SRCDIR)/ucl_emitter.c
+$(OBJDIR)/ucl_hash.o: $(SRCDIR)/ucl_hash.c $(HDEPS)
+	$(CC) -o $(OBJDIR)/ucl_hash.o $(CPPFLAGS) $(COPT_FLAGS) $(CFLAGS) $(C_COMMON_FLAGS) $(SSL_CFLAGS) $(FETCH_FLAGS) -c $(SRCDIR)/ucl_hash.c
+$(OBJDIR)/ucl_schema.o: $(SRCDIR)/ucl_schema.c $(HDEPS)
+	$(CC) -o $(OBJDIR)/ucl_schema.o $(CPPFLAGS) $(COPT_FLAGS) $(CFLAGS) $(C_COMMON_FLAGS) $(SSL_CFLAGS) $(FETCH_FLAGS) -c $(SRCDIR)/ucl_schema.c
+$(OBJDIR)/xxhash.o: $(SRCDIR)/xxhash.c $(HDEPS)
+	$(CC) -o $(OBJDIR)/xxhash.o $(CPPFLAGS) $(COPT_FLAGS) $(CFLAGS) $(C_COMMON_FLAGS) $(SSL_CFLAGS) $(FETCH_FLAGS) -c $(SRCDIR)/xxhash.c
+
+clean:
+	$(RM) $(OBJDIR)/*.o $(OBJDIR)/$(SONAME_FULL) $(OBJDIR)/$(SONAME) $(OBJDIR)/chargen $(OBJDIR)/test_basic $(OBJDIR)/test_speed $(OBJDIR)/objdump $(OBJDIR)/test_generate $(OBJDIR)/test_schema || true
+	$(RMDIR) $(OBJDIR)
+	
+# Utils
+
+chargen: utils/chargen.c $(OBJDIR)/$(SONAME)
+	$(CC) -o $(OBJDIR)/chargen $(CPPFLAGS) $(COPT_FLAGS) $(CFLAGS) $(C_COMMON_FLAGS) $(SSL_CFLAGS) $(FETCH_FLAGS) $(LDFLAGS) utils/chargen.c
+objdump: utils/objdump.c $(OBJDIR)/$(SONAME)
+	$(CC) -o $(OBJDIR)/objdump $(CPPFLAGS) $(COPT_FLAGS) $(CFLAGS) $(C_COMMON_FLAGS) $(SSL_CFLAGS) $(FETCH_FLAGS) $(LDFLAGS) utils/objdump.c $(LD_UCL_FLAGS)
+
+# Tests
+
+test: $(OBJDIR) $(OBJDIR)/$(SONAME) $(OBJDIR)/test_basic $(OBJDIR)/test_speed $(OBJDIR)/test_generate $(OBJDIR)/test_schema
+
+run-test: test
+	TEST_DIR=$(TESTDIR) $(TESTDIR)/run_tests.sh $(OBJDIR)/test_basic $(OBJDIR)/test_speed $(OBJDIR)/test_generate $(OBJDIR)/test_schema
+	
+$(OBJDIR)/test_basic: $(TESTDIR)/test_basic.c $(OBJDIR)/$(SONAME)
+	$(CC) -o $(OBJDIR)/test_basic $(CPPFLAGS) $(COPT_FLAGS) $(CFLAGS) $(C_COMMON_FLAGS) $(SSL_CFLAGS) $(FETCH_FLAGS) $(LDFLAGS) $(TESTDIR)/test_basic.c $(LD_UCL_FLAGS)
+$(OBJDIR)/test_schema: $(TESTDIR)/test_schema.c $(OBJDIR)/$(SONAME)
+	$(CC) -o $(OBJDIR)/test_schema $(CPPFLAGS) $(COPT_FLAGS) $(CFLAGS) $(C_COMMON_FLAGS) $(SSL_CFLAGS) $(FETCH_FLAGS) $(LDFLAGS) $(TESTDIR)/test_schema.c $(LD_UCL_FLAGS)
+$(OBJDIR)/test_speed: $(TESTDIR)/test_speed.c $(OBJDIR)/$(SONAME)
+	$(CC) -o $(OBJDIR)/test_speed $(CPPFLAGS) $(COPT_FLAGS) $(CFLAGS) $(C_COMMON_FLAGS) $(SSL_CFLAGS) $(FETCH_FLAGS) $(LDFLAGS) $(TESTDIR)/test_speed.c $(LD_UCL_FLAGS) $(LD_ADD)
+$(OBJDIR)/test_generate: $(TESTDIR)/test_generate.c $(OBJDIR)/$(SONAME)
+	$(CC) -o $(OBJDIR)/test_generate $(CPPFLAGS) $(COPT_FLAGS) $(CFLAGS) $(C_COMMON_FLAGS) $(SSL_CFLAGS) $(FETCH_FLAGS) $(LDFLAGS) $(TESTDIR)/test_generate.c $(LD_UCL_FLAGS) $(LD_ADD)
+
+install: $(OBJDIR)/$(SONAME)
+	$(INSTALL) -m0755 $(SONAME) $(DESTDIR)/lib/$(SONAME)
+	$(INSTALL) -m0644 include/ucl.h $(DESTDIR)/include/ucl.h
+
+.PHONY: clean $(OBJDIR)

Added: vendor/libucl/dist/Makefile.w32
===================================================================
--- vendor/libucl/dist/Makefile.w32	                        (rev 0)
+++ vendor/libucl/dist/Makefile.w32	2018-06-19 13:29:00 UTC (rev 11074)
@@ -0,0 +1,90 @@
+CC ?= gcc
+DESTDIR ?= /usr/local
+LD ?= gcc
+C_COMMON_FLAGS ?= -fPIC -Wall -W -Wno-unused-parameter -Wno-pointer-sign -I./include -I./uthash -I./src
+MAJOR_VERSION = 0
+MINOR_VERSION = 2
+PATCH_VERSION = 9
+VERSION = "$(MAJOR_VERSION).$(MINOR_VERSION).$(PATCH_VERSION)"
+SONAME = libucl.dll
+OBJDIR ?= .obj
+TESTDIR ?= tests
+SRCDIR ?= src
+INCLUDEDIR ?= include
+MKDIR ?= mkdir
+INSTALL ?= install
+RM ?= rm
+RMDIR ?= rmdir
+ifeq (Windows_NT, $(OS))
+LN ?= ln
+else
+LN ?= rem ln
+endif
+LD_SHARED_FLAGS ?= -Wl,-soname,$(SONAME) -shared -lm
+LD_UCL_FLAGS ?= -L$(OBJDIR) -Wl,-rpath,$(OBJDIR) -lucl
+LD_ADD ?= -lrt
+COPT_FLAGS ?= -O2
+HDEPS = $(SRCDIR)/ucl_hash.h \
+		$(SRCDIR)/ucl_chartable.h \
+		$(SRCDIR)/ucl_internal.h \
+		$(INCLUDEDIR)/ucl.h \
+		$(SRCDIR)/xxhash.h
+OBJECTS = $(OBJDIR)/ucl_hash.o \
+		$(OBJDIR)/ucl_util.o \
+		$(OBJDIR)/ucl_parser.o \
+		$(OBJDIR)/ucl_emitter.o \
+		$(OBJDIR)/ucl_schema.o \
+		$(OBJDIR)/xxhash.o
+
+all: $(OBJDIR) $(OBJDIR)/$(SONAME)
+
+$(OBJDIR)/$(SONAME): $(OBJECTS)
+	$(CC) -o $(OBJDIR)/$(SONAME) $(OBJECTS) $(LD_SHARED_FLAGS) $(LDFLAGS) $(SSL_LIBS) $(FETCH_LIBS)
+
+$(OBJDIR):
+	@$(MKDIR) -p $(OBJDIR)
+
+# Compile rules
+$(OBJDIR)/ucl_util.o: $(SRCDIR)/ucl_util.c $(HDEPS)
+	$(CC) -o $(OBJDIR)/ucl_util.o $(CPPFLAGS) $(COPT_FLAGS) $(CFLAGS) $(C_COMMON_FLAGS) $(SSL_CFLAGS) $(FETCH_FLAGS) -c $(SRCDIR)/ucl_util.c
+$(OBJDIR)/ucl_parser.o: $(SRCDIR)/ucl_parser.c $(HDEPS)
+	$(CC) -o $(OBJDIR)/ucl_parser.o $(CPPFLAGS) $(COPT_FLAGS) $(CFLAGS) $(C_COMMON_FLAGS) $(SSL_CFLAGS) $(FETCH_FLAGS) -c $(SRCDIR)/ucl_parser.c
+$(OBJDIR)/ucl_emitter.o: $(SRCDIR)/ucl_emitter.c $(HDEPS)
+	$(CC) -o $(OBJDIR)/ucl_emitter.o $(CPPFLAGS) $(COPT_FLAGS) $(CFLAGS) $(C_COMMON_FLAGS) $(SSL_CFLAGS) $(FETCH_FLAGS) -c $(SRCDIR)/ucl_emitter.c
+$(OBJDIR)/ucl_hash.o: $(SRCDIR)/ucl_hash.c $(HDEPS)
+	$(CC) -o $(OBJDIR)/ucl_hash.o $(CPPFLAGS) $(COPT_FLAGS) $(CFLAGS) $(C_COMMON_FLAGS) $(SSL_CFLAGS) $(FETCH_FLAGS) -c $(SRCDIR)/ucl_hash.c
+$(OBJDIR)/ucl_schema.o: $(SRCDIR)/ucl_schema.c $(HDEPS)
+	$(CC) -o $(OBJDIR)/ucl_schema.o $(CPPFLAGS) $(COPT_FLAGS) $(CFLAGS) $(C_COMMON_FLAGS) $(SSL_CFLAGS) $(FETCH_FLAGS) -c $(SRCDIR)/ucl_schema.c
+$(OBJDIR)/xxhash.o: $(SRCDIR)/xxhash.c $(HDEPS)
+	$(CC) -o $(OBJDIR)/xxhash.o $(CPPFLAGS) $(COPT_FLAGS) $(CFLAGS) $(C_COMMON_FLAGS) $(SSL_CFLAGS) $(FETCH_FLAGS) -c $(SRCDIR)/xxhash.c
+
+clean:
+	$(RM) $(OBJDIR)/*.o $(OBJDIR)/$(SONAME) $(OBJDIR)/$(SONAME) $(OBJDIR)/chargen $(OBJDIR)/test_basic $(OBJDIR)/test_speed $(OBJDIR)/objdump $(OBJDIR)/test_generate
+	$(RMDIR) $(OBJDIR)
+	
+# Utils
+
+chargen: utils/chargen.c $(OBJDIR)/$(SONAME)
+	$(CC) -o $(OBJDIR)/chargen $(CPPFLAGS) $(COPT_FLAGS) $(CFLAGS) $(C_COMMON_FLAGS) $(SSL_CFLAGS) $(FETCH_FLAGS) $(LDFLAGS) utils/chargen.c
+objdump: utils/objdump.c $(OBJDIR)/$(SONAME)
+	$(CC) -o $(OBJDIR)/objdump $(CPPFLAGS) $(COPT_FLAGS) $(CFLAGS) $(C_COMMON_FLAGS) $(SSL_CFLAGS) $(FETCH_FLAGS) $(LDFLAGS) utils/objdump.c $(LD_UCL_FLAGS)
+
+# Tests
+
+test: $(OBJDIR) $(OBJDIR)/$(SONAME) $(OBJDIR)/test_basic $(OBJDIR)/test_speed $(OBJDIR)/test_generate
+
+run-test: test
+	TEST_DIR=$(TESTDIR) $(TESTDIR)/run_tests.sh $(OBJDIR)/test_basic $(OBJDIR)/test_speed $(OBJDIR)/test_generate
+	
+$(OBJDIR)/test_basic: $(TESTDIR)/test_basic.c $(OBJDIR)/$(SONAME)
+	$(CC) -o $(OBJDIR)/test_basic $(CPPFLAGS) $(COPT_FLAGS) $(CFLAGS) $(C_COMMON_FLAGS) $(SSL_CFLAGS) $(FETCH_FLAGS) $(LDFLAGS) $(TESTDIR)/test_basic.c $(LD_UCL_FLAGS)
+$(OBJDIR)/test_speed: $(TESTDIR)/test_speed.c $(OBJDIR)/$(SONAME)
+	$(CC) -o $(OBJDIR)/test_speed $(CPPFLAGS) $(COPT_FLAGS) $(CFLAGS) $(C_COMMON_FLAGS) $(SSL_CFLAGS) $(FETCH_FLAGS) $(LDFLAGS) $(TESTDIR)/test_speed.c $(LD_UCL_FLAGS) $(LD_ADD)
+$(OBJDIR)/test_generate: $(TESTDIR)/test_generate.c $(OBJDIR)/$(SONAME)
+	$(CC) -o $(OBJDIR)/test_generate $(CPPFLAGS) $(COPT_FLAGS) $(CFLAGS) $(C_COMMON_FLAGS) $(SSL_CFLAGS) $(FETCH_FLAGS) $(LDFLAGS) $(TESTDIR)/test_generate.c $(LD_UCL_FLAGS) $(LD_ADD)
+
+install: $(OBJDIR)/$(SONAME)
+	$(INSTALL) -m0755 $(SONAME) $(DESTDIR)/lib/$(SONAME)
+	$(INSTALL) -m0644 include/ucl.h $(DESTDIR)/include/ucl.h
+
+.PHONY: clean $(OBJDIR)

Added: vendor/libucl/dist/README.md
===================================================================
--- vendor/libucl/dist/README.md	                        (rev 0)
+++ vendor/libucl/dist/README.md	2018-06-19 13:29:00 UTC (rev 11074)
@@ -0,0 +1,327 @@
+# LIBUCL
+
+[![Build Status](https://travis-ci.org/vstakhov/libucl.svg?branch=master)](https://travis-ci.org/vstakhov/libucl)
+
+**Table of Contents**  *generated with [DocToc](http://doctoc.herokuapp.com/)*
+
+- [Introduction](#introduction)
+- [Basic structure](#basic-structure)
+- [Improvements to the json notation](#improvements-to-the-json-notation)
+	- [General syntax sugar](#general-syntax-sugar)
+	- [Automatic arrays creation](#automatic-arrays-creation)
+	- [Named keys hierarchy](#named-keys-hierarchy)
+	- [Convenient numbers and booleans](#convenient-numbers-and-booleans)
+- [General improvements](#general-improvements)
+	- [Commments](#commments)
+	- [Macros support](#macros-support)
+	- [Variables support](#variables-support)
+	- [Multiline strings](#multiline-strings)
+- [Emitter](#emitter)
+- [Validation](#validation)
+- [Performance](#performance)
+- [Conclusion](#conclusion)
+
+## Introduction
+
+This document describes the main features and principles of the configuration
+language called `UCL` - universal configuration language.
+
+If you are looking for the libucl API documentation you can find it at [this page](doc/api.md).
+
+## Basic structure
+
+UCL is heavily infused by `nginx` configuration as the example of a convenient configuration
+system. However, UCL is fully compatible with `JSON` format and is able to parse json files.
+For example, you can write the same configuration in the following ways:
+
+* in nginx like:
+
+```nginx
+param = value;
+section {
+    param = value;
+    param1 = value1;
+    flag = true;
+    number = 10k;
+    time = 0.2s;
+    string = "something";
+    subsection {
+        host = {
+            host = "hostname"; 
+            port = 900;
+        }
+        host = {
+            host = "hostname";
+            port = 901;
+        }
+    }
+}
+```
+
+* or in JSON:
+
+```json
+{
+    "param": "value",
+    "param1": "value1",
+    "flag": true,
+    "subsection": {
+        "host": [
+        {
+            "host": "hostname",
+            "port": 900
+        },
+        {
+            "host": "hostname",
+            "port": 901
+        }
+        ]
+    }
+}
+```
+
+## Improvements to the json notation.
+
+There are various things that make ucl configuration more convenient for editing than strict json:
+
+### General syntax sugar
+
+* Braces are not necessary to enclose a top object: it is automatically treated as an object:
+
+```json
+"key": "value"
+```
+is equal to:
+```json
+{"key": "value"}
+```
+
+* There is no requirement of quotes for strings and keys, moreover, `:` may be replaced `=` or even be skipped for objects:
+
+```nginx
+key = value;
+section {
+    key = value;
+}
+```
+is equal to:
+```json
+{
+    "key": "value",
+    "section": {
+        "key": "value"
+    }
+}
+```
+
+* No commas mess: you can safely place a comma or semicolon for the last element in an array or an object:
+
+```json
+{
+    "key1": "value",
+    "key2": "value",
+}
+```
+### Automatic arrays creation
+
+* Non-unique keys in an object are allowed and are automatically converted to the arrays internally:
+
+```json
+{
+    "key": "value1",
+    "key": "value2"
+}
+```
+is converted to:
+```json
+{
+    "key": ["value1", "value2"]
+}
+```
+
+### Named keys hierarchy
+
+UCL accepts named keys and organize them into objects hierarchy internally. Here is an example of this process:
+```nginx
+section "blah" {
+	key = value;
+}
+section foo {
+	key = value;
+}
+```
+
+is converted to the following object:
+
+```nginx
+section {
+	blah {
+			key = value;
+	}
+	foo {
+			key = value;
+	}
+}
+```
+    
+Plain definitions may be more complex and contain more than a single level of nested objects:
+   
+```nginx
+section "blah" "foo" {
+	key = value;
+}
+```
+
+is presented as:
+
+```nginx    
+section {
+	blah {
+			foo {
+					key = value;
+			}
+	}
+}
+```
+
+### Convenient numbers and booleans
+
+* Numbers can have suffixes to specify standard multipliers:
+    + `[kKmMgG]` - standard 10 base multipliers (so `1k` is translated to 1000)
+    + `[kKmMgG]b` - 2 power multipliers (so `1kb` is translated to 1024)
+    + `[s|min|d|w|y]` - time multipliers, all time values are translated to float number of seconds, for example `10min` is translated to 600.0 and `10ms` is translated to 0.01
+* Hexadecimal integers can be used by `0x` prefix, for example `key = 0xff`. However, floating point values can use decimal base only.
+* Booleans can be specified as `true` or `yes` or `on` and `false` or `no` or `off`.
+* It is still possible to treat numbers and booleans as strings by enclosing them in double quotes.
+
+## General improvements
+
+### Commments
+
+UCL supports different style of comments:
+
+* single line: `#` 
+* multiline: `/* ... */`
+
+Multiline comments may be nested:
+```c
+# Sample single line comment
+/* 
+ some comment
+ /* nested comment */
+ end of comment
+*/
+```
+
+### Macros support
+
+UCL supports external macros both multiline and single line ones:
+```nginx
+.macro "sometext";
+.macro {
+     Some long text
+     ....
+};
+```
+There are two internal macros provided by UCL:
+
+* `include` - read a file `/path/to/file` or an url `http://example.com/file` and include it to the current place of
+UCL configuration;
+* `try\_include` - try to read a file or url and include it but do not create a fatal error if a file or url is not accessible;
+* `includes` - read a file or an url like the previous macro, but fetch and check the signature file (which is obtained
+by `.sig` suffix appending).
+
+Public keys which are used for the last command are specified by the concrete UCL user.
+
+### Variables support
+
+UCL supports variables in input. Variables are registered by a user of the UCL parser and can be presented in the following forms:
+
+* `${VARIABLE}`
+* `$VARIABLE`
+
+UCL currently does not support nested variables. To escape variables one could use double dollar signs:
+
+* `$${VARIABLE}` is converted to `${VARIABLE}`
+* `$$VARIABLE` is converted to `$VARIABLE`
+
+However, if no valid variables are found in a string, no expansion will be performed (and `$$` thus remains unchanged). This may be a subject
+to change in future libucl releases.
+
+### Multiline strings
+
+UCL can handle multiline strings as well as single line ones. It uses shell/perl like notation for such objects:
+```
+key = <<EOD
+some text
+splitted to
+lines
+EOD
+```
+
+In this example `key` will be interpreted as the following string: `some text\nsplitted to\nlines`.
+Here are some rules for this syntax:
+
+* Multiline terminator must start just after `<<` symbols and it must consist of capital letters only (e.g. `<<eof` or `<< EOF` won't work);
+* Terminator must end with a single newline character (and no spaces are allowed between terminator and newline character);
+* To finish multiline string you need to include a terminator string just after newline and followed by a newline (no spaces or other characters are allowed as well);
+* The initial and the final newlines are not inserted to the resulting string, but you can still specify newlines at the begin and at the end of a value, for example:
+
+```
+key <<EOD
+
+some
+text
+
+EOD
+```
+
+## Emitter
+
+Each UCL object can be serialized to one of the three supported formats:
+
+* `JSON` - canonic json notation (with spaces indented structure);
+* `Compacted JSON` - compact json notation (without spaces or newlines);
+* `Configuration` - nginx like notation;
+* `YAML` - yaml inlined notation.
+
+## Validation
+
+UCL allows validation of objects. It uses the same schema that is used for json: [json schema v4](http://json-schema.org). UCL supports the full set of json schema with the exception of remote references. This feature is unlikely useful for configuration objects. Of course, a schema definition can be in UCL format instead of JSON that simplifies schemas writing. Moreover, since UCL supports multiple values for keys in an object it is possible to specify generic integer constraints `maxValues` and `minValues` to define the limits of values count in a single key. UCL currently is not absolutely strict about validation schemas themselves, therefore UCL users should supply valid schemas (as it is defined in json-schema draft v4) to ensure that the input objects are validated properly.
+
+## Performance
+
+Are UCL parser and emitter fast enough? Well, there are some numbers.
+I got a 19Mb file that consist of ~700 thousands lines of json (obtained via
+http://www.json-generator.com/). Then I checked jansson library that performs json
+parsing and emitting and compared it with UCL. Here are results:
+
+```
+jansson: parsed json in 1.3899 seconds
+jansson: emitted object in 0.2609 seconds
+
+ucl: parsed input in 0.6649 seconds
+ucl: emitted config in 0.2423 seconds
+ucl: emitted json in 0.2329 seconds
+ucl: emitted compact json in 0.1811 seconds
+ucl: emitted yaml in 0.2489 seconds
+```
+
+So far, UCL seems to be significantly faster than jansson on parsing and slightly faster on emitting. Moreover,
+UCL compiled with optimizations (-O3) performs significantly faster:
+```
+ucl: parsed input in 0.3002 seconds
+ucl: emitted config in 0.1174 seconds
+ucl: emitted json in 0.1174 seconds
+ucl: emitted compact json in 0.0991 seconds
+ucl: emitted yaml in 0.1354 seconds
+```
+
+You can do your own benchmarks by running `make test` in libucl top directory.
+
+## Conclusion
+
+UCL has clear design that should be very convenient for reading and writing. At the same time it is compatible with
+JSON language and therefore can be used as a simple JSON parser. Macroes logic provides an ability to extend configuration
+language (for example by including some lua code) and comments allows to disable or enable the parts of a configuration
+quickly.

Added: vendor/libucl/dist/autogen.sh
===================================================================
--- vendor/libucl/dist/autogen.sh	                        (rev 0)
+++ vendor/libucl/dist/autogen.sh	2018-06-19 13:29:00 UTC (rev 11074)
@@ -0,0 +1,2 @@
+#!/bin/sh
+autoreconf -i

Added: vendor/libucl/dist/cmake/CMakeLists.txt
===================================================================
--- vendor/libucl/dist/cmake/CMakeLists.txt	                        (rev 0)
+++ vendor/libucl/dist/cmake/CMakeLists.txt	2018-06-19 13:29:00 UTC (rev 11074)
@@ -0,0 +1,112 @@
+PROJECT(libucl C)
+
+SET(LIBUCL_VERSION_MAJOR 0)
+SET(LIBUCL_VERSION_MINOR 2)
+SET(LIBUCL_VERSION_PATCH 9)
+
+SET(LIBUCL_VERSION         "${LIBUCL_VERSION_MAJOR}.${LIBUCL_VERSION_MINOR}.${LIBUCL_VERSION_PATCH}")
+
+INCLUDE(CheckCCompilerFlag)
+INCLUDE(FindOpenSSL)
+CMAKE_MINIMUM_REQUIRED(VERSION 2.6.0 FATAL_ERROR)
+
+OPTION(ENABLE_URL_INCLUDE  "Enable urls in ucl includes (requires libcurl or libfetch) [default: OFF]" OFF)
+OPTION(ENABLE_URL_SIGN  "Enable signatures check in ucl includes (requires openssl) [default: OFF]" OFF)
+OPTION(BUILD_SHARED_LIBS "Build Shared Libraries [default: OFF]" OFF)
+
+IF(CMAKE_SYSTEM_NAME STREQUAL "Linux")
+	LIST(APPEND CMAKE_REQUIRED_LIBRARIES rt)
+ENDIF(CMAKE_SYSTEM_NAME STREQUAL "Linux")
+
+IF(ENABLE_URL_INCLUDE MATCHES "ON")
+    FIND_LIBRARY(LIBFETCH_LIBRARY NAMES fetch PATHS	PATH_SUFFIXES lib64 lib
+                      PATHS
+                          ~/Library/Frameworks
+                          /Library/Frameworks
+                          /usr/local
+                          /usr
+                          /sw
+                          /opt/local
+                          /opt/csw
+                          /opt
+                     DOC "Path where the libfetch library can be found")
+    IF(LIBFETCH_LIBRARY)
+    	FIND_FILE(HAVE_FETCH_H NAMES fetch.h PATHS /usr/include
+    											   /opt/include
+    											   /usr/local/include
+    				DOC "Path to libfetch header")
+    ELSE(LIBFETCH_LIBRARY)
+    	# Try to find libcurl
+    	ProcessPackage(CURL libcurl)
+    	IF(NOT CURL_FOUND)
+    		MESSAGE(WARNING "Neither libcurl nor libfetch were found, no support of URL includes in configuration")
+    	ENDIF(NOT CURL_FOUND)
+    ENDIF(LIBFETCH_LIBRARY)
+ENDIF(ENABLE_URL_INCLUDE MATCHES "ON")
+
+SET(CMAKE_C_WARN_FLAGS "")
+CHECK_C_COMPILER_FLAG(-Wall SUPPORT_WALL)
+CHECK_C_COMPILER_FLAG(-W SUPPORT_W)
+CHECK_C_COMPILER_FLAG(-Wno-unused-parameter SUPPORT_WPARAM)
+CHECK_C_COMPILER_FLAG(-Wno-pointer-sign SUPPORT_WPOINTER_SIGN)
+CHECK_C_COMPILER_FLAG(-Wstrict-prototypes SUPPORT_WSTRICT_PROTOTYPES)
+IF(NOT "${CMAKE_C_COMPILER_ID}" MATCHES SunPro)
+	CHECK_C_COMPILER_FLAG("-std=c99" SUPPORT_STD_FLAG)
+ENDIF(NOT "${CMAKE_C_COMPILER_ID}" MATCHES SunPro)
+IF(SUPPORT_W)
+	SET(CMAKE_C_WARN_FLAGS "${CMAKE_C_WARN_FLAGS} -W")
+ENDIF(SUPPORT_W)
+IF(SUPPORT_WALL)
+	SET(CMAKE_C_WARN_FLAGS "${CMAKE_C_WARN_FLAGS} -Wall")
+ENDIF(SUPPORT_WALL)
+IF(SUPPORT_WPARAM)
+	SET(CMAKE_C_WARN_FLAGS "${CMAKE_C_WARN_FLAGS} -Wno-unused-parameter")
+ENDIF(SUPPORT_WPARAM)
+IF(SUPPORT_WPOINTER_SIGN)
+	SET(CMAKE_C_WARN_FLAGS "${CMAKE_C_WARN_FLAGS} -Wno-pointer-sign")
+ENDIF(SUPPORT_WPOINTER_SIGN)
+IF(SUPPORT_WSTRICT_PROTOTYPES)
+	SET(CMAKE_C_WARN_FLAGS "${CMAKE_C_WARN_FLAGS} -Wstrict-prototypes")
+ENDIF(SUPPORT_WSTRICT_PROTOTYPES)
+IF(SUPPORT_STD_FLAG)
+	SET(CMAKE_C_WARN_FLAGS "${CMAKE_C_WARN_FLAGS} -std=c99")
+ENDIF(SUPPORT_STD_FLAG)
+
+IF(ENABLE_URL_SIGN MATCHES "ON")
+	IF(OPENSSL_FOUND)
+		SET(HAVE_OPENSSL 1)
+		INCLUDE_DIRECTORIES("${OPENSSL_INCLUDE_DIR}")
+	ENDIF(OPENSSL_FOUND)
+ENDIF(ENABLE_URL_SIGN MATCHES "ON")
+
+INCLUDE_DIRECTORIES("${CMAKE_CURRENT_SOURCE_DIR}/../src")
+INCLUDE_DIRECTORIES("${CMAKE_CURRENT_SOURCE_DIR}/../include")
+INCLUDE_DIRECTORIES("${CMAKE_CURRENT_SOURCE_DIR}/../uthash")
+
+SET(UCLSRC            ../src/ucl_util.c
+                      ../src/ucl_parser.c
+                      ../src/ucl_emitter.c
+                      ../src/ucl_hash.c
+                      ../src/ucl_schema.c
+                      ../src/xxhash.c)
+
+
+SET (LIB_TYPE STATIC)
+IF (BUILD_SHARED_LIBS)
+  SET (LIB_TYPE SHARED)
+ENDIF (BUILD_SHARED_LIBS)
+ADD_LIBRARY(ucl ${LIB_TYPE} ${UCLSRC})
+SET_TARGET_PROPERTIES(ucl PROPERTIES VERSION ${LIBUCL_VERSION} SOVERSION ${LIBUCL_VERSION_MAJOR})
+
+IF(HAVE_FETCH_H)
+    TARGET_LINK_LIBRARIES(ucl fetch)
+ELSE(HAVE_FETCH_H)
+    IF(CURL_FOUND)
+        TARGET_LINK_LIBRARIES(ucl ${CURL_LIBRARIES})
+    ENDIF(CURL_FOUND)
+ENDIF(HAVE_FETCH_H)
+IF(ENABLE_URL_SIGN MATCHES "ON")
+	IF(OPENSSL_FOUND)
+		TARGET_LINK_LIBRARIES(ucl ${OPENSSL_LIBRARIES})
+	ENDIF(OPENSSL_FOUND)
+ENDIF(ENABLE_URL_SIGN MATCHES "ON")

Added: vendor/libucl/dist/configure.ac
===================================================================
--- vendor/libucl/dist/configure.ac	                        (rev 0)
+++ vendor/libucl/dist/configure.ac	2018-06-19 13:29:00 UTC (rev 11074)
@@ -0,0 +1,163 @@
+m4_define([maj_ver], [0])
+m4_define([med_ver], [4])
+m4_define([min_ver], [1])
+m4_define([so_version], [1:0:0])
+m4_define([ucl_version], [maj_ver.med_ver.min_ver])
+
+AC_INIT([libucl],[ucl_version],[https://github.com/vstakhov/libucl],[libucl])
+AC_CONFIG_SRCDIR([configure.ac])
+AM_INIT_AUTOMAKE([1.11 foreign silent-rules -Wall -Wportability no-dist-gzip dist-xz])
+
+UCL_VERSION=ucl_version
+SO_VERSION=so_version
+
+AC_SUBST(UCL_VERSION)
+AC_SUBST(SO_VERSION)
+
+AC_PROG_CC_C99
+AM_PROG_CC_C_O
+AM_PROG_AR
+LT_INIT
+AC_CONFIG_MACRO_DIR([m4])
+AC_CONFIG_HEADERS([config.h])
+
+AC_C_CONST
+AC_TYPE_SIZE_T
+
+AC_CHECK_HEADERS_ONCE([fcntl.h unistd.h])
+AC_TYPE_OFF_T
+AC_FUNC_MMAP
+AC_CHECK_HEADERS_ONCE([fcntl.h])
+AC_CHECK_HEADERS_ONCE([sys/types.h])
+AC_CHECK_HEADERS_ONCE([sys/stat.h])
+AC_CHECK_HEADERS_ONCE([sys/param.h])
+AC_CHECK_HEADERS_ONCE([sys/mman.h])
+AC_CHECK_HEADERS_ONCE([stdlib.h])
+AC_CHECK_HEADERS_ONCE([stddef.h])
+AC_CHECK_HEADERS_ONCE([stdarg.h])
+AC_CHECK_HEADERS_ONCE([stdbool.h])
+AC_CHECK_HEADERS_ONCE([stdint.h])
+AC_CHECK_HEADERS_ONCE([string.h])
+AC_CHECK_HEADERS_ONCE([unistd.h])
+AC_CHECK_HEADERS_ONCE([ctype.h])
+AC_CHECK_HEADERS_ONCE([errno.h])
+AC_CHECK_HEADERS_ONCE([limits.h])
+AC_CHECK_HEADERS_ONCE([libgen.h])
+AC_CHECK_HEADERS_ONCE([stdio.h])
+AC_CHECK_HEADERS_ONCE([float.h])
+AC_CHECK_HEADERS_ONCE([math.h])
+
+dnl Example of default-disabled feature
+AC_ARG_ENABLE([urls], AS_HELP_STRING([--enable-urls], 
+	[Enable URLs fetch (requires libfetch or libcurl) @<:@default=no@:>@]), [],
+	[enable_urls=no])
+AC_ARG_ENABLE([regex], AS_HELP_STRING([--enable-regex], 
+	[Enable regex checking for schema @<:@default=yes@:>@]), [],
+	[enable_regex=yes])
+AC_ARG_ENABLE([signatures], AS_HELP_STRING([--enable-signatures],
+	[Enable signatures check (requires openssl) @<:@default=no@:>@]), [],
+	[enable_signatures=no])
+AC_ARG_ENABLE([utils],
+	AS_HELP_STRING([--enable-utils], [Build and install utils @<:@default=no@:>@]),
+	[case "${enableval}" in
+  		yes) utils=true ;;
+  		no)  utils=false ;;
+  		*) AC_MSG_ERROR([bad value ${enableval} for --enable-utils]) ;;
+	esac],[utils=false])
+AM_CONDITIONAL([UTILS], [test x$utils = xtrue])
+
+AS_IF([test "x$enable_signatures" = "xyes"], [
+	AC_SEARCH_LIBS([EVP_MD_CTX_create], [crypto], [
+		AC_DEFINE(HAVE_OPENSSL, 1, [Define to 1 if you have the 'crypto' library (-lcrypto).])
+		LIBCRYPTO_LIB="-lcrypto"
+		LIBS_EXTRA="${LIBS_EXTRA} -lcrypto"
+		], [AC_MSG_ERROR([unable to find the EVP_MD_CTX_create() function])])
+])
+AC_SUBST(LIBCRYPTO_LIB)
+AC_PATH_PROG(PANDOC, pandoc, [/non/existent])
+
+AC_SEARCH_LIBS([clock_gettime], [rt], [], [
+	AC_CHECK_HEADER([mach/mach_time.h], [
+		AC_DEFINE(HAVE_MACH_MACH_TIME_H, 1, [Define to 1 on Darwin])
+	], [AC_MSG_ERROR([unable to find clock_gettime or mach_absolute_time])])
+])
+AC_SEARCH_LIBS([remainder], [m], [], [AC_MSG_ERROR([unable to find remainder() function])])
+
+AS_IF([test "x$enable_regex" = "xyes"], [
+	AC_CHECK_HEADER([regex.h], [
+		AC_DEFINE(HAVE_REGEX_H, 1, [Define to 1 if you have the <regex.h> header file.])
+		AC_SEARCH_LIBS([regexec], [regex], [
+			AS_IF([test "x$ac_cv_search_regexec" = "x-lregex"], [
+				LIBREGEX_LIB="-lregex"
+				LIBS_EXTRA="${LIBS_EXTRA} -lregex"
+				]
+			)], 
+			[AC_MSG_ERROR([unable to find the regexec() function])])],
+			[AC_MSG_ERROR([unable to find the regex.h header])
+		],
+		[#include <sys/types.h>])
+])
+AC_SUBST(LIBREGEX_LIB)
+
+AS_IF([test "x$enable_urls" = "xyes"], [
+	AC_CHECK_HEADER([fetch.h], [
+		AC_DEFINE(HAVE_FETCH_H, 1, [Define to 1 if you have the <fetch.h> header file.])
+		AC_CHECK_LIB(fetch, fetchXGet, [
+			AC_DEFINE(HAVE_LIBFETCH, 1, [Define to 1 if you have the 'fetch' library (-lfetch).])
+			LIBFETCH_LIBS="-lfetch"
+			have_libfetch="yes"
+			LIBS_EXTRA="${LIBS_EXTRA} -lfetch"
+		])
+	], [],[
+	#include <stdio.h>
+	#ifdef HAVE_SYS_PARAM_H
+	#include <sys/param.h>
+	#endif
+	])
+	AC_SUBST(LIBFETCH_LIBS)
+
+	AS_IF([ test "x$have_libfetch" != "xyes"], [
+		dnl Fallback to libcurl
+		PKG_CHECK_MODULES([CURL], [libcurl], [
+			AC_DEFINE(CURL_FOUND, 1, [Use libcurl])
+			LIBS_EXTRA="${LIBS_EXTRA} -lcurl"],
+		[AC_MSG_ERROR([unable to find neither libfetch nor libcurl])])
+	])
+	AC_SUBST(CURL_FOUND)
+	AC_SUBST(CURL_LIBS)
+	AC_SUBST(CURL_CFLAGS)
+])
+
+AC_SUBST(LIBS_EXTRA)
+
+AC_MSG_CHECKING(for GCC atomic builtins)
+AC_LINK_IFELSE([
+	AC_LANG_SOURCE([[
+		int main() {
+			volatile unsigned long val = 1;
+			__sync_synchronize();
+			__sync_val_compare_and_swap(&val, 1, 0);
+			__sync_add_and_fetch(&val, 1);
+			__sync_sub_and_fetch(&val, 1);
+			return 0;
+		}
+	]])
+],
+[
+	AC_MSG_RESULT([yes])
+	AC_DEFINE([HAVE_ATOMIC_BUILTINS], [1], [Has gcc/MSVC atomic intrinsics])
+],
+[
+	AC_MSG_RESULT([no])
+	AC_DEFINE([HAVE_ATOMIC_BUILTINS], [0], [Has gcc/MSVC atomic intrinsics])
+	AC_MSG_WARN([Libucl references could be thread-unsafe because atomic builtins are missing])
+])
+
+AC_CONFIG_FILES(Makefile \
+	src/Makefile \
+	tests/Makefile \
+	utils/Makefile \
+	doc/Makefile \
+	libucl.pc)
+AC_CONFIG_FILES([stamp-h], [echo timestamp > stamp-h])
+AC_OUTPUT

Added: vendor/libucl/dist/doc/Makefile.am
===================================================================
--- vendor/libucl/dist/doc/Makefile.am	                        (rev 0)
+++ vendor/libucl/dist/doc/Makefile.am	2018-06-19 13:29:00 UTC (rev 11074)
@@ -0,0 +1,8 @@
+EXTRA_DIST = api.md
+
+dist_man_MANS = libucl.3
+
+gen-man: @PANDOC@
+	tail -n +$$(grep -n '# Synopsis' api.md | cut -d':' -f1) api.md | \
+	cat pandoc.template - | sed -e 's/^# \(.*\)/# \U\1/' | \
+	@PANDOC@ -s -f markdown -t man -o libucl.3 

Added: vendor/libucl/dist/doc/api.md
===================================================================
--- vendor/libucl/dist/doc/api.md	                        (rev 0)
+++ vendor/libucl/dist/doc/api.md	2018-06-19 13:29:00 UTC (rev 11074)
@@ -0,0 +1,439 @@
+# API documentation
+
+**Table of Contents**  *generated with [DocToc](http://doctoc.herokuapp.com/)*
+
+- [Synopsis](#synopsis)
+- [Description](#description)
+	- [Parser functions](#parser-functions)
+	- [Emitting functions](#emitting-functions)
+	- [Conversion functions](#conversion-functions)
+	- [Generation functions](#generation-functions)
+	- [Iteration functions](#iteration-functions)
+	- [Validation functions](#validation-functions)
+	- [Utility functions](#utility-functions)
+- [Parser functions](#parser-functions-1)
+	- [ucl_parser_new](#ucl_parser_new)
+	- [ucl_parser_register_macro](#ucl_parser_register_macro)
+	- [ucl_parser_register_variable](#ucl_parser_register_variable)
+	- [ucl_parser_add_chunk](#ucl_parser_add_chunk)
+	- [ucl_parser_add_string](#ucl_parser_add_string)
+	- [ucl_parser_add_file](#ucl_parser_add_file)
+	- [ucl_parser_get_object](#ucl_parser_get_object)
+	- [ucl_parser_get_error](#ucl_parser_get_error)
+	- [ucl_parser_free](#ucl_parser_free)
+	- [ucl_pubkey_add](#ucl_pubkey_add)
+	- [ucl_parser_set_filevars](#ucl_parser_set_filevars)
+	- [Parser usage example](#parser-usage-example)
+- [Emitting functions](#emitting-functions-1)
+	- [ucl_object_emit](#ucl_object_emit)
+	- [ucl_object_emit_full](#ucl_object_emit_full)
+- [Conversion functions](#conversion-functions-1)
+- [Generation functions](#generation-functions-1)
+	- [ucl_object_new](#ucl_object_new)
+	- [ucl_object_typed_new](#ucl_object_typed_new)
+	- [Primitive objects generation](#primitive-objects-generation)
+	- [ucl_object_fromstring_common](#ucl_object_fromstring_common)
+- [Iteration functions](#iteration-functions-1)
+	- [ucl_iterate_object](#ucl_iterate_object)
+- [Validation functions](#validation-functions-1)
+	- [ucl_object_validate](#ucl_object_validate)
+
+# Synopsis
+
+`#include <ucl.h>`
+
+# Description
+
+Libucl is a parser and `C` API to parse and generate `ucl` objects. Libucl consist of several groups of functions:
+
+### Parser functions
+Used to parse `ucl` files and provide interface to extract `ucl` object. Currently, `libucl` can parse only full `ucl` documents, for instance, it is impossible to parse a part of document and therefore it is impossible to use `libucl` as a streaming parser. In future, this limitation can be removed.
+
+### Emitting functions
+Convert `ucl` objects to some textual or binary representation. Currently, libucl supports the following exports:
+
+- `JSON` - valid json format (can possibly loose some original data, such as implicit arrays)
+- `Config` - human-readable configuration format (losseless)
+- `YAML` - embedded yaml format (has the same limitations as `json` output)
+
+### Conversion functions
+Help to convert `ucl` objects to C types. These functions are used to convert `ucl_object_t` to C primitive types, such as numbers, strings or boolean values.
+
+### Generation functions
+Allow creating of `ucl` objects from C types and creating of complex `ucl` objects, such as hashes or arrays from primitive `ucl` objects, such as numbers or strings.
+
+### Iteration functions
+Iterate over `ucl` complex objects or over a chain of values, for example when a key in an object has multiple values (that can be treated as implicit array or implicit consolidation).
+
+### Validation functions
+Validation functions are used to validate some object `obj` using json-schema compatible object `schema`. Both input and schema must be UCL objects to perform validation.
+
+### Utility functions
+Provide basic utilities to manage `ucl` objects: creating, removing, retaining and releasing reference count and so on.
+
+# Parser functions
+
+Parser functions operates with `struct ucl_parser`.
+
+### ucl_parser_new
+
+~~~C
+struct ucl_parser* ucl_parser_new (int flags);
+~~~
+
+Creates new parser with the specified flags:
+
+- `UCL_PARSER_KEY_LOWERCASE` - lowercase keys parsed
+- `UCL_PARSER_ZEROCOPY` - try to use zero-copy mode when reading files (in zero-copy mode text chunk being parsed without copying strings so it should exist till any object parsed is used)
+- `UCL_PARSER_NO_TIME` - treat time values as strings without parsing them as floats
+
+### ucl_parser_register_macro
+
+~~~C
+void ucl_parser_register_macro (struct ucl_parser *parser,
+    const char *macro, ucl_macro_handler handler, void* ud);
+~~~
+
+Register new macro with name .`macro` parsed by handler `handler` that accepts opaque data pointer `ud`. Macro handler should be of the following type:
+
+~~~C
+bool (*ucl_macro_handler) (const unsigned char *data,
+    size_t len, void* ud);`
+~~~
+
+Handler function accepts macro text `data` of length `len` and the opaque pointer `ud`. If macro is parsed successfully the handler should return `true`. `false` indicates parsing failure and the parser can be terminated.
+
+### ucl_parser_register_variable
+
+~~~C
+void ucl_parser_register_variable (struct ucl_parser *parser,
+    const char *var, const char *value);
+~~~
+
+Register new variable $`var` that should be replaced by the parser to the `value` string.
+
+### ucl_parser_add_chunk
+
+~~~C
+bool ucl_parser_add_chunk (struct ucl_parser *parser, 
+    const unsigned char *data, size_t len);
+~~~
+
+Add new text chunk with `data` of length `len` to the parser. At the moment, `libucl` parser is not a streamlined parser and chunk *must* contain the *valid* ucl object. For example, this object should be valid:
+
+~~~json
+{ "var": "value" }
+~~~
+
+while this one won't be parsed correctly:
+
+~~~json
+{ "var": 
+~~~
+
+This limitation may possible be removed in future.
+
+### ucl_parser_add_string
+~~~C
+bool ucl_parser_add_string (struct ucl_parser *parser, 
+    const char *data, size_t len);
+~~~
+
+This function acts exactly like `ucl_parser_add_chunk` does but if `len` argument is zero, then the string `data` must be zero-terminated and the actual length is calculated up to `\0` character. 
+
+### ucl_parser_add_file
+
+~~~C
+bool ucl_parser_add_file (struct ucl_parser *parser, 
+    const char *filename);
+~~~
+
+Load file `filename` and parse it with the specified `parser`. This function uses `mmap` call to load file, therefore, it should not be `shrinked` during parsing. Otherwise, `libucl` can cause memory corruption and terminate the calling application. This function is also used by the internal handler of `include` macro, hence, this macro has the same limitation.
+
+### ucl_parser_get_object
+
+~~~C
+ucl_object_t* ucl_parser_get_object (struct ucl_parser *parser);
+~~~
+
+If the `ucl` data has been parsed correctly this function returns the top object for the parser. Otherwise, this function returns the `NULL` pointer. The reference count for `ucl` object returned is increased by one, therefore, a caller should decrease reference by using `ucl_object_unref` to free object after usage.
+
+### ucl_parser_get_error
+
+~~~C
+const char *ucl_parser_get_error(struct ucl_parser *parser);
+~~~
+
+Returns the constant error string for the parser object. If no error occurred during parsing a `NULL` object is returned. A caller should not try to free or modify this string.
+
+### ucl_parser_free
+
+~~~C
+void ucl_parser_free (struct ucl_parser *parser);
+~~~
+
+Frees memory occupied by the parser object. The reference count for top object is decreased as well, however if the function `ucl_parser_get_object` was called previously then the top object won't be freed.
+
+### ucl_pubkey_add
+
+~~~C
+bool ucl_pubkey_add (struct ucl_parser *parser, 
+    const unsigned char *key, size_t len);
+~~~
+
+This function adds a public key from text blob `key` of length `len` to the `parser` object. This public key should be in the `PEM` format and can be used by `.includes` macro for checking signatures of files included. `Openssl` support should be enabled to make this function working. If a key cannot be added (e.g. due to format error) or `openssl` was not linked to `libucl` then this function returns `false`.
+
+### ucl_parser_set_filevars
+
+~~~C
+bool ucl_parser_set_filevars (struct ucl_parser *parser, 
+    const char *filename, bool need_expand);
+~~~
+
+Add the standard file variables to the `parser` based on the `filename` specified:
+
+- `$FILENAME` - a filename of `ucl` input
+- `$CURDIR` - a current directory of the input
+
+For example, if a `filename` param is `../something.conf` then the variables will have the following values:
+
+- `$FILENAME` - "../something.conf"
+- `$CURDIR` - ".."
+
+if `need_expand` parameter is `true` then all relative paths are expanded using `realpath` call. In this example if `..` is `/etc/dir` then variables will have these values:
+
+- `$FILENAME` - "/etc/something.conf"
+- `$CURDIR` - "/etc"
+
+## Parser usage example
+
+The following example loads, parses and extracts `ucl` object from stdin using `libucl` parser functions (the length of input is limited to 8K):
+
+~~~C
+char inbuf[8192];
+struct ucl_parser *parser = NULL;
+int ret = 0, r = 0;
+ucl_object_t *obj = NULL;
+FILE *in;
+
+in = stdin;
+parser = ucl_parser_new (0);
+while (!feof (in) && r < (int)sizeof (inbuf)) {
+	r += fread (inbuf + r, 1, sizeof (inbuf) - r, in);
+}
+ucl_parser_add_chunk (parser, inbuf, r);
+fclose (in);
+
+if (ucl_parser_get_error (parser)) {
+	printf ("Error occured: %s\n", ucl_parser_get_error (parser));
+	ret = 1;
+}
+else {
+    obj = ucl_parser_get_object (parser);
+}
+
+if (parser != NULL) {
+	ucl_parser_free (parser);
+}
+if (obj != NULL) {
+	ucl_object_unref (obj);
+}
+return ret;
+~~~
+
+# Emitting functions
+
+Libucl can transform UCL objects to a number of tectual formats:
+
+- configuration (`UCL_EMIT_CONFIG`) - nginx like human readable configuration file where implicit arrays are transformed to the duplicate keys
+- compact json: `UCL_EMIT_JSON_COMPACT` - single line valid json without spaces
+- formatted json: `UCL_EMIT_JSON` - pretty formatted JSON with newlines and spaces
+- compact yaml: `UCL_EMIT_YAML` - compact YAML output
+
+Moreover, libucl API allows to select a custom set of emitting functions allowing 
+efficent and zero-copy output of libucl objects. Libucl uses the following structure to support this feature:
+
+~~~C
+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);
+	/** Opaque userdata pointer */
+	void *ud;
+};
+~~~
+
+This structure defines the following callbacks:
+
+- `ucl_emitter_append_character` - a function that is called to append `nchars` characters equal to `c`
+- `ucl_emitter_append_len` - used to append a string of length `len` starting from pointer `str`
+- `ucl_emitter_append_int` - this function applies to integer numbers
+- `ucl_emitter_append_double` - this function is intended to output floating point variable
+
+The set of these functions could be used to output text formats of `UCL` objects to different structures or streams.
+
+Libucl provides the following functions for emitting UCL objects:
+
+### ucl_object_emit
+
+~~~C
+unsigned char *ucl_object_emit (const ucl_object_t *obj, enum ucl_emitter emit_type);
+~~~
+
+Allocate a string that is suitable to fit the underlying UCL object `obj` and fill it with the textual representation of the object `obj` according to style `emit_type`. The caller should free the returned string after using.
+
+### ucl_object_emit_full
+
+~~~C
+bool ucl_object_emit_full (const ucl_object_t *obj, enum ucl_emitter emit_type,
+		struct ucl_emitter_functions *emitter);
+~~~
+
+This function is similar to the previous with the exception that it accepts the additional argument `emitter` that defines the concrete set of output functions. This emit function could be useful for custom structures or streams emitters (including C++ ones, for example).
+
+# Conversion functions
+
+Conversion functions are used to convert UCL objects to primitive types, such as strings, numbers or boolean values. There are two types of conversion functions:
+
+- safe: try to convert an ucl object to a primitive type and fail if such a conversion is not possible
+- unsafe: return primitive type without additional checks, if the object cannot be converted then some reasonable default is returned (NULL for strings and 0 for numbers)
+
+Also there is a single `ucl_object_tostring_forced` function that converts any UCL object (including compound types - arrays and objects) to a string representation. For compound and numeric types this function performs emitting to a compact json format actually.
+
+Here is a list of all conversion functions:
+
+- `ucl_object_toint` - returns `int64_t` of UCL object
+- `ucl_object_todouble` - returns `double` of UCL object
+- `ucl_object_toboolean` - returns `bool` of UCL object
+- `ucl_object_tostring` - returns `const char *` of UCL object (this string is NULL terminated)
+- `ucl_object_tolstring` - returns `const char *` and `size_t` len of UCL object (string can be not NULL terminated)
+- `ucl_object_tostring_forced` - returns string representation of any UCL object
+
+Strings returned by these pointers are associated with the UCL object and exist over its lifetime. A caller should not free this memory.
+
+# Generation functions
+
+It is possible to generate UCL objects from C primitive types. Moreover, libucl permits to create and modify complex UCL objects, such as arrays or associative objects. 
+
+## ucl_object_new
+~~~C
+ucl_object_t * ucl_object_new (void)
+~~~
+
+Creates new object of type `UCL_NULL`. This object should be released by caller.
+
+## ucl_object_typed_new
+~~~C
+ucl_object_t * ucl_object_typed_new (unsigned int type)
+~~~
+
+Create an object of a specified type:
+- `UCL_OBJECT` - 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
+
+This object should be released by caller.
+
+## Primitive objects generation
+Libucl provides the functions similar to inverse conversion functions called with the specific C type:
+- `ucl_object_fromint` - converts `int64_t` to UCL object
+- `ucl_object_fromdouble` - converts `double` to UCL object
+- `ucl_object_fromboolean` - converts `bool` to UCL object
+- `ucl_object_fromstring` - converts `const char *` to UCL object (this string is NULL terminated)
+- `ucl_object_fromlstring` - converts `const char *` and `size_t` len to UCL object (string can be not NULL terminated)
+
+Also there is a function to generate UCL object from a string performing various parsing or conversion operations called `ucl_object_fromstring_common`.
+
+## ucl_object_fromstring_common
+~~~C
+ucl_object_t * ucl_object_fromstring_common (const char *str, 
+	size_t len, enum ucl_string_flags flags)
+~~~
+
+This function is used to convert a string `str` of size `len` to an UCL objects applying `flags` conversions. If `len` is equal to zero then a `str` is assumed as NULL-terminated. This function supports the following flags (a set of flags can be specified using logical `OR` operation):
+
+- `UCL_STRING_ESCAPE` - perform JSON escape
+- `UCL_STRING_TRIM` - trim leading and trailing whitespaces
+- `UCL_STRING_PARSE_BOOLEAN` - parse passed string and detect boolean
+- `UCL_STRING_PARSE_INT` - parse passed string and detect integer number
+- `UCL_STRING_PARSE_DOUBLE` - parse passed string and detect integer or float number
+- `UCL_STRING_PARSE_TIME` - parse time values as floating point numbers
+- `UCL_STRING_PARSE_NUMBER` - parse passed string and detect number (both float, integer and time types)
+- `UCL_STRING_PARSE` - parse passed string (and detect booleans, numbers and time values)
+- `UCL_STRING_PARSE_BYTES` - assume that numeric multipliers are in bytes notation, for example `10k` means `10*1024` and not `10*1000` as assumed without this flag
+
+If parsing operations fail then the resulting UCL object will be a `UCL_STRING`. A caller should always check the type of the returned object and release it after using.
+
+# Iteration functions
+
+Iteration are used to iterate over UCL compound types: arrays and objects. Moreover, iterations could be performed over the keys with multiple values (implicit arrays). To iterate over an object, an array or a key with multiple values there is a function `ucl_iterate_object`.
+
+## ucl_iterate_object
+~~~C
+const ucl_object_t* ucl_iterate_object (const ucl_object_t *obj, 
+	ucl_object_iter_t *iter, bool expand_values);
+~~~
+
+This function accept opaque iterator pointer `iter`. In the first call this iterator *must* be initialized to `NULL`. Iterator is changed by this function call. `ucl_iterate_object` returns the next UCL object in the compound object `obj` or `NULL` if all objects have been iterated. The reference count of the object returned is not increased, so a caller should not unref the object or modify its content (e.g. by inserting to another compound object). The object `obj` should not be changed during the iteration process as well. `expand_values` flag speicifies whether `ucl_iterate_object` should expand keys with multiple values. The general rule is that if you need to iterate throught the *object* or *explicit array*, then you always need to set this flag to `true`. However, if you get some key in the object and want to extract all its values then you should set `expand_values` to `false`. Mixing of iteration types are not permitted since the iterator is set according to the iteration type and cannot be reused. Here is an example of iteration over the objects using libucl API (assuming that `top` is `UCL_OBJECT` in this example):
+
+~~~C
+ucl_object_iter_t it = NULL, it_obj = NULL;
+const ucl_object_t *cur, *tmp;
+
+/* Iterate over the object */
+while ((obj = ucl_iterate_object (top, &it, true))) {
+	printf ("key: \"%s\"\n", ucl_object_key (obj));
+	/* Iterate over the values of a key */
+	while ((cur = ucl_iterate_object (obj, &it_obj, false))) {
+		printf ("value: \"%s\"\n", 
+			ucl_object_tostring_forced (cur));
+	}
+}
+~~~
+
+# Validation functions
+
+Currently, there is only one validation function called `ucl_object_validate`. It performs validation of object using the specified schema. This function is defined as following:
+
+## ucl_object_validate
+~~~C
+bool ucl_object_validate (const ucl_object_t *schema,
+	const ucl_object_t *obj, struct ucl_schema_error *err);
+~~~
+
+This function uses ucl object `schema`, that must be valid in terms of `json-schema` draft v4, to validate input object `obj`. If this function returns `true` then validation procedure has been succeed. Otherwise, `false` is returned and `err` is set to a specific value. If caller set `err` to NULL then this function does not set any error just returning `false`. Error is the structure defined as following:
+
+~~~C
+struct ucl_schema_error {
+	enum ucl_schema_error_code code;	/* error code */
+	char msg[128];				/* error message */
+	ucl_object_t *obj;			/* object where error occured */
+};
+~~~
+
+Caller may use `code` field to get a numeric error code:
+
+~~~C
+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,/* missing properties */
+	UCL_SCHEMA_CONSTRAINT,      /* constraint found */
+	UCL_SCHEMA_MISSING_DEPENDENCY, /* missing dependency */
+	UCL_SCHEMA_UNKNOWN          /* generic error */
+};
+~~~
+
+`msg` is a stiring description of an error and `obj` is an object where error has been occurred. Error object is not allocated by libucl, so there is no need to free it after validation (a static object should thus be used).
\ No newline at end of file

Added: vendor/libucl/dist/doc/libucl.3
===================================================================
--- vendor/libucl/dist/doc/libucl.3	                        (rev 0)
+++ vendor/libucl/dist/doc/libucl.3	2018-06-19 13:29:00 UTC (rev 11074)
@@ -0,0 +1,637 @@
+.TH LIBUCL 5 "March 20, 2014" "Libucl manual"
+.SH NAME
+.PP
+\f[B]ucl_parser_new\f[], \f[B]ucl_parser_register_macro\f[],
+\f[B]ucl_parser_register_variable\f[], \f[B]ucl_parser_add_chunk\f[],
+\f[B]ucl_parser_add_string\f[], \f[B]ucl_parser_add_file\f[],
+\f[B]ucl_parser_get_object\f[], \f[B]ucl_parser_get_error\f[],
+\f[B]ucl_parser_free\f[], \f[B]ucl_pubkey_add\f[],
+\f[B]ucl_parser_set_filevars\f[] \- universal configuration library
+parser and utility functions
+.SH LIBRARY
+.PP
+UCL library (libucl, \-lucl)
+.SH SYNOPSIS
+.PP
+\f[C]#include\ <ucl.h>\f[]
+.SH DESCRIPTION
+.PP
+Libucl is a parser and \f[C]C\f[] API to parse and generate \f[C]ucl\f[]
+objects.
+Libucl consist of several groups of functions:
+.SS Parser functions
+.PP
+Used to parse \f[C]ucl\f[] files and provide interface to extract
+\f[C]ucl\f[] object.
+Currently, \f[C]libucl\f[] can parse only full \f[C]ucl\f[] documents,
+for instance, it is impossible to parse a part of document and therefore
+it is impossible to use \f[C]libucl\f[] as a streaming parser.
+In future, this limitation can be removed.
+.SS Emitting functions
+.PP
+Convert \f[C]ucl\f[] objects to some textual or binary representation.
+Currently, libucl supports the following exports:
+.IP \[bu] 2
+\f[C]JSON\f[] \- valid json format (can possibly loose some original
+data, such as implicit arrays)
+.IP \[bu] 2
+\f[C]Config\f[] \- human\-readable configuration format (losseless)
+.IP \[bu] 2
+\f[C]YAML\f[] \- embedded yaml format (has the same limitations as
+\f[C]json\f[] output)
+.SS Conversion functions
+.PP
+Help to convert \f[C]ucl\f[] objects to C types.
+These functions are used to convert \f[C]ucl_object_t\f[] to C primitive
+types, such as numbers, strings or boolean values.
+.SS Generation functions
+.PP
+Allow creating of \f[C]ucl\f[] objects from C types and creating of
+complex \f[C]ucl\f[] objects, such as hashes or arrays from primitive
+\f[C]ucl\f[] objects, such as numbers or strings.
+.SS Iteration functions
+.PP
+Iterate over \f[C]ucl\f[] complex objects or over a chain of values, for
+example when a key in an object has multiple values (that can be treated
+as implicit array or implicit consolidation).
+.SS Validation functions
+.PP
+Validation functions are used to validate some object \f[C]obj\f[] using
+json\-schema compatible object \f[C]schema\f[].
+Both input and schema must be UCL objects to perform validation.
+.SS Utility functions
+.PP
+Provide basic utilities to manage \f[C]ucl\f[] objects: creating,
+removing, retaining and releasing reference count and so on.
+.SH PARSER FUNCTIONS
+.PP
+Parser functions operates with \f[C]struct\ ucl_parser\f[].
+.SS ucl_parser_new
+.IP
+.nf
+\f[C]
+struct\ ucl_parser*\ ucl_parser_new\ (int\ flags);
+\f[]
+.fi
+.PP
+Creates new parser with the specified flags:
+.IP \[bu] 2
+\f[C]UCL_PARSER_KEY_LOWERCASE\f[] \- lowercase keys parsed
+.IP \[bu] 2
+\f[C]UCL_PARSER_ZEROCOPY\f[] \- try to use zero\-copy mode when reading
+files (in zero\-copy mode text chunk being parsed without copying
+strings so it should exist till any object parsed is used)
+.IP \[bu] 2
+\f[C]UCL_PARSER_NO_TIME\f[] \- treat time values as strings without
+parsing them as floats
+.SS ucl_parser_register_macro
+.IP
+.nf
+\f[C]
+void\ ucl_parser_register_macro\ (struct\ ucl_parser\ *parser,
+\ \ \ \ const\ char\ *macro,\ ucl_macro_handler\ handler,\ void*\ ud);
+\f[]
+.fi
+.PP
+Register new macro with name .\f[C]macro\f[] parsed by handler
+\f[C]handler\f[] that accepts opaque data pointer \f[C]ud\f[].
+Macro handler should be of the following type:
+.IP
+.nf
+\f[C]
+bool\ (*ucl_macro_handler)\ (const\ unsigned\ char\ *data,
+\ \ \ \ size_t\ len,\ void*\ ud);`
+\f[]
+.fi
+.PP
+Handler function accepts macro text \f[C]data\f[] of length \f[C]len\f[]
+and the opaque pointer \f[C]ud\f[].
+If macro is parsed successfully the handler should return \f[C]true\f[].
+\f[C]false\f[] indicates parsing failure and the parser can be
+terminated.
+.SS ucl_parser_register_variable
+.IP
+.nf
+\f[C]
+void\ ucl_parser_register_variable\ (struct\ ucl_parser\ *parser,
+\ \ \ \ const\ char\ *var,\ const\ char\ *value);
+\f[]
+.fi
+.PP
+Register new variable $\f[C]var\f[] that should be replaced by the
+parser to the \f[C]value\f[] string.
+.SS ucl_parser_add_chunk
+.IP
+.nf
+\f[C]
+bool\ ucl_parser_add_chunk\ (struct\ ucl_parser\ *parser,\ 
+\ \ \ \ const\ unsigned\ char\ *data,\ size_t\ len);
+\f[]
+.fi
+.PP
+Add new text chunk with \f[C]data\f[] of length \f[C]len\f[] to the
+parser.
+At the moment, \f[C]libucl\f[] parser is not a streamlined parser and
+chunk \f[I]must\f[] contain the \f[I]valid\f[] ucl object.
+For example, this object should be valid:
+.IP
+.nf
+\f[C]
+{\ "var":\ "value"\ }
+\f[]
+.fi
+.PP
+while this one won\[aq]t be parsed correctly:
+.IP
+.nf
+\f[C]
+{\ "var":\ 
+\f[]
+.fi
+.PP
+This limitation may possible be removed in future.
+.SS ucl_parser_add_string
+.IP
+.nf
+\f[C]
+bool\ ucl_parser_add_string\ (struct\ ucl_parser\ *parser,\ 
+\ \ \ \ const\ char\ *data,\ size_t\ len);
+\f[]
+.fi
+.PP
+This function acts exactly like \f[C]ucl_parser_add_chunk\f[] does but
+if \f[C]len\f[] argument is zero, then the string \f[C]data\f[] must be
+zero\-terminated and the actual length is calculated up to \f[C]\\0\f[]
+character.
+.SS ucl_parser_add_file
+.IP
+.nf
+\f[C]
+bool\ ucl_parser_add_file\ (struct\ ucl_parser\ *parser,\ 
+\ \ \ \ const\ char\ *filename);
+\f[]
+.fi
+.PP
+Load file \f[C]filename\f[] and parse it with the specified
+\f[C]parser\f[].
+This function uses \f[C]mmap\f[] call to load file, therefore, it should
+not be \f[C]shrinked\f[] during parsing.
+Otherwise, \f[C]libucl\f[] can cause memory corruption and terminate the
+calling application.
+This function is also used by the internal handler of \f[C]include\f[]
+macro, hence, this macro has the same limitation.
+.SS ucl_parser_get_object
+.IP
+.nf
+\f[C]
+ucl_object_t*\ ucl_parser_get_object\ (struct\ ucl_parser\ *parser);
+\f[]
+.fi
+.PP
+If the \f[C]ucl\f[] data has been parsed correctly this function returns
+the top object for the parser.
+Otherwise, this function returns the \f[C]NULL\f[] pointer.
+The reference count for \f[C]ucl\f[] object returned is increased by
+one, therefore, a caller should decrease reference by using
+\f[C]ucl_object_unref\f[] to free object after usage.
+.SS ucl_parser_get_error
+.IP
+.nf
+\f[C]
+const\ char\ *ucl_parser_get_error(struct\ ucl_parser\ *parser);
+\f[]
+.fi
+.PP
+Returns the constant error string for the parser object.
+If no error occurred during parsing a \f[C]NULL\f[] object is returned.
+A caller should not try to free or modify this string.
+.SS ucl_parser_free
+.IP
+.nf
+\f[C]
+void\ ucl_parser_free\ (struct\ ucl_parser\ *parser);
+\f[]
+.fi
+.PP
+Frees memory occupied by the parser object.
+The reference count for top object is decreased as well, however if the
+function \f[C]ucl_parser_get_object\f[] was called previously then the
+top object won\[aq]t be freed.
+.SS ucl_pubkey_add
+.IP
+.nf
+\f[C]
+bool\ ucl_pubkey_add\ (struct\ ucl_parser\ *parser,\ 
+\ \ \ \ const\ unsigned\ char\ *key,\ size_t\ len);
+\f[]
+.fi
+.PP
+This function adds a public key from text blob \f[C]key\f[] of length
+\f[C]len\f[] to the \f[C]parser\f[] object.
+This public key should be in the \f[C]PEM\f[] format and can be used by
+\f[C]\&.includes\f[] macro for checking signatures of files included.
+\f[C]Openssl\f[] support should be enabled to make this function
+working.
+If a key cannot be added (e.g.
+due to format error) or \f[C]openssl\f[] was not linked to
+\f[C]libucl\f[] then this function returns \f[C]false\f[].
+.SS ucl_parser_set_filevars
+.IP
+.nf
+\f[C]
+bool\ ucl_parser_set_filevars\ (struct\ ucl_parser\ *parser,\ 
+\ \ \ \ const\ char\ *filename,\ bool\ need_expand);
+\f[]
+.fi
+.PP
+Add the standard file variables to the \f[C]parser\f[] based on the
+\f[C]filename\f[] specified:
+.IP \[bu] 2
+\f[C]$FILENAME\f[] \- a filename of \f[C]ucl\f[] input
+.IP \[bu] 2
+\f[C]$CURDIR\f[] \- a current directory of the input
+.PP
+For example, if a \f[C]filename\f[] param is
+\f[C]\&../something.conf\f[] then the variables will have the following
+values:
+.IP \[bu] 2
+\f[C]$FILENAME\f[] \- "../something.conf"
+.IP \[bu] 2
+\f[C]$CURDIR\f[] \- ".."
+.PP
+if \f[C]need_expand\f[] parameter is \f[C]true\f[] then all relative
+paths are expanded using \f[C]realpath\f[] call.
+In this example if \f[C]\&..\f[] is \f[C]/etc/dir\f[] then variables
+will have these values:
+.IP \[bu] 2
+\f[C]$FILENAME\f[] \- "/etc/something.conf"
+.IP \[bu] 2
+\f[C]$CURDIR\f[] \- "/etc"
+.SS Parser usage example
+.PP
+The following example loads, parses and extracts \f[C]ucl\f[] object
+from stdin using \f[C]libucl\f[] parser functions (the length of input
+is limited to 8K):
+.IP
+.nf
+\f[C]
+char\ inbuf[8192];
+struct\ ucl_parser\ *parser\ =\ NULL;
+int\ ret\ =\ 0,\ r\ =\ 0;
+ucl_object_t\ *obj\ =\ NULL;
+FILE\ *in;
+
+in\ =\ stdin;
+parser\ =\ ucl_parser_new\ (0);
+while\ (!feof\ (in)\ &&\ r\ <\ (int)sizeof\ (inbuf))\ {
+\ \ \ \ r\ +=\ fread\ (inbuf\ +\ r,\ 1,\ sizeof\ (inbuf)\ \-\ r,\ in);
+}
+ucl_parser_add_chunk\ (parser,\ inbuf,\ r);
+fclose\ (in);
+
+if\ (ucl_parser_get_error\ (parser))\ {
+\ \ \ \ printf\ ("Error\ occured:\ %s\\n",\ ucl_parser_get_error\ (parser));
+\ \ \ \ ret\ =\ 1;
+}
+else\ {
+\ \ \ \ obj\ =\ ucl_parser_get_object\ (parser);
+}
+
+if\ (parser\ !=\ NULL)\ {
+\ \ \ \ ucl_parser_free\ (parser);
+}
+if\ (obj\ !=\ NULL)\ {
+\ \ \ \ ucl_object_unref\ (obj);
+}
+return\ ret;
+\f[]
+.fi
+.SH EMITTING FUNCTIONS
+.PP
+Libucl can transform UCL objects to a number of tectual formats:
+.IP \[bu] 2
+configuration (\f[C]UCL_EMIT_CONFIG\f[]) \- nginx like human readable
+configuration file where implicit arrays are transformed to the
+duplicate keys
+.IP \[bu] 2
+compact json: \f[C]UCL_EMIT_JSON_COMPACT\f[] \- single line valid json
+without spaces
+.IP \[bu] 2
+formatted json: \f[C]UCL_EMIT_JSON\f[] \- pretty formatted JSON with
+newlines and spaces
+.IP \[bu] 2
+compact yaml: \f[C]UCL_EMIT_YAML\f[] \- compact YAML output
+.PP
+Moreover, libucl API allows to select a custom set of emitting functions
+allowing efficent and zero\-copy output of libucl objects.
+Libucl uses the following structure to support this feature:
+.IP
+.nf
+\f[C]
+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);
+\ \ \ \ /**\ Opaque\ userdata\ pointer\ */
+\ \ \ \ void\ *ud;
+};
+\f[]
+.fi
+.PP
+This structure defines the following callbacks:
+.IP \[bu] 2
+\f[C]ucl_emitter_append_character\f[] \- a function that is called to
+append \f[C]nchars\f[] characters equal to \f[C]c\f[]
+.IP \[bu] 2
+\f[C]ucl_emitter_append_len\f[] \- used to append a string of length
+\f[C]len\f[] starting from pointer \f[C]str\f[]
+.IP \[bu] 2
+\f[C]ucl_emitter_append_int\f[] \- this function applies to integer
+numbers
+.IP \[bu] 2
+\f[C]ucl_emitter_append_double\f[] \- this function is intended to
+output floating point variable
+.PP
+The set of these functions could be used to output text formats of
+\f[C]UCL\f[] objects to different structures or streams.
+.PP
+Libucl provides the following functions for emitting UCL objects:
+.SS ucl_object_emit
+.IP
+.nf
+\f[C]
+unsigned\ char\ *ucl_object_emit\ (const\ ucl_object_t\ *obj,\ enum\ ucl_emitter\ emit_type);
+\f[]
+.fi
+.PP
+Allocate a string that is suitable to fit the underlying UCL object
+\f[C]obj\f[] and fill it with the textual representation of the object
+\f[C]obj\f[] according to style \f[C]emit_type\f[].
+The caller should free the returned string after using.
+.SS ucl_object_emit_full
+.IP
+.nf
+\f[C]
+bool\ ucl_object_emit_full\ (const\ ucl_object_t\ *obj,\ enum\ ucl_emitter\ emit_type,
+\ \ \ \ \ \ \ \ struct\ ucl_emitter_functions\ *emitter);
+\f[]
+.fi
+.PP
+This function is similar to the previous with the exception that it
+accepts the additional argument \f[C]emitter\f[] that defines the
+concrete set of output functions.
+This emit function could be useful for custom structures or streams
+emitters (including C++ ones, for example).
+.SH CONVERSION FUNCTIONS
+.PP
+Conversion functions are used to convert UCL objects to primitive types,
+such as strings, numbers or boolean values.
+There are two types of conversion functions:
+.IP \[bu] 2
+safe: try to convert an ucl object to a primitive type and fail if such
+a conversion is not possible
+.IP \[bu] 2
+unsafe: return primitive type without additional checks, if the object
+cannot be converted then some reasonable default is returned (NULL for
+strings and 0 for numbers)
+.PP
+Also there is a single \f[C]ucl_object_tostring_forced\f[] function that
+converts any UCL object (including compound types \- arrays and objects)
+to a string representation.
+For compound and numeric types this function performs emitting to a
+compact json format actually.
+.PP
+Here is a list of all conversion functions:
+.IP \[bu] 2
+\f[C]ucl_object_toint\f[] \- returns \f[C]int64_t\f[] of UCL object
+.IP \[bu] 2
+\f[C]ucl_object_todouble\f[] \- returns \f[C]double\f[] of UCL object
+.IP \[bu] 2
+\f[C]ucl_object_toboolean\f[] \- returns \f[C]bool\f[] of UCL object
+.IP \[bu] 2
+\f[C]ucl_object_tostring\f[] \- returns \f[C]const\ char\ *\f[] of UCL
+object (this string is NULL terminated)
+.IP \[bu] 2
+\f[C]ucl_object_tolstring\f[] \- returns \f[C]const\ char\ *\f[] and
+\f[C]size_t\f[] len of UCL object (string can be not NULL terminated)
+.IP \[bu] 2
+\f[C]ucl_object_tostring_forced\f[] \- returns string representation of
+any UCL object
+.PP
+Strings returned by these pointers are associated with the UCL object
+and exist over its lifetime.
+A caller should not free this memory.
+.SH GENERATION FUNCTIONS
+.PP
+It is possible to generate UCL objects from C primitive types.
+Moreover, libucl permits to create and modify complex UCL objects, such
+as arrays or associative objects.
+.SS ucl_object_new
+.IP
+.nf
+\f[C]
+ucl_object_t\ *\ ucl_object_new\ (void)
+\f[]
+.fi
+.PP
+Creates new object of type \f[C]UCL_NULL\f[].
+This object should be released by caller.
+.SS ucl_object_typed_new
+.IP
+.nf
+\f[C]
+ucl_object_t\ *\ ucl_object_typed_new\ (unsigned\ int\ type)
+\f[]
+.fi
+.PP
+Create an object of a specified type: \- \f[C]UCL_OBJECT\f[] \- UCL
+object \- key/value pairs \- \f[C]UCL_ARRAY\f[] \- UCL array \-
+\f[C]UCL_INT\f[] \- integer number \- \f[C]UCL_FLOAT\f[] \- floating
+point number \- \f[C]UCL_STRING\f[] \- NULL terminated string \-
+\f[C]UCL_BOOLEAN\f[] \- boolean value \- \f[C]UCL_TIME\f[] \- time value
+(floating point number of seconds) \- \f[C]UCL_USERDATA\f[] \- opaque
+userdata pointer (may be used in macros) \- \f[C]UCL_NULL\f[] \- null
+value
+.PP
+This object should be released by caller.
+.SS Primitive objects generation
+.PP
+Libucl provides the functions similar to inverse conversion functions
+called with the specific C type: \- \f[C]ucl_object_fromint\f[] \-
+converts \f[C]int64_t\f[] to UCL object \-
+\f[C]ucl_object_fromdouble\f[] \- converts \f[C]double\f[] to UCL object
+\- \f[C]ucl_object_fromboolean\f[] \- converts \f[C]bool\f[] to UCL
+object \- \f[C]ucl_object_fromstring\f[] \- converts
+\f[C]const\ char\ *\f[] to UCL object (this string is NULL terminated)
+\- \f[C]ucl_object_fromlstring\f[] \- converts \f[C]const\ char\ *\f[]
+and \f[C]size_t\f[] len to UCL object (string can be not NULL
+terminated)
+.PP
+Also there is a function to generate UCL object from a string performing
+various parsing or conversion operations called
+\f[C]ucl_object_fromstring_common\f[].
+.SS ucl_object_fromstring_common
+.IP
+.nf
+\f[C]
+ucl_object_t\ *\ ucl_object_fromstring_common\ (const\ char\ *str,\ 
+\ \ \ \ size_t\ len,\ enum\ ucl_string_flags\ flags)
+\f[]
+.fi
+.PP
+This function is used to convert a string \f[C]str\f[] of size
+\f[C]len\f[] to an UCL objects applying \f[C]flags\f[] conversions.
+If \f[C]len\f[] is equal to zero then a \f[C]str\f[] is assumed as
+NULL\-terminated.
+This function supports the following flags (a set of flags can be
+specified using logical \f[C]OR\f[] operation):
+.IP \[bu] 2
+\f[C]UCL_STRING_ESCAPE\f[] \- perform JSON escape
+.IP \[bu] 2
+\f[C]UCL_STRING_TRIM\f[] \- trim leading and trailing whitespaces
+.IP \[bu] 2
+\f[C]UCL_STRING_PARSE_BOOLEAN\f[] \- parse passed string and detect
+boolean
+.IP \[bu] 2
+\f[C]UCL_STRING_PARSE_INT\f[] \- parse passed string and detect integer
+number
+.IP \[bu] 2
+\f[C]UCL_STRING_PARSE_DOUBLE\f[] \- parse passed string and detect
+integer or float number
+.IP \[bu] 2
+\f[C]UCL_STRING_PARSE_TIME\f[] \- parse time values as floating point
+numbers
+.IP \[bu] 2
+\f[C]UCL_STRING_PARSE_NUMBER\f[] \- parse passed string and detect
+number (both float, integer and time types)
+.IP \[bu] 2
+\f[C]UCL_STRING_PARSE\f[] \- parse passed string (and detect booleans,
+numbers and time values)
+.IP \[bu] 2
+\f[C]UCL_STRING_PARSE_BYTES\f[] \- assume that numeric multipliers are
+in bytes notation, for example \f[C]10k\f[] means \f[C]10*1024\f[] and
+not \f[C]10*1000\f[] as assumed without this flag
+.PP
+If parsing operations fail then the resulting UCL object will be a
+\f[C]UCL_STRING\f[].
+A caller should always check the type of the returned object and release
+it after using.
+.SH ITERATION FUNCTIONS
+.PP
+Iteration are used to iterate over UCL compound types: arrays and
+objects.
+Moreover, iterations could be performed over the keys with multiple
+values (implicit arrays).
+To iterate over an object, an array or a key with multiple values there
+is a function \f[C]ucl_iterate_object\f[].
+.SS ucl_iterate_object
+.IP
+.nf
+\f[C]
+const\ ucl_object_t*\ ucl_iterate_object\ (const\ ucl_object_t\ *obj,\ 
+\ \ \ \ ucl_object_iter_t\ *iter,\ bool\ expand_values);
+\f[]
+.fi
+.PP
+This function accept opaque iterator pointer \f[C]iter\f[].
+In the first call this iterator \f[I]must\f[] be initialized to
+\f[C]NULL\f[].
+Iterator is changed by this function call.
+\f[C]ucl_iterate_object\f[] returns the next UCL object in the compound
+object \f[C]obj\f[] or \f[C]NULL\f[] if all objects have been iterated.
+The reference count of the object returned is not increased, so a caller
+should not unref the object or modify its content (e.g.
+by inserting to another compound object).
+The object \f[C]obj\f[] should not be changed during the iteration
+process as well.
+\f[C]expand_values\f[] flag speicifies whether
+\f[C]ucl_iterate_object\f[] should expand keys with multiple values.
+The general rule is that if you need to iterate throught the
+\f[I]object\f[] or \f[I]explicit array\f[], then you always need to set
+this flag to \f[C]true\f[].
+However, if you get some key in the object and want to extract all its
+values then you should set \f[C]expand_values\f[] to \f[C]false\f[].
+Mixing of iteration types are not permitted since the iterator is set
+according to the iteration type and cannot be reused.
+Here is an example of iteration over the objects using libucl API
+(assuming that \f[C]top\f[] is \f[C]UCL_OBJECT\f[] in this example):
+.IP
+.nf
+\f[C]
+ucl_object_iter_t\ it\ =\ NULL,\ it_obj\ =\ NULL;
+const\ ucl_object_t\ *cur,\ *tmp;
+
+/*\ Iterate\ over\ the\ object\ */
+while\ ((obj\ =\ ucl_iterate_object\ (top,\ &it,\ true)))\ {
+\ \ \ \ printf\ ("key:\ \\"%s\\"\\n",\ ucl_object_key\ (obj));
+\ \ \ \ /*\ Iterate\ over\ the\ values\ of\ a\ key\ */
+\ \ \ \ while\ ((cur\ =\ ucl_iterate_object\ (obj,\ &it_obj,\ false)))\ {
+\ \ \ \ \ \ \ \ printf\ ("value:\ \\"%s\\"\\n",\ 
+\ \ \ \ \ \ \ \ \ \ \ \ ucl_object_tostring_forced\ (cur));
+\ \ \ \ }
+}
+\f[]
+.fi
+.SH VALIDATION FUNCTIONS
+.PP
+Currently, there is only one validation function called
+\f[C]ucl_object_validate\f[].
+It performs validation of object using the specified schema.
+This function is defined as following:
+.SS ucl_object_validate
+.IP
+.nf
+\f[C]
+bool\ ucl_object_validate\ (const\ ucl_object_t\ *schema,
+\ \ \ \ const\ ucl_object_t\ *obj,\ struct\ ucl_schema_error\ *err);
+\f[]
+.fi
+.PP
+This function uses ucl object \f[C]schema\f[], that must be valid in
+terms of \f[C]json\-schema\f[] draft v4, to validate input object
+\f[C]obj\f[].
+If this function returns \f[C]true\f[] then validation procedure has
+been succeed.
+Otherwise, \f[C]false\f[] is returned and \f[C]err\f[] is set to a
+specific value.
+If caller set \f[C]err\f[] to NULL then this function does not set any
+error just returning \f[C]false\f[].
+Error is the structure defined as following:
+.IP
+.nf
+\f[C]
+struct\ ucl_schema_error\ {
+\ \ \ \ enum\ ucl_schema_error_code\ code;\ \ \ \ /*\ error\ code\ */
+\ \ \ \ char\ msg[128];\ \ \ \ \ \ \ \ \ \ \ \ \ \ /*\ error\ message\ */
+\ \ \ \ ucl_object_t\ *obj;\ \ \ \ \ \ \ \ \ \ /*\ object\ where\ error\ occured\ */
+};
+\f[]
+.fi
+.PP
+Caller may use \f[C]code\f[] field to get a numeric error code:
+.IP
+.nf
+\f[C]
+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,/*\ missing\ properties\ */
+\ \ \ \ UCL_SCHEMA_CONSTRAINT,\ \ \ \ \ \ /*\ constraint\ found\ */
+\ \ \ \ UCL_SCHEMA_MISSING_DEPENDENCY,\ /*\ missing\ dependency\ */
+\ \ \ \ UCL_SCHEMA_UNKNOWN\ \ \ \ \ \ \ \ \ \ /*\ generic\ error\ */
+};
+\f[]
+.fi
+.PP
+\f[C]msg\f[] is a stiring description of an error and \f[C]obj\f[] is an
+object where error has been occurred.
+Error object is not allocated by libucl, so there is no need to free it
+after validation (a static object should thus be used).
+.SH AUTHORS
+Vsevolod Stakhov <vsevolod at highsecure.ru>.

Added: vendor/libucl/dist/doc/pandoc.template
===================================================================
--- vendor/libucl/dist/doc/pandoc.template	                        (rev 0)
+++ vendor/libucl/dist/doc/pandoc.template	2018-06-19 13:29:00 UTC (rev 11074)
@@ -0,0 +1,12 @@
+% LIBUCL(5) Libucl manual
+% Vsevolod Stakhov <vsevolod at highsecure.ru>
+% March 20, 2014
+
+# Name
+
+**ucl_parser_new**, **ucl_parser_register_macro**, **ucl_parser_register_variable**, **ucl_parser_add_chunk**, **ucl_parser_add_string**, **ucl_parser_add_file**, **ucl_parser_get_object**, **ucl_parser_get_error**, **ucl_parser_free**, **ucl_pubkey_add**, **ucl_parser_set_filevars** - universal configuration library parser and utility functions
+
+# Library
+
+UCL library (libucl, -lucl)
+

Added: vendor/libucl/dist/include/ucl.h
===================================================================
--- vendor/libucl/dist/include/ucl.h	                        (rev 0)
+++ vendor/libucl/dist/include/ucl.h	2018-06-19 13:29:00 UTC (rev 11074)
@@ -0,0 +1,893 @@
+/* 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_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 = 1, /**< An object has key allocated internally */
+	UCL_OBJECT_ALLOCATED_VALUE = 2, /**< An object has a string value allocated internally */
+	UCL_OBJECT_NEED_KEY_ESCAPE = 4 /**< The key of an object need to be escaped on output */
+} 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 */
+		struct ucl_object_s *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			*/
+	unsigned char* trash_stack[2];			/**< Pointer to allocated chunks */
+	unsigned keylen;						/**< Lenght of a key		*/
+	unsigned len;							/**< Size of an object		*/
+	enum ucl_type type;						/**< Real type				*/
+	uint16_t ref;							/**< Reference count		*/
+	uint16_t flags;							/**< Object flags			*/
+} ucl_object_t;
+
+/** @} */
+
+/**
+ * @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;
+
+/**
+ * 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 (will be created automatically if top is NULL)
+ * @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 (will be created automatically if top is NULL)
+ * @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);
+
+/**
+ * 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);
+
+
+/**
+ * Delete key from `top` object returning the object deleted. This object is not
+ * released
+ * @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;
+
+/**
+ * Delete key from `top` object returning the object deleted. This object is not
+ * released
+ * @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 (will be created automatically if top is NULL)
+ * @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 front of array object
+ * @param top destination object (will be created automatically if top is 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 (will be created automatically if top is 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);
+
+/**
+ * Removes an element `elt` from the array `top`. Caller must unref the returned object when it is not
+ * 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`. Caller must unref the returned object when it is not
+ * 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);
+
+/**
+ * Return object identified by an index of the array `top`
+ * @param obj object to get a key from (must be of type UCL_ARRAY)
+ * @param index 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);
+
+/**
+ * Removes the first element from the array `top`. Caller must unref the returned object when it is not
+ * 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);
+
+/**
+ * Append a element to another element forming an implicit array
+ * @param head head to append (may be NULL)
+ * @param elt new element
+ * @return true if element has been inserted
+ */
+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 matched the specified key or NULL if key is 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 matched the specified key or NULL if key is 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
+ */
+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));
+
+/**
+ * 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);
+/** @} */
+
+
+/**
+ * @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 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, 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
+ * @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_chunk (struct ucl_parser *parser,
+		const unsigned char *data, size_t len);
+
+/**
+ * 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);
+/**
+ * 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.
+ *
+ * @{
+ */
+
+/**
+ * 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);
+	/** Opaque userdata pointer */
+	void *ud;
+};
+
+/**
+ * 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
+ * @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);
+/** @} */
+
+/**
+ * @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_ */

Added: vendor/libucl/dist/libucl.pc.in
===================================================================
--- vendor/libucl/dist/libucl.pc.in	                        (rev 0)
+++ vendor/libucl/dist/libucl.pc.in	2018-06-19 13:29:00 UTC (rev 11074)
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: LibUCL
+Description: Universal configuration library
+Version: @UCL_VERSION@
+Libs: -L${libdir} -lucl
+Libs.private: @LIBS_EXTRA@
+Cflags: -I${includedir}/

Added: vendor/libucl/dist/src/Makefile.am
===================================================================
--- vendor/libucl/dist/src/Makefile.am	                        (rev 0)
+++ vendor/libucl/dist/src/Makefile.am	2018-06-19 13:29:00 UTC (rev 11074)
@@ -0,0 +1,25 @@
+libucl_common_cflags=	-I$(top_srcdir)/src \
+			-I$(top_srcdir)/include \
+			-I$(top_srcdir)/uthash \
+			-Wall -W -Wno-unused-parameter -Wno-pointer-sign
+lib_LTLIBRARIES=	libucl.la
+libucl_la_SOURCES=	ucl_emitter.c \
+					ucl_hash.c \
+					ucl_parser.c \
+					ucl_schema.c \
+					ucl_util.c \
+					xxhash.c
+libucl_la_CFLAGS=	$(libucl_common_cflags) \
+					@CURL_CFLAGS@
+libucl_la_LDFLAGS = -version-info @SO_VERSION@
+libucl_la_LIBADD=	@LIBFETCH_LIBS@ \
+					@LIBCRYPTO_LIB@ \
+					@LIBREGEX_LIB@ \
+					@CURL_LIBS@
+
+include_HEADERS=	$(top_srcdir)/include/ucl.h
+noinst_HEADERS=	ucl_internal.h \
+				xxhash.h \
+				ucl_hash.h \
+				ucl_chartable.h \
+				tree.h

Added: vendor/libucl/dist/src/tree.h
===================================================================
--- vendor/libucl/dist/src/tree.h	                        (rev 0)
+++ vendor/libucl/dist/src/tree.h	2018-06-19 13:29:00 UTC (rev 11074)
@@ -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 */

Added: vendor/libucl/dist/src/ucl_chartable.h
===================================================================
--- vendor/libucl/dist/src/ucl_chartable.h	                        (rev 0)
+++ vendor/libucl/dist/src/ucl_chartable.h	2018-06-19 13:29:00 UTC (rev 11074)
@@ -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_ */

Added: vendor/libucl/dist/src/ucl_emitter.c
===================================================================
--- vendor/libucl/dist/src/ucl_emitter.c	                        (rev 0)
+++ vendor/libucl/dist/src/ucl_emitter.c	2018-06-19 13:29:00 UTC (rev 11074)
@@ -0,0 +1,838 @@
+/* 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 rcl_emitter.c
+ * Serialise UCL object to various of output formats
+ */
+
+
+static void ucl_obj_write_json (const ucl_object_t *obj,
+		struct ucl_emitter_functions *func,
+		unsigned int tabs,
+		bool start_tabs,
+		bool compact);
+static void ucl_elt_write_json (const ucl_object_t *obj,
+		struct ucl_emitter_functions *func,
+		unsigned int tabs,
+		bool start_tabs,
+		bool compact);
+static void ucl_elt_write_config (const ucl_object_t *obj,
+		struct ucl_emitter_functions *func,
+		unsigned int tabs,
+		bool start_tabs,
+		bool is_top,
+		bool expand_array);
+static void ucl_elt_write_yaml (const ucl_object_t *obj,
+		struct ucl_emitter_functions *func,
+		unsigned int tabs,
+		bool start_tabs,
+		bool compact,
+		bool expand_array);
+static void ucl_elt_array_write_yaml (const ucl_object_t *obj,
+		struct ucl_emitter_functions *func,
+		unsigned int tabs,
+		bool start_tabs,
+		bool is_top);
+
+/**
+ * Add tabulation to the output buffer
+ * @param buf target buffer
+ * @param tabs number of tabs to add
+ */
+static inline void
+ucl_add_tabs (struct ucl_emitter_functions *func, unsigned int tabs, bool compact)
+{
+	if (!compact) {
+		func->ucl_emitter_append_character (' ', tabs * 4, func->ud);
+	}
+}
+
+/**
+ * Serialise string
+ * @param str string to emit
+ * @param buf target buffer
+ */
+static void
+ucl_elt_string_write_json (const char *str, size_t size,
+		struct ucl_emitter_functions *func)
+{
+	const char *p = str, *c = str;
+	size_t len = 0;
+
+	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);
+}
+
+/**
+ * Write a single object to the buffer
+ * @param obj object to write
+ * @param buf target buffer
+ */
+static void
+ucl_elt_obj_write_json (const ucl_object_t *obj, struct ucl_emitter_functions *func,
+		unsigned int tabs, bool start_tabs, bool compact)
+{
+	const ucl_object_t *cur;
+	ucl_hash_iter_t it = NULL;
+
+	if (start_tabs) {
+		ucl_add_tabs (func, tabs, compact);
+	}
+	if (compact) {
+		func->ucl_emitter_append_character ('{', 1, func->ud);
+	}
+	else {
+		func->ucl_emitter_append_len ("{\n", 2, func->ud);
+	}
+	while ((cur = ucl_hash_iterate (obj->value.ov, &it))) {
+		ucl_add_tabs (func, tabs + 1, compact);
+		if (cur->keylen > 0) {
+			ucl_elt_string_write_json (cur->key, cur->keylen, func);
+		}
+		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);
+		}
+		ucl_obj_write_json (cur, func, tabs + 1, false, compact);
+		if (ucl_hash_iter_has_next (it)) {
+			if (compact) {
+				func->ucl_emitter_append_character (',', 1, func->ud);
+			}
+			else {
+				func->ucl_emitter_append_len (",\n", 2, func->ud);
+			}
+		}
+		else if (!compact) {
+			func->ucl_emitter_append_character ('\n', 1, func->ud);
+		}
+	}
+	ucl_add_tabs (func, tabs, compact);
+	func->ucl_emitter_append_character ('}', 1, func->ud);
+}
+
+/**
+ * Write a single array to the buffer
+ * @param obj array to write
+ * @param buf target buffer
+ */
+static void
+ucl_elt_array_write_json (const ucl_object_t *obj, struct ucl_emitter_functions *func,
+		unsigned int tabs, bool start_tabs, bool compact)
+{
+	const ucl_object_t *cur = obj;
+
+	if (start_tabs) {
+		ucl_add_tabs (func, tabs, compact);
+	}
+	if (compact) {
+		func->ucl_emitter_append_character ('[', 1, func->ud);
+	}
+	else {
+		func->ucl_emitter_append_len ("[\n", 2, func->ud);
+	}
+	while (cur) {
+		ucl_elt_write_json (cur, func, tabs + 1, true, compact);
+		if (cur->next != NULL) {
+			if (compact) {
+				func->ucl_emitter_append_character (',', 1, func->ud);
+			}
+			else {
+				func->ucl_emitter_append_len (",\n", 2, func->ud);
+			}
+		}
+		else if (!compact) {
+			func->ucl_emitter_append_character ('\n', 1, func->ud);
+		}
+		cur = cur->next;
+	}
+	ucl_add_tabs (func, tabs, compact);
+	func->ucl_emitter_append_character (']', 1, func->ud);
+}
+
+/**
+ * Emit a single element
+ * @param obj object
+ * @param buf buffer
+ */
+static void
+ucl_elt_write_json (const ucl_object_t *obj, struct ucl_emitter_functions *func,
+		unsigned int tabs, bool start_tabs, bool compact)
+{
+	bool flag;
+
+	switch (obj->type) {
+	case UCL_INT:
+		if (start_tabs) {
+			ucl_add_tabs (func, tabs, compact);
+		}
+		func->ucl_emitter_append_int (ucl_object_toint (obj), func->ud);
+		break;
+	case UCL_FLOAT:
+	case UCL_TIME:
+		if (start_tabs) {
+			ucl_add_tabs (func, tabs, compact);
+		}
+		func->ucl_emitter_append_double (ucl_object_todouble (obj), func->ud);
+		break;
+	case UCL_BOOLEAN:
+		if (start_tabs) {
+			ucl_add_tabs (func, tabs, 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);
+		}
+		break;
+	case UCL_STRING:
+		if (start_tabs) {
+			ucl_add_tabs (func, tabs, compact);
+		}
+		ucl_elt_string_write_json (obj->value.sv, obj->len, func);
+		break;
+	case UCL_NULL:
+		if (start_tabs) {
+			ucl_add_tabs (func, tabs, compact);
+		}
+		func->ucl_emitter_append_len ("null", 4, func->ud);
+		break;
+	case UCL_OBJECT:
+		ucl_elt_obj_write_json (obj, func, tabs, start_tabs, compact);
+		break;
+	case UCL_ARRAY:
+		ucl_elt_array_write_json (obj->value.av, func, tabs, start_tabs, compact);
+		break;
+	case UCL_USERDATA:
+		break;
+	}
+}
+
+/**
+ * Write a single object to the buffer
+ * @param obj object
+ * @param buf target buffer
+ */
+static void
+ucl_obj_write_json (const ucl_object_t *obj, struct ucl_emitter_functions *func,
+		unsigned int tabs, bool start_tabs, bool compact)
+{
+	const ucl_object_t *cur;
+	bool is_array = (obj->next != NULL);
+
+	if (is_array) {
+		/* This is an array actually */
+		if (start_tabs) {
+			ucl_add_tabs (func, tabs, compact);
+		}
+
+		if (compact) {
+			func->ucl_emitter_append_character ('[', 1, func->ud);
+		}
+		else {
+			func->ucl_emitter_append_len ("[\n", 2, func->ud);
+		}
+		cur = obj;
+		while (cur != NULL) {
+			ucl_elt_write_json (cur, func, tabs + 1, true, compact);
+			if (cur->next) {
+				func->ucl_emitter_append_character (',', 1, func->ud);
+			}
+			if (!compact) {
+				func->ucl_emitter_append_character ('\n', 1, func->ud);
+			}
+			cur = cur->next;
+		}
+		ucl_add_tabs (func, tabs, compact);
+		func->ucl_emitter_append_character (']', 1, func->ud);
+	}
+	else {
+		ucl_elt_write_json (obj, func, tabs, start_tabs, compact);
+	}
+
+}
+
+/**
+ * Emit an object to json
+ * @param obj object
+ * @return json output (should be freed after using)
+ */
+static void
+ucl_object_emit_json (const ucl_object_t *obj, bool compact,
+		struct ucl_emitter_functions *func)
+{
+	ucl_obj_write_json (obj, func, 0, false, compact);
+}
+
+/**
+ * Write a single object to the buffer
+ * @param obj object to write
+ * @param buf target buffer
+ */
+static void
+ucl_elt_obj_write_config (const ucl_object_t *obj, struct ucl_emitter_functions *func,
+		unsigned int tabs, bool start_tabs, bool is_top)
+{
+	const ucl_object_t *cur, *cur_obj;
+	ucl_hash_iter_t it = NULL;
+
+	if (start_tabs) {
+		ucl_add_tabs (func, tabs, is_top);
+	}
+	if (!is_top) {
+		func->ucl_emitter_append_len ("{\n", 2, func->ud);
+	}
+
+	while ((cur = ucl_hash_iterate (obj->value.ov, &it))) {
+		LL_FOREACH (cur, cur_obj) {
+			ucl_add_tabs (func, tabs + 1, is_top);
+			if (cur_obj->flags & UCL_OBJECT_NEED_KEY_ESCAPE) {
+				ucl_elt_string_write_json (cur_obj->key, cur_obj->keylen, func);
+			}
+			else {
+				func->ucl_emitter_append_len (cur_obj->key, cur_obj->keylen, func->ud);
+			}
+			if (cur_obj->type != UCL_OBJECT && cur_obj->type != UCL_ARRAY) {
+				func->ucl_emitter_append_len (" = ", 3, func->ud);
+			}
+			else {
+				func->ucl_emitter_append_character (' ', 1, func->ud);
+			}
+			ucl_elt_write_config (cur_obj, func,
+					is_top ? tabs : tabs + 1,
+					false, false, false);
+			if (cur_obj->type != UCL_OBJECT && cur_obj->type != UCL_ARRAY) {
+				func->ucl_emitter_append_len (";\n", 2, func->ud);
+			}
+			else {
+				func->ucl_emitter_append_character ('\n', 1, func->ud);
+			}
+		}
+	}
+
+	ucl_add_tabs (func, tabs, is_top);
+	if (!is_top) {
+		func->ucl_emitter_append_character ('}', 1, func->ud);
+	}
+}
+
+/**
+ * Write a single array to the buffer
+ * @param obj array to write
+ * @param buf target buffer
+ */
+static void
+ucl_elt_array_write_config (const ucl_object_t *obj, struct ucl_emitter_functions *func,
+		unsigned int tabs, bool start_tabs, bool is_top)
+{
+	const ucl_object_t *cur = obj;
+
+	if (start_tabs) {
+		ucl_add_tabs (func, tabs, false);
+	}
+
+	func->ucl_emitter_append_len ("[\n", 2, func->ud);
+	while (cur) {
+		ucl_elt_write_config (cur, func, tabs + 1, true, false, false);
+		func->ucl_emitter_append_len (",\n", 2, func->ud);
+		cur = cur->next;
+	}
+	ucl_add_tabs (func, tabs, false);
+	func->ucl_emitter_append_character (']', 1, func->ud);
+}
+
+/**
+ * Emit a single element
+ * @param obj object
+ * @param buf buffer
+ */
+static void
+ucl_elt_write_config (const ucl_object_t *obj, struct ucl_emitter_functions *func,
+		unsigned int tabs, bool start_tabs, bool is_top, bool expand_array)
+{
+	bool flag;
+
+	if (expand_array && obj->next != NULL) {
+		ucl_elt_array_write_config (obj, func, tabs, start_tabs, is_top);
+	}
+	else {
+		switch (obj->type) {
+		case UCL_INT:
+			if (start_tabs) {
+				ucl_add_tabs (func, tabs, false);
+			}
+			func->ucl_emitter_append_int (ucl_object_toint (obj), func->ud);
+			break;
+		case UCL_FLOAT:
+		case UCL_TIME:
+			if (start_tabs) {
+				ucl_add_tabs (func, tabs, false);
+			}
+			func->ucl_emitter_append_double (ucl_object_todouble (obj), func->ud);
+			break;
+		case UCL_BOOLEAN:
+			if (start_tabs) {
+				ucl_add_tabs (func, tabs, false);
+			}
+			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);
+			}
+			break;
+		case UCL_STRING:
+			if (start_tabs) {
+				ucl_add_tabs (func, tabs, false);
+			}
+			ucl_elt_string_write_json (obj->value.sv, obj->len, func);
+			break;
+		case UCL_NULL:
+			if (start_tabs) {
+				ucl_add_tabs (func, tabs, false);
+			}
+			func->ucl_emitter_append_len ("null", 4, func->ud);
+			break;
+		case UCL_OBJECT:
+			ucl_elt_obj_write_config (obj, func, tabs, start_tabs, is_top);
+			break;
+		case UCL_ARRAY:
+			ucl_elt_array_write_config (obj->value.av, func, tabs, start_tabs, is_top);
+			break;
+		case UCL_USERDATA:
+			break;
+		}
+	}
+}
+
+/**
+ * Emit an object to rcl
+ * @param obj object
+ * @return rcl output (should be freed after using)
+ */
+static void
+ucl_object_emit_config (const ucl_object_t *obj, struct ucl_emitter_functions *func)
+{
+	ucl_elt_write_config (obj, func, 0, false, true, true);
+}
+
+
+static void
+ucl_obj_write_yaml (const ucl_object_t *obj, struct ucl_emitter_functions *func,
+		unsigned int tabs, bool start_tabs)
+{
+	bool is_array = (obj->next != NULL);
+
+	if (is_array) {
+		ucl_elt_array_write_yaml (obj, func, tabs, start_tabs, false);
+	}
+	else {
+		ucl_elt_write_yaml (obj, func, tabs, start_tabs, false, true);
+	}
+}
+
+/**
+ * Write a single object to the buffer
+ * @param obj object to write
+ * @param buf target buffer
+ */
+static void
+ucl_elt_obj_write_yaml (const ucl_object_t *obj, struct ucl_emitter_functions *func,
+		unsigned int tabs, bool start_tabs, bool is_top)
+{
+	const ucl_object_t *cur;
+	ucl_hash_iter_t it = NULL;
+
+	if (start_tabs) {
+		ucl_add_tabs (func, tabs, is_top);
+	}
+	if (!is_top) {
+		func->ucl_emitter_append_len ("{\n", 2, func->ud);
+	}
+
+	while ((cur = ucl_hash_iterate (obj->value.ov, &it))) {
+		ucl_add_tabs (func, tabs + 1, is_top);
+		if (cur->keylen > 0) {
+			ucl_elt_string_write_json (cur->key, cur->keylen, func);
+		}
+		else {
+			func->ucl_emitter_append_len ("null", 4, func->ud);
+		}
+		func->ucl_emitter_append_len (": ", 2, func->ud);
+		ucl_obj_write_yaml (cur, func, is_top ? tabs : tabs + 1, false);
+		if (ucl_hash_iter_has_next(it)) {
+			if (!is_top) {
+				func->ucl_emitter_append_len (",\n", 2, func->ud);
+			}
+			else {
+				func->ucl_emitter_append_character ('\n', 1, func->ud);
+			}
+		}
+		else {
+			func->ucl_emitter_append_character ('\n', 1, func->ud);
+		}
+	}
+
+	ucl_add_tabs (func, tabs, is_top);
+	if (!is_top) {
+		func->ucl_emitter_append_character ('}', 1, func->ud);
+	}
+}
+
+/**
+ * Write a single array to the buffer
+ * @param obj array to write
+ * @param buf target buffer
+ */
+static void
+ucl_elt_array_write_yaml (const ucl_object_t *obj, struct ucl_emitter_functions *func,
+		unsigned int tabs, bool start_tabs, bool is_top)
+{
+	const ucl_object_t *cur = obj;
+
+	if (start_tabs) {
+		ucl_add_tabs (func, tabs, false);
+	}
+
+	func->ucl_emitter_append_len ("[\n", 2, func->ud);
+	while (cur) {
+		ucl_elt_write_yaml (cur, func, tabs + 1, true, false, false);
+		func->ucl_emitter_append_len (",\n", 2, func->ud);
+		cur = cur->next;
+	}
+	ucl_add_tabs (func, tabs, false);
+	func->ucl_emitter_append_character (']', 1, func->ud);
+}
+
+/**
+ * Emit a single element
+ * @param obj object
+ * @param buf buffer
+ */
+static void
+ucl_elt_write_yaml (const ucl_object_t *obj, struct ucl_emitter_functions *func,
+		unsigned int tabs, bool start_tabs, bool is_top, bool expand_array)
+{
+	bool flag;
+
+	if (expand_array && obj->next != NULL ) {
+		ucl_elt_array_write_yaml (obj, func, tabs, start_tabs, is_top);
+	}
+	else {
+		switch (obj->type) {
+		case UCL_INT:
+			if (start_tabs) {
+				ucl_add_tabs (func, tabs, false);
+			}
+			func->ucl_emitter_append_int (ucl_object_toint (obj), func->ud);
+			break;
+		case UCL_FLOAT:
+		case UCL_TIME:
+			if (start_tabs) {
+				ucl_add_tabs (func, tabs, false);
+			}
+			func->ucl_emitter_append_double (ucl_object_todouble (obj), func->ud);
+			break;
+		case UCL_BOOLEAN:
+			if (start_tabs) {
+				ucl_add_tabs (func, tabs, false);
+			}
+			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);
+			}
+			break;
+		case UCL_STRING:
+			if (start_tabs) {
+				ucl_add_tabs (func, tabs, false);
+			}
+			ucl_elt_string_write_json (obj->value.sv, obj->len, func);
+			break;
+		case UCL_NULL:
+			if (start_tabs) {
+				ucl_add_tabs (func, tabs, false);
+			}
+			func->ucl_emitter_append_len ("null", 4, func->ud);
+			break;
+		case UCL_OBJECT:
+			ucl_elt_obj_write_yaml (obj, func, tabs, start_tabs, is_top);
+			break;
+		case UCL_ARRAY:
+			ucl_elt_array_write_yaml (obj->value.av, func, tabs, start_tabs, is_top);
+			break;
+		case UCL_USERDATA:
+			break;
+		}
+	}
+}
+
+/**
+ * Emit an object to rcl
+ * @param obj object
+ * @return rcl output (should be freed after using)
+ */
+static void
+ucl_object_emit_yaml (const ucl_object_t *obj, struct ucl_emitter_functions *func)
+{
+	ucl_elt_write_yaml (obj, func, 0, false, true, true);
+}
+
+/*
+ * Generic utstring output
+ */
+static int
+ucl_utstring_append_character (unsigned char c, size_t len, void *ud)
+{
+	UT_string *buf = ud;
+
+	if (len == 1) {
+		utstring_append_c (buf, c);
+	}
+	else {
+		utstring_reserve (buf, len);
+		memset (&buf->d[buf->i], c, len);
+		buf->i += len;
+		buf->d[buf->i] = '\0';
+	}
+
+	return 0;
+}
+
+static int
+ucl_utstring_append_len (const unsigned char *str, size_t len, void *ud)
+{
+	UT_string *buf = ud;
+
+	utstring_append_len (buf, str, len);
+
+	return 0;
+}
+
+static int
+ucl_utstring_append_int (int64_t val, void *ud)
+{
+	UT_string *buf = ud;
+
+	utstring_printf (buf, "%jd", (intmax_t)val);
+	return 0;
+}
+
+static int
+ucl_utstring_append_double (double val, void *ud)
+{
+	UT_string *buf = ud;
+	const double delta = 0.0000001;
+
+	if (val == (double)(int)val) {
+		utstring_printf (buf, "%.1lf", val);
+	}
+	else if (fabs (val - (double)(int)val) < delta) {
+		/* Write at maximum precision */
+		utstring_printf (buf, "%.*lg", DBL_DIG, val);
+	}
+	else {
+		utstring_printf (buf, "%lf", val);
+	}
+
+	return 0;
+}
+
+
+unsigned char *
+ucl_object_emit (const ucl_object_t *obj, enum ucl_emitter emit_type)
+{
+	UT_string *buf = NULL;
+	unsigned char *res = NULL;
+	struct ucl_emitter_functions func = {
+		.ucl_emitter_append_character = ucl_utstring_append_character,
+		.ucl_emitter_append_len = ucl_utstring_append_len,
+		.ucl_emitter_append_int = ucl_utstring_append_int,
+		.ucl_emitter_append_double = ucl_utstring_append_double
+	};
+
+	if (obj == NULL) {
+		return NULL;
+	}
+
+	utstring_new (buf);
+	func.ud = buf;
+
+	if (buf != NULL) {
+		if (emit_type == UCL_EMIT_JSON) {
+			ucl_object_emit_json (obj, false, &func);
+		}
+		else if (emit_type == UCL_EMIT_JSON_COMPACT) {
+			ucl_object_emit_json (obj, true, &func);
+		}
+		else if (emit_type == UCL_EMIT_YAML) {
+			ucl_object_emit_yaml (obj, &func);
+		}
+		else {
+			ucl_object_emit_config (obj, &func);
+		}
+
+		res = utstring_body (buf);
+		free (buf);
+	}
+
+	return res;
+}
+
+bool
+ucl_object_emit_full (const ucl_object_t *obj, enum ucl_emitter emit_type,
+		struct ucl_emitter_functions *emitter)
+{
+	if (emit_type == UCL_EMIT_JSON) {
+		ucl_object_emit_json (obj, false, emitter);
+	}
+	else if (emit_type == UCL_EMIT_JSON_COMPACT) {
+		ucl_object_emit_json (obj, true, emitter);
+	}
+	else if (emit_type == UCL_EMIT_YAML) {
+		ucl_object_emit_yaml (obj, emitter);
+	}
+	else {
+		ucl_object_emit_config (obj, emitter);
+	}
+
+	/* XXX: need some error checks here */
+	return true;
+}
+
+
+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;
+}

Added: vendor/libucl/dist/src/ucl_hash.c
===================================================================
--- vendor/libucl/dist/src/ucl_hash.c	                        (rev 0)
+++ vendor/libucl/dist/src/ucl_hash.c	2018-06-19 13:29:00 UTC (rev 11074)
@@ -0,0 +1,126 @@
+/* Copyright (c) 2013, Vsevolod Stakhov
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *       * Redistributions of source code must retain the above copyright
+ *         notice, this list of conditions and the following disclaimer.
+ *       * Redistributions in binary form must reproduce the above copyright
+ *         notice, this list of conditions and the following disclaimer in the
+ *         documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "ucl_internal.h"
+#include "ucl_hash.h"
+#include "utlist.h"
+
+ucl_hash_t*
+ucl_hash_create (void)
+{
+	ucl_hash_t *new;
+
+	new = UCL_ALLOC (sizeof (ucl_hash_t));
+	if (new != NULL) {
+		new->buckets = NULL;
+	}
+	return new;
+}
+
+void ucl_hash_destroy (ucl_hash_t* hashlin, ucl_hash_free_func *func)
+{
+	ucl_hash_node_t *elt, *tmp;
+	const ucl_object_t *cur, *otmp;
+
+	HASH_ITER (hh, hashlin->buckets, elt, tmp) {
+		HASH_DELETE (hh, hashlin->buckets, elt);
+		if (func) {
+			DL_FOREACH_SAFE (elt->data, cur, otmp) {
+				/* Need to deconst here */
+				func (__DECONST (ucl_object_t *, cur));
+			}
+		}
+		UCL_FREE (sizeof (ucl_hash_node_t), elt);
+	}
+	UCL_FREE (sizeof (ucl_hash_t), hashlin);
+}
+
+void
+ucl_hash_insert (ucl_hash_t* hashlin, const ucl_object_t *obj,
+		const char *key, unsigned keylen)
+{
+	ucl_hash_node_t *node;
+
+	node = UCL_ALLOC (sizeof (ucl_hash_node_t));
+	node->data = obj;
+	HASH_ADD_KEYPTR (hh, hashlin->buckets, key, keylen, node);
+}
+
+const void*
+ucl_hash_iterate (ucl_hash_t *hashlin, ucl_hash_iter_t *iter)
+{
+	ucl_hash_node_t *elt = *iter;
+
+	if (elt == NULL) {
+		if (hashlin == NULL || hashlin->buckets == NULL) {
+			return NULL;
+		}
+		elt = hashlin->buckets;
+		if (elt == NULL) {
+			return NULL;
+		}
+	}
+	else if (elt == hashlin->buckets) {
+		return NULL;
+	}
+
+	*iter = elt->hh.next ? elt->hh.next : hashlin->buckets;
+	return elt->data;
+}
+
+bool
+ucl_hash_iter_has_next (ucl_hash_iter_t iter)
+{
+	ucl_hash_node_t *elt = iter;
+
+	return (elt == NULL || elt->hh.prev != NULL);
+}
+
+
+const ucl_object_t*
+ucl_hash_search (ucl_hash_t* hashlin, const char *key, unsigned keylen)
+{
+	ucl_hash_node_t *found;
+
+	if (hashlin == NULL) {
+		return NULL;
+	}
+	HASH_FIND (hh, hashlin->buckets, key, keylen, found);
+
+	if (found) {
+		return found->data;
+	}
+	return NULL;
+}
+
+void
+ucl_hash_delete (ucl_hash_t* hashlin, const ucl_object_t *obj)
+{
+	ucl_hash_node_t *found;
+
+	HASH_FIND (hh, hashlin->buckets, obj->key, obj->keylen, found);
+
+	if (found) {
+		HASH_DELETE (hh, hashlin->buckets, found);
+	}
+}

Added: vendor/libucl/dist/src/ucl_hash.h
===================================================================
--- vendor/libucl/dist/src/ucl_hash.h	                        (rev 0)
+++ vendor/libucl/dist/src/ucl_hash.h	2018-06-19 13:29:00 UTC (rev 11074)
@@ -0,0 +1,93 @@
+/* Copyright (c) 2013, Vsevolod Stakhov
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *       * Redistributions of source code must retain the above copyright
+ *         notice, this list of conditions and the following disclaimer.
+ *       * Redistributions in binary form must reproduce the above copyright
+ *         notice, this list of conditions and the following disclaimer in the
+ *         documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __UCL_HASH_H
+#define __UCL_HASH_H
+
+#include "ucl.h"
+#include "uthash.h"
+
+/******************************************************************************/
+
+typedef struct ucl_hash_node_s
+{
+	const ucl_object_t *data;
+	UT_hash_handle hh;
+} ucl_hash_node_t;
+
+typedef int ucl_hash_cmp_func (const void* void_a, const void* void_b);
+typedef void ucl_hash_free_func (void *ptr);
+typedef void* ucl_hash_iter_t;
+
+
+/**
+ * Linear chained hashtable.
+ */
+typedef struct ucl_hash_struct
+{
+	ucl_hash_node_t *buckets; /**< array of hash buckets. One list for each hash modulus. */
+} ucl_hash_t;
+
+
+/**
+ * Initializes the hashtable.
+ */
+ucl_hash_t* ucl_hash_create (void);
+
+/**
+ * Deinitializes the hashtable.
+ */
+void ucl_hash_destroy (ucl_hash_t* hashlin, ucl_hash_free_func *func);
+
+/**
+ * Inserts an element in the the hashtable.
+ */
+void ucl_hash_insert (ucl_hash_t* hashlin, const ucl_object_t *obj, const char *key,
+		unsigned keylen);
+
+/**
+ * Delete an element from the the hashtable.
+ */
+void ucl_hash_delete (ucl_hash_t* hashlin, const ucl_object_t *obj);
+
+/**
+ * Searches an element in the hashtable.
+ */
+const ucl_object_t* ucl_hash_search (ucl_hash_t* hashlin, const char *key,
+		unsigned keylen);
+
+
+/**
+ * Iterate over hash table
+ * @param hashlin hash
+ * @param iter iterator (must be NULL on first iteration)
+ * @return the next object
+ */
+const void* ucl_hash_iterate (ucl_hash_t *hashlin, ucl_hash_iter_t *iter);
+
+/**
+ * Check whether an iterator has next element
+ */
+bool ucl_hash_iter_has_next (ucl_hash_iter_t iter);
+
+#endif

Added: vendor/libucl/dist/src/ucl_internal.h
===================================================================
--- vendor/libucl/dist/src/ucl_internal.h	                        (rev 0)
+++ vendor/libucl/dist/src/ucl_internal.h	2018-06-19 13:29:00 UTC (rev 11074)
@@ -0,0 +1,352 @@
+/* Copyright (c) 2013, Vsevolod Stakhov
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *       * Redistributions of source code must retain the above copyright
+ *         notice, this list of conditions and the following disclaimer.
+ *       * Redistributions in binary form must reproduce the above copyright
+ *         notice, this list of conditions and the following disclaimer in the
+ *         documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef UCL_INTERNAL_H_
+#define UCL_INTERNAL_H_
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#else
+/* Help embedded builds */
+#define HAVE_SYS_TYPES_H
+#define HAVE_SYS_MMAN_H
+#define HAVE_SYS_STAT_H
+#define HAVE_SYS_PARAM_H
+#define HAVE_LIMITS_H
+#define HAVE_FCNTL_H
+#define HAVE_ERRNO_H
+#define HAVE_UNISTD_H
+#define HAVE_CTYPE_H
+#define HAVE_STDIO_H
+#define HAVE_STRING_H
+#define HAVE_FLOAT_H
+#define HAVE_LIBGEN_H
+#define HAVE_MATH_H
+#define HAVE_STDBOOL_H
+#define HAVE_STDINT_H
+#define HAVE_STDARG_H
+#ifndef _WIN32
+# define HAVE_REGEX_H
+#endif
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#ifdef HAVE_SYS_MMAN_H
+# ifndef _WIN32
+#  include <sys/mman.h>
+# endif
+#endif
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_CTYPE_H
+#include <ctype.h>
+#endif
+#ifdef HAVE_STDIO_H
+#include <stdio.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "utlist.h"
+#include "utstring.h"
+#include "uthash.h"
+#include "ucl.h"
+#include "ucl_hash.h"
+#include "xxhash.h"
+
+#ifdef HAVE_OPENSSL
+#include <openssl/evp.h>
+#endif
+
+#ifndef __DECONST
+#define __DECONST(type, var)    ((type)(uintptr_t)(const void *)(var))
+#endif
+
+/**
+ * @file rcl_internal.h
+ * Internal structures and functions of UCL library
+ */
+
+#define UCL_MAX_RECURSION 16
+#define UCL_TRASH_KEY 0
+#define UCL_TRASH_VALUE 1
+
+enum ucl_parser_state {
+	UCL_STATE_INIT = 0,
+	UCL_STATE_OBJECT,
+	UCL_STATE_ARRAY,
+	UCL_STATE_KEY,
+	UCL_STATE_VALUE,
+	UCL_STATE_AFTER_VALUE,
+	UCL_STATE_ARRAY_VALUE,
+	UCL_STATE_SCOMMENT,
+	UCL_STATE_MCOMMENT,
+	UCL_STATE_MACRO_NAME,
+	UCL_STATE_MACRO,
+	UCL_STATE_ERROR
+};
+
+enum ucl_character_type {
+	UCL_CHARACTER_DENIED = 0,
+	UCL_CHARACTER_KEY = 1,
+	UCL_CHARACTER_KEY_START = 1 << 1,
+	UCL_CHARACTER_WHITESPACE = 1 << 2,
+	UCL_CHARACTER_WHITESPACE_UNSAFE = 1 << 3,
+	UCL_CHARACTER_VALUE_END = 1 << 4,
+	UCL_CHARACTER_VALUE_STR = 1 << 5,
+	UCL_CHARACTER_VALUE_DIGIT = 1 << 6,
+	UCL_CHARACTER_VALUE_DIGIT_START = 1 << 7,
+	UCL_CHARACTER_ESCAPE = 1 << 8,
+	UCL_CHARACTER_KEY_SEP = 1 << 9,
+	UCL_CHARACTER_JSON_UNSAFE = 1 << 10,
+	UCL_CHARACTER_UCL_UNSAFE = 1 << 11
+};
+
+struct ucl_macro {
+	char *name;
+	ucl_macro_handler handler;
+	void* ud;
+	UT_hash_handle hh;
+};
+
+struct ucl_stack {
+	ucl_object_t *obj;
+	struct ucl_stack *next;
+	int level;
+};
+
+struct ucl_chunk {
+	const unsigned char *begin;
+	const unsigned char *end;
+	const unsigned char *pos;
+	size_t remain;
+	unsigned int line;
+	unsigned int column;
+	struct ucl_chunk *next;
+};
+
+#ifdef HAVE_OPENSSL
+struct ucl_pubkey {
+	EVP_PKEY *key;
+	struct ucl_pubkey *next;
+};
+#else
+struct ucl_pubkey {
+	struct ucl_pubkey *next;
+};
+#endif
+
+struct ucl_variable {
+	char *var;
+	char *value;
+	size_t var_len;
+	size_t value_len;
+	struct ucl_variable *next;
+};
+
+struct ucl_parser {
+	enum ucl_parser_state state;
+	enum ucl_parser_state prev_state;
+	unsigned int recursion;
+	int flags;
+	ucl_object_t *top_obj;
+	ucl_object_t *cur_obj;
+	struct ucl_macro *macroes;
+	struct ucl_stack *stack;
+	struct ucl_chunk *chunks;
+	struct ucl_pubkey *keys;
+	struct ucl_variable *variables;
+	ucl_variable_handler var_handler;
+	void *var_data;
+	UT_string *err;
+};
+
+/**
+ * Unescape json string inplace
+ * @param str
+ */
+size_t ucl_unescape_json_string (char *str, size_t len);
+
+/**
+ * Handle include macro
+ * @param data include data
+ * @param len length of data
+ * @param ud user data
+ * @param err error ptr
+ * @return
+ */
+bool ucl_include_handler (const unsigned char *data, size_t len, void* ud);
+
+bool ucl_try_include_handler (const unsigned char *data, size_t len, void* ud);
+
+/**
+ * Handle includes macro
+ * @param data include data
+ * @param len length of data
+ * @param ud user data
+ * @param err error ptr
+ * @return
+ */
+bool ucl_includes_handler (const unsigned char *data, size_t len, void* ud);
+
+size_t ucl_strlcpy (char *dst, const char *src, size_t siz);
+size_t ucl_strlcpy_unsafe (char *dst, const char *src, size_t siz);
+size_t ucl_strlcpy_tolower (char *dst, const char *src, size_t siz);
+
+
+#ifdef __GNUC__
+static inline void
+ucl_create_err (UT_string **err, const char *fmt, ...)
+__attribute__ (( format( printf, 2, 3) ));
+#endif
+
+static inline void
+ucl_create_err (UT_string **err, const char *fmt, ...)
+
+{
+	if (*err == NULL) {
+		utstring_new (*err);
+		va_list ap;
+		va_start (ap, fmt);
+		utstring_printf_va (*err, fmt, ap);
+		va_end (ap);
+	}
+}
+
+/**
+ * Check whether a given string contains a boolean value
+ * @param obj object to set
+ * @param start start of a string
+ * @param len length of a string
+ * @return true if a string is a boolean value
+ */
+static inline bool
+ucl_maybe_parse_boolean (ucl_object_t *obj, const unsigned char *start, size_t len)
+{
+	const unsigned char *p = start;
+	bool ret = false, val = false;
+
+	if (len == 5) {
+		if ((p[0] == 'f' || p[0] == 'F') && strncasecmp (p, "false", 5) == 0) {
+			ret = true;
+			val = false;
+		}
+	}
+	else if (len == 4) {
+		if ((p[0] == 't' || p[0] == 'T') && strncasecmp (p, "true", 4) == 0) {
+			ret = true;
+			val = true;
+		}
+	}
+	else if (len == 3) {
+		if ((p[0] == 'y' || p[0] == 'Y') && strncasecmp (p, "yes", 3) == 0) {
+			ret = true;
+			val = true;
+		}
+		else if ((p[0] == 'o' || p[0] == 'O') && strncasecmp (p, "off", 3) == 0) {
+			ret = true;
+			val = false;
+		}
+	}
+	else if (len == 2) {
+		if ((p[0] == 'n' || p[0] == 'N') && strncasecmp (p, "no", 2) == 0) {
+			ret = true;
+			val = false;
+		}
+		else if ((p[0] == 'o' || p[0] == 'O') && strncasecmp (p, "on", 2) == 0) {
+			ret = true;
+			val = true;
+		}
+	}
+
+	if (ret) {
+		obj->type = UCL_BOOLEAN;
+		obj->value.iv = val;
+	}
+
+	return ret;
+}
+
+/**
+ * Check numeric string
+ * @param obj object to set if a string is numeric
+ * @param start start of string
+ * @param end end of string
+ * @param pos position where parsing has stopped
+ * @param allow_double allow parsing of floating point values
+ * @return 0 if string is numeric and error code (EINVAL or ERANGE) in case of conversion error
+ */
+int ucl_maybe_parse_number (ucl_object_t *obj,
+		const char *start, const char *end, const char **pos,
+		bool allow_double, bool number_bytes, bool allow_time);
+
+
+static inline const ucl_object_t *
+ucl_hash_search_obj (ucl_hash_t* hashlin, ucl_object_t *obj)
+{
+	return (const ucl_object_t *)ucl_hash_search (hashlin, obj->key, obj->keylen);
+}
+
+static inline ucl_hash_t *
+ucl_hash_insert_object (ucl_hash_t *hashlin, const ucl_object_t *obj) UCL_WARN_UNUSED_RESULT;
+
+static inline ucl_hash_t *
+ucl_hash_insert_object (ucl_hash_t *hashlin, const ucl_object_t *obj)
+{
+	if (hashlin == NULL) {
+		hashlin = ucl_hash_create ();
+	}
+	ucl_hash_insert (hashlin, obj, obj->key, obj->keylen);
+
+	return hashlin;
+}
+
+/**
+ * Emit a single object to string
+ * @param obj
+ * @return
+ */
+unsigned char * ucl_object_emit_single_json (const ucl_object_t *obj);
+
+#endif /* UCL_INTERNAL_H_ */

Added: vendor/libucl/dist/src/ucl_parser.c
===================================================================
--- vendor/libucl/dist/src/ucl_parser.c	                        (rev 0)
+++ vendor/libucl/dist/src/ucl_parser.c	2018-06-19 13:29:00 UTC (rev 11074)
@@ -0,0 +1,1969 @@
+/* Copyright (c) 2013, Vsevolod Stakhov
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *       * Redistributions of source code must retain the above copyright
+ *         notice, this list of conditions and the following disclaimer.
+ *       * Redistributions in binary form must reproduce the above copyright
+ *         notice, this list of conditions and the following disclaimer in the
+ *         documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "ucl.h"
+#include "ucl_internal.h"
+#include "ucl_chartable.h"
+
+/**
+ * @file rcl_parser.c
+ * The implementation of rcl parser
+ */
+
+struct ucl_parser_saved_state {
+	unsigned int line;
+	unsigned int column;
+	size_t remain;
+	const unsigned char *pos;
+};
+
+/**
+ * Move up to len characters
+ * @param parser
+ * @param begin
+ * @param len
+ * @return new position in chunk
+ */
+#define ucl_chunk_skipc(chunk, p)    do{					\
+    if (*(p) == '\n') {										\
+        (chunk)->line ++;									\
+        (chunk)->column = 0;								\
+    }														\
+    else (chunk)->column ++;								\
+    (p++);													\
+    (chunk)->pos ++;										\
+    (chunk)->remain --;										\
+    } while (0)
+
+static inline void
+ucl_set_err (struct ucl_chunk *chunk, int code, const char *str, UT_string **err)
+{
+	if (chunk->pos < chunk->end) {
+		if (isgraph (*chunk->pos)) {
+			ucl_create_err (err, "error on line %d at column %d: '%s', character: '%c'",
+					chunk->line, chunk->column, str, *chunk->pos);
+		}
+		else {
+			ucl_create_err (err, "error on line %d at column %d: '%s', character: '0x%02x'",
+					chunk->line, chunk->column, str, (int)*chunk->pos);
+		}
+	}
+	else {
+		ucl_create_err (err, "error at the end of chunk: %s", str);
+	}
+}
+
+/**
+ * Skip all comments from the current pos resolving nested and multiline comments
+ * @param parser
+ * @return
+ */
+static bool
+ucl_skip_comments (struct ucl_parser *parser)
+{
+	struct ucl_chunk *chunk = parser->chunks;
+	const unsigned char *p;
+	int comments_nested = 0;
+
+	p = chunk->pos;
+
+start:
+	if (*p == '#') {
+		if (parser->state != UCL_STATE_SCOMMENT &&
+				parser->state != UCL_STATE_MCOMMENT) {
+			while (p < chunk->end) {
+				if (*p == '\n') {
+					ucl_chunk_skipc (chunk, p);
+					goto start;
+				}
+				ucl_chunk_skipc (chunk, p);
+			}
+		}
+	}
+	else if (*p == '/' && chunk->remain >= 2) {
+		if (p[1] == '*') {
+			ucl_chunk_skipc (chunk, p);
+			comments_nested ++;
+			ucl_chunk_skipc (chunk, p);
+
+			while (p < chunk->end) {
+				if (*p == '*') {
+					ucl_chunk_skipc (chunk, p);
+					if (*p == '/') {
+						comments_nested --;
+						if (comments_nested == 0) {
+							ucl_chunk_skipc (chunk, p);
+							goto start;
+						}
+					}
+					ucl_chunk_skipc (chunk, p);
+				}
+				else if (p[0] == '/' && chunk->remain >= 2 && p[1] == '*') {
+					comments_nested ++;
+					ucl_chunk_skipc (chunk, p);
+					ucl_chunk_skipc (chunk, p);
+					continue;
+				}
+				ucl_chunk_skipc (chunk, p);
+			}
+			if (comments_nested != 0) {
+				ucl_set_err (chunk, UCL_ENESTED, "unfinished multiline comment", &parser->err);
+				return false;
+			}
+		}
+	}
+
+	return true;
+}
+
+/**
+ * Return multiplier for a character
+ * @param c multiplier character
+ * @param is_bytes if true use 1024 multiplier
+ * @return multiplier
+ */
+static inline unsigned long
+ucl_lex_num_multiplier (const unsigned char c, bool is_bytes) {
+	const struct {
+		char c;
+		long mult_normal;
+		long mult_bytes;
+	} multipliers[] = {
+			{'m', 1000 * 1000, 1024 * 1024},
+			{'k', 1000, 1024},
+			{'g', 1000 * 1000 * 1000, 1024 * 1024 * 1024}
+	};
+	int i;
+
+	for (i = 0; i < 3; i ++) {
+		if (tolower (c) == multipliers[i].c) {
+			if (is_bytes) {
+				return multipliers[i].mult_bytes;
+			}
+			return multipliers[i].mult_normal;
+		}
+	}
+
+	return 1;
+}
+
+
+/**
+ * Return multiplier for time scaling
+ * @param c
+ * @return
+ */
+static inline double
+ucl_lex_time_multiplier (const unsigned char c) {
+	const struct {
+		char c;
+		double mult;
+	} multipliers[] = {
+			{'m', 60},
+			{'h', 60 * 60},
+			{'d', 60 * 60 * 24},
+			{'w', 60 * 60 * 24 * 7},
+			{'y', 60 * 60 * 24 * 7 * 365}
+	};
+	int i;
+
+	for (i = 0; i < 5; i ++) {
+		if (tolower (c) == multipliers[i].c) {
+			return multipliers[i].mult;
+		}
+	}
+
+	return 1;
+}
+
+/**
+ * Return true if a character is a end of an atom
+ * @param c
+ * @return
+ */
+static inline bool
+ucl_lex_is_atom_end (const unsigned char c)
+{
+	return ucl_test_character (c, UCL_CHARACTER_VALUE_END);
+}
+
+static inline bool
+ucl_lex_is_comment (const unsigned char c1, const unsigned char c2)
+{
+	if (c1 == '/') {
+		if (c2 == '*') {
+			return true;
+		}
+	}
+	else if (c1 == '#') {
+		return true;
+	}
+	return false;
+}
+
+/**
+ * Check variable found
+ * @param parser
+ * @param ptr
+ * @param remain
+ * @param out_len
+ * @param strict
+ * @param found
+ * @return
+ */
+static inline const char *
+ucl_check_variable_safe (struct ucl_parser *parser, const char *ptr, size_t remain,
+		size_t *out_len, bool strict, bool *found)
+{
+	struct ucl_variable *var;
+	unsigned char *dst;
+	size_t dstlen;
+	bool need_free = false;
+
+	LL_FOREACH (parser->variables, var) {
+		if (strict) {
+			if (remain == var->var_len) {
+				if (memcmp (ptr, var->var, var->var_len) == 0) {
+					*out_len += var->value_len;
+					*found = true;
+					return (ptr + var->var_len);
+				}
+			}
+		}
+		else {
+			if (remain >= var->var_len) {
+				if (memcmp (ptr, var->var, var->var_len) == 0) {
+					*out_len += var->value_len;
+					*found = true;
+					return (ptr + var->var_len);
+				}
+			}
+		}
+	}
+
+	/* XXX: can only handle ${VAR} */
+	if (!(*found) && parser->var_handler != NULL && strict) {
+		/* Call generic handler */
+		if (parser->var_handler (ptr, remain, &dst, &dstlen, &need_free,
+				parser->var_data)) {
+			*found = true;
+			if (need_free) {
+				free (dst);
+			}
+			return (ptr + remain);
+		}
+	}
+
+	return ptr;
+}
+
+/**
+ * Check for a variable in a given string
+ * @param parser
+ * @param ptr
+ * @param remain
+ * @param out_len
+ * @param vars_found
+ * @return
+ */
+static const char *
+ucl_check_variable (struct ucl_parser *parser, const char *ptr,
+		size_t remain, size_t *out_len, bool *vars_found)
+{
+	const char *p, *end, *ret = ptr;
+	bool found = false;
+
+	if (*ptr == '{') {
+		/* We need to match the variable enclosed in braces */
+		p = ptr + 1;
+		end = ptr + remain;
+		while (p < end) {
+			if (*p == '}') {
+				ret = ucl_check_variable_safe (parser, ptr + 1, p - ptr - 1,
+						out_len, true, &found);
+				if (found) {
+					/* {} must be excluded actually */
+					ret ++;
+					if (!*vars_found) {
+						*vars_found = true;
+					}
+				}
+				else {
+					*out_len += 2;
+				}
+				break;
+			}
+			p ++;
+		}
+	}
+	else if (*ptr != '$') {
+		/* Not count escaped dollar sign */
+		ret = ucl_check_variable_safe (parser, ptr, remain, out_len, false, &found);
+		if (found && !*vars_found) {
+			*vars_found = true;
+		}
+		if (!found) {
+			(*out_len) ++;
+		}
+	}
+	else {
+		ret ++;
+		(*out_len) ++;
+	}
+
+	return ret;
+}
+
+/**
+ * Expand a single variable
+ * @param parser
+ * @param ptr
+ * @param remain
+ * @param dest
+ * @return
+ */
+static const char *
+ucl_expand_single_variable (struct ucl_parser *parser, const char *ptr,
+		size_t remain, unsigned char **dest)
+{
+	unsigned char *d = *dest, *dst;
+	const char *p = ptr + 1, *ret;
+	struct ucl_variable *var;
+	size_t dstlen;
+	bool need_free = false;
+	bool found = false;
+	bool strict = false;
+
+	ret = ptr + 1;
+	remain --;
+
+	if (*p == '$') {
+		*d++ = *p++;
+		*dest = d;
+		return p;
+	}
+	else if (*p == '{') {
+		p ++;
+		strict = true;
+		ret += 2;
+		remain -= 2;
+	}
+
+	LL_FOREACH (parser->variables, var) {
+		if (remain >= var->var_len) {
+			if (memcmp (p, var->var, var->var_len) == 0) {
+				memcpy (d, var->value, var->value_len);
+				ret += var->var_len;
+				d += var->value_len;
+				found = true;
+				break;
+			}
+		}
+	}
+	if (!found) {
+		if (strict && parser->var_handler != NULL) {
+			if (parser->var_handler (ptr, remain, &dst, &dstlen, &need_free,
+							parser->var_data)) {
+				memcpy (d, dst, dstlen);
+				ret += dstlen;
+				d += remain;
+				found = true;
+			}
+		}
+
+		/* Leave variable as is */
+		if (!found) {
+			memcpy (d, ptr, 2);
+			d += 2;
+			ret --;
+		}
+	}
+
+	*dest = d;
+	return ret;
+}
+
+/**
+ * Expand variables in string
+ * @param parser
+ * @param dst
+ * @param src
+ * @param in_len
+ * @return
+ */
+static ssize_t
+ucl_expand_variable (struct ucl_parser *parser, unsigned char **dst,
+		const char *src, size_t in_len)
+{
+	const char *p, *end = src + in_len;
+	unsigned char *d;
+	size_t out_len = 0;
+	bool vars_found = false;
+
+	p = src;
+	while (p != end) {
+		if (*p == '$') {
+			p = ucl_check_variable (parser, p + 1, end - p - 1, &out_len, &vars_found);
+		}
+		else {
+			p ++;
+			out_len ++;
+		}
+	}
+
+	if (!vars_found) {
+		/* Trivial case */
+		*dst = NULL;
+		return in_len;
+	}
+
+	*dst = UCL_ALLOC (out_len + 1);
+	if (*dst == NULL) {
+		return in_len;
+	}
+
+	d = *dst;
+	p = src;
+	while (p != end) {
+		if (*p == '$') {
+			p = ucl_expand_single_variable (parser, p, end - p, &d);
+		}
+		else {
+			*d++ = *p++;
+		}
+	}
+
+	*d = '\0';
+
+	return out_len;
+}
+
+/**
+ * Store or copy pointer to the trash stack
+ * @param parser parser object
+ * @param src src string
+ * @param dst destination buffer (trash stack pointer)
+ * @param dst_const const destination pointer (e.g. value of object)
+ * @param in_len input length
+ * @param need_unescape need to unescape source (and copy it)
+ * @param need_lowercase need to lowercase value (and copy)
+ * @param need_expand need to expand variables (and copy as well)
+ * @return output length (excluding \0 symbol)
+ */
+static inline ssize_t
+ucl_copy_or_store_ptr (struct ucl_parser *parser,
+		const unsigned char *src, unsigned char **dst,
+		const char **dst_const, size_t in_len,
+		bool need_unescape, bool need_lowercase, bool need_expand)
+{
+	ssize_t ret = -1, tret;
+	unsigned char *tmp;
+
+	if (need_unescape || need_lowercase ||
+			(need_expand && parser->variables != NULL) ||
+			!(parser->flags & UCL_PARSER_ZEROCOPY)) {
+		/* Copy string */
+		*dst = UCL_ALLOC (in_len + 1);
+		if (*dst == NULL) {
+			ucl_set_err (parser->chunks, 0, "cannot allocate memory for a string", &parser->err);
+			return false;
+		}
+		if (need_lowercase) {
+			ret = ucl_strlcpy_tolower (*dst, src, in_len + 1);
+		}
+		else {
+			ret = ucl_strlcpy_unsafe (*dst, src, in_len + 1);
+		}
+
+		if (need_unescape) {
+			ret = ucl_unescape_json_string (*dst, ret);
+		}
+		if (need_expand) {
+			tmp = *dst;
+			tret = ret;
+			ret = ucl_expand_variable (parser, dst, tmp, ret);
+			if (*dst == NULL) {
+				/* Nothing to expand */
+				*dst = tmp;
+				ret = tret;
+			}
+		}
+		*dst_const = *dst;
+	}
+	else {
+		*dst_const = src;
+		ret = in_len;
+	}
+
+	return ret;
+}
+
+/**
+ * Create and append an object at the specified level
+ * @param parser
+ * @param is_array
+ * @param level
+ * @return
+ */
+static inline ucl_object_t *
+ucl_add_parser_stack (ucl_object_t *obj, struct ucl_parser *parser, bool is_array, int level)
+{
+	struct ucl_stack *st;
+
+	if (!is_array) {
+		if (obj == NULL) {
+			obj = ucl_object_typed_new (UCL_OBJECT);
+		}
+		else {
+			obj->type = UCL_OBJECT;
+		}
+		obj->value.ov = ucl_hash_create ();
+		parser->state = UCL_STATE_KEY;
+	}
+	else {
+		if (obj == NULL) {
+			obj = ucl_object_typed_new (UCL_ARRAY);
+		}
+		else {
+			obj->type = UCL_ARRAY;
+		}
+		parser->state = UCL_STATE_VALUE;
+	}
+
+	st = UCL_ALLOC (sizeof (struct ucl_stack));
+	if (st == NULL) {
+		ucl_set_err (parser->chunks, 0, "cannot allocate memory for an object", &parser->err);
+		return NULL;
+	}
+	st->obj = obj;
+	st->level = level;
+	LL_PREPEND (parser->stack, st);
+	parser->cur_obj = obj;
+
+	return obj;
+}
+
+int
+ucl_maybe_parse_number (ucl_object_t *obj,
+		const char *start, const char *end, const char **pos,
+		bool allow_double, bool number_bytes, bool allow_time)
+{
+	const char *p = start, *c = start;
+	char *endptr;
+	bool got_dot = false, got_exp = false, need_double = false,
+			is_time = false, valid_start = false, is_hex = false,
+			is_neg = false;
+	double dv = 0;
+	int64_t lv = 0;
+
+	if (*p == '-') {
+		is_neg = true;
+		c ++;
+		p ++;
+	}
+	while (p < end) {
+		if (is_hex && isxdigit (*p)) {
+			p ++;
+		}
+		else if (isdigit (*p)) {
+			valid_start = true;
+			p ++;
+		}
+		else if (!is_hex && (*p == 'x' || *p == 'X')) {
+			is_hex = true;
+			allow_double = false;
+			c = p + 1;
+		}
+		else if (allow_double) {
+			if (p == c) {
+				/* Empty digits sequence, not a number */
+				*pos = start;
+				return EINVAL;
+			}
+			else if (*p == '.') {
+				if (got_dot) {
+					/* Double dots, not a number */
+					*pos = start;
+					return EINVAL;
+				}
+				else {
+					got_dot = true;
+					need_double = true;
+					p ++;
+				}
+			}
+			else if (*p == 'e' || *p == 'E') {
+				if (got_exp) {
+					/* Double exp, not a number */
+					*pos = start;
+					return EINVAL;
+				}
+				else {
+					got_exp = true;
+					need_double = true;
+					p ++;
+					if (p >= end) {
+						*pos = start;
+						return EINVAL;
+					}
+					if (!isdigit (*p) && *p != '+' && *p != '-') {
+						/* Wrong exponent sign */
+						*pos = start;
+						return EINVAL;
+					}
+					else {
+						p ++;
+					}
+				}
+			}
+			else {
+				/* Got the end of the number, need to check */
+				break;
+			}
+		}
+		else {
+			break;
+		}
+	}
+
+	if (!valid_start) {
+		*pos = start;
+		return EINVAL;
+	}
+
+	errno = 0;
+	if (need_double) {
+		dv = strtod (c, &endptr);
+	}
+	else {
+		if (is_hex) {
+			lv = strtoimax (c, &endptr, 16);
+		}
+		else {
+			lv = strtoimax (c, &endptr, 10);
+		}
+	}
+	if (errno == ERANGE) {
+		*pos = start;
+		return ERANGE;
+	}
+
+	/* Now check endptr */
+	if (endptr == NULL || ucl_lex_is_atom_end (*endptr) || *endptr == '\0' ||
+			ucl_test_character (*endptr, UCL_CHARACTER_WHITESPACE_UNSAFE)) {
+		p = endptr;
+		goto set_obj;
+	}
+
+	if (endptr < end && endptr != start) {
+		p = endptr;
+		switch (*p) {
+		case 'm':
+		case 'M':
+		case 'g':
+		case 'G':
+		case 'k':
+		case 'K':
+			if (end - p >= 2) {
+				if (p[1] == 's' || p[1] == 'S') {
+					/* Milliseconds */
+					if (!need_double) {
+						need_double = true;
+						dv = lv;
+					}
+					is_time = true;
+					if (p[0] == 'm' || p[0] == 'M') {
+						dv /= 1000.;
+					}
+					else {
+						dv *= ucl_lex_num_multiplier (*p, false);
+					}
+					p += 2;
+					goto set_obj;
+				}
+				else if (number_bytes || (p[1] == 'b' || p[1] == 'B')) {
+					/* Bytes */
+					if (need_double) {
+						need_double = false;
+						lv = dv;
+					}
+					lv *= ucl_lex_num_multiplier (*p, true);
+					p += 2;
+					goto set_obj;
+				}
+				else if (ucl_lex_is_atom_end (p[1])) {
+					if (need_double) {
+						dv *= ucl_lex_num_multiplier (*p, false);
+					}
+					else {
+						lv *= ucl_lex_num_multiplier (*p, number_bytes);
+					}
+					p ++;
+					goto set_obj;
+				}
+				else if (allow_time && end - p >= 3) {
+					if (tolower (p[0]) == 'm' &&
+							tolower (p[1]) == 'i' &&
+							tolower (p[2]) == 'n') {
+						/* Minutes */
+						if (!need_double) {
+							need_double = true;
+							dv = lv;
+						}
+						is_time = true;
+						dv *= 60.;
+						p += 3;
+						goto set_obj;
+					}
+				}
+			}
+			else {
+				if (need_double) {
+					dv *= ucl_lex_num_multiplier (*p, false);
+				}
+				else {
+					lv *= ucl_lex_num_multiplier (*p, number_bytes);
+				}
+				p ++;
+				goto set_obj;
+			}
+			break;
+		case 'S':
+		case 's':
+			if (allow_time &&
+					(p == end - 1 || ucl_lex_is_atom_end (p[1]))) {
+				if (!need_double) {
+					need_double = true;
+					dv = lv;
+				}
+				p ++;
+				is_time = true;
+				goto set_obj;
+			}
+			break;
+		case 'h':
+		case 'H':
+		case 'd':
+		case 'D':
+		case 'w':
+		case 'W':
+		case 'Y':
+		case 'y':
+			if (allow_time &&
+					(p == end - 1 || ucl_lex_is_atom_end (p[1]))) {
+				if (!need_double) {
+					need_double = true;
+					dv = lv;
+				}
+				is_time = true;
+				dv *= ucl_lex_time_multiplier (*p);
+				p ++;
+				goto set_obj;
+			}
+			break;
+		}
+	}
+
+	*pos = c;
+	return EINVAL;
+
+	set_obj:
+	if (allow_double && (need_double || is_time)) {
+		if (!is_time) {
+			obj->type = UCL_FLOAT;
+		}
+		else {
+			obj->type = UCL_TIME;
+		}
+		obj->value.dv = is_neg ? (-dv) : dv;
+	}
+	else {
+		obj->type = UCL_INT;
+		obj->value.iv = is_neg ? (-lv) : lv;
+	}
+	*pos = p;
+	return 0;
+}
+
+/**
+ * Parse possible number
+ * @param parser
+ * @param chunk
+ * @return true if a number has been parsed
+ */
+static bool
+ucl_lex_number (struct ucl_parser *parser,
+		struct ucl_chunk *chunk, ucl_object_t *obj)
+{
+	const unsigned char *pos;
+	int ret;
+
+	ret = ucl_maybe_parse_number (obj, chunk->pos, chunk->end, (const char **)&pos,
+			true, false, ((parser->flags & UCL_PARSER_NO_TIME) == 0));
+
+	if (ret == 0) {
+		chunk->remain -= pos - chunk->pos;
+		chunk->column += pos - chunk->pos;
+		chunk->pos = pos;
+		return true;
+	}
+	else if (ret == ERANGE) {
+		ucl_set_err (chunk, ERANGE, "numeric value out of range", &parser->err);
+	}
+
+	return false;
+}
+
+/**
+ * Parse quoted string with possible escapes
+ * @param parser
+ * @param chunk
+ * @return true if a string has been parsed
+ */
+static bool
+ucl_lex_json_string (struct ucl_parser *parser,
+		struct ucl_chunk *chunk, bool *need_unescape, bool *ucl_escape, bool *var_expand)
+{
+	const unsigned char *p = chunk->pos;
+	unsigned char c;
+	int i;
+
+	while (p < chunk->end) {
+		c = *p;
+		if (c < 0x1F) {
+			/* Unmasked control character */
+			if (c == '\n') {
+				ucl_set_err (chunk, UCL_ESYNTAX, "unexpected newline", &parser->err);
+			}
+			else {
+				ucl_set_err (chunk, UCL_ESYNTAX, "unexpected control character", &parser->err);
+			}
+			return false;
+		}
+		else if (c == '\\') {
+			ucl_chunk_skipc (chunk, p);
+			c = *p;
+			if (p >= chunk->end) {
+				ucl_set_err (chunk, UCL_ESYNTAX, "unfinished escape character", &parser->err);
+				return false;
+			}
+			else if (ucl_test_character (c, UCL_CHARACTER_ESCAPE)) {
+				if (c == 'u') {
+					ucl_chunk_skipc (chunk, p);
+					for (i = 0; i < 4 && p < chunk->end; i ++) {
+						if (!isxdigit (*p)) {
+							ucl_set_err (chunk, UCL_ESYNTAX, "invalid utf escape", &parser->err);
+							return false;
+						}
+						ucl_chunk_skipc (chunk, p);
+					}
+					if (p >= chunk->end) {
+						ucl_set_err (chunk, UCL_ESYNTAX, "unfinished escape character", &parser->err);
+						return false;
+					}
+				}
+				else {
+					ucl_chunk_skipc (chunk, p);
+				}
+			}
+			*need_unescape = true;
+			*ucl_escape = true;
+			continue;
+		}
+		else if (c == '"') {
+			ucl_chunk_skipc (chunk, p);
+			return true;
+		}
+		else if (ucl_test_character (c, UCL_CHARACTER_UCL_UNSAFE)) {
+			*ucl_escape = true;
+		}
+		else if (c == '$') {
+			*var_expand = true;
+		}
+		ucl_chunk_skipc (chunk, p);
+	}
+
+	ucl_set_err (chunk, UCL_ESYNTAX, "no quote at the end of json string", &parser->err);
+	return false;
+}
+
+/**
+ * Parse a key in an object
+ * @param parser
+ * @param chunk
+ * @return true if a key has been parsed
+ */
+static bool
+ucl_parse_key (struct ucl_parser *parser, struct ucl_chunk *chunk, bool *next_key, bool *end_of_object)
+{
+	const unsigned char *p, *c = NULL, *end, *t;
+	const char *key = NULL;
+	bool got_quote = false, got_eq = false, got_semicolon = false,
+			need_unescape = false, ucl_escape = false, var_expand = false,
+			got_content = false, got_sep = false;
+	ucl_object_t *nobj, *tobj;
+	ucl_hash_t *container;
+	ssize_t keylen;
+
+	p = chunk->pos;
+
+	if (*p == '.') {
+		/* It is macro actually */
+		ucl_chunk_skipc (chunk, p);
+		parser->prev_state = parser->state;
+		parser->state = UCL_STATE_MACRO_NAME;
+		return true;
+	}
+	while (p < chunk->end) {
+		/*
+		 * A key must start with alpha, number, '/' or '_' and end with space character
+		 */
+		if (c == NULL) {
+			if (chunk->remain >= 2 && ucl_lex_is_comment (p[0], p[1])) {
+				if (!ucl_skip_comments (parser)) {
+					return false;
+				}
+				p = chunk->pos;
+			}
+			else if (ucl_test_character (*p, UCL_CHARACTER_WHITESPACE_UNSAFE)) {
+				ucl_chunk_skipc (chunk, p);
+			}
+			else if (ucl_test_character (*p, UCL_CHARACTER_KEY_START)) {
+				/* The first symbol */
+				c = p;
+				ucl_chunk_skipc (chunk, p);
+				got_content = true;
+			}
+			else if (*p == '"') {
+				/* JSON style key */
+				c = p + 1;
+				got_quote = true;
+				got_content = true;
+				ucl_chunk_skipc (chunk, p);
+			}
+			else if (*p == '}') {
+				/* We have actually end of an object */
+				*end_of_object = true;
+				return true;
+			}
+			else if (*p == '.') {
+				ucl_chunk_skipc (chunk, p);
+				parser->prev_state = parser->state;
+				parser->state = UCL_STATE_MACRO_NAME;
+				return true;
+			}
+			else {
+				/* Invalid identifier */
+				ucl_set_err (chunk, UCL_ESYNTAX, "key must begin with a letter", &parser->err);
+				return false;
+			}
+		}
+		else {
+			/* Parse the body of a key */
+			if (!got_quote) {
+				if (ucl_test_character (*p, UCL_CHARACTER_KEY)) {
+					got_content = true;
+					ucl_chunk_skipc (chunk, p);
+				}
+				else if (ucl_test_character (*p, UCL_CHARACTER_KEY_SEP)) {
+					end = p;
+					break;
+				}
+				else {
+					ucl_set_err (chunk, UCL_ESYNTAX, "invalid character in a key", &parser->err);
+					return false;
+				}
+			}
+			else {
+				/* We need to parse json like quoted string */
+				if (!ucl_lex_json_string (parser, chunk, &need_unescape, &ucl_escape, &var_expand)) {
+					return false;
+				}
+				/* Always escape keys obtained via json */
+				end = chunk->pos - 1;
+				p = chunk->pos;
+				break;
+			}
+		}
+	}
+
+	if (p >= chunk->end && got_content) {
+		ucl_set_err (chunk, UCL_ESYNTAX, "unfinished key", &parser->err);
+		return false;
+	}
+	else if (!got_content) {
+		return true;
+	}
+	*end_of_object = false;
+	/* We are now at the end of the key, need to parse the rest */
+	while (p < chunk->end) {
+		if (ucl_test_character (*p, UCL_CHARACTER_WHITESPACE)) {
+			ucl_chunk_skipc (chunk, p);
+		}
+		else if (*p == '=') {
+			if (!got_eq && !got_semicolon) {
+				ucl_chunk_skipc (chunk, p);
+				got_eq = true;
+			}
+			else {
+				ucl_set_err (chunk, UCL_ESYNTAX, "unexpected '=' character", &parser->err);
+				return false;
+			}
+		}
+		else if (*p == ':') {
+			if (!got_eq && !got_semicolon) {
+				ucl_chunk_skipc (chunk, p);
+				got_semicolon = true;
+			}
+			else {
+				ucl_set_err (chunk, UCL_ESYNTAX, "unexpected ':' character", &parser->err);
+				return false;
+			}
+		}
+		else if (chunk->remain >= 2 && ucl_lex_is_comment (p[0], p[1])) {
+			/* Check for comment */
+			if (!ucl_skip_comments (parser)) {
+				return false;
+			}
+			p = chunk->pos;
+		}
+		else {
+			/* Start value */
+			break;
+		}
+	}
+
+	if (p >= chunk->end && got_content) {
+		ucl_set_err (chunk, UCL_ESYNTAX, "unfinished key", &parser->err);
+		return false;
+	}
+
+	got_sep = got_semicolon || got_eq;
+
+	if (!got_sep) {
+		/*
+		 * Maybe we have more keys nested, so search for termination character.
+		 * Possible choices:
+		 * 1) key1 key2 ... keyN [:=] value <- we treat that as error
+		 * 2) key1 ... keyN {} or [] <- we treat that as nested objects
+		 * 3) key1 value[;,\n] <- we treat that as linear object
+		 */
+		t = p;
+		*next_key = false;
+		while (ucl_test_character (*t, UCL_CHARACTER_WHITESPACE)) {
+			t ++;
+		}
+		/* Check first non-space character after a key */
+		if (*t != '{' && *t != '[') {
+			while (t < chunk->end) {
+				if (*t == ',' || *t == ';' || *t == '\n' || *t == '\r') {
+					break;
+				}
+				else if (*t == '{' || *t == '[') {
+					*next_key = true;
+					break;
+				}
+				t ++;
+			}
+		}
+	}
+
+	/* Create a new object */
+	nobj = ucl_object_new ();
+	keylen = ucl_copy_or_store_ptr (parser, c, &nobj->trash_stack[UCL_TRASH_KEY],
+			&key, end - c, need_unescape, parser->flags & UCL_PARSER_KEY_LOWERCASE, false);
+	if (keylen == -1) {
+		ucl_object_unref (nobj);
+		return false;
+	}
+	else if (keylen == 0) {
+		ucl_set_err (chunk, UCL_ESYNTAX, "empty keys are not allowed", &parser->err);
+		ucl_object_unref (nobj);
+		return false;
+	}
+
+	container = parser->stack->obj->value.ov;
+	nobj->key = key;
+	nobj->keylen = keylen;
+	tobj = __DECONST (ucl_object_t *, ucl_hash_search_obj (container, nobj));
+	if (tobj == NULL) {
+		container = ucl_hash_insert_object (container, nobj);
+		nobj->prev = nobj;
+		nobj->next = NULL;
+		parser->stack->obj->len ++;
+	}
+	else {
+		DL_APPEND (tobj, nobj);
+	}
+
+	if (ucl_escape) {
+		nobj->flags |= UCL_OBJECT_NEED_KEY_ESCAPE;
+	}
+	parser->stack->obj->value.ov = container;
+
+	parser->cur_obj = nobj;
+
+	return true;
+}
+
+/**
+ * Parse a cl string
+ * @param parser
+ * @param chunk
+ * @return true if a key has been parsed
+ */
+static bool
+ucl_parse_string_value (struct ucl_parser *parser,
+		struct ucl_chunk *chunk, bool *var_expand, bool *need_unescape)
+{
+	const unsigned char *p;
+	enum {
+		UCL_BRACE_ROUND = 0,
+		UCL_BRACE_SQUARE,
+		UCL_BRACE_FIGURE
+	};
+	int braces[3][2] = {{0, 0}, {0, 0}, {0, 0}};
+
+	p = chunk->pos;
+
+	while (p < chunk->end) {
+
+		/* Skip pairs of figure braces */
+		if (*p == '{') {
+			braces[UCL_BRACE_FIGURE][0] ++;
+		}
+		else if (*p == '}') {
+			braces[UCL_BRACE_FIGURE][1] ++;
+			if (braces[UCL_BRACE_FIGURE][1] <= braces[UCL_BRACE_FIGURE][0]) {
+				/* This is not a termination symbol, continue */
+				ucl_chunk_skipc (chunk, p);
+				continue;
+			}
+		}
+		/* Skip pairs of square braces */
+		else if (*p == '[') {
+			braces[UCL_BRACE_SQUARE][0] ++;
+		}
+		else if (*p == ']') {
+			braces[UCL_BRACE_SQUARE][1] ++;
+			if (braces[UCL_BRACE_SQUARE][1] <= braces[UCL_BRACE_SQUARE][0]) {
+				/* This is not a termination symbol, continue */
+				ucl_chunk_skipc (chunk, p);
+				continue;
+			}
+		}
+		else if (*p == '$') {
+			*var_expand = true;
+		}
+		else if (*p == '\\') {
+			*need_unescape = true;
+			ucl_chunk_skipc (chunk, p);
+			if (p < chunk->end) {
+				ucl_chunk_skipc (chunk, p);
+			}
+			continue;
+		}
+
+		if (ucl_lex_is_atom_end (*p) || (chunk->remain >= 2 && ucl_lex_is_comment (p[0], p[1]))) {
+			break;
+		}
+		ucl_chunk_skipc (chunk, p);
+	}
+
+	if (p >= chunk->end) {
+		ucl_set_err (chunk, UCL_ESYNTAX, "unfinished value", &parser->err);
+		return false;
+	}
+
+	return true;
+}
+
+/**
+ * Parse multiline string ending with \n{term}\n
+ * @param parser
+ * @param chunk
+ * @param term
+ * @param term_len
+ * @return size of multiline string or 0 in case of error
+ */
+static int
+ucl_parse_multiline_string (struct ucl_parser *parser,
+		struct ucl_chunk *chunk, const unsigned char *term,
+		int term_len, unsigned char const **beg,
+		bool *var_expand)
+{
+	const unsigned char *p, *c;
+	bool newline = false;
+	int len = 0;
+
+	p = chunk->pos;
+
+	c = p;
+
+	while (p < chunk->end) {
+		if (newline) {
+			if (chunk->end - p < term_len) {
+				return 0;
+			}
+			else if (memcmp (p, term, term_len) == 0 && (p[term_len] == '\n' || p[term_len] == '\r')) {
+				len = p - c;
+				chunk->remain -= term_len;
+				chunk->pos = p + term_len;
+				chunk->column = term_len;
+				*beg = c;
+				break;
+			}
+		}
+		if (*p == '\n') {
+			newline = true;
+		}
+		else {
+			if (*p == '$') {
+				*var_expand = true;
+			}
+			newline = false;
+		}
+		ucl_chunk_skipc (chunk, p);
+	}
+
+	return len;
+}
+
+static ucl_object_t*
+ucl_get_value_object (struct ucl_parser *parser)
+{
+	ucl_object_t *t, *obj = NULL;
+
+	if (parser->stack->obj->type == UCL_ARRAY) {
+		/* Object must be allocated */
+		obj = ucl_object_new ();
+		t = parser->stack->obj->value.av;
+		DL_APPEND (t, obj);
+		parser->cur_obj = obj;
+		parser->stack->obj->value.av = t;
+		parser->stack->obj->len ++;
+	}
+	else {
+		/* Object has been already allocated */
+		obj = parser->cur_obj;
+	}
+
+	return obj;
+}
+
+/**
+ * Handle value data
+ * @param parser
+ * @param chunk
+ * @return
+ */
+static bool
+ucl_parse_value (struct ucl_parser *parser, struct ucl_chunk *chunk)
+{
+	const unsigned char *p, *c;
+	ucl_object_t *obj = NULL;
+	unsigned int stripped_spaces;
+	int str_len;
+	bool need_unescape = false, ucl_escape = false, var_expand = false;
+
+	p = chunk->pos;
+
+	/* Skip any spaces and comments */
+	if (ucl_test_character (*p, UCL_CHARACTER_WHITESPACE_UNSAFE) ||
+			(chunk->remain >= 2 && ucl_lex_is_comment (p[0], p[1]))) {
+		while (p < chunk->end && ucl_test_character (*p, UCL_CHARACTER_WHITESPACE_UNSAFE)) {
+			ucl_chunk_skipc (chunk, p);
+		}
+		if (!ucl_skip_comments (parser)) {
+			return false;
+		}
+		p = chunk->pos;
+	}
+
+	while (p < chunk->end) {
+		c = p;
+		switch (*p) {
+		case '"':
+			obj = ucl_get_value_object (parser);
+			ucl_chunk_skipc (chunk, p);
+			if (!ucl_lex_json_string (parser, chunk, &need_unescape, &ucl_escape, &var_expand)) {
+				return false;
+			}
+			str_len = chunk->pos - c - 2;
+			obj->type = UCL_STRING;
+			if ((str_len = ucl_copy_or_store_ptr (parser, c + 1, &obj->trash_stack[UCL_TRASH_VALUE],
+					&obj->value.sv, str_len, need_unescape, false, var_expand)) == -1) {
+				return false;
+			}
+			obj->len = str_len;
+			parser->state = UCL_STATE_AFTER_VALUE;
+			p = chunk->pos;
+			return true;
+			break;
+		case '{':
+			obj = ucl_get_value_object (parser);
+			/* We have a new object */
+			obj = ucl_add_parser_stack (obj, parser, false, parser->stack->level);
+			if (obj == NULL) {
+				return false;
+			}
+
+			ucl_chunk_skipc (chunk, p);
+			return true;
+			break;
+		case '[':
+			obj = ucl_get_value_object (parser);
+			/* We have a new array */
+			obj = ucl_add_parser_stack (obj, parser, true, parser->stack->level);
+			if (obj == NULL) {
+				return false;
+			}
+
+			ucl_chunk_skipc (chunk, p);
+			return true;
+			break;
+		case ']':
+			/* We have the array ending */
+			if (parser->stack && parser->stack->obj->type == UCL_ARRAY) {
+				parser->state = UCL_STATE_AFTER_VALUE;
+				return true;
+			}
+			else {
+				goto parse_string;
+			}
+			break;
+		case '<':
+			obj = ucl_get_value_object (parser);
+			/* We have something like multiline value, which must be <<[A-Z]+\n */
+			if (chunk->end - p > 3) {
+				if (memcmp (p, "<<", 2) == 0) {
+					p += 2;
+					/* We allow only uppercase characters in multiline definitions */
+					while (p < chunk->end && *p >= 'A' && *p <= 'Z') {
+						p ++;
+					}
+					if (*p =='\n') {
+						/* Set chunk positions and start multiline parsing */
+						c += 2;
+						chunk->remain -= p - c;
+						chunk->pos = p + 1;
+						chunk->column = 0;
+						chunk->line ++;
+						if ((str_len = ucl_parse_multiline_string (parser, chunk, c,
+								p - c, &c, &var_expand)) == 0) {
+							ucl_set_err (chunk, UCL_ESYNTAX, "unterminated multiline value", &parser->err);
+							return false;
+						}
+						obj->type = UCL_STRING;
+						if ((str_len = ucl_copy_or_store_ptr (parser, c, &obj->trash_stack[UCL_TRASH_VALUE],
+							&obj->value.sv, str_len - 1, false, false, var_expand)) == -1) {
+							return false;
+						}
+						obj->len = str_len;
+						parser->state = UCL_STATE_AFTER_VALUE;
+						return true;
+					}
+				}
+			}
+			/* Fallback to ordinary strings */
+		default:
+parse_string:
+			if (obj == NULL) {
+				obj = ucl_get_value_object (parser);
+			}
+			/* Parse atom */
+			if (ucl_test_character (*p, UCL_CHARACTER_VALUE_DIGIT_START)) {
+				if (!ucl_lex_number (parser, chunk, obj)) {
+					if (parser->state == UCL_STATE_ERROR) {
+						return false;
+					}
+				}
+				else {
+					parser->state = UCL_STATE_AFTER_VALUE;
+					return true;
+				}
+				/* Fallback to normal string */
+			}
+
+			if (!ucl_parse_string_value (parser, chunk, &var_expand, &need_unescape)) {
+				return false;
+			}
+			/* Cut trailing spaces */
+			stripped_spaces = 0;
+			while (ucl_test_character (*(chunk->pos - 1 - stripped_spaces),
+					UCL_CHARACTER_WHITESPACE)) {
+				stripped_spaces ++;
+			}
+			str_len = chunk->pos - c - stripped_spaces;
+			if (str_len <= 0) {
+				ucl_set_err (chunk, 0, "string value must not be empty", &parser->err);
+				return false;
+			}
+			else if (str_len == 4 && memcmp (c, "null", 4) == 0) {
+				obj->len = 0;
+				obj->type = UCL_NULL;
+			}
+			else if (!ucl_maybe_parse_boolean (obj, c, str_len)) {
+				obj->type = UCL_STRING;
+				if ((str_len = ucl_copy_or_store_ptr (parser, c, &obj->trash_stack[UCL_TRASH_VALUE],
+						&obj->value.sv, str_len, need_unescape,
+						false, var_expand)) == -1) {
+					return false;
+				}
+				obj->len = str_len;
+			}
+			parser->state = UCL_STATE_AFTER_VALUE;
+			p = chunk->pos;
+
+			return true;
+			break;
+		}
+	}
+
+	return true;
+}
+
+/**
+ * Handle after value data
+ * @param parser
+ * @param chunk
+ * @return
+ */
+static bool
+ucl_parse_after_value (struct ucl_parser *parser, struct ucl_chunk *chunk)
+{
+	const unsigned char *p;
+	bool got_sep = false;
+	struct ucl_stack *st;
+
+	p = chunk->pos;
+
+	while (p < chunk->end) {
+		if (ucl_test_character (*p, UCL_CHARACTER_WHITESPACE)) {
+			/* Skip whitespaces */
+			ucl_chunk_skipc (chunk, p);
+		}
+		else if (chunk->remain >= 2 && ucl_lex_is_comment (p[0], p[1])) {
+			/* Skip comment */
+			if (!ucl_skip_comments (parser)) {
+				return false;
+			}
+			/* Treat comment as a separator */
+			got_sep = true;
+			p = chunk->pos;
+		}
+		else if (ucl_test_character (*p, UCL_CHARACTER_VALUE_END)) {
+			if (*p == '}' || *p == ']') {
+				if (parser->stack == NULL) {
+					ucl_set_err (chunk, UCL_ESYNTAX, "end of array or object detected without corresponding start", &parser->err);
+					return false;
+				}
+				if ((*p == '}' && parser->stack->obj->type == UCL_OBJECT) ||
+						(*p == ']' && parser->stack->obj->type == UCL_ARRAY)) {
+
+					/* Pop all nested objects from a stack */
+					st = parser->stack;
+					parser->stack = st->next;
+					UCL_FREE (sizeof (struct ucl_stack), st);
+
+					while (parser->stack != NULL) {
+						st = parser->stack;
+						if (st->next == NULL || st->next->level == st->level) {
+							break;
+						}
+						parser->stack = st->next;
+						UCL_FREE (sizeof (struct ucl_stack), st);
+					}
+				}
+				else {
+					ucl_set_err (chunk, UCL_ESYNTAX, "unexpected terminating symbol detected", &parser->err);
+					return false;
+				}
+
+				if (parser->stack == NULL) {
+					/* Ignore everything after a top object */
+					return true;
+				}
+				else {
+					ucl_chunk_skipc (chunk, p);
+				}
+				got_sep = true;
+			}
+			else {
+				/* Got a separator */
+				got_sep = true;
+				ucl_chunk_skipc (chunk, p);
+			}
+		}
+		else {
+			/* Anything else */
+			if (!got_sep) {
+				ucl_set_err (chunk, UCL_ESYNTAX, "delimiter is missing", &parser->err);
+				return false;
+			}
+			return true;
+		}
+	}
+
+	return true;
+}
+
+/**
+ * Handle macro data
+ * @param parser
+ * @param chunk
+ * @return
+ */
+static bool
+ucl_parse_macro_value (struct ucl_parser *parser,
+		struct ucl_chunk *chunk, struct ucl_macro *macro,
+		unsigned char const **macro_start, size_t *macro_len)
+{
+	const unsigned char *p, *c;
+	bool need_unescape = false, ucl_escape = false, var_expand = false;
+
+	p = chunk->pos;
+
+	switch (*p) {
+	case '"':
+		/* We have macro value encoded in quotes */
+		c = p;
+		ucl_chunk_skipc (chunk, p);
+		if (!ucl_lex_json_string (parser, chunk, &need_unescape, &ucl_escape, &var_expand)) {
+			return false;
+		}
+
+		*macro_start = c + 1;
+		*macro_len = chunk->pos - c - 2;
+		p = chunk->pos;
+		break;
+	case '{':
+		/* We got a multiline macro body */
+		ucl_chunk_skipc (chunk, p);
+		/* Skip spaces at the beginning */
+		while (p < chunk->end) {
+			if (ucl_test_character (*p, UCL_CHARACTER_WHITESPACE_UNSAFE)) {
+				ucl_chunk_skipc (chunk, p);
+			}
+			else {
+				break;
+			}
+		}
+		c = p;
+		while (p < chunk->end) {
+			if (*p == '}') {
+				break;
+			}
+			ucl_chunk_skipc (chunk, p);
+		}
+		*macro_start = c;
+		*macro_len = p - c;
+		ucl_chunk_skipc (chunk, p);
+		break;
+	default:
+		/* Macro is not enclosed in quotes or braces */
+		c = p;
+		while (p < chunk->end) {
+			if (ucl_lex_is_atom_end (*p)) {
+				break;
+			}
+			ucl_chunk_skipc (chunk, p);
+		}
+		*macro_start = c;
+		*macro_len = p - c;
+		break;
+	}
+
+	/* We are at the end of a macro */
+	/* Skip ';' and space characters and return to previous state */
+	while (p < chunk->end) {
+		if (!ucl_test_character (*p, UCL_CHARACTER_WHITESPACE_UNSAFE) && *p != ';') {
+			break;
+		}
+		ucl_chunk_skipc (chunk, p);
+	}
+	return true;
+}
+
+/**
+ * Handle the main states of rcl parser
+ * @param parser parser structure
+ * @param data the pointer to the beginning of a chunk
+ * @param len the length of a chunk
+ * @return true if chunk has been parsed and false in case of error
+ */
+static bool
+ucl_state_machine (struct ucl_parser *parser)
+{
+	ucl_object_t *obj;
+	struct ucl_chunk *chunk = parser->chunks;
+	const unsigned char *p, *c = NULL, *macro_start = NULL;
+	unsigned char *macro_escaped;
+	size_t macro_len = 0;
+	struct ucl_macro *macro = NULL;
+	bool next_key = false, end_of_object = false;
+
+	if (parser->top_obj == NULL) {
+		if (*chunk->pos == '[') {
+			obj = ucl_add_parser_stack (NULL, parser, true, 0);
+		}
+		else {
+			obj = ucl_add_parser_stack (NULL, parser, false, 0);
+		}
+		if (obj == NULL) {
+			return false;
+		}
+		parser->top_obj = obj;
+		parser->cur_obj = obj;
+		parser->state = UCL_STATE_INIT;
+	}
+
+	p = chunk->pos;
+	while (chunk->pos < chunk->end) {
+		switch (parser->state) {
+		case UCL_STATE_INIT:
+			/*
+			 * At the init state we can either go to the parse array or object
+			 * if we got [ or { correspondingly or can just treat new data as
+			 * a key of newly created object
+			 */
+			obj = parser->cur_obj;
+			if (!ucl_skip_comments (parser)) {
+				parser->prev_state = parser->state;
+				parser->state = UCL_STATE_ERROR;
+				return false;
+			}
+			else {
+				p = chunk->pos;
+				if (*p == '[') {
+					parser->state = UCL_STATE_VALUE;
+					ucl_chunk_skipc (chunk, p);
+				}
+				else {
+					parser->state = UCL_STATE_KEY;
+					if (*p == '{') {
+						ucl_chunk_skipc (chunk, p);
+					}
+				}
+			}
+			break;
+		case UCL_STATE_KEY:
+			/* Skip any spaces */
+			while (p < chunk->end && ucl_test_character (*p, UCL_CHARACTER_WHITESPACE_UNSAFE)) {
+				ucl_chunk_skipc (chunk, p);
+			}
+			if (*p == '}') {
+				/* We have the end of an object */
+				parser->state = UCL_STATE_AFTER_VALUE;
+				continue;
+			}
+			if (parser->stack == NULL) {
+				/* No objects are on stack, but we want to parse a key */
+				ucl_set_err (chunk, UCL_ESYNTAX, "top object is finished but the parser "
+						"expects a key", &parser->err);
+				parser->prev_state = parser->state;
+				parser->state = UCL_STATE_ERROR;
+				return false;
+			}
+			if (!ucl_parse_key (parser, chunk, &next_key, &end_of_object)) {
+				parser->prev_state = parser->state;
+				parser->state = UCL_STATE_ERROR;
+				return false;
+			}
+			if (end_of_object) {
+				p = chunk->pos;
+				parser->state = UCL_STATE_AFTER_VALUE;
+				continue;
+			}
+			else if (parser->state != UCL_STATE_MACRO_NAME) {
+				if (next_key && parser->stack->obj->type == UCL_OBJECT) {
+					/* Parse more keys and nest objects accordingly */
+					obj = ucl_add_parser_stack (parser->cur_obj, parser, false,
+							parser->stack->level + 1);
+					if (obj == NULL) {
+						return false;
+					}
+				}
+				else {
+					parser->state = UCL_STATE_VALUE;
+				}
+			}
+			else {
+				c = chunk->pos;
+			}
+			p = chunk->pos;
+			break;
+		case UCL_STATE_VALUE:
+			/* We need to check what we do have */
+			if (!ucl_parse_value (parser, chunk)) {
+				parser->prev_state = parser->state;
+				parser->state = UCL_STATE_ERROR;
+				return false;
+			}
+			/* State is set in ucl_parse_value call */
+			p = chunk->pos;
+			break;
+		case UCL_STATE_AFTER_VALUE:
+			if (!ucl_parse_after_value (parser, chunk)) {
+				parser->prev_state = parser->state;
+				parser->state = UCL_STATE_ERROR;
+				return false;
+			}
+			if (parser->stack != NULL) {
+				if (parser->stack->obj->type == UCL_OBJECT) {
+					parser->state = UCL_STATE_KEY;
+				}
+				else {
+					/* Array */
+					parser->state = UCL_STATE_VALUE;
+				}
+			}
+			else {
+				/* Skip everything at the end */
+				return true;
+			}
+			p = chunk->pos;
+			break;
+		case UCL_STATE_MACRO_NAME:
+			if (!ucl_test_character (*p, UCL_CHARACTER_WHITESPACE_UNSAFE)) {
+				ucl_chunk_skipc (chunk, p);
+			}
+			else if (p - c > 0) {
+				/* We got macro name */
+				macro_len = (size_t)(p - c);
+				HASH_FIND (hh, parser->macroes, c, macro_len, macro);
+				if (macro == NULL) {
+					ucl_create_err (&parser->err, "error on line %d at column %d: "
+							"unknown macro: '%.*s', character: '%c'",
+								chunk->line, chunk->column, (int)(p - c), c, *chunk->pos);
+					parser->state = UCL_STATE_ERROR;
+					return false;
+				}
+				/* Now we need to skip all spaces */
+				while (p < chunk->end) {
+					if (!ucl_test_character (*p, UCL_CHARACTER_WHITESPACE_UNSAFE)) {
+						if (chunk->remain >= 2 && ucl_lex_is_comment (p[0], p[1])) {
+							/* Skip comment */
+							if (!ucl_skip_comments (parser)) {
+								return false;
+							}
+							p = chunk->pos;
+						}
+						break;
+					}
+					ucl_chunk_skipc (chunk, p);
+				}
+				parser->state = UCL_STATE_MACRO;
+			}
+			break;
+		case UCL_STATE_MACRO:
+			if (!ucl_parse_macro_value (parser, chunk, macro,
+					&macro_start, &macro_len)) {
+				parser->prev_state = parser->state;
+				parser->state = UCL_STATE_ERROR;
+				return false;
+			}
+			macro_len = ucl_expand_variable (parser, &macro_escaped, macro_start, macro_len);
+			parser->state = parser->prev_state;
+			if (macro_escaped == NULL) {
+				if (!macro->handler (macro_start, macro_len, macro->ud)) {
+					return false;
+				}
+			}
+			else {
+				if (!macro->handler (macro_escaped, macro_len, macro->ud)) {
+					UCL_FREE (macro_len + 1, macro_escaped);
+					return false;
+				}
+				UCL_FREE (macro_len + 1, macro_escaped);
+			}
+			p = chunk->pos;
+			break;
+		default:
+			/* TODO: add all states */
+			ucl_set_err (chunk, UCL_EINTERNAL, "internal error: parser is in an unknown state", &parser->err);
+			parser->state = UCL_STATE_ERROR;
+			return false;
+		}
+	}
+
+	return true;
+}
+
+struct ucl_parser*
+ucl_parser_new (int flags)
+{
+	struct ucl_parser *new;
+
+	new = UCL_ALLOC (sizeof (struct ucl_parser));
+	if (new == NULL) {
+		return NULL;
+	}
+	memset (new, 0, sizeof (struct ucl_parser));
+
+	ucl_parser_register_macro (new, "include", ucl_include_handler, new);
+	ucl_parser_register_macro (new, "try_include", ucl_try_include_handler, new);
+	ucl_parser_register_macro (new, "includes", ucl_includes_handler, new);
+
+	new->flags = flags;
+
+	/* Initial assumption about filevars */
+	ucl_parser_set_filevars (new, NULL, false);
+
+	return new;
+}
+
+
+void
+ucl_parser_register_macro (struct ucl_parser *parser, const char *macro,
+		ucl_macro_handler handler, void* ud)
+{
+	struct ucl_macro *new;
+
+	if (macro == NULL || handler == NULL) {
+		return;
+	}
+	new = UCL_ALLOC (sizeof (struct ucl_macro));
+	if (new == NULL) {
+		return;
+	}
+	memset (new, 0, sizeof (struct ucl_macro));
+	new->handler = handler;
+	new->name = strdup (macro);
+	new->ud = ud;
+	HASH_ADD_KEYPTR (hh, parser->macroes, new->name, strlen (new->name), new);
+}
+
+void
+ucl_parser_register_variable (struct ucl_parser *parser, const char *var,
+		const char *value)
+{
+	struct ucl_variable *new = NULL, *cur;
+
+	if (var == NULL) {
+		return;
+	}
+
+	/* Find whether a variable already exists */
+	LL_FOREACH (parser->variables, cur) {
+		if (strcmp (cur->var, var) == 0) {
+			new = cur;
+			break;
+		}
+	}
+
+	if (value == NULL) {
+
+		if (new != NULL) {
+			/* Remove variable */
+			LL_DELETE (parser->variables, new);
+			free (new->var);
+			free (new->value);
+			UCL_FREE (sizeof (struct ucl_variable), new);
+		}
+		else {
+			/* Do nothing */
+			return;
+		}
+	}
+	else {
+		if (new == NULL) {
+			new = UCL_ALLOC (sizeof (struct ucl_variable));
+			if (new == NULL) {
+				return;
+			}
+			memset (new, 0, sizeof (struct ucl_variable));
+			new->var = strdup (var);
+			new->var_len = strlen (var);
+			new->value = strdup (value);
+			new->value_len = strlen (value);
+
+			LL_PREPEND (parser->variables, new);
+		}
+		else {
+			free (new->value);
+			new->value = strdup (value);
+			new->value_len = strlen (value);
+		}
+	}
+}
+
+void
+ucl_parser_set_variables_handler (struct ucl_parser *parser,
+		ucl_variable_handler handler, void *ud)
+{
+	parser->var_handler = handler;
+	parser->var_data = ud;
+}
+
+bool
+ucl_parser_add_chunk (struct ucl_parser *parser, const unsigned char *data,
+		size_t len)
+{
+	struct ucl_chunk *chunk;
+
+	if (data == NULL || len == 0) {
+		ucl_create_err (&parser->err, "invalid chunk added");
+		return false;
+	}
+	if (parser->state != UCL_STATE_ERROR) {
+		chunk = UCL_ALLOC (sizeof (struct ucl_chunk));
+		if (chunk == NULL) {
+			ucl_create_err (&parser->err, "cannot allocate chunk structure");
+			return false;
+		}
+		chunk->begin = data;
+		chunk->remain = len;
+		chunk->pos = chunk->begin;
+		chunk->end = chunk->begin + len;
+		chunk->line = 1;
+		chunk->column = 0;
+		LL_PREPEND (parser->chunks, chunk);
+		parser->recursion ++;
+		if (parser->recursion > UCL_MAX_RECURSION) {
+			ucl_create_err (&parser->err, "maximum include nesting limit is reached: %d",
+					parser->recursion);
+			return false;
+		}
+		return ucl_state_machine (parser);
+	}
+
+	ucl_create_err (&parser->err, "a parser is in an invalid state");
+
+	return false;
+}
+
+bool
+ucl_parser_add_string (struct ucl_parser *parser, const char *data,
+		size_t len)
+{
+	if (data == NULL) {
+		ucl_create_err (&parser->err, "invalid string added");
+		return false;
+	}
+	if (len == 0) {
+		len = strlen (data);
+	}
+
+	return ucl_parser_add_chunk (parser, (const unsigned char *)data, len);
+}

Added: vendor/libucl/dist/src/ucl_schema.c
===================================================================
--- vendor/libucl/dist/src/ucl_schema.c	                        (rev 0)
+++ vendor/libucl/dist/src/ucl_schema.c	2018-06-19 13:29:00 UTC (rev 11074)
@@ -0,0 +1,1014 @@
+/*
+ * Copyright (c) 2014, Vsevolod Stakhov
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *	 * Redistributions of source code must retain the above copyright
+ *	   notice, this list of conditions and the following disclaimer.
+ *	 * Redistributions in binary form must reproduce the above copyright
+ *	   notice, this list of conditions and the following disclaimer in the
+ *	   documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR ''AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "ucl.h"
+#include "ucl_internal.h"
+#include "tree.h"
+#include "utlist.h"
+#ifdef HAVE_STDARG_H
+#include <stdarg.h>
+#endif
+#ifdef HAVE_STDIO_H
+#include <stdio.h>
+#endif
+#ifdef HAVE_REGEX_H
+#include <regex.h>
+#endif
+#ifdef HAVE_MATH_H
+#include <math.h>
+#endif
+
+static bool ucl_schema_validate (const ucl_object_t *schema,
+		const ucl_object_t *obj, bool try_array,
+		struct ucl_schema_error *err,
+		const ucl_object_t *root);
+
+static bool
+ucl_string_to_type (const char *input, ucl_type_t *res)
+{
+	if (strcasecmp (input, "object") == 0) {
+		*res = UCL_OBJECT;
+	}
+	else if (strcasecmp (input, "array") == 0) {
+		*res = UCL_ARRAY;
+	}
+	else if (strcasecmp (input, "integer") == 0) {
+		*res = UCL_INT;
+	}
+	else if (strcasecmp (input, "number") == 0) {
+		*res = UCL_FLOAT;
+	}
+	else if (strcasecmp (input, "string") == 0) {
+		*res = UCL_STRING;
+	}
+	else if (strcasecmp (input, "boolean") == 0) {
+		*res = UCL_BOOLEAN;
+	}
+	else if (strcasecmp (input, "null") == 0) {
+		*res = UCL_NULL;
+	}
+	else {
+		return false;
+	}
+
+	return true;
+}
+
+static const char *
+ucl_object_type_to_string (ucl_type_t type)
+{
+	const char *res = "unknown";
+
+	switch (type) {
+	case UCL_OBJECT:
+		res = "object";
+		break;
+	case UCL_ARRAY:
+		res = "array";
+		break;
+	case UCL_INT:
+		res = "integer";
+		break;
+	case UCL_FLOAT:
+	case UCL_TIME:
+		res = "number";
+		break;
+	case UCL_STRING:
+		res = "string";
+		break;
+	case UCL_BOOLEAN:
+		res = "boolean";
+		break;
+	case UCL_NULL:
+	case UCL_USERDATA:
+		res = "null";
+		break;
+	}
+
+	return res;
+}
+
+/*
+ * Create validation error
+ */
+static void
+ucl_schema_create_error (struct ucl_schema_error *err,
+		enum ucl_schema_error_code code, const ucl_object_t *obj,
+		const char *fmt, ...)
+{
+	va_list va;
+
+	if (err != NULL) {
+		err->code = code;
+		err->obj = obj;
+		va_start (va, fmt);
+		vsnprintf (err->msg, sizeof (err->msg), fmt, va);
+		va_end (va);
+	}
+}
+
+/*
+ * Check whether we have a pattern specified
+ */
+static const ucl_object_t *
+ucl_schema_test_pattern (const ucl_object_t *obj, const char *pattern)
+{
+	const ucl_object_t *res = NULL;
+#ifdef HAVE_REGEX_H
+	regex_t reg;
+	const ucl_object_t *elt;
+	ucl_object_iter_t iter = NULL;
+
+	if (regcomp (&reg, pattern, REG_EXTENDED | REG_NOSUB) == 0) {
+		while ((elt = ucl_iterate_object (obj, &iter, true)) != NULL) {
+			if (regexec (&reg, ucl_object_key (elt), 0, NULL, 0) == 0) {
+				res = elt;
+				break;
+			}
+		}
+		regfree (&reg);
+	}
+#endif
+	return res;
+}
+
+/*
+ * Check dependencies for an object
+ */
+static bool
+ucl_schema_validate_dependencies (const ucl_object_t *deps,
+		const ucl_object_t *obj, struct ucl_schema_error *err,
+		const ucl_object_t *root)
+{
+	const ucl_object_t *elt, *cur, *cur_dep;
+	ucl_object_iter_t iter = NULL, piter;
+	bool ret = true;
+
+	while (ret && (cur = ucl_iterate_object (deps, &iter, true)) != NULL) {
+		elt = ucl_object_find_key (obj, ucl_object_key (cur));
+		if (elt != NULL) {
+			/* Need to check dependencies */
+			if (cur->type == UCL_ARRAY) {
+				piter = NULL;
+				while (ret && (cur_dep = ucl_iterate_object (cur, &piter, true)) != NULL) {
+					if (ucl_object_find_key (obj, ucl_object_tostring (cur_dep)) == NULL) {
+						ucl_schema_create_error (err, UCL_SCHEMA_MISSING_DEPENDENCY, elt,
+								"dependency %s is missing for key %s",
+								ucl_object_tostring (cur_dep), ucl_object_key (cur));
+						ret = false;
+						break;
+					}
+				}
+			}
+			else if (cur->type == UCL_OBJECT) {
+				ret = ucl_schema_validate (cur, obj, true, err, root);
+			}
+		}
+	}
+
+	return ret;
+}
+
+/*
+ * Validate object
+ */
+static bool
+ucl_schema_validate_object (const ucl_object_t *schema,
+		const ucl_object_t *obj, struct ucl_schema_error *err,
+		const ucl_object_t *root)
+{
+	const ucl_object_t *elt, *prop, *found, *additional_schema = NULL,
+			*required = NULL, *pat, *pelt;
+	ucl_object_iter_t iter = NULL, piter = NULL;
+	bool ret = true, allow_additional = true;
+	int64_t minmax;
+
+	while (ret && (elt = ucl_iterate_object (schema, &iter, true)) != NULL) {
+		if (elt->type == UCL_OBJECT &&
+				strcmp (ucl_object_key (elt), "properties") == 0) {
+			piter = NULL;
+			while (ret && (prop = ucl_iterate_object (elt, &piter, true)) != NULL) {
+				found = ucl_object_find_key (obj, ucl_object_key (prop));
+				if (found) {
+					ret = ucl_schema_validate (prop, found, true, err, root);
+				}
+			}
+		}
+		else if (strcmp (ucl_object_key (elt), "additionalProperties") == 0) {
+			if (elt->type == UCL_BOOLEAN) {
+				if (!ucl_object_toboolean (elt)) {
+					/* Deny additional fields completely */
+					allow_additional = false;
+				}
+			}
+			else if (elt->type == UCL_OBJECT) {
+				/* Define validator for additional fields */
+				additional_schema = elt;
+			}
+			else {
+				ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, elt,
+						"additionalProperties attribute is invalid in schema");
+				ret = false;
+				break;
+			}
+		}
+		else if (strcmp (ucl_object_key (elt), "required") == 0) {
+			if (elt->type == UCL_ARRAY) {
+				required = elt;
+			}
+			else {
+				ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, elt,
+						"required attribute is invalid in schema");
+				ret = false;
+				break;
+			}
+		}
+		else if (strcmp (ucl_object_key (elt), "minProperties") == 0
+				&& ucl_object_toint_safe (elt, &minmax)) {
+			if (obj->len < minmax) {
+				ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
+						"object has not enough properties: %u, minimum is: %u",
+						obj->len, (unsigned)minmax);
+				ret = false;
+				break;
+			}
+		}
+		else if (strcmp (ucl_object_key (elt), "maxProperties") == 0
+				&& ucl_object_toint_safe (elt, &minmax)) {
+			if (obj->len > minmax) {
+				ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
+						"object has too many properties: %u, maximum is: %u",
+						obj->len, (unsigned)minmax);
+				ret = false;
+				break;
+			}
+		}
+		else if (strcmp (ucl_object_key (elt), "patternProperties") == 0) {
+			piter = NULL;
+			while (ret && (prop = ucl_iterate_object (elt, &piter, true)) != NULL) {
+				found = ucl_schema_test_pattern (obj, ucl_object_key (prop));
+				if (found) {
+					ret = ucl_schema_validate (prop, found, true, err, root);
+				}
+			}
+		}
+		else if (elt->type == UCL_OBJECT &&
+				strcmp (ucl_object_key (elt), "dependencies") == 0) {
+			ret = ucl_schema_validate_dependencies (elt, obj, err, root);
+		}
+	}
+
+	if (ret) {
+		/* Additional properties */
+		if (!allow_additional || additional_schema != NULL) {
+			/* Check if we have exactly the same properties in schema and object */
+			iter = NULL;
+			prop = ucl_object_find_key (schema, "properties");
+			while ((elt = ucl_iterate_object (obj, &iter, true)) != NULL) {
+				found = ucl_object_find_key (prop, ucl_object_key (elt));
+				if (found == NULL) {
+					/* Try patternProperties */
+					piter = NULL;
+					pat = ucl_object_find_key (schema, "patternProperties");
+					while ((pelt = ucl_iterate_object (pat, &piter, true)) != NULL) {
+						found = ucl_schema_test_pattern (obj, ucl_object_key (pelt));
+						if (found != NULL) {
+							break;
+						}
+					}
+				}
+				if (found == NULL) {
+					if (!allow_additional) {
+						ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
+								"object has non-allowed property %s",
+								ucl_object_key (elt));
+						ret = false;
+						break;
+					}
+					else if (additional_schema != NULL) {
+						if (!ucl_schema_validate (additional_schema, elt, true, err, root)) {
+							ret = false;
+							break;
+						}
+					}
+				}
+			}
+		}
+		/* Required properties */
+		if (required != NULL) {
+			iter = NULL;
+			while ((elt = ucl_iterate_object (required, &iter, true)) != NULL) {
+				if (ucl_object_find_key (obj, ucl_object_tostring (elt)) == NULL) {
+					ucl_schema_create_error (err, UCL_SCHEMA_MISSING_PROPERTY, obj,
+							"object has missing property %s",
+							ucl_object_tostring (elt));
+					ret = false;
+					break;
+				}
+			}
+		}
+	}
+
+
+	return ret;
+}
+
+static bool
+ucl_schema_validate_number (const ucl_object_t *schema,
+		const ucl_object_t *obj, struct ucl_schema_error *err)
+{
+	const ucl_object_t *elt, *test;
+	ucl_object_iter_t iter = NULL;
+	bool ret = true, exclusive = false;
+	double constraint, val;
+	const double alpha = 1e-16;
+
+	while (ret && (elt = ucl_iterate_object (schema, &iter, true)) != NULL) {
+		if ((elt->type == UCL_FLOAT || elt->type == UCL_INT) &&
+				strcmp (ucl_object_key (elt), "multipleOf") == 0) {
+			constraint = ucl_object_todouble (elt);
+			if (constraint <= 0) {
+				ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, elt,
+						"multipleOf must be greater than zero");
+				ret = false;
+				break;
+			}
+			val = ucl_object_todouble (obj);
+			if (fabs (remainder (val, constraint)) > alpha) {
+				ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
+						"number %.4f is not multiple of %.4f, remainder is %.7f",
+						val, constraint);
+				ret = false;
+				break;
+			}
+		}
+		else if ((elt->type == UCL_FLOAT || elt->type == UCL_INT) &&
+			strcmp (ucl_object_key (elt), "maximum") == 0) {
+			constraint = ucl_object_todouble (elt);
+			test = ucl_object_find_key (schema, "exclusiveMaximum");
+			if (test && test->type == UCL_BOOLEAN) {
+				exclusive = ucl_object_toboolean (test);
+			}
+			val = ucl_object_todouble (obj);
+			if (val > constraint || (exclusive && val >= constraint)) {
+				ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
+						"number is too big: %.3f, maximum is: %.3f",
+						val, constraint);
+				ret = false;
+				break;
+			}
+		}
+		else if ((elt->type == UCL_FLOAT || elt->type == UCL_INT) &&
+				strcmp (ucl_object_key (elt), "minimum") == 0) {
+			constraint = ucl_object_todouble (elt);
+			test = ucl_object_find_key (schema, "exclusiveMinimum");
+			if (test && test->type == UCL_BOOLEAN) {
+				exclusive = ucl_object_toboolean (test);
+			}
+			val = ucl_object_todouble (obj);
+			if (val < constraint || (exclusive && val <= constraint)) {
+				ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
+						"number is too small: %.3f, minimum is: %.3f",
+						val, constraint);
+				ret = false;
+				break;
+			}
+		}
+	}
+
+	return ret;
+}
+
+static bool
+ucl_schema_validate_string (const ucl_object_t *schema,
+		const ucl_object_t *obj, struct ucl_schema_error *err)
+{
+	const ucl_object_t *elt;
+	ucl_object_iter_t iter = NULL;
+	bool ret = true;
+	int64_t constraint;
+#ifdef HAVE_REGEX_H
+	regex_t re;
+#endif
+
+	while (ret && (elt = ucl_iterate_object (schema, &iter, true)) != NULL) {
+		if (elt->type == UCL_INT &&
+			strcmp (ucl_object_key (elt), "maxLength") == 0) {
+			constraint = ucl_object_toint (elt);
+			if (obj->len > constraint) {
+				ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
+						"string is too big: %.3f, maximum is: %.3f",
+						obj->len, constraint);
+				ret = false;
+				break;
+			}
+		}
+		else if (elt->type == UCL_INT &&
+				strcmp (ucl_object_key (elt), "minLength") == 0) {
+			constraint = ucl_object_toint (elt);
+			if (obj->len < constraint) {
+				ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
+						"string is too short: %.3f, minimum is: %.3f",
+						obj->len, constraint);
+				ret = false;
+				break;
+			}
+		}
+#ifdef HAVE_REGEX_H
+		else if (elt->type == UCL_STRING &&
+				strcmp (ucl_object_key (elt), "pattern") == 0) {
+			if (regcomp (&re, ucl_object_tostring (elt),
+					REG_EXTENDED | REG_NOSUB) != 0) {
+				ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, elt,
+						"cannot compile pattern %s", ucl_object_tostring (elt));
+				ret = false;
+				break;
+			}
+			if (regexec (&re, ucl_object_tostring (obj), 0, NULL, 0) != 0) {
+				ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
+						"string doesn't match regexp %s",
+						ucl_object_tostring (elt));
+				ret = false;
+			}
+			regfree (&re);
+		}
+#endif
+	}
+
+	return ret;
+}
+
+struct ucl_compare_node {
+	const ucl_object_t *obj;
+	TREE_ENTRY(ucl_compare_node) link;
+	struct ucl_compare_node *next;
+};
+
+typedef TREE_HEAD(_tree, ucl_compare_node) ucl_compare_tree_t;
+
+TREE_DEFINE(ucl_compare_node, link)
+
+static int
+ucl_schema_elt_compare (struct ucl_compare_node *n1, struct ucl_compare_node *n2)
+{
+	const ucl_object_t *o1 = n1->obj, *o2 = n2->obj;
+
+	return ucl_object_compare (o1, o2);
+}
+
+static bool
+ucl_schema_array_is_unique (const ucl_object_t *obj, struct ucl_schema_error *err)
+{
+	ucl_compare_tree_t tree = TREE_INITIALIZER (ucl_schema_elt_compare);
+	ucl_object_iter_t iter = NULL;
+	const ucl_object_t *elt;
+	struct ucl_compare_node *node, test, *nodes = NULL, *tmp;
+	bool ret = true;
+
+	while ((elt = ucl_iterate_object (obj, &iter, true)) != NULL) {
+		test.obj = elt;
+		node = TREE_FIND (&tree, ucl_compare_node, link, &test);
+		if (node != NULL) {
+			ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, elt,
+					"duplicate values detected while uniqueItems is true");
+			ret = false;
+			break;
+		}
+		node = calloc (1, sizeof (*node));
+		if (node == NULL) {
+			ucl_schema_create_error (err, UCL_SCHEMA_UNKNOWN, elt,
+					"cannot allocate tree node");
+			ret = false;
+			break;
+		}
+		node->obj = elt;
+		TREE_INSERT (&tree, ucl_compare_node, link, node);
+		LL_PREPEND (nodes, node);
+	}
+
+	LL_FOREACH_SAFE (nodes, node, tmp) {
+		free (node);
+	}
+
+	return ret;
+}
+
+static bool
+ucl_schema_validate_array (const ucl_object_t *schema,
+		const ucl_object_t *obj, struct ucl_schema_error *err,
+		const ucl_object_t *root)
+{
+	const ucl_object_t *elt, *it, *found, *additional_schema = NULL,
+			*first_unvalidated = NULL;
+	ucl_object_iter_t iter = NULL, piter = NULL;
+	bool ret = true, allow_additional = true, need_unique = false;
+	int64_t minmax;
+
+	while (ret && (elt = ucl_iterate_object (schema, &iter, true)) != NULL) {
+		if (strcmp (ucl_object_key (elt), "items") == 0) {
+			if (elt->type == UCL_ARRAY) {
+				found = obj->value.av;
+				while (ret && (it = ucl_iterate_object (elt, &piter, true)) != NULL) {
+					if (found) {
+						ret = ucl_schema_validate (it, found, false, err, root);
+						found = found->next;
+					}
+				}
+				if (found != NULL) {
+					/* The first element that is not validated */
+					first_unvalidated = found;
+				}
+			}
+			else if (elt->type == UCL_OBJECT) {
+				/* Validate all items using the specified schema */
+				while (ret && (it = ucl_iterate_object (obj, &piter, true)) != NULL) {
+					ret = ucl_schema_validate (elt, it, false, err, root);
+				}
+			}
+			else {
+				ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, elt,
+						"items attribute is invalid in schema");
+				ret = false;
+				break;
+			}
+		}
+		else if (strcmp (ucl_object_key (elt), "additionalItems") == 0) {
+			if (elt->type == UCL_BOOLEAN) {
+				if (!ucl_object_toboolean (elt)) {
+					/* Deny additional fields completely */
+					allow_additional = false;
+				}
+			}
+			else if (elt->type == UCL_OBJECT) {
+				/* Define validator for additional fields */
+				additional_schema = elt;
+			}
+			else {
+				ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, elt,
+						"additionalItems attribute is invalid in schema");
+				ret = false;
+				break;
+			}
+		}
+		else if (elt->type == UCL_BOOLEAN &&
+				strcmp (ucl_object_key (elt), "uniqueItems") == 0) {
+			need_unique = ucl_object_toboolean (elt);
+		}
+		else if (strcmp (ucl_object_key (elt), "minItems") == 0
+				&& ucl_object_toint_safe (elt, &minmax)) {
+			if (obj->len < minmax) {
+				ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
+						"array has not enough items: %u, minimum is: %u",
+						obj->len, (unsigned)minmax);
+				ret = false;
+				break;
+			}
+		}
+		else if (strcmp (ucl_object_key (elt), "maxItems") == 0
+				&& ucl_object_toint_safe (elt, &minmax)) {
+			if (obj->len > minmax) {
+				ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
+						"array has too many items: %u, maximum is: %u",
+						obj->len, (unsigned)minmax);
+				ret = false;
+				break;
+			}
+		}
+	}
+
+	if (ret) {
+		/* Additional properties */
+		if (!allow_additional || additional_schema != NULL) {
+			if (first_unvalidated != NULL) {
+				if (!allow_additional) {
+					ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
+							"array has undefined item");
+					ret = false;
+				}
+				else if (additional_schema != NULL) {
+					elt = first_unvalidated;
+					while (elt) {
+						if (!ucl_schema_validate (additional_schema, elt, false,
+								err, root)) {
+							ret = false;
+							break;
+						}
+						elt = elt->next;
+					}
+				}
+			}
+		}
+		/* Required properties */
+		if (ret && need_unique) {
+			ret = ucl_schema_array_is_unique (obj, err);
+		}
+	}
+
+	return ret;
+}
+
+/*
+ * Returns whether this object is allowed for this type
+ */
+static bool
+ucl_schema_type_is_allowed (const ucl_object_t *type, const ucl_object_t *obj,
+		struct ucl_schema_error *err)
+{
+	ucl_object_iter_t iter = NULL;
+	const ucl_object_t *elt;
+	const char *type_str;
+	ucl_type_t t;
+
+	if (type == NULL) {
+		/* Any type is allowed */
+		return true;
+	}
+
+	if (type->type == UCL_ARRAY) {
+		/* One of allowed types */
+		while ((elt = ucl_iterate_object (type, &iter, true)) != NULL) {
+			if (ucl_schema_type_is_allowed (elt, obj, err)) {
+				return true;
+			}
+		}
+	}
+	else if (type->type == UCL_STRING) {
+		type_str = ucl_object_tostring (type);
+		if (!ucl_string_to_type (type_str, &t)) {
+			ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, type,
+					"Type attribute is invalid in schema");
+			return false;
+		}
+		if (obj->type != t) {
+			/* Some types are actually compatible */
+			if (obj->type == UCL_TIME && t == UCL_FLOAT) {
+				return true;
+			}
+			else if (obj->type == UCL_INT && t == UCL_FLOAT) {
+				return true;
+			}
+			else {
+				ucl_schema_create_error (err, UCL_SCHEMA_TYPE_MISMATCH, obj,
+						"Invalid type of %s, expected %s",
+						ucl_object_type_to_string (obj->type),
+						ucl_object_type_to_string (t));
+			}
+		}
+		else {
+			/* Types are equal */
+			return true;
+		}
+	}
+
+	return false;
+}
+
+/*
+ * Check if object is equal to one of elements of enum
+ */
+static bool
+ucl_schema_validate_enum (const ucl_object_t *en, const ucl_object_t *obj,
+		struct ucl_schema_error *err)
+{
+	ucl_object_iter_t iter = NULL;
+	const ucl_object_t *elt;
+	bool ret = false;
+
+	while ((elt = ucl_iterate_object (en, &iter, true)) != NULL) {
+		if (ucl_object_compare (elt, obj) == 0) {
+			ret = true;
+			break;
+		}
+	}
+
+	if (!ret) {
+		ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
+				"object is not one of enumerated patterns");
+	}
+
+	return ret;
+}
+
+
+/*
+ * Check a single ref component
+ */
+static const ucl_object_t *
+ucl_schema_resolve_ref_component (const ucl_object_t *cur,
+		const char *refc, int len,
+		struct ucl_schema_error *err)
+{
+	const ucl_object_t *res = NULL;
+	char *err_str;
+	int num, i;
+
+	if (cur->type == UCL_OBJECT) {
+		/* Find a key inside an object */
+		res = ucl_object_find_keyl (cur, refc, len);
+		if (res == NULL) {
+			ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, cur,
+					"reference %s is invalid, missing path component", refc);
+			return NULL;
+		}
+	}
+	else if (cur->type == UCL_ARRAY) {
+		/* We must figure out a number inside array */
+		num = strtoul (refc, &err_str, 10);
+		if (err_str != NULL && *err_str != '/' && *err_str != '\0') {
+			ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, cur,
+					"reference %s is invalid, invalid item number", refc);
+			return NULL;
+		}
+		res = cur->value.av;
+		i = 0;
+		while (res != NULL) {
+			if (i == num) {
+				break;
+			}
+			res = res->next;
+		}
+		if (res == NULL) {
+			ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, cur,
+					"reference %s is invalid, item number %d does not exist",
+					refc, num);
+			return NULL;
+		}
+	}
+	else {
+		ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, res,
+				"reference %s is invalid, contains primitive object in the path",
+				refc);
+		return NULL;
+	}
+
+	return res;
+}
+/*
+ * Find reference schema
+ */
+static const ucl_object_t *
+ucl_schema_resolve_ref (const ucl_object_t *root, const char *ref,
+		struct ucl_schema_error *err)
+{
+	const char *p, *c;
+	const ucl_object_t *res = NULL;
+
+
+	if (ref[0] != '#') {
+		ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, root,
+				"reference %s is invalid, not started with #", ref);
+		return NULL;
+	}
+	if (ref[1] == '/') {
+		p = &ref[2];
+	}
+	else if (ref[1] == '\0') {
+		return root;
+	}
+	else {
+		ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, root,
+				"reference %s is invalid, not started with #/", ref);
+		return NULL;
+	}
+
+	c = p;
+	res = root;
+
+	while (*p != '\0') {
+		if (*p == '/') {
+			if (p - c == 0) {
+				ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, res,
+						"reference %s is invalid, empty path component", ref);
+				return NULL;
+			}
+			/* Now we have some url part, so we need to figure out where we are */
+			res = ucl_schema_resolve_ref_component (res, c, p - c, err);
+			if (res == NULL) {
+				return NULL;
+			}
+			c = p + 1;
+		}
+		p ++;
+	}
+
+	if (p - c != 0) {
+		res = ucl_schema_resolve_ref_component (res, c, p - c, err);
+	}
+
+	if (res == NULL || res->type != UCL_OBJECT) {
+		ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, res,
+				"reference %s is invalid, cannot find specified object",
+				ref);
+		return NULL;
+	}
+
+	return res;
+}
+
+static bool
+ucl_schema_validate_values (const ucl_object_t *schema, const ucl_object_t *obj,
+		struct ucl_schema_error *err)
+{
+	const ucl_object_t *elt, *cur;
+	int64_t constraint, i;
+
+	elt = ucl_object_find_key (schema, "maxValues");
+	if (elt != NULL && elt->type == UCL_INT) {
+		constraint = ucl_object_toint (elt);
+		cur = obj;
+		i = 0;
+		while (cur) {
+			if (i > constraint) {
+				ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
+					"object has more values than defined: %ld",
+					(long int)constraint);
+				return false;
+			}
+			i ++;
+			cur = cur->next;
+		}
+	}
+	elt = ucl_object_find_key (schema, "minValues");
+	if (elt != NULL && elt->type == UCL_INT) {
+		constraint = ucl_object_toint (elt);
+		cur = obj;
+		i = 0;
+		while (cur) {
+			if (i >= constraint) {
+				break;
+			}
+			i ++;
+			cur = cur->next;
+		}
+		if (i < constraint) {
+			ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
+					"object has less values than defined: %ld",
+					(long int)constraint);
+			return false;
+		}
+	}
+
+	return true;
+}
+
+static bool
+ucl_schema_validate (const ucl_object_t *schema,
+		const ucl_object_t *obj, bool try_array,
+		struct ucl_schema_error *err,
+		const ucl_object_t *root)
+{
+	const ucl_object_t *elt, *cur;
+	ucl_object_iter_t iter = NULL;
+	bool ret;
+
+	if (schema->type != UCL_OBJECT) {
+		ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, schema,
+				"schema is %s instead of object", ucl_object_type_to_string (schema->type));
+		return false;
+	}
+
+	if (try_array) {
+		/*
+		 * Special case for multiple values
+		 */
+		if (!ucl_schema_validate_values (schema, obj, err)) {
+			return false;
+		}
+		LL_FOREACH (obj, cur) {
+			if (!ucl_schema_validate (schema, cur, false, err, root)) {
+				return false;
+			}
+		}
+		return true;
+	}
+
+	elt = ucl_object_find_key (schema, "enum");
+	if (elt != NULL && elt->type == UCL_ARRAY) {
+		if (!ucl_schema_validate_enum (elt, obj, err)) {
+			return false;
+		}
+	}
+
+	elt = ucl_object_find_key (schema, "allOf");
+	if (elt != NULL && elt->type == UCL_ARRAY) {
+		iter = NULL;
+		while ((cur = ucl_iterate_object (elt, &iter, true)) != NULL) {
+			ret = ucl_schema_validate (cur, obj, true, err, root);
+			if (!ret) {
+				return false;
+			}
+		}
+	}
+
+	elt = ucl_object_find_key (schema, "anyOf");
+	if (elt != NULL && elt->type == UCL_ARRAY) {
+		iter = NULL;
+		while ((cur = ucl_iterate_object (elt, &iter, true)) != NULL) {
+			ret = ucl_schema_validate (cur, obj, true, err, root);
+			if (ret) {
+				break;
+			}
+		}
+		if (!ret) {
+			return false;
+		}
+		else {
+			/* Reset error */
+			err->code = UCL_SCHEMA_OK;
+		}
+	}
+
+	elt = ucl_object_find_key (schema, "oneOf");
+	if (elt != NULL && elt->type == UCL_ARRAY) {
+		iter = NULL;
+		ret = false;
+		while ((cur = ucl_iterate_object (elt, &iter, true)) != NULL) {
+			if (!ret) {
+				ret = ucl_schema_validate (cur, obj, true, err, root);
+			}
+			else if (ucl_schema_validate (cur, obj, true, err, root)) {
+				ret = false;
+				break;
+			}
+		}
+		if (!ret) {
+			return false;
+		}
+	}
+
+	elt = ucl_object_find_key (schema, "not");
+	if (elt != NULL && elt->type == UCL_OBJECT) {
+		if (ucl_schema_validate (elt, obj, true, err, root)) {
+			return false;
+		}
+		else {
+			/* Reset error */
+			err->code = UCL_SCHEMA_OK;
+		}
+	}
+
+	elt = ucl_object_find_key (schema, "$ref");
+	if (elt != NULL) {
+		cur = ucl_schema_resolve_ref (root, ucl_object_tostring (elt), err);
+		if (cur == NULL) {
+			return false;
+		}
+		if (!ucl_schema_validate (cur, obj, try_array, err, root)) {
+			return false;
+		}
+	}
+
+	elt = ucl_object_find_key (schema, "type");
+	if (!ucl_schema_type_is_allowed (elt, obj, err)) {
+		return false;
+	}
+
+	switch (obj->type) {
+	case UCL_OBJECT:
+		return ucl_schema_validate_object (schema, obj, err, root);
+		break;
+	case UCL_ARRAY:
+		return ucl_schema_validate_array (schema, obj, err, root);
+		break;
+	case UCL_INT:
+	case UCL_FLOAT:
+		return ucl_schema_validate_number (schema, obj, err);
+		break;
+	case UCL_STRING:
+		return ucl_schema_validate_string (schema, obj, err);
+		break;
+	default:
+		break;
+	}
+
+	return true;
+}
+
+bool
+ucl_object_validate (const ucl_object_t *schema,
+		const ucl_object_t *obj, struct ucl_schema_error *err)
+{
+	return ucl_schema_validate (schema, obj, true, err, schema);
+}

Added: vendor/libucl/dist/src/ucl_util.c
===================================================================
--- vendor/libucl/dist/src/ucl_util.c	                        (rev 0)
+++ vendor/libucl/dist/src/ucl_util.c	2018-06-19 13:29:00 UTC (rev 11074)
@@ -0,0 +1,1958 @@
+/* Copyright (c) 2013, Vsevolod Stakhov
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *       * Redistributions of source code must retain the above copyright
+ *         notice, this list of conditions and the following disclaimer.
+ *       * Redistributions in binary form must reproduce the above copyright
+ *         notice, this list of conditions and the following disclaimer in the
+ *         documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "ucl.h"
+#include "ucl_internal.h"
+#include "ucl_chartable.h"
+
+#ifdef HAVE_LIBGEN_H
+#include <libgen.h> /* For dirname */
+#endif
+
+#ifdef HAVE_OPENSSL
+#include <openssl/err.h>
+#include <openssl/sha.h>
+#include <openssl/rsa.h>
+#include <openssl/ssl.h>
+#include <openssl/evp.h>
+#endif
+
+#ifdef CURL_FOUND
+#include <curl/curl.h>
+#endif
+#ifdef HAVE_FETCH_H
+#include <fetch.h>
+#endif
+
+#ifdef _WIN32
+#include <windows.h>
+
+#ifndef PROT_READ
+#define PROT_READ       1
+#endif
+#ifndef PROT_WRITE
+#define PROT_WRITE      2
+#endif
+#ifndef PROT_READWRITE
+#define PROT_READWRITE  3
+#endif
+#ifndef MAP_SHARED
+#define MAP_SHARED      1
+#endif
+#ifndef MAP_PRIVATE
+#define MAP_PRIVATE     2
+#endif
+#ifndef MAP_FAILED
+#define MAP_FAILED      ((void *) -1)
+#endif
+
+static void *ucl_mmap(char *addr, size_t length, int prot, int access, int fd, off_t offset)
+{
+	void *map = NULL;
+	HANDLE handle = INVALID_HANDLE_VALUE;
+
+	switch (prot) {
+	default:
+	case PROT_READ:
+		{
+			handle = CreateFileMapping((HANDLE) _get_osfhandle(fd), 0, PAGE_READONLY, 0, length, 0);
+			if (!handle) break;
+			map = (void *) MapViewOfFile(handle, FILE_MAP_READ, 0, 0, length);
+			CloseHandle(handle);
+			break;
+		}
+	case PROT_WRITE:
+		{
+			handle = CreateFileMapping((HANDLE) _get_osfhandle(fd), 0, PAGE_READWRITE, 0, length, 0);
+			if (!handle) break;
+			map = (void *) MapViewOfFile(handle, FILE_MAP_WRITE, 0, 0, length);
+			CloseHandle(handle);
+			break;
+		}
+	case PROT_READWRITE:
+		{
+			handle = CreateFileMapping((HANDLE) _get_osfhandle(fd), 0, PAGE_READWRITE, 0, length, 0);
+			if (!handle) break;
+			map = (void *) MapViewOfFile(handle, FILE_MAP_ALL_ACCESS, 0, 0, length);
+			CloseHandle(handle);
+			break;
+		}
+	}
+	if (map == (void *) NULL) {
+		return (void *) MAP_FAILED;
+	}
+	return (void *) ((char *) map + offset);
+}
+
+static int ucl_munmap(void *map,size_t length)
+{
+	if (!UnmapViewOfFile(map)) {
+		return(-1);
+	}
+	return(0);
+}
+
+static char* ucl_realpath(const char *path, char *resolved_path) {
+    char *p;
+    char tmp[MAX_PATH + 1];
+    strncpy(tmp, path, sizeof(tmp)-1);
+    p = tmp;
+    while(*p) {
+        if (*p == '/') *p = '\\';
+        p++;
+    }
+    return _fullpath(resolved_path, tmp, MAX_PATH);
+}
+#else
+#define ucl_mmap mmap
+#define ucl_munmap munmap
+#define ucl_realpath realpath
+#endif
+
+/**
+ * @file rcl_util.c
+ * Utilities for rcl parsing
+ */
+
+typedef void (*ucl_object_dtor) (ucl_object_t *obj);
+static void ucl_object_free_internal (ucl_object_t *obj, bool allow_rec,
+		ucl_object_dtor dtor);
+static void ucl_object_dtor_unref (ucl_object_t *obj);
+
+static void
+ucl_object_dtor_free (ucl_object_t *obj)
+{
+	if (obj->trash_stack[UCL_TRASH_KEY] != NULL) {
+		UCL_FREE (obj->hh.keylen, obj->trash_stack[UCL_TRASH_KEY]);
+	}
+	if (obj->trash_stack[UCL_TRASH_VALUE] != NULL) {
+		UCL_FREE (obj->len, obj->trash_stack[UCL_TRASH_VALUE]);
+	}
+	UCL_FREE (sizeof (ucl_object_t), obj);
+}
+
+/*
+ * This is a helper function that performs exactly the same as
+ * `ucl_object_unref` but it doesn't iterate over elements allowing
+ * to use it for individual elements of arrays and multiple values
+ */
+static void
+ucl_object_dtor_unref_single (ucl_object_t *obj)
+{
+	if (obj != NULL) {
+#ifdef HAVE_ATOMIC_BUILTINS
+		unsigned int rc = __sync_sub_and_fetch (&obj->ref, 1);
+		if (rc == 0) {
+#else
+		if (--obj->ref == 0) {
+#endif
+			ucl_object_free_internal (obj, false, ucl_object_dtor_unref);
+		}
+	}
+}
+
+static void
+ucl_object_dtor_unref (ucl_object_t *obj)
+{
+	if (obj->ref == 0) {
+		ucl_object_dtor_free (obj);
+	}
+	else {
+		/* This may cause dtor unref being called one more time */
+		ucl_object_dtor_unref_single (obj);
+	}
+}
+
+static void
+ucl_object_free_internal (ucl_object_t *obj, bool allow_rec, ucl_object_dtor dtor)
+{
+	ucl_object_t *sub, *tmp;
+
+	while (obj != NULL) {
+		if (obj->type == UCL_ARRAY) {
+			sub = obj->value.av;
+			while (sub != NULL) {
+				tmp = sub->next;
+				dtor (sub);
+				sub = tmp;
+			}
+		}
+		else if (obj->type == UCL_OBJECT) {
+			if (obj->value.ov != NULL) {
+				ucl_hash_destroy (obj->value.ov, (ucl_hash_free_func *)dtor);
+			}
+		}
+		tmp = obj->next;
+		dtor (obj);
+		obj = tmp;
+
+		if (!allow_rec) {
+			break;
+		}
+	}
+}
+
+void
+ucl_object_free (ucl_object_t *obj)
+{
+	ucl_object_free_internal (obj, true, ucl_object_dtor_free);
+}
+
+size_t
+ucl_unescape_json_string (char *str, size_t len)
+{
+	char *t = str, *h = str;
+	int i, uval;
+
+	if (len <= 1) {
+		return len;
+	}
+	/* t is target (tortoise), h is source (hare) */
+
+	while (len) {
+		if (*h == '\\') {
+			h ++;
+			switch (*h) {
+			case 'n':
+				*t++ = '\n';
+				break;
+			case 'r':
+				*t++ = '\r';
+				break;
+			case 'b':
+				*t++ = '\b';
+				break;
+			case 't':
+				*t++ = '\t';
+				break;
+			case 'f':
+				*t++ = '\f';
+				break;
+			case '\\':
+				*t++ = '\\';
+				break;
+			case '"':
+				*t++ = '"';
+				break;
+			case 'u':
+				/* Unicode escape */
+				uval = 0;
+				if (len > 3) {
+					for (i = 0; i < 4; i++) {
+						uval <<= 4;
+						if (isdigit (h[i])) {
+							uval += h[i] - '0';
+						}
+						else if (h[i] >= 'a' && h[i] <= 'f') {
+							uval += h[i] - 'a' + 10;
+						}
+						else if (h[i] >= 'A' && h[i] <= 'F') {
+							uval += h[i] - 'A' + 10;
+						}
+						else {
+							break;
+						}
+					}
+					h += 3;
+					len -= 3;
+					/* Encode */
+					if(uval < 0x80) {
+						t[0] = (char)uval;
+						t ++;
+					}
+					else if(uval < 0x800) {
+						t[0] = 0xC0 + ((uval & 0x7C0) >> 6);
+						t[1] = 0x80 + ((uval & 0x03F));
+						t += 2;
+					}
+					else if(uval < 0x10000) {
+						t[0] = 0xE0 + ((uval & 0xF000) >> 12);
+						t[1] = 0x80 + ((uval & 0x0FC0) >> 6);
+						t[2] = 0x80 + ((uval & 0x003F));
+						t += 3;
+					}
+					else if(uval <= 0x10FFFF) {
+						t[0] = 0xF0 + ((uval & 0x1C0000) >> 18);
+						t[1] = 0x80 + ((uval & 0x03F000) >> 12);
+						t[2] = 0x80 + ((uval & 0x000FC0) >> 6);
+						t[3] = 0x80 + ((uval & 0x00003F));
+						t += 4;
+					}
+					else {
+						*t++ = '?';
+					}
+				}
+				else {
+					*t++ = 'u';
+				}
+				break;
+			default:
+				*t++ = *h;
+				break;
+			}
+			h ++;
+			len --;
+		}
+		else {
+			*t++ = *h++;
+		}
+		len --;
+	}
+	*t = '\0';
+
+	return (t - str);
+}
+
+char *
+ucl_copy_key_trash (const ucl_object_t *obj)
+{
+	ucl_object_t *deconst;
+
+	if (obj == NULL) {
+		return NULL;
+	}
+	if (obj->trash_stack[UCL_TRASH_KEY] == NULL && obj->key != NULL) {
+		deconst = __DECONST (ucl_object_t *, obj);
+		deconst->trash_stack[UCL_TRASH_KEY] = malloc (obj->keylen + 1);
+		if (deconst->trash_stack[UCL_TRASH_KEY] != NULL) {
+			memcpy (deconst->trash_stack[UCL_TRASH_KEY], obj->key, obj->keylen);
+			deconst->trash_stack[UCL_TRASH_KEY][obj->keylen] = '\0';
+		}
+		deconst->key = obj->trash_stack[UCL_TRASH_KEY];
+		deconst->flags |= UCL_OBJECT_ALLOCATED_KEY;
+	}
+
+	return obj->trash_stack[UCL_TRASH_KEY];
+}
+
+char *
+ucl_copy_value_trash (const ucl_object_t *obj)
+{
+	ucl_object_t *deconst;
+
+	if (obj == NULL) {
+		return NULL;
+	}
+	if (obj->trash_stack[UCL_TRASH_VALUE] == NULL) {
+		deconst = __DECONST (ucl_object_t *, obj);
+		if (obj->type == UCL_STRING) {
+
+			/* Special case for strings */
+			deconst->trash_stack[UCL_TRASH_VALUE] = malloc (obj->len + 1);
+			if (deconst->trash_stack[UCL_TRASH_VALUE] != NULL) {
+				memcpy (deconst->trash_stack[UCL_TRASH_VALUE], obj->value.sv, obj->len);
+				deconst->trash_stack[UCL_TRASH_VALUE][obj->len] = '\0';
+				deconst->value.sv = obj->trash_stack[UCL_TRASH_VALUE];
+			}
+		}
+		else {
+			/* Just emit value in json notation */
+			deconst->trash_stack[UCL_TRASH_VALUE] = ucl_object_emit_single_json (obj);
+			deconst->len = strlen (obj->trash_stack[UCL_TRASH_VALUE]);
+		}
+		deconst->flags |= UCL_OBJECT_ALLOCATED_VALUE;
+	}
+	return obj->trash_stack[UCL_TRASH_VALUE];
+}
+
+UCL_EXTERN ucl_object_t*
+ucl_parser_get_object (struct ucl_parser *parser)
+{
+	if (parser->state != UCL_STATE_ERROR && parser->top_obj != NULL) {
+		return ucl_object_ref (parser->top_obj);
+	}
+
+	return NULL;
+}
+
+UCL_EXTERN void
+ucl_parser_free (struct ucl_parser *parser)
+{
+	struct ucl_stack *stack, *stmp;
+	struct ucl_macro *macro, *mtmp;
+	struct ucl_chunk *chunk, *ctmp;
+	struct ucl_pubkey *key, *ktmp;
+	struct ucl_variable *var, *vtmp;
+
+	if (parser == NULL) {
+		return;
+	}
+
+	if (parser->top_obj != NULL) {
+		ucl_object_unref (parser->top_obj);
+	}
+
+	LL_FOREACH_SAFE (parser->stack, stack, stmp) {
+		free (stack);
+	}
+	HASH_ITER (hh, parser->macroes, macro, mtmp) {
+		free (macro->name);
+		HASH_DEL (parser->macroes, macro);
+		UCL_FREE (sizeof (struct ucl_macro), macro);
+	}
+	LL_FOREACH_SAFE (parser->chunks, chunk, ctmp) {
+		UCL_FREE (sizeof (struct ucl_chunk), chunk);
+	}
+	LL_FOREACH_SAFE (parser->keys, key, ktmp) {
+		UCL_FREE (sizeof (struct ucl_pubkey), key);
+	}
+	LL_FOREACH_SAFE (parser->variables, var, vtmp) {
+		free (var->value);
+		free (var->var);
+		UCL_FREE (sizeof (struct ucl_variable), var);
+	}
+
+	if (parser->err != NULL) {
+		utstring_free(parser->err);
+	}
+
+	UCL_FREE (sizeof (struct ucl_parser), parser);
+}
+
+UCL_EXTERN const char *
+ucl_parser_get_error(struct ucl_parser *parser)
+{
+	if (parser == NULL) {
+		return NULL;
+	}
+
+	if (parser->err == NULL)
+		return NULL;
+
+	return utstring_body(parser->err);
+}
+
+UCL_EXTERN bool
+ucl_pubkey_add (struct ucl_parser *parser, const unsigned char *key, size_t len)
+{
+#ifndef HAVE_OPENSSL
+	ucl_create_err (&parser->err, "cannot check signatures without openssl");
+	return false;
+#else
+# if (OPENSSL_VERSION_NUMBER < 0x10000000L)
+	ucl_create_err (&parser->err, "cannot check signatures, openssl version is unsupported");
+	return EXIT_FAILURE;
+# else
+	struct ucl_pubkey *nkey;
+	BIO *mem;
+
+	mem = BIO_new_mem_buf ((void *)key, len);
+	nkey = UCL_ALLOC (sizeof (struct ucl_pubkey));
+	if (nkey == NULL) {
+		ucl_create_err (&parser->err, "cannot allocate memory for key");
+		return false;
+	}
+	nkey->key = PEM_read_bio_PUBKEY (mem, &nkey->key, NULL, NULL);
+	BIO_free (mem);
+	if (nkey->key == NULL) {
+		UCL_FREE (sizeof (struct ucl_pubkey), nkey);
+		ucl_create_err (&parser->err, "%s",
+				ERR_error_string (ERR_get_error (), NULL));
+		return false;
+	}
+	LL_PREPEND (parser->keys, nkey);
+# endif
+#endif
+	return true;
+}
+
+#ifdef CURL_FOUND
+struct ucl_curl_cbdata {
+	unsigned char *buf;
+	size_t buflen;
+};
+
+static size_t
+ucl_curl_write_callback (void* contents, size_t size, size_t nmemb, void* ud)
+{
+	struct ucl_curl_cbdata *cbdata = ud;
+	size_t realsize = size * nmemb;
+
+	cbdata->buf = realloc (cbdata->buf, cbdata->buflen + realsize + 1);
+	if (cbdata->buf == NULL) {
+		return 0;
+	}
+
+	memcpy (&(cbdata->buf[cbdata->buflen]), contents, realsize);
+	cbdata->buflen += realsize;
+	cbdata->buf[cbdata->buflen] = 0;
+
+	return realsize;
+}
+#endif
+
+/**
+ * Fetch a url and save results to the memory buffer
+ * @param url url to fetch
+ * @param len length of url
+ * @param buf target buffer
+ * @param buflen target length
+ * @return
+ */
+static bool
+ucl_fetch_url (const unsigned char *url, unsigned char **buf, size_t *buflen,
+		UT_string **err, bool must_exist)
+{
+
+#ifdef HAVE_FETCH_H
+	struct url *fetch_url;
+	struct url_stat us;
+	FILE *in;
+
+	fetch_url = fetchParseURL (url);
+	if (fetch_url == NULL) {
+		ucl_create_err (err, "invalid URL %s: %s",
+				url, strerror (errno));
+		return false;
+	}
+	if ((in = fetchXGet (fetch_url, &us, "")) == NULL) {
+		if (!must_exist) {
+			ucl_create_err (err, "cannot fetch URL %s: %s",
+				url, strerror (errno));
+		}
+		fetchFreeURL (fetch_url);
+		return false;
+	}
+
+	*buflen = us.size;
+	*buf = malloc (*buflen);
+	if (*buf == NULL) {
+		ucl_create_err (err, "cannot allocate buffer for URL %s: %s",
+				url, strerror (errno));
+		fclose (in);
+		fetchFreeURL (fetch_url);
+		return false;
+	}
+
+	if (fread (*buf, *buflen, 1, in) != 1) {
+		ucl_create_err (err, "cannot read URL %s: %s",
+				url, strerror (errno));
+		fclose (in);
+		fetchFreeURL (fetch_url);
+		return false;
+	}
+
+	fetchFreeURL (fetch_url);
+	return true;
+#elif defined(CURL_FOUND)
+	CURL *curl;
+	int r;
+	struct ucl_curl_cbdata cbdata;
+
+	curl = curl_easy_init ();
+	if (curl == NULL) {
+		ucl_create_err (err, "CURL interface is broken");
+		return false;
+	}
+	if ((r = curl_easy_setopt (curl, CURLOPT_URL, url)) != CURLE_OK) {
+		ucl_create_err (err, "invalid URL %s: %s",
+				url, curl_easy_strerror (r));
+		curl_easy_cleanup (curl);
+		return false;
+	}
+	curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, ucl_curl_write_callback);
+	cbdata.buf = *buf;
+	cbdata.buflen = *buflen;
+	curl_easy_setopt (curl, CURLOPT_WRITEDATA, &cbdata);
+
+	if ((r = curl_easy_perform (curl)) != CURLE_OK) {
+		if (!must_exist) {
+			ucl_create_err (err, "error fetching URL %s: %s",
+				url, curl_easy_strerror (r));
+		}
+		curl_easy_cleanup (curl);
+		if (cbdata.buf) {
+			free (cbdata.buf);
+		}
+		return false;
+	}
+	*buf = cbdata.buf;
+	*buflen = cbdata.buflen;
+
+	return true;
+#else
+	ucl_create_err (err, "URL support is disabled");
+	return false;
+#endif
+}
+
+/**
+ * Fetch a file and save results to the memory buffer
+ * @param filename filename to fetch
+ * @param len length of filename
+ * @param buf target buffer
+ * @param buflen target length
+ * @return
+ */
+static bool
+ucl_fetch_file (const unsigned char *filename, unsigned char **buf, size_t *buflen,
+		UT_string **err, bool must_exist)
+{
+	int fd;
+	struct stat st;
+
+	if (stat (filename, &st) == -1 || !S_ISREG (st.st_mode)) {
+		if (must_exist) {
+			ucl_create_err (err, "cannot stat file %s: %s",
+					filename, strerror (errno));
+		}
+		return false;
+	}
+	if (st.st_size == 0) {
+		/* Do not map empty files */
+		*buf = "";
+		*buflen = 0;
+	}
+	else {
+		if ((fd = open (filename, O_RDONLY)) == -1) {
+			ucl_create_err (err, "cannot open file %s: %s",
+					filename, strerror (errno));
+			return false;
+		}
+		if ((*buf = ucl_mmap (NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0)) == MAP_FAILED) {
+			close (fd);
+			ucl_create_err (err, "cannot mmap file %s: %s",
+					filename, strerror (errno));
+			return false;
+		}
+		*buflen = st.st_size;
+		close (fd);
+	}
+
+	return true;
+}
+
+
+#if (defined(HAVE_OPENSSL) && OPENSSL_VERSION_NUMBER >= 0x10000000L)
+static inline bool
+ucl_sig_check (const unsigned char *data, size_t datalen,
+		const unsigned char *sig, size_t siglen, struct ucl_parser *parser)
+{
+	struct ucl_pubkey *key;
+	char dig[EVP_MAX_MD_SIZE];
+	unsigned int diglen;
+	EVP_PKEY_CTX *key_ctx;
+	EVP_MD_CTX *sign_ctx = NULL;
+
+	sign_ctx = EVP_MD_CTX_create ();
+
+	LL_FOREACH (parser->keys, key) {
+		key_ctx = EVP_PKEY_CTX_new (key->key, NULL);
+		if (key_ctx != NULL) {
+			if (EVP_PKEY_verify_init (key_ctx) <= 0) {
+				EVP_PKEY_CTX_free (key_ctx);
+				continue;
+			}
+			if (EVP_PKEY_CTX_set_rsa_padding (key_ctx, RSA_PKCS1_PADDING) <= 0) {
+				EVP_PKEY_CTX_free (key_ctx);
+				continue;
+			}
+			if (EVP_PKEY_CTX_set_signature_md (key_ctx, EVP_sha256 ()) <= 0) {
+				EVP_PKEY_CTX_free (key_ctx);
+				continue;
+			}
+			EVP_DigestInit (sign_ctx, EVP_sha256 ());
+			EVP_DigestUpdate (sign_ctx, data, datalen);
+			EVP_DigestFinal (sign_ctx, dig, &diglen);
+
+			if (EVP_PKEY_verify (key_ctx, sig, siglen, dig, diglen) == 1) {
+				EVP_MD_CTX_destroy (sign_ctx);
+				EVP_PKEY_CTX_free (key_ctx);
+				return true;
+			}
+
+			EVP_PKEY_CTX_free (key_ctx);
+		}
+	}
+
+	EVP_MD_CTX_destroy (sign_ctx);
+
+	return false;
+}
+#endif
+
+/**
+ * Include an url to configuration
+ * @param data
+ * @param len
+ * @param parser
+ * @param err
+ * @return
+ */
+static bool
+ucl_include_url (const unsigned char *data, size_t len,
+		struct ucl_parser *parser, bool check_signature, bool must_exist)
+{
+
+	bool res;
+	unsigned char *buf = NULL;
+	size_t buflen = 0;
+	struct ucl_chunk *chunk;
+	char urlbuf[PATH_MAX];
+	int prev_state;
+
+	snprintf (urlbuf, sizeof (urlbuf), "%.*s", (int)len, data);
+
+	if (!ucl_fetch_url (urlbuf, &buf, &buflen, &parser->err, must_exist)) {
+		return (!must_exist || false);
+	}
+
+	if (check_signature) {
+#if (defined(HAVE_OPENSSL) && OPENSSL_VERSION_NUMBER >= 0x10000000L)
+		unsigned char *sigbuf = NULL;
+		size_t siglen = 0;
+		/* We need to check signature first */
+		snprintf (urlbuf, sizeof (urlbuf), "%.*s.sig", (int)len, data);
+		if (!ucl_fetch_url (urlbuf, &sigbuf, &siglen, &parser->err, true)) {
+			return false;
+		}
+		if (!ucl_sig_check (buf, buflen, sigbuf, siglen, parser)) {
+			ucl_create_err (&parser->err, "cannot verify url %s: %s",
+							urlbuf,
+							ERR_error_string (ERR_get_error (), NULL));
+			if (siglen > 0) {
+				ucl_munmap (sigbuf, siglen);
+			}
+			return false;
+		}
+		if (siglen > 0) {
+			ucl_munmap (sigbuf, siglen);
+		}
+#endif
+	}
+
+	prev_state = parser->state;
+	parser->state = UCL_STATE_INIT;
+
+	res = ucl_parser_add_chunk (parser, buf, buflen);
+	if (res == true) {
+		/* Remove chunk from the stack */
+		chunk = parser->chunks;
+		if (chunk != NULL) {
+			parser->chunks = chunk->next;
+			UCL_FREE (sizeof (struct ucl_chunk), chunk);
+		}
+	}
+
+	parser->state = prev_state;
+	free (buf);
+
+	return res;
+}
+
+/**
+ * Include a file to configuration
+ * @param data
+ * @param len
+ * @param parser
+ * @param err
+ * @return
+ */
+static bool
+ucl_include_file (const unsigned char *data, size_t len,
+		struct ucl_parser *parser, bool check_signature, bool must_exist)
+{
+	bool res;
+	struct ucl_chunk *chunk;
+	unsigned char *buf = NULL;
+	size_t buflen;
+	char filebuf[PATH_MAX], realbuf[PATH_MAX];
+	int prev_state;
+
+	snprintf (filebuf, sizeof (filebuf), "%.*s", (int)len, data);
+	if (ucl_realpath (filebuf, realbuf) == NULL) {
+		if (!must_exist) {
+			return true;
+		}
+		ucl_create_err (&parser->err, "cannot open file %s: %s",
+									filebuf,
+									strerror (errno));
+		return false;
+	}
+
+	if (!ucl_fetch_file (realbuf, &buf, &buflen, &parser->err, must_exist)) {
+		return (!must_exist || false);
+	}
+
+	if (check_signature) {
+#if (defined(HAVE_OPENSSL) && OPENSSL_VERSION_NUMBER >= 0x10000000L)
+		unsigned char *sigbuf = NULL;
+		size_t siglen = 0;
+		/* We need to check signature first */
+		snprintf (filebuf, sizeof (filebuf), "%s.sig", realbuf);
+		if (!ucl_fetch_file (filebuf, &sigbuf, &siglen, &parser->err, true)) {
+			return false;
+		}
+		if (!ucl_sig_check (buf, buflen, sigbuf, siglen, parser)) {
+			ucl_create_err (&parser->err, "cannot verify file %s: %s",
+							filebuf,
+							ERR_error_string (ERR_get_error (), NULL));
+			if (siglen > 0) {
+				ucl_munmap (sigbuf, siglen);
+			}
+			return false;
+		}
+		if (siglen > 0) {
+			ucl_munmap (sigbuf, siglen);
+		}
+#endif
+	}
+
+	ucl_parser_set_filevars (parser, realbuf, false);
+
+	prev_state = parser->state;
+	parser->state = UCL_STATE_INIT;
+
+	res = ucl_parser_add_chunk (parser, buf, buflen);
+	if (res == true) {
+		/* Remove chunk from the stack */
+		chunk = parser->chunks;
+		if (chunk != NULL) {
+			parser->chunks = chunk->next;
+			UCL_FREE (sizeof (struct ucl_chunk), chunk);
+		}
+	}
+
+	parser->state = prev_state;
+
+	if (buflen > 0) {
+		ucl_munmap (buf, buflen);
+	}
+
+	return res;
+}
+
+/**
+ * Handle include macro
+ * @param data include data
+ * @param len length of data
+ * @param ud user data
+ * @param err error ptr
+ * @return
+ */
+UCL_EXTERN bool
+ucl_include_handler (const unsigned char *data, size_t len, void* ud)
+{
+	struct ucl_parser *parser = ud;
+
+	if (*data == '/' || *data == '.') {
+		/* Try to load a file */
+		return ucl_include_file (data, len, parser, false, true);
+	}
+
+	return ucl_include_url (data, len, parser, false, true);
+}
+
+/**
+ * Handle includes macro
+ * @param data include data
+ * @param len length of data
+ * @param ud user data
+ * @param err error ptr
+ * @return
+ */
+UCL_EXTERN bool
+ucl_includes_handler (const unsigned char *data, size_t len, void* ud)
+{
+	struct ucl_parser *parser = ud;
+
+	if (*data == '/' || *data == '.') {
+		/* Try to load a file */
+		return ucl_include_file (data, len, parser, true, true);
+	}
+
+	return ucl_include_url (data, len, parser, true, true);
+}
+
+
+UCL_EXTERN bool
+ucl_try_include_handler (const unsigned char *data, size_t len, void* ud)
+{
+	struct ucl_parser *parser = ud;
+
+	if (*data == '/' || *data == '.') {
+		/* Try to load a file */
+		return ucl_include_file (data, len, parser, false, false);
+	}
+
+	return ucl_include_url (data, len, parser, false, false);
+}
+
+UCL_EXTERN bool
+ucl_parser_set_filevars (struct ucl_parser *parser, const char *filename, bool need_expand)
+{
+	char realbuf[PATH_MAX], *curdir;
+
+	if (filename != NULL) {
+		if (need_expand) {
+			if (ucl_realpath (filename, realbuf) == NULL) {
+				return false;
+			}
+		}
+		else {
+			ucl_strlcpy (realbuf, filename, sizeof (realbuf));
+		}
+
+		/* Define variables */
+		ucl_parser_register_variable (parser, "FILENAME", realbuf);
+		curdir = dirname (realbuf);
+		ucl_parser_register_variable (parser, "CURDIR", curdir);
+	}
+	else {
+		/* Set everything from the current dir */
+		curdir = getcwd (realbuf, sizeof (realbuf));
+		ucl_parser_register_variable (parser, "FILENAME", "undef");
+		ucl_parser_register_variable (parser, "CURDIR", curdir);
+	}
+
+	return true;
+}
+
+UCL_EXTERN bool
+ucl_parser_add_file (struct ucl_parser *parser, const char *filename)
+{
+	unsigned char *buf;
+	size_t len;
+	bool ret;
+	char realbuf[PATH_MAX];
+
+	if (ucl_realpath (filename, realbuf) == NULL) {
+		ucl_create_err (&parser->err, "cannot open file %s: %s",
+				filename,
+				strerror (errno));
+		return false;
+	}
+
+	if (!ucl_fetch_file (realbuf, &buf, &len, &parser->err, true)) {
+		return false;
+	}
+
+	ucl_parser_set_filevars (parser, realbuf, false);
+	ret = ucl_parser_add_chunk (parser, buf, len);
+
+	if (len > 0) {
+		ucl_munmap (buf, len);
+	}
+
+	return ret;
+}
+
+size_t
+ucl_strlcpy (char *dst, const char *src, size_t siz)
+{
+	char *d = dst;
+	const char *s = src;
+	size_t n = siz;
+
+	/* Copy as many bytes as will fit */
+	if (n != 0) {
+		while (--n != 0) {
+			if ((*d++ = *s++) == '\0') {
+				break;
+			}
+		}
+	}
+
+	if (n == 0 && siz != 0) {
+		*d = '\0';
+	}
+
+	return (s - src - 1);    /* count does not include NUL */
+}
+
+size_t
+ucl_strlcpy_unsafe (char *dst, const char *src, size_t siz)
+{
+	memcpy (dst, src, siz - 1);
+	dst[siz - 1] = '\0';
+
+	return siz - 1;
+}
+
+size_t
+ucl_strlcpy_tolower (char *dst, const char *src, size_t siz)
+{
+	char *d = dst;
+	const char *s = src;
+	size_t n = siz;
+
+	/* Copy as many bytes as will fit */
+	if (n != 0) {
+		while (--n != 0) {
+			if ((*d++ = tolower (*s++)) == '\0') {
+				break;
+			}
+		}
+	}
+
+	if (n == 0 && siz != 0) {
+		*d = '\0';
+	}
+
+	return (s - src);    /* count does not include NUL */
+}
+
+ucl_object_t *
+ucl_object_fromstring_common (const char *str, size_t len, enum ucl_string_flags flags)
+{
+	ucl_object_t *obj;
+	const char *start, *end, *p, *pos;
+	char *dst, *d;
+	size_t escaped_len;
+
+	if (str == NULL) {
+		return NULL;
+	}
+
+	obj = ucl_object_new ();
+	if (obj) {
+		if (len == 0) {
+			len = strlen (str);
+		}
+		if (flags & UCL_STRING_TRIM) {
+			/* Skip leading spaces */
+			for (start = str; (size_t)(start - str) < len; start ++) {
+				if (!ucl_test_character (*start, UCL_CHARACTER_WHITESPACE_UNSAFE)) {
+					break;
+				}
+			}
+			/* Skip trailing spaces */
+			for (end = str + len - 1; end > start; end --) {
+				if (!ucl_test_character (*end, UCL_CHARACTER_WHITESPACE_UNSAFE)) {
+					break;
+				}
+			}
+			end ++;
+		}
+		else {
+			start = str;
+			end = str + len;
+		}
+
+		obj->type = UCL_STRING;
+		if (flags & UCL_STRING_ESCAPE) {
+			for (p = start, escaped_len = 0; p < end; p ++, escaped_len ++) {
+				if (ucl_test_character (*p, UCL_CHARACTER_JSON_UNSAFE)) {
+					escaped_len ++;
+				}
+			}
+			dst = malloc (escaped_len + 1);
+			if (dst != NULL) {
+				for (p = start, d = dst; p < end; p ++, d ++) {
+					if (ucl_test_character (*p, UCL_CHARACTER_JSON_UNSAFE)) {
+						switch (*p) {
+						case '\n':
+							*d++ = '\\';
+							*d = 'n';
+							break;
+						case '\r':
+							*d++ = '\\';
+							*d = 'r';
+							break;
+						case '\b':
+							*d++ = '\\';
+							*d = 'b';
+							break;
+						case '\t':
+							*d++ = '\\';
+							*d = 't';
+							break;
+						case '\f':
+							*d++ = '\\';
+							*d = 'f';
+							break;
+						case '\\':
+							*d++ = '\\';
+							*d = '\\';
+							break;
+						case '"':
+							*d++ = '\\';
+							*d = '"';
+							break;
+						}
+					}
+					else {
+						*d = *p;
+					}
+				}
+				*d = '\0';
+				obj->value.sv = dst;
+				obj->trash_stack[UCL_TRASH_VALUE] = dst;
+				obj->len = escaped_len;
+			}
+		}
+		else {
+			dst = malloc (end - start + 1);
+			if (dst != NULL) {
+				ucl_strlcpy_unsafe (dst, start, end - start + 1);
+				obj->value.sv = dst;
+				obj->trash_stack[UCL_TRASH_VALUE] = dst;
+				obj->len = end - start;
+			}
+		}
+		if ((flags & UCL_STRING_PARSE) && dst != NULL) {
+			/* Parse what we have */
+			if (flags & UCL_STRING_PARSE_BOOLEAN) {
+				if (!ucl_maybe_parse_boolean (obj, dst, obj->len) && (flags & UCL_STRING_PARSE_NUMBER)) {
+					ucl_maybe_parse_number (obj, dst, dst + obj->len, &pos,
+							flags & UCL_STRING_PARSE_DOUBLE,
+							flags & UCL_STRING_PARSE_BYTES,
+							flags & UCL_STRING_PARSE_TIME);
+				}
+			}
+			else {
+				ucl_maybe_parse_number (obj, dst, dst + obj->len, &pos,
+						flags & UCL_STRING_PARSE_DOUBLE,
+						flags & UCL_STRING_PARSE_BYTES,
+						flags & UCL_STRING_PARSE_TIME);
+			}
+		}
+	}
+
+	return obj;
+}
+
+static bool
+ucl_object_insert_key_common (ucl_object_t *top, ucl_object_t *elt,
+		const char *key, size_t keylen, bool copy_key, bool merge, bool replace)
+{
+	ucl_object_t *found, *tmp;
+	const ucl_object_t *cur;
+	ucl_object_iter_t it = NULL;
+	const char *p;
+	int ret = true;
+
+	if (elt == NULL || key == NULL) {
+		return false;
+	}
+
+	if (top == NULL) {
+		return false;
+	}
+
+	if (top->type != UCL_OBJECT) {
+		/* It is possible to convert NULL type to an object */
+		if (top->type == UCL_NULL) {
+			top->type = UCL_OBJECT;
+		}
+		else {
+			/* Refuse converting of other object types */
+			return false;
+		}
+	}
+
+	if (top->value.ov == NULL) {
+		top->value.ov = ucl_hash_create ();
+	}
+
+	if (keylen == 0) {
+		keylen = strlen (key);
+	}
+
+	for (p = key; p < key + keylen; p ++) {
+		if (ucl_test_character (*p, UCL_CHARACTER_UCL_UNSAFE)) {
+			elt->flags |= UCL_OBJECT_NEED_KEY_ESCAPE;
+			break;
+		}
+	}
+
+	elt->key = key;
+	elt->keylen = keylen;
+
+	if (copy_key) {
+		ucl_copy_key_trash (elt);
+	}
+
+	found = __DECONST (ucl_object_t *, ucl_hash_search_obj (top->value.ov, elt));
+
+	if (!found) {
+		top->value.ov = ucl_hash_insert_object (top->value.ov, elt);
+		DL_APPEND (found, elt);
+		top->len ++;
+		if (replace) {
+			ret = false;
+		}
+	}
+	else {
+		if (replace) {
+			ucl_hash_delete (top->value.ov, found);
+			ucl_object_unref (found);
+			top->value.ov = ucl_hash_insert_object (top->value.ov, elt);
+			found = NULL;
+			DL_APPEND (found, elt);
+		}
+		else if (merge) {
+			if (found->type != UCL_OBJECT && elt->type == UCL_OBJECT) {
+				/* Insert old elt to new one */
+				ucl_object_insert_key_common (elt, found, found->key,
+						found->keylen, copy_key, false, false);
+				ucl_hash_delete (top->value.ov, found);
+				top->value.ov = ucl_hash_insert_object (top->value.ov, elt);
+			}
+			else if (found->type == UCL_OBJECT && elt->type != UCL_OBJECT) {
+				/* Insert new to old */
+				ucl_object_insert_key_common (found, elt, elt->key,
+						elt->keylen, copy_key, false, false);
+			}
+			else if (found->type == UCL_OBJECT && elt->type == UCL_OBJECT) {
+				/* Mix two hashes */
+				while ((cur = ucl_iterate_object (elt, &it, true)) != NULL) {
+					tmp = ucl_object_ref (cur);
+					ucl_object_insert_key_common (found, tmp, cur->key,
+							cur->keylen, copy_key, false, false);
+				}
+				ucl_object_unref (elt);
+			}
+			else {
+				/* Just make a list of scalars */
+				DL_APPEND (found, elt);
+			}
+		}
+		else {
+			DL_APPEND (found, elt);
+		}
+	}
+
+	return ret;
+}
+
+bool
+ucl_object_delete_keyl (ucl_object_t *top, const char *key, size_t keylen)
+{
+	ucl_object_t *found;
+
+	if (top == NULL || key == NULL) {
+		return false;
+	}
+
+	found = __DECONST (ucl_object_t *, ucl_object_find_keyl (top, key, keylen));
+
+	if (found == NULL) {
+		return false;
+	}
+
+	ucl_hash_delete (top->value.ov, found);
+	ucl_object_unref (found);
+	top->len --;
+
+	return true;
+}
+
+bool
+ucl_object_delete_key (ucl_object_t *top, const char *key)
+{
+	return ucl_object_delete_keyl (top, key, strlen(key));
+}
+
+ucl_object_t*
+ucl_object_pop_keyl (ucl_object_t *top, const char *key, size_t keylen)
+{
+	const ucl_object_t *found;
+
+	if (top == NULL || key == NULL) {
+		return false;
+	}
+	found = ucl_object_find_keyl (top, key, keylen);
+
+	if (found == NULL) {
+		return NULL;
+	}
+	ucl_hash_delete (top->value.ov, found);
+	top->len --;
+
+	return __DECONST (ucl_object_t *, found);
+}
+
+ucl_object_t*
+ucl_object_pop_key (ucl_object_t *top, const char *key)
+{
+	return ucl_object_pop_keyl (top, key, strlen(key));
+}
+
+bool
+ucl_object_insert_key (ucl_object_t *top, ucl_object_t *elt,
+		const char *key, size_t keylen, bool copy_key)
+{
+	return ucl_object_insert_key_common (top, elt, key, keylen, copy_key, false, false);
+}
+
+bool
+ucl_object_insert_key_merged (ucl_object_t *top, ucl_object_t *elt,
+		const char *key, size_t keylen, bool copy_key)
+{
+	return ucl_object_insert_key_common (top, elt, key, keylen, copy_key, true, false);
+}
+
+bool
+ucl_object_replace_key (ucl_object_t *top, ucl_object_t *elt,
+		const char *key, size_t keylen, bool copy_key)
+{
+	return ucl_object_insert_key_common (top, elt, key, keylen, copy_key, false, true);
+}
+
+const ucl_object_t *
+ucl_object_find_keyl (const ucl_object_t *obj, const char *key, size_t klen)
+{
+	const ucl_object_t *ret;
+	ucl_object_t srch;
+
+	if (obj == NULL || obj->type != UCL_OBJECT || key == NULL) {
+		return NULL;
+	}
+
+	srch.key = key;
+	srch.keylen = klen;
+	ret = ucl_hash_search_obj (obj->value.ov, &srch);
+
+	return ret;
+}
+
+const ucl_object_t *
+ucl_object_find_key (const ucl_object_t *obj, const char *key)
+{
+	if (key == NULL)
+		return NULL;
+
+	return ucl_object_find_keyl (obj, key, strlen(key));
+}
+
+const ucl_object_t*
+ucl_iterate_object (const ucl_object_t *obj, ucl_object_iter_t *iter, bool expand_values)
+{
+	const ucl_object_t *elt;
+
+	if (obj == NULL || iter == NULL) {
+		return NULL;
+	}
+
+	if (expand_values) {
+		switch (obj->type) {
+		case UCL_OBJECT:
+			return (const ucl_object_t*)ucl_hash_iterate (obj->value.ov, iter);
+			break;
+		case UCL_ARRAY:
+			elt = *iter;
+			if (elt == NULL) {
+				elt = obj->value.av;
+				if (elt == NULL) {
+					return NULL;
+				}
+			}
+			else if (elt == obj->value.av) {
+				return NULL;
+			}
+			*iter = elt->next ? elt->next : obj->value.av;
+			return elt;
+		default:
+			/* Go to linear iteration */
+			break;
+		}
+	}
+	/* Treat everything as a linear list */
+	elt = *iter;
+	if (elt == NULL) {
+		elt = obj;
+		if (elt == NULL) {
+			return NULL;
+		}
+	}
+	else if (elt == obj) {
+		return NULL;
+	}
+	*iter = __DECONST (void *, elt->next ? elt->next : obj);
+	return elt;
+
+	/* Not reached */
+	return NULL;
+}
+
+const ucl_object_t *
+ucl_lookup_path (const ucl_object_t *top, const char *path_in) {
+	const ucl_object_t *o = NULL, *found;
+	const char *p, *c;
+	char *err_str;
+	unsigned index;
+
+	if (path_in == NULL || top == NULL) {
+		return NULL;
+	}
+
+	found = NULL;
+	p = path_in;
+
+	/* Skip leading dots */
+	while (*p == '.') {
+		p ++;
+	}
+
+	c = p;
+	while (*p != '\0') {
+		p ++;
+		if (*p == '.' || *p == '\0') {
+			if (p > c) {
+				switch (top->type) {
+				case UCL_ARRAY:
+					/* Key should be an int */
+					index = strtoul (c, &err_str, 10);
+					if (err_str != NULL && (*err_str != '.' && *err_str != '\0')) {
+						return NULL;
+					}
+					o = ucl_array_find_index (top, index);
+					break;
+				default:
+					o = ucl_object_find_keyl (top, c, p - c);
+					break;
+				}
+				if (o == NULL) {
+					return NULL;
+				}
+				top = o;
+			}
+			if (*p != '\0') {
+				c = p + 1;
+			}
+		}
+	}
+	found = o;
+
+	return found;
+}
+
+
+ucl_object_t *
+ucl_object_new (void)
+{
+	ucl_object_t *new;
+	new = malloc (sizeof (ucl_object_t));
+	if (new != NULL) {
+		memset (new, 0, sizeof (ucl_object_t));
+		new->ref = 1;
+		new->type = UCL_NULL;
+	}
+	return new;
+}
+
+ucl_object_t *
+ucl_object_typed_new (ucl_type_t type)
+{
+	ucl_object_t *new;
+	new = malloc (sizeof (ucl_object_t));
+	if (new != NULL) {
+		memset (new, 0, sizeof (ucl_object_t));
+		new->ref = 1;
+		new->type = (type <= UCL_NULL ? type : UCL_NULL);
+	}
+	return new;
+}
+
+ucl_type_t
+ucl_object_type (const ucl_object_t *obj)
+{
+	return obj->type;
+}
+
+ucl_object_t*
+ucl_object_fromstring (const char *str)
+{
+	return ucl_object_fromstring_common (str, 0, UCL_STRING_ESCAPE);
+}
+
+ucl_object_t *
+ucl_object_fromlstring (const char *str, size_t len)
+{
+	return ucl_object_fromstring_common (str, len, UCL_STRING_ESCAPE);
+}
+
+ucl_object_t *
+ucl_object_fromint (int64_t iv)
+{
+	ucl_object_t *obj;
+
+	obj = ucl_object_new ();
+	if (obj != NULL) {
+		obj->type = UCL_INT;
+		obj->value.iv = iv;
+	}
+
+	return obj;
+}
+
+ucl_object_t *
+ucl_object_fromdouble (double dv)
+{
+	ucl_object_t *obj;
+
+	obj = ucl_object_new ();
+	if (obj != NULL) {
+		obj->type = UCL_FLOAT;
+		obj->value.dv = dv;
+	}
+
+	return obj;
+}
+
+ucl_object_t*
+ucl_object_frombool (bool bv)
+{
+	ucl_object_t *obj;
+
+	obj = ucl_object_new ();
+	if (obj != NULL) {
+		obj->type = UCL_BOOLEAN;
+		obj->value.iv = bv;
+	}
+
+	return obj;
+}
+
+bool
+ucl_array_append (ucl_object_t *top, ucl_object_t *elt)
+{
+	ucl_object_t *head;
+
+	if (elt == NULL || top == NULL) {
+		return false;
+	}
+
+	head = top->value.av;
+	if (head == NULL) {
+		top->value.av = elt;
+		elt->prev = elt;
+	}
+	else {
+		elt->prev = head->prev;
+		head->prev->next = elt;
+		head->prev = elt;
+	}
+	elt->next = NULL;
+	top->len ++;
+
+	return true;
+}
+
+bool
+ucl_array_prepend (ucl_object_t *top, ucl_object_t *elt)
+{
+	ucl_object_t *head;
+
+	if (elt == NULL || top == NULL) {
+		return false;
+	}
+
+
+	head = top->value.av;
+	if (head == NULL) {
+		top->value.av = elt;
+		elt->prev = elt;
+	}
+	else {
+		elt->prev = head->prev;
+		head->prev = elt;
+	}
+	elt->next = head;
+	top->value.av = elt;
+	top->len ++;
+
+	return true;
+}
+
+ucl_object_t *
+ucl_array_delete (ucl_object_t *top, ucl_object_t *elt)
+{
+	ucl_object_t *head;
+
+	if (top == NULL || top->type != UCL_ARRAY || top->value.av == NULL) {
+		return NULL;
+	}
+	head = top->value.av;
+
+	if (elt->prev == elt) {
+		top->value.av = NULL;
+	}
+	else if (elt == head) {
+		elt->next->prev = elt->prev;
+		top->value.av = elt->next;
+	}
+	else {
+		elt->prev->next = elt->next;
+		if (elt->next) {
+			elt->next->prev = elt->prev;
+		}
+		else {
+			head->prev = elt->prev;
+		}
+	}
+	elt->next = NULL;
+	elt->prev = elt;
+	top->len --;
+
+	return elt;
+}
+
+const ucl_object_t *
+ucl_array_head (const ucl_object_t *top)
+{
+	if (top == NULL || top->type != UCL_ARRAY || top->value.av == NULL) {
+		return NULL;
+	}
+	return top->value.av;
+}
+
+const ucl_object_t *
+ucl_array_tail (const ucl_object_t *top)
+{
+	if (top == NULL || top->type != UCL_ARRAY || top->value.av == NULL) {
+		return NULL;
+	}
+	return top->value.av->prev;
+}
+
+ucl_object_t *
+ucl_array_pop_last (ucl_object_t *top)
+{
+	return ucl_array_delete (top, __DECONST(ucl_object_t *, ucl_array_tail (top)));
+}
+
+ucl_object_t *
+ucl_array_pop_first (ucl_object_t *top)
+{
+	return ucl_array_delete (top, __DECONST(ucl_object_t *, ucl_array_head (top)));
+}
+
+const ucl_object_t *
+ucl_array_find_index (const ucl_object_t *top, unsigned int index)
+{
+	ucl_object_iter_t it = NULL;
+	const ucl_object_t *ret;
+
+	if (top == NULL || top->type != UCL_ARRAY || top->len == 0 ||
+	    (index + 1) > top->len) {
+		return NULL;
+	}
+
+	while ((ret = ucl_iterate_object (top, &it, true)) != NULL) {
+		if (index == 0) {
+			return ret;
+		}
+		--index;
+	}
+
+	return NULL;
+}
+
+ucl_object_t *
+ucl_elt_append (ucl_object_t *head, ucl_object_t *elt)
+{
+
+	if (head == NULL) {
+		elt->next = NULL;
+		elt->prev = elt;
+		head = elt;
+	}
+	else {
+		elt->prev = head->prev;
+		head->prev->next = elt;
+		head->prev = elt;
+		elt->next = NULL;
+	}
+
+	return head;
+}
+
+bool
+ucl_object_todouble_safe (const ucl_object_t *obj, double *target)
+{
+	if (obj == NULL || target == NULL) {
+		return false;
+	}
+	switch (obj->type) {
+	case UCL_INT:
+		*target = obj->value.iv; /* Probaly could cause overflow */
+		break;
+	case UCL_FLOAT:
+	case UCL_TIME:
+		*target = obj->value.dv;
+		break;
+	default:
+		return false;
+	}
+
+	return true;
+}
+
+double
+ucl_object_todouble (const ucl_object_t *obj)
+{
+	double result = 0.;
+
+	ucl_object_todouble_safe (obj, &result);
+	return result;
+}
+
+bool
+ucl_object_toint_safe (const ucl_object_t *obj, int64_t *target)
+{
+	if (obj == NULL || target == NULL) {
+		return false;
+	}
+	switch (obj->type) {
+	case UCL_INT:
+		*target = obj->value.iv;
+		break;
+	case UCL_FLOAT:
+	case UCL_TIME:
+		*target = obj->value.dv; /* Loosing of decimal points */
+		break;
+	default:
+		return false;
+	}
+
+	return true;
+}
+
+int64_t
+ucl_object_toint (const ucl_object_t *obj)
+{
+	int64_t result = 0;
+
+	ucl_object_toint_safe (obj, &result);
+	return result;
+}
+
+bool
+ucl_object_toboolean_safe (const ucl_object_t *obj, bool *target)
+{
+	if (obj == NULL || target == NULL) {
+		return false;
+	}
+	switch (obj->type) {
+	case UCL_BOOLEAN:
+		*target = (obj->value.iv == true);
+		break;
+	default:
+		return false;
+	}
+
+	return true;
+}
+
+bool
+ucl_object_toboolean (const ucl_object_t *obj)
+{
+	bool result = false;
+
+	ucl_object_toboolean_safe (obj, &result);
+	return result;
+}
+
+bool
+ucl_object_tostring_safe (const ucl_object_t *obj, const char **target)
+{
+	if (obj == NULL || target == NULL) {
+		return false;
+	}
+
+	switch (obj->type) {
+	case UCL_STRING:
+		*target = ucl_copy_value_trash (obj);
+		break;
+	default:
+		return false;
+	}
+
+	return true;
+}
+
+const char *
+ucl_object_tostring (const ucl_object_t *obj)
+{
+	const char *result = NULL;
+
+	ucl_object_tostring_safe (obj, &result);
+	return result;
+}
+
+const char *
+ucl_object_tostring_forced (const ucl_object_t *obj)
+{
+	return ucl_copy_value_trash (obj);
+}
+
+bool
+ucl_object_tolstring_safe (const ucl_object_t *obj, const char **target, size_t *tlen)
+{
+	if (obj == NULL || target == NULL) {
+		return false;
+	}
+	switch (obj->type) {
+	case UCL_STRING:
+		*target = obj->value.sv;
+		if (tlen != NULL) {
+			*tlen = obj->len;
+		}
+		break;
+	default:
+		return false;
+	}
+
+	return true;
+}
+
+const char *
+ucl_object_tolstring (const ucl_object_t *obj, size_t *tlen)
+{
+	const char *result = NULL;
+
+	ucl_object_tolstring_safe (obj, &result, tlen);
+	return result;
+}
+
+const char *
+ucl_object_key (const ucl_object_t *obj)
+{
+	return ucl_copy_key_trash (obj);
+}
+
+const char *
+ucl_object_keyl (const ucl_object_t *obj, size_t *len)
+{
+	if (len == NULL || obj == NULL) {
+		return NULL;
+	}
+	*len = obj->keylen;
+	return obj->key;
+}
+
+ucl_object_t *
+ucl_object_ref (const ucl_object_t *obj)
+{
+	ucl_object_t *res = NULL;
+
+	if (obj != NULL) {
+		res = __DECONST (ucl_object_t *, obj);
+#ifdef HAVE_ATOMIC_BUILTINS
+		(void)__sync_add_and_fetch (&res->ref, 1);
+#else
+		res->ref ++;
+#endif
+	}
+	return res;
+}
+
+void
+ucl_object_unref (ucl_object_t *obj)
+{
+	if (obj != NULL) {
+#ifdef HAVE_ATOMIC_BUILTINS
+		unsigned int rc = __sync_sub_and_fetch (&obj->ref, 1);
+		if (rc == 0) {
+#else
+		if (--obj->ref == 0) {
+#endif
+			ucl_object_free_internal (obj, true, ucl_object_dtor_unref);
+		}
+	}
+}
+
+int
+ucl_object_compare (const ucl_object_t *o1, const ucl_object_t *o2)
+{
+	const ucl_object_t *it1, *it2;
+	ucl_object_iter_t iter = NULL;
+	int ret = 0;
+
+	if (o1->type != o2->type) {
+		return (o1->type) - (o2->type);
+	}
+
+	switch (o1->type) {
+	case UCL_STRING:
+		if (o1->len == o2->len) {
+			ret = strcmp (ucl_object_tostring(o1), ucl_object_tostring(o2));
+		}
+		else {
+			ret = o1->len - o2->len;
+		}
+		break;
+	case UCL_FLOAT:
+	case UCL_INT:
+	case UCL_TIME:
+		ret = ucl_object_todouble (o1) - ucl_object_todouble (o2);
+		break;
+	case UCL_BOOLEAN:
+		ret = ucl_object_toboolean (o1) - ucl_object_toboolean (o2);
+		break;
+	case UCL_ARRAY:
+		if (o1->len == o2->len) {
+			it1 = o1->value.av;
+			it2 = o2->value.av;
+			/* Compare all elements in both arrays */
+			while (it1 != NULL && it2 != NULL) {
+				ret = ucl_object_compare (it1, it2);
+				if (ret != 0) {
+					break;
+				}
+				it1 = it1->next;
+				it2 = it2->next;
+			}
+		}
+		else {
+			ret = o1->len - o2->len;
+		}
+		break;
+	case UCL_OBJECT:
+		if (o1->len == o2->len) {
+			while ((it1 = ucl_iterate_object (o1, &iter, true)) != NULL) {
+				it2 = ucl_object_find_key (o2, ucl_object_key (it1));
+				if (it2 == NULL) {
+					ret = 1;
+					break;
+				}
+				ret = ucl_object_compare (it1, it2);
+				if (ret != 0) {
+					break;
+				}
+			}
+		}
+		else {
+			ret = o1->len - o2->len;
+		}
+		break;
+	default:
+		ret = 0;
+		break;
+	}
+
+	return ret;
+}
+
+void
+ucl_object_array_sort (ucl_object_t *ar,
+		int (*cmp)(const ucl_object_t *o1, const ucl_object_t *o2))
+{
+	if (cmp == NULL || ar == NULL || ar->type != UCL_ARRAY) {
+		return;
+	}
+
+	DL_SORT (ar->value.av, cmp);
+}

Added: vendor/libucl/dist/src/xxhash.c
===================================================================
--- vendor/libucl/dist/src/xxhash.c	                        (rev 0)
+++ vendor/libucl/dist/src/xxhash.c	2018-06-19 13:29:00 UTC (rev 11074)
@@ -0,0 +1,475 @@
+/*
+xxHash - Fast Hash algorithm
+Copyright (C) 2012-2013, Yann Collet.
+BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+* Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+You can contact the author at :
+- xxHash source repository : http://code.google.com/p/xxhash/
+*/
+
+
+//**************************************
+// Tuning parameters
+//**************************************
+// Unaligned memory access is automatically enabled for "common" CPU, such as x86.
+// For others CPU, the compiler will be more cautious, and insert extra code to ensure aligned access is respected.
+// If you know your target CPU supports unaligned memory access, you want to force this option manually to improve performance.
+// You can also enable this parameter if you know your input data will always be aligned (boundaries of 4, for U32).
+#if defined(__ARM_FEATURE_UNALIGNED) || defined(__i386) || defined(_M_IX86) || defined(__x86_64__) || defined(_M_X64)
+#  define XXH_USE_UNALIGNED_ACCESS 1
+#endif
+
+// XXH_ACCEPT_NULL_INPUT_POINTER :
+// If the input pointer is a null pointer, xxHash default behavior is to trigger a memory access error, since it is a bad pointer.
+// When this option is enabled, xxHash output for null input pointers will be the same as a null-length input.
+// This option has a very small performance cost (only measurable on small inputs).
+// By default, this option is disabled. To enable it, uncomment below define :
+//#define XXH_ACCEPT_NULL_INPUT_POINTER 1
+
+// XXH_FORCE_NATIVE_FORMAT :
+// By default, xxHash library provides endian-independant Hash values, based on little-endian convention.
+// Results are therefore identical for little-endian and big-endian CPU.
+// This comes at a performance cost for big-endian CPU, since some swapping is required to emulate little-endian format.
+// Should endian-independance be of no importance for your application, you may set the #define below to 1.
+// It will improve speed for Big-endian CPU.
+// This option has no impact on Little_Endian CPU.
+#define XXH_FORCE_NATIVE_FORMAT 0
+
+
+//**************************************
+// Compiler Specific Options
+//**************************************
+// Disable some Visual warning messages
+#ifdef _MSC_VER  // Visual Studio
+#  pragma warning(disable : 4127)      // disable: C4127: conditional expression is constant
+#endif
+
+#ifdef _MSC_VER    // Visual Studio
+#  define forceinline static __forceinline
+#else 
+#  ifdef __GNUC__
+#    define forceinline static inline __attribute__((always_inline))
+#  else
+#    define forceinline static inline
+#  endif
+#endif
+
+
+//**************************************
+// Includes & Memory related functions
+//**************************************
+#include "xxhash.h"
+// Modify the local functions below should you wish to use some other memory related routines
+// for malloc(), free()
+#include <stdlib.h>
+forceinline void* XXH_malloc(size_t s) { return malloc(s); }
+forceinline void  XXH_free  (void* p)  { free(p); }
+// for memcpy()
+#include <string.h>
+forceinline void* XXH_memcpy(void* dest, const void* src, size_t size) { return memcpy(dest,src,size); }
+
+
+//**************************************
+// Basic Types
+//**************************************
+#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L   // C99
+# include <stdint.h>
+  typedef uint8_t  BYTE;
+  typedef uint16_t U16;
+  typedef uint32_t U32;
+  typedef  int32_t S32;
+  typedef uint64_t U64;
+#else
+  typedef unsigned char      BYTE;
+  typedef unsigned short     U16;
+  typedef unsigned int       U32;
+  typedef   signed int       S32;
+  typedef unsigned long long U64;
+#endif
+
+#if defined(__GNUC__)  && !defined(XXH_USE_UNALIGNED_ACCESS)
+#  define _PACKED __attribute__ ((packed))
+#else
+#  define _PACKED
+#endif
+
+#if !defined(XXH_USE_UNALIGNED_ACCESS) && !defined(__GNUC__)
+#  ifdef __IBMC__
+#    pragma pack(1)
+#  else
+#    pragma pack(push, 1)
+#  endif
+#endif
+
+typedef struct _U32_S { U32 v; } _PACKED U32_S;
+
+#if !defined(XXH_USE_UNALIGNED_ACCESS) && !defined(__GNUC__)
+#  pragma pack(pop)
+#endif
+
+#define A32(x) (((U32_S *)(x))->v)
+
+
+//***************************************
+// Compiler-specific Functions and Macros
+//***************************************
+#define GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__)
+
+// Note : although _rotl exists for minGW (GCC under windows), performance seems poor
+#if defined(_MSC_VER)
+#  define XXH_rotl32(x,r) _rotl(x,r)
+#else
+#  define XXH_rotl32(x,r) ((x << r) | (x >> (32 - r)))
+#endif
+
+#if defined(_MSC_VER)     // Visual Studio
+#  define XXH_swap32 _byteswap_ulong
+#elif GCC_VERSION >= 403
+#  define XXH_swap32 __builtin_bswap32
+#else
+static inline U32 XXH_swap32 (U32 x) {
+    return  ((x << 24) & 0xff000000 ) |
+        ((x <<  8) & 0x00ff0000 ) |
+        ((x >>  8) & 0x0000ff00 ) |
+        ((x >> 24) & 0x000000ff );}
+#endif
+
+
+//**************************************
+// Constants
+//**************************************
+#define PRIME32_1   2654435761U
+#define PRIME32_2   2246822519U
+#define PRIME32_3   3266489917U
+#define PRIME32_4    668265263U
+#define PRIME32_5    374761393U
+
+
+//**************************************
+// Architecture Macros
+//**************************************
+typedef enum { XXH_bigEndian=0, XXH_littleEndian=1 } XXH_endianess;
+#ifndef XXH_CPU_LITTLE_ENDIAN   // It is possible to define XXH_CPU_LITTLE_ENDIAN externally, for example using a compiler switch
+    static const int one = 1;
+#   define XXH_CPU_LITTLE_ENDIAN   (*(char*)(&one))
+#endif
+
+
+//**************************************
+// Macros
+//**************************************
+#define XXH_STATIC_ASSERT(c)   { enum { XXH_static_assert = 1/(!!(c)) }; }    // use only *after* variable declarations
+
+
+//****************************
+// Memory reads
+//****************************
+typedef enum { XXH_aligned, XXH_unaligned } XXH_alignment;
+
+forceinline U32 XXH_readLE32_align(const U32* ptr, XXH_endianess endian, XXH_alignment align)
+{ 
+    if (align==XXH_unaligned)
+        return endian==XXH_littleEndian ? A32(ptr) : XXH_swap32(A32(ptr)); 
+    else
+        return endian==XXH_littleEndian ? *ptr : XXH_swap32(*ptr); 
+}
+
+forceinline U32 XXH_readLE32(const U32* ptr, XXH_endianess endian) { return XXH_readLE32_align(ptr, endian, XXH_unaligned); }
+
+
+//****************************
+// Simple Hash Functions
+//****************************
+forceinline U32 XXH32_endian_align(const void* input, int len, U32 seed, XXH_endianess endian, XXH_alignment align)
+{
+    const BYTE* p = (const BYTE*)input;
+    const BYTE* const bEnd = p + len;
+    U32 h32;
+
+#ifdef XXH_ACCEPT_NULL_INPUT_POINTER
+    if (p==NULL) { len=0; p=(const BYTE*)(size_t)16; }
+#endif
+
+    if (len>=16)
+    {
+        const BYTE* const limit = bEnd - 16;
+        U32 v1 = seed + PRIME32_1 + PRIME32_2;
+        U32 v2 = seed + PRIME32_2;
+        U32 v3 = seed + 0;
+        U32 v4 = seed - PRIME32_1;
+
+        do
+        {
+            v1 += XXH_readLE32_align((const U32*)p, endian, align) * PRIME32_2; v1 = XXH_rotl32(v1, 13); v1 *= PRIME32_1; p+=4;
+            v2 += XXH_readLE32_align((const U32*)p, endian, align) * PRIME32_2; v2 = XXH_rotl32(v2, 13); v2 *= PRIME32_1; p+=4;
+            v3 += XXH_readLE32_align((const U32*)p, endian, align) * PRIME32_2; v3 = XXH_rotl32(v3, 13); v3 *= PRIME32_1; p+=4;
+            v4 += XXH_readLE32_align((const U32*)p, endian, align) * PRIME32_2; v4 = XXH_rotl32(v4, 13); v4 *= PRIME32_1; p+=4;
+        } while (p<=limit);
+
+        h32 = XXH_rotl32(v1, 1) + XXH_rotl32(v2, 7) + XXH_rotl32(v3, 12) + XXH_rotl32(v4, 18);
+    }
+    else
+    {
+        h32  = seed + PRIME32_5;
+    }
+
+    h32 += (U32) len;
+
+    while (p<=bEnd-4)
+    {
+        h32 += XXH_readLE32_align((const U32*)p, endian, align) * PRIME32_3;
+        h32  = XXH_rotl32(h32, 17) * PRIME32_4 ;
+        p+=4;
+    }
+
+    while (p<bEnd)
+    {
+        h32 += (*p) * PRIME32_5;
+        h32 = XXH_rotl32(h32, 11) * PRIME32_1 ;
+        p++;
+    }
+
+    h32 ^= h32 >> 15;
+    h32 *= PRIME32_2;
+    h32 ^= h32 >> 13;
+    h32 *= PRIME32_3;
+    h32 ^= h32 >> 16;
+
+    return h32;
+}
+
+
+U32 XXH32(const void* input, int len, U32 seed)
+{
+#if 0
+    // Simple version, good for code maintenance, but unfortunately slow for small inputs
+    void* state = XXH32_init(seed);
+    XXH32_update(state, input, len);
+    return XXH32_digest(state);
+#else
+    XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN;
+
+#  if !defined(XXH_USE_UNALIGNED_ACCESS)
+    if (!(((size_t)input) & 3))   // Input is aligned, let's leverage the speed advantage
+    {
+        if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT)
+            return XXH32_endian_align(input, len, seed, XXH_littleEndian, XXH_aligned);
+        else
+            return XXH32_endian_align(input, len, seed, XXH_bigEndian, XXH_aligned);
+    }
+#  endif
+
+    if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT)
+        return XXH32_endian_align(input, len, seed, XXH_littleEndian, XXH_unaligned);
+    else
+        return XXH32_endian_align(input, len, seed, XXH_bigEndian, XXH_unaligned);
+#endif
+}
+
+
+//****************************
+// Advanced Hash Functions
+//****************************
+
+struct XXH_state32_t
+{
+    U64 total_len;
+    U32 seed;
+    U32 v1;
+    U32 v2;
+    U32 v3;
+    U32 v4;
+    int memsize;
+    char memory[16];
+};
+
+
+int XXH32_sizeofState(void)
+{
+    XXH_STATIC_ASSERT(XXH32_SIZEOFSTATE >= sizeof(struct XXH_state32_t));   // A compilation error here means XXH32_SIZEOFSTATE is not large enough
+    return sizeof(struct XXH_state32_t); 
+}
+
+
+XXH_errorcode XXH32_resetState(void* state_in, U32 seed)
+{ 
+    struct XXH_state32_t * state = (struct XXH_state32_t *) state_in;
+    state->seed = seed;
+    state->v1 = seed + PRIME32_1 + PRIME32_2;
+    state->v2 = seed + PRIME32_2;
+    state->v3 = seed + 0;
+    state->v4 = seed - PRIME32_1;
+    state->total_len = 0;
+    state->memsize = 0;
+    return XXH_OK;
+}
+
+
+void* XXH32_init (U32 seed)
+{
+    void* state = XXH_malloc (sizeof(struct XXH_state32_t));
+    XXH32_resetState(state, seed);
+    return state;
+}
+
+
+forceinline XXH_errorcode XXH32_update_endian (void* state_in, const void* input, int len, XXH_endianess endian)
+{
+    struct XXH_state32_t * state = (struct XXH_state32_t *) state_in;
+    const BYTE* p = (const BYTE*)input;
+    const BYTE* const bEnd = p + len;
+
+#ifdef XXH_ACCEPT_NULL_INPUT_POINTER
+    if (input==NULL) return XXH_ERROR;
+#endif
+
+    state->total_len += len;
+
+    if (state->memsize + len < 16)   // fill in tmp buffer
+    {
+        XXH_memcpy(state->memory + state->memsize, input, len);
+        state->memsize +=  len;
+        return XXH_OK;
+    }
+
+    if (state->memsize)   // some data left from previous update
+    {
+        XXH_memcpy(state->memory + state->memsize, input, 16-state->memsize);
+        {
+            const U32* p32 = (const U32*)state->memory;
+            state->v1 += XXH_readLE32(p32, endian) * PRIME32_2; state->v1 = XXH_rotl32(state->v1, 13); state->v1 *= PRIME32_1; p32++;
+            state->v2 += XXH_readLE32(p32, endian) * PRIME32_2; state->v2 = XXH_rotl32(state->v2, 13); state->v2 *= PRIME32_1; p32++; 
+            state->v3 += XXH_readLE32(p32, endian) * PRIME32_2; state->v3 = XXH_rotl32(state->v3, 13); state->v3 *= PRIME32_1; p32++;
+            state->v4 += XXH_readLE32(p32, endian) * PRIME32_2; state->v4 = XXH_rotl32(state->v4, 13); state->v4 *= PRIME32_1; p32++;
+        }
+        p += 16-state->memsize;
+        state->memsize = 0;
+    }
+
+    if (p <= bEnd-16)
+    {
+        const BYTE* const limit = bEnd - 16;
+        U32 v1 = state->v1;
+        U32 v2 = state->v2;
+        U32 v3 = state->v3;
+        U32 v4 = state->v4;
+
+        do
+        {
+            v1 += XXH_readLE32((const U32*)p, endian) * PRIME32_2; v1 = XXH_rotl32(v1, 13); v1 *= PRIME32_1; p+=4;
+            v2 += XXH_readLE32((const U32*)p, endian) * PRIME32_2; v2 = XXH_rotl32(v2, 13); v2 *= PRIME32_1; p+=4;
+            v3 += XXH_readLE32((const U32*)p, endian) * PRIME32_2; v3 = XXH_rotl32(v3, 13); v3 *= PRIME32_1; p+=4;
+            v4 += XXH_readLE32((const U32*)p, endian) * PRIME32_2; v4 = XXH_rotl32(v4, 13); v4 *= PRIME32_1; p+=4;
+        } while (p<=limit);
+
+        state->v1 = v1;
+        state->v2 = v2;
+        state->v3 = v3;
+        state->v4 = v4;
+    }
+
+    if (p < bEnd)
+    {
+        XXH_memcpy(state->memory, p, bEnd-p);
+        state->memsize = (int)(bEnd-p);
+    }
+
+    return XXH_OK;
+}
+
+XXH_errorcode XXH32_update (void* state_in, const void* input, int len)
+{
+    XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN;
+    
+    if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT)
+        return XXH32_update_endian(state_in, input, len, XXH_littleEndian);
+    else
+        return XXH32_update_endian(state_in, input, len, XXH_bigEndian);
+}
+
+
+
+forceinline U32 XXH32_intermediateDigest_endian (void* state_in, XXH_endianess endian)
+{
+    struct XXH_state32_t * state = (struct XXH_state32_t *) state_in;
+    const BYTE * p = (const BYTE*)state->memory;
+    BYTE* bEnd = (BYTE*)state->memory + state->memsize;
+    U32 h32;
+
+    if (state->total_len >= 16)
+    {
+        h32 = XXH_rotl32(state->v1, 1) + XXH_rotl32(state->v2, 7) + XXH_rotl32(state->v3, 12) + XXH_rotl32(state->v4, 18);
+    }
+    else
+    {
+        h32  = state->seed + PRIME32_5;
+    }
+
+    h32 += (U32) state->total_len;
+
+    while (p<=bEnd-4)
+    {
+        h32 += XXH_readLE32((const U32*)p, endian) * PRIME32_3;
+        h32  = XXH_rotl32(h32, 17) * PRIME32_4;
+        p+=4;
+    }
+
+    while (p<bEnd)
+    {
+        h32 += (*p) * PRIME32_5;
+        h32 = XXH_rotl32(h32, 11) * PRIME32_1;
+        p++;
+    }
+
+    h32 ^= h32 >> 15;
+    h32 *= PRIME32_2;
+    h32 ^= h32 >> 13;
+    h32 *= PRIME32_3;
+    h32 ^= h32 >> 16;
+
+    return h32;
+}
+
+
+U32 XXH32_intermediateDigest (void* state_in)
+{
+    XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN;
+    
+    if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT)
+        return XXH32_intermediateDigest_endian(state_in, XXH_littleEndian);
+    else
+        return XXH32_intermediateDigest_endian(state_in, XXH_bigEndian);
+}
+
+
+U32 XXH32_digest (void* state_in)
+{
+    U32 h32 = XXH32_intermediateDigest(state_in);
+
+    XXH_free(state_in);
+
+    return h32;
+}

Added: vendor/libucl/dist/src/xxhash.h
===================================================================
--- vendor/libucl/dist/src/xxhash.h	                        (rev 0)
+++ vendor/libucl/dist/src/xxhash.h	2018-06-19 13:29:00 UTC (rev 11074)
@@ -0,0 +1,164 @@
+/*
+   xxHash - Fast Hash algorithm
+   Header File
+   Copyright (C) 2012-2013, Yann Collet.
+   BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
+
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions are
+   met:
+  
+       * Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+       * Redistributions in binary form must reproduce the above
+   copyright notice, this list of conditions and the following disclaimer
+   in the documentation and/or other materials provided with the
+   distribution.
+  
+   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+   You can contact the author at :
+   - xxHash source repository : http://code.google.com/p/xxhash/
+*/
+
+/* Notice extracted from xxHash homepage :
+
+xxHash is an extremely fast Hash algorithm, running at RAM speed limits.
+It also successfully passes all tests from the SMHasher suite.
+
+Comparison (single thread, Windows Seven 32 bits, using SMHasher on a Core 2 Duo @3GHz)
+
+Name            Speed       Q.Score   Author
+xxHash          5.4 GB/s     10
+CrapWow         3.2 GB/s      2       Andrew
+MumurHash 3a    2.7 GB/s     10       Austin Appleby
+SpookyHash      2.0 GB/s     10       Bob Jenkins
+SBox            1.4 GB/s      9       Bret Mulvey
+Lookup3         1.2 GB/s      9       Bob Jenkins
+SuperFastHash   1.2 GB/s      1       Paul Hsieh
+CityHash64      1.05 GB/s    10       Pike & Alakuijala
+FNV             0.55 GB/s     5       Fowler, Noll, Vo
+CRC32           0.43 GB/s     9
+MD5-32          0.33 GB/s    10       Ronald L. Rivest
+SHA1-32         0.28 GB/s    10
+
+Q.Score is a measure of quality of the hash function. 
+It depends on successfully passing SMHasher test set. 
+10 is a perfect score.
+*/
+
+#pragma once
+
+#if defined (__cplusplus)
+extern "C" {
+#endif
+
+
+//****************************
+// Type
+//****************************
+typedef enum { XXH_OK=0, XXH_ERROR } XXH_errorcode;
+
+
+
+//****************************
+// Simple Hash Functions
+//****************************
+
+unsigned int XXH32 (const void* input, int len, unsigned int seed);
+
+/*
+XXH32() :
+    Calculate the 32-bits hash of sequence of length "len" stored at memory address "input".
+    The memory between input & input+len must be valid (allocated and read-accessible).
+    "seed" can be used to alter the result predictably.
+    This function successfully passes all SMHasher tests.
+    Speed on Core 2 Duo @ 3 GHz (single thread, SMHasher benchmark) : 5.4 GB/s
+    Note that "len" is type "int", which means it is limited to 2^31-1.
+    If your data is larger, use the advanced functions below.
+*/
+
+
+
+//****************************
+// Advanced Hash Functions
+//****************************
+
+void*         XXH32_init   (unsigned int seed);
+XXH_errorcode XXH32_update (void* state, const void* input, int len);
+unsigned int  XXH32_digest (void* state);
+
+/*
+These functions calculate the xxhash of an input provided in several small packets,
+as opposed to an input provided as a single block.
+
+It must be started with :
+void* XXH32_init()
+The function returns a pointer which holds the state of calculation.
+
+This pointer must be provided as "void* state" parameter for XXH32_update().
+XXH32_update() can be called as many times as necessary.
+The user must provide a valid (allocated) input.
+The function returns an error code, with 0 meaning OK, and any other value meaning there is an error.
+Note that "len" is type "int", which means it is limited to 2^31-1. 
+If your data is larger, it is recommended to chunk your data into blocks 
+of size for example 2^30 (1GB) to avoid any "int" overflow issue.
+
+Finally, you can end the calculation anytime, by using XXH32_digest().
+This function returns the final 32-bits hash.
+You must provide the same "void* state" parameter created by XXH32_init().
+Memory will be freed by XXH32_digest().
+*/
+
+
+int           XXH32_sizeofState(void);
+XXH_errorcode XXH32_resetState(void* state, unsigned int seed);
+
+#define       XXH32_SIZEOFSTATE 48
+typedef struct { long long ll[(XXH32_SIZEOFSTATE+(sizeof(long long)-1))/sizeof(long long)]; } XXH32_stateSpace_t;
+/*
+These functions allow user application to make its own allocation for state.
+
+XXH32_sizeofState() is used to know how much space must be allocated for the xxHash 32-bits state.
+Note that the state must be aligned to access 'long long' fields. Memory must be allocated and referenced by a pointer.
+This pointer must then be provided as 'state' into XXH32_resetState(), which initializes the state.
+
+For static allocation purposes (such as allocation on stack, or freestanding systems without malloc()),
+use the structure XXH32_stateSpace_t, which will ensure that memory space is large enough and correctly aligned to access 'long long' fields.
+*/
+
+
+unsigned int XXH32_intermediateDigest (void* state);
+/*
+This function does the same as XXH32_digest(), generating a 32-bit hash,
+but preserve memory context.
+This way, it becomes possible to generate intermediate hashes, and then continue feeding data with XXH32_update().
+To free memory context, use XXH32_digest(), or free().
+*/
+
+
+
+//****************************
+// Deprecated function names
+//****************************
+// The following translations are provided to ease code transition
+// You are encouraged to no longer this function names
+#define XXH32_feed   XXH32_update
+#define XXH32_result XXH32_digest
+#define XXH32_getIntermediateResult XXH32_intermediateDigest
+
+
+
+#if defined (__cplusplus)
+}
+#endif

Added: vendor/libucl/dist/stamp-h.in
===================================================================
--- vendor/libucl/dist/stamp-h.in	                        (rev 0)
+++ vendor/libucl/dist/stamp-h.in	2018-06-19 13:29:00 UTC (rev 11074)
@@ -0,0 +1 @@
+timestamp

Added: vendor/libucl/dist/tests/Makefile.am
===================================================================
--- vendor/libucl/dist/tests/Makefile.am	                        (rev 0)
+++ vendor/libucl/dist/tests/Makefile.am	2018-06-19 13:29:00 UTC (rev 11074)
@@ -0,0 +1,33 @@
+EXTRA_DIST = $(TESTS) basic schema generate.res rcl_test.json.xz
+
+TESTS = basic.test \
+		generate.test \
+		schema.test \
+		speed.test
+TESTS_ENVIRONMENT = $(SH) \
+			TEST_DIR=$(top_srcdir)/tests \
+			TEST_OUT_DIR=$(top_builddir)/tests \
+			TEST_BINARY_DIR=$(top_builddir)/tests
+
+common_test_cflags = -I$(top_srcdir)/include \
+					-I$(top_srcdir)/src \
+					-I$(top_srcdir)/uthash
+common_test_ldadd = $(top_builddir)/src/libucl.la
+
+test_basic_SOURCES = test_basic.c
+test_basic_LDADD = $(common_test_ldadd)
+test_basic_CFLAGS = $(common_test_cflags)
+
+test_speed_SOURCES = test_speed.c
+test_speed_LDADD = $(common_test_ldadd)
+test_speed_CFLAGS = $(common_test_cflags)
+
+test_generate_SOURCES = test_generate.c
+test_generate_LDADD = $(common_test_ldadd)
+test_generate_CFLAGS = $(common_test_cflags)
+
+test_schema_SOURCES = test_schema.c
+test_schema_LDADD = $(common_test_ldadd)
+test_schema_CFLAGS = $(common_test_cflags)
+
+check_PROGRAMS = test_basic test_speed test_generate test_schema

Added: vendor/libucl/dist/tests/basic/1.in
===================================================================
--- vendor/libucl/dist/tests/basic/1.in	                        (rev 0)
+++ vendor/libucl/dist/tests/basic/1.in	2018-06-19 13:29:00 UTC (rev 11074)
@@ -0,0 +1,14 @@
+{
+"key1": value;
+"key1": value2;
+"key1": "value;"
+"key1": 1.0,
+"key1": -0xdeadbeef
+"key1": 0xdeadbeef.1
+"key1": 0xreadbeef
+"key1": -1e-10,
+"key1": 1
+"key1": true
+"key1": no
+"key1": yes
+}

Added: vendor/libucl/dist/tests/basic/1.res
===================================================================
--- vendor/libucl/dist/tests/basic/1.res	                        (rev 0)
+++ vendor/libucl/dist/tests/basic/1.res	2018-06-19 13:29:00 UTC (rev 11074)
@@ -0,0 +1,13 @@
+key1 = "value";
+key1 = "value2";
+key1 = "value;";
+key1 = 1.0;
+key1 = -3735928559;
+key1 = "0xdeadbeef.1";
+key1 = "0xreadbeef";
+key1 = -1e-10;
+key1 = 1;
+key1 = true;
+key1 = false;
+key1 = true;
+

Added: vendor/libucl/dist/tests/basic/10.in
===================================================================
--- vendor/libucl/dist/tests/basic/10.in	                        (rev 0)
+++ vendor/libucl/dist/tests/basic/10.in	2018-06-19 13:29:00 UTC (rev 11074)
@@ -0,0 +1,9 @@
+section foo bar {
+	  key = value
+}
+section foo baz {
+	  key = value
+}
+section foo {
+	  bar = lol /* removing this line makes parsing successful */
+}

Added: vendor/libucl/dist/tests/basic/2.in
===================================================================
--- vendor/libucl/dist/tests/basic/2.in	                        (rev 0)
+++ vendor/libucl/dist/tests/basic/2.in	2018-06-19 13:29:00 UTC (rev 11074)
@@ -0,0 +1,24 @@
+section1 { param1 = value; param2 = value, 
+section3 {param = value; param2 = value, param3 = ["value1", value2, 100500]}}
+section2 { param1 = {key = value}, param1 = ["key"]}
+
+# Numbers
+key1 = 1s
+key2 = 1min
+key3 = 1kb
+key4 = 5M
+key5 = 10mS
+key6 = 10y
+
+# Strings
+key1 = "some string";
+key2 = /some/path;
+key3 = 111some,
+key4: s1,
+"key5": "\n\r123"
+
+# Variables
+keyvar = "$ABItest";
+keyvar = "${ABI}$ABI${ABI}${$ABI}";
+keyvar = "${some}$no${}$$test$$$$$$$";
+keyvar = "$ABI$$ABI$$$ABI$$$$";

Added: vendor/libucl/dist/tests/basic/2.res
===================================================================
--- vendor/libucl/dist/tests/basic/2.res	                        (rev 0)
+++ vendor/libucl/dist/tests/basic/2.res	2018-06-19 13:29:00 UTC (rev 11074)
@@ -0,0 +1,37 @@
+section1 {
+    param1 = "value";
+    param2 = "value";
+    section3 {
+        param = "value";
+        param2 = "value";
+        param3 [
+            "value1",
+            "value2",
+            100500,
+        ]
+    }
+}
+section2 {
+    param1 {
+        key = "value";
+    }
+    param1 [
+        "key",
+    ]
+}
+key1 = 1.0;
+key1 = "some string";
+key2 = 60.0;
+key2 = "/some/path";
+key3 = 1024;
+key3 = "111some";
+key4 = 5000000;
+key4 = "s1";
+key5 = 0.010000;
+key5 = "\n\r123";
+key6 = 2207520000.000000;
+keyvar = "unknowntest";
+keyvar = "unknownunknownunknown${unknown}";
+keyvar = "${some}$no${}$$test$$$$$$$";
+keyvar = "unknown$ABI$unknown$$";
+

Added: vendor/libucl/dist/tests/basic/3.in
===================================================================
--- vendor/libucl/dist/tests/basic/3.in	                        (rev 0)
+++ vendor/libucl/dist/tests/basic/3.in	2018-06-19 13:29:00 UTC (rev 11074)
@@ -0,0 +1,31 @@
+/*
+ * Pkg conf
+ */
+
+#packagesite http//pkg.freebsd.org/freebsd-9-amd64/latest
+#packagesite http//pkg.freebsd.org/freebsd-9-amd64/latest
+packagesite: http://pkg-test.freebsd.org/pkg-test/${ABI}/latest
+squaretest: some[]value
+ALIAS             : {
+  all-depends: query %dn-%dv,
+  annotations: info -A,
+  build-depends: info -qd,
+  download: fetch,
+  iinfo: info -i -g -x,
+  isearch: search -i -g -x,
+  leaf: query -e '%a == 0' '%n-%v',
+  leaf: query -e '%a == 0' '%n-%v',
+  list: info -ql,
+  origin: info -qo,
+  provided-depends: info -qb,
+  raw: info -R,
+  required-depends: info -qr,
+  shared-depends: info -qB,
+  show: info -f -k,
+  size: info -sq,
+  }
+
+repo_dirs : [
+  /home/bapt,
+  /usr/local/etc
+]

Added: vendor/libucl/dist/tests/basic/3.res
===================================================================
--- vendor/libucl/dist/tests/basic/3.res	                        (rev 0)
+++ vendor/libucl/dist/tests/basic/3.res	2018-06-19 13:29:00 UTC (rev 11074)
@@ -0,0 +1,25 @@
+packagesite = "http://pkg-test.freebsd.org/pkg-test/unknown/latest";
+squaretest = "some[]value";
+alias {
+    all-depends = "query %dn-%dv";
+    annotations = "info -A";
+    build-depends = "info -qd";
+    download = "fetch";
+    iinfo = "info -i -g -x";
+    isearch = "search -i -g -x";
+    leaf = "query -e '%a == 0' '%n-%v'";
+    leaf = "query -e '%a == 0' '%n-%v'";
+    list = "info -ql";
+    origin = "info -qo";
+    provided-depends = "info -qb";
+    raw = "info -R";
+    required-depends = "info -qr";
+    shared-depends = "info -qB";
+    show = "info -f -k";
+    size = "info -sq";
+}
+repo_dirs [
+    "/home/bapt",
+    "/usr/local/etc",
+]
+

Added: vendor/libucl/dist/tests/basic/4.in
===================================================================
--- vendor/libucl/dist/tests/basic/4.in	                        (rev 0)
+++ vendor/libucl/dist/tests/basic/4.in	2018-06-19 13:29:00 UTC (rev 11074)
@@ -0,0 +1,47 @@
+name : "pkgconf"
+version : "0.9.3"
+origin : "devel/pkgconf"
+comment : "Utility to help to configure compiler and linker flags"
+arch : "freebsd:9:x86:64"
+maintainer : "bapt at FreeBSD.org"
+prefix : "/usr/local"
+licenselogic : "single"
+licenses : [
+    "BSD",
+]
+flatsize : 60523
+desc : "pkgconf is a program which helps to configure compiler and linker flags for\ndevelopment frameworks. It is similar to pkg-config, but was written from\nscratch in Summer of 2011 to replace pkg-config, which now needs itself to build\nitself.\n\nWWW: https://github.com/pkgconf/pkgconf"
+categories : [
+    "devel",
+]
+files : {
+    /usr/local/bin/pkg-config : "-",
+    /usr/local/bin/pkgconf : "4a0fc53e5ad64e8085da2e61652d61c50b192a086421d865703f1de9f724da38",
+    /usr/local/share/aclocal/pkg.m4 : "cffab33d659adfe36497ec57665eec36fa6fb7b007e578e6ac2434cc28be8820",
+    /usr/local/share/licenses/pkgconf-0.9.3/BSD : "85e7a53b5e2d3e350e2d084fed2f94b7f63005f8e1168740e1e84aa9fa5d48ce",
+    /usr/local/share/licenses/pkgconf-0.9.3/LICENSE : "d9cce0db43502eb1bd8fbef7e960cfaa43b5647186f7f7379923b336209fd77b",
+    /usr/local/share/licenses/pkgconf-0.9.3/catalog.mk : "e7b131acce7c3d3c61f2214607b11b34526e03b05afe89a608f50586a898e2ef",
+}
+directories : {
+    /usr/local/share/licenses/pkgconf-0.9.3/ : false,
+    /usr/local/share/licenses/ : true,
+}
+scripts : {
+    post-install : "cd /usr/local\nn",
+    pre-deinstall : "cd /usr/local\nn",
+    post-deinstall : "cd /usr/local\nn",
+}
+multiline-key : <<EOD
+test
+test
+test\n
+/* comment like */
+# Some invalid endings
+ EOD
+EOD   
+EOF
+# Valid ending + empty string
+
+EOD
+
+normal-key : <<EODnot

Added: vendor/libucl/dist/tests/basic/4.res
===================================================================
--- vendor/libucl/dist/tests/basic/4.res	                        (rev 0)
+++ vendor/libucl/dist/tests/basic/4.res	2018-06-19 13:29:00 UTC (rev 11074)
@@ -0,0 +1,36 @@
+name = "pkgconf";
+version = "0.9.3";
+origin = "devel/pkgconf";
+comment = "Utility to help to configure compiler and linker flags";
+arch = "freebsd:9:x86:64";
+maintainer = "bapt at FreeBSD.org";
+prefix = "/usr/local";
+licenselogic = "single";
+licenses [
+    "BSD",
+]
+flatsize = 60523;
+desc = "pkgconf is a program which helps to configure compiler and linker flags for\ndevelopment frameworks. It is similar to pkg-config, but was written from\nscratch in Summer of 2011 to replace pkg-config, which now needs itself to build\nitself.\n\nWWW: https://github.com/pkgconf/pkgconf";
+categories [
+    "devel",
+]
+files {
+    /usr/local/bin/pkg-config = "-";
+    /usr/local/bin/pkgconf = "4a0fc53e5ad64e8085da2e61652d61c50b192a086421d865703f1de9f724da38";
+    /usr/local/share/aclocal/pkg.m4 = "cffab33d659adfe36497ec57665eec36fa6fb7b007e578e6ac2434cc28be8820";
+    /usr/local/share/licenses/pkgconf-0.9.3/bsd = "85e7a53b5e2d3e350e2d084fed2f94b7f63005f8e1168740e1e84aa9fa5d48ce";
+    /usr/local/share/licenses/pkgconf-0.9.3/license = "d9cce0db43502eb1bd8fbef7e960cfaa43b5647186f7f7379923b336209fd77b";
+    /usr/local/share/licenses/pkgconf-0.9.3/catalog.mk = "e7b131acce7c3d3c61f2214607b11b34526e03b05afe89a608f50586a898e2ef";
+}
+directories {
+    /usr/local/share/licenses/pkgconf-0.9.3/ = false;
+    /usr/local/share/licenses/ = true;
+}
+scripts {
+    post-install = "cd /usr/local\nn";
+    pre-deinstall = "cd /usr/local\nn";
+    post-deinstall = "cd /usr/local\nn";
+}
+multiline-key = "test\ntest\ntest\\n\n/* comment like */\n# Some invalid endings\n EOD\nEOD   \nEOF\n# Valid ending + empty string\n";
+normal-key = "<<EODnot";
+

Added: vendor/libucl/dist/tests/basic/6.in
===================================================================
--- vendor/libucl/dist/tests/basic/6.in	                        (rev 0)
+++ vendor/libucl/dist/tests/basic/6.in	2018-06-19 13:29:00 UTC (rev 11074)
@@ -0,0 +1,4 @@
+
+# test
+#
+key = value

Added: vendor/libucl/dist/tests/basic/6.res
===================================================================
--- vendor/libucl/dist/tests/basic/6.res	                        (rev 0)
+++ vendor/libucl/dist/tests/basic/6.res	2018-06-19 13:29:00 UTC (rev 11074)
@@ -0,0 +1,2 @@
+key = "value";
+

Added: vendor/libucl/dist/tests/basic/8.in
===================================================================
--- vendor/libucl/dist/tests/basic/8.in	                        (rev 0)
+++ vendor/libucl/dist/tests/basic/8.in	2018-06-19 13:29:00 UTC (rev 11074)
@@ -0,0 +1,23 @@
+section blah { # test
+	param = "value"
+}
+section test {
+	key = test;
+	subsection testsub {
+		flag on;
+		subsubsection testsubsub1 testsubsub2 {
+			key = [1, 2, 3];
+		}
+	}
+}
+
+section test {
+	/* Empty */
+}
+
+
+section foo { # test
+	param = 123.2;
+}
+
+array = []

Added: vendor/libucl/dist/tests/basic/8.res
===================================================================
--- vendor/libucl/dist/tests/basic/8.res	                        (rev 0)
+++ vendor/libucl/dist/tests/basic/8.res	2018-06-19 13:29:00 UTC (rev 11074)
@@ -0,0 +1,38 @@
+section {
+    blah {
+        param = "value";
+    }
+}
+section {
+    test {
+        key = "test";
+        subsection {
+            testsub {
+                flag = true;
+                subsubsection {
+                    testsubsub1 {
+                        testsubsub2 {
+                            key [
+                                1,
+                                2,
+                                3,
+                            ]
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
+section {
+    test {
+    }
+}
+section {
+    foo {
+        param = 123.200000;
+    }
+}
+array [
+]
+

Added: vendor/libucl/dist/tests/basic/9-comment.inc
===================================================================
--- vendor/libucl/dist/tests/basic/9-comment.inc	                        (rev 0)
+++ vendor/libucl/dist/tests/basic/9-comment.inc	2018-06-19 13:29:00 UTC (rev 11074)
@@ -0,0 +1 @@
+#key = value

Added: vendor/libucl/dist/tests/basic/9-empty.inc
===================================================================
Added: vendor/libucl/dist/tests/basic/9.in
===================================================================
--- vendor/libucl/dist/tests/basic/9.in	                        (rev 0)
+++ vendor/libucl/dist/tests/basic/9.in	2018-06-19 13:29:00 UTC (rev 11074)
@@ -0,0 +1,12 @@
+.include "$CURDIR/9.inc"
+.include "$CURDIR/9-comment.inc"
+#.include "$CURDIR/9.inc"
+.include "$CURDIR/9.inc"
+
+key = value;
+.include "$CURDIR/9.inc"
+
+.try_include "/non/existent"
+#.try_include "$CURDIR/9.incorrect.inc"
+# 9.incorrect.inc contains '{}}'
+#key = value;

Added: vendor/libucl/dist/tests/basic/9.inc
===================================================================
--- vendor/libucl/dist/tests/basic/9.inc	                        (rev 0)
+++ vendor/libucl/dist/tests/basic/9.inc	2018-06-19 13:29:00 UTC (rev 11074)
@@ -0,0 +1 @@
+key1 = value

Added: vendor/libucl/dist/tests/basic/9.res
===================================================================
--- vendor/libucl/dist/tests/basic/9.res	                        (rev 0)
+++ vendor/libucl/dist/tests/basic/9.res	2018-06-19 13:29:00 UTC (rev 11074)
@@ -0,0 +1,5 @@
+key1 = "value";
+key1 = "value";
+key1 = "value";
+key = "value";
+

Added: vendor/libucl/dist/tests/basic.test
===================================================================
--- vendor/libucl/dist/tests/basic.test	                        (rev 0)
+++ vendor/libucl/dist/tests/basic.test	2018-06-19 13:29:00 UTC (rev 11074)
@@ -0,0 +1,26 @@
+#!/bin/sh
+
+PROG=${TEST_BINARY_DIR}/test_basic
+
+for _tin in ${TEST_DIR}/basic/*.in ; do
+	_t=`echo $_tin | sed -e 's/.in$//'`
+	_out=${TEST_OUT_DIR}/basic.out
+	$PROG $_t.in $_out
+	if [ $? -ne 0 ] ; then
+		echo "Test: $_t failed, output:"
+		cat $_out
+		rm $_out
+		exit 1
+	fi
+	if [ -f $_t.res ] ; then
+	diff -s $_out $_t.res -u 2>/dev/null
+		if [ $? -ne 0 ] ; then
+			rm $_out
+			echo "Test: $_t output missmatch"
+			exit 1
+		fi
+	fi
+	rm $_out
+done
+
+

Added: vendor/libucl/dist/tests/generate.res
===================================================================
--- vendor/libucl/dist/tests/generate.res	                        (rev 0)
+++ vendor/libucl/dist/tests/generate.res	2018-06-19 13:29:00 UTC (rev 11074)
@@ -0,0 +1,20 @@
+key1 = "test string";
+key2 = "test \\nstring";
+key3 = "  test string    \n";
+key4 [
+    9.999000,
+    10,
+    10.100000,
+]
+key4 = true;
+key5 = "";
+key6 = "";
+key7 = "   \\n";
+key8 = 1048576;
+key9 = 3.140000;
+key10 = true;
+key11 = false;
+key12 = "gslin at gslin.org";
+key13 = "#test";
+"k=3" = true;
+

Added: vendor/libucl/dist/tests/generate.test
===================================================================
--- vendor/libucl/dist/tests/generate.test	                        (rev 0)
+++ vendor/libucl/dist/tests/generate.test	2018-06-19 13:29:00 UTC (rev 11074)
@@ -0,0 +1,13 @@
+#!/bin/sh
+
+PROG=${TEST_BINARY_DIR}/test_generate
+
+$PROG ${TEST_OUT_DIR}/generate.out
+diff -s ${TEST_OUT_DIR}/generate.out ${TEST_DIR}/generate.res -u 2>/dev/null
+if [ $? -ne 0 ] ; then
+	rm ${TEST_OUT_DIR}/generate.out
+	echo "Test: generate.res output missmatch"
+	exit 1
+fi
+rm ${TEST_OUT_DIR}/generate.out
+

Added: vendor/libucl/dist/tests/run_tests.sh
===================================================================
--- vendor/libucl/dist/tests/run_tests.sh	                        (rev 0)
+++ vendor/libucl/dist/tests/run_tests.sh	2018-06-19 13:29:00 UTC (rev 11074)
@@ -0,0 +1,67 @@
+#!/bin/sh
+
+if [ $# -lt 1 ] ; then
+	echo 'Specify binary to run as the first argument'
+	exit 1
+fi
+
+
+for _tin in ${TEST_DIR}/*.in ; do
+	_t=`echo $_tin | sed -e 's/.in$//'`
+	$1 $_t.in $_t.out
+	if [ $? -ne 0 ] ; then
+		echo "Test: $_t failed, output:"
+		cat $_t.out
+		rm $_t.out
+		exit 1
+	fi
+	if [ -f $_t.res ] ; then
+	diff -s $_t.out $_t.res -u 2>/dev/null
+		if [ $? -ne 0 ] ; then
+			rm $_t.out
+			echo "Test: $_t output missmatch"
+			exit 1
+		fi
+	fi
+	rm $_t.out
+done
+
+if [ $# -gt 2 ] ; then
+	$3 ${TEST_DIR}/generate.out
+	diff -s ${TEST_DIR}/generate.out ${TEST_DIR}/generate.res -u 2>/dev/null
+	if [ $? -ne 0 ] ; then
+		rm ${TEST_DIR}/generate.out
+		echo "Test: generate.res output missmatch"
+    	exit 1
+	fi
+	rm ${TEST_DIR}/generate.out
+fi
+
+if [ $# -gt 3 ] ; then
+	rm /tmp/_ucl_test_schema.out ||true
+	for i in ${TEST_DIR}/schema/*.json ; do
+		_name=`basename $i`
+		printf "running schema test suite $_name... "
+		cat $i | $4 >> /tmp/_ucl_test_schema.out && ( echo "OK" ) || ( echo "Fail" )
+	done
+fi
+
+sh -c "xz -c < /dev/null > /dev/null"
+if [ $? -eq 0 -a $# -gt 1 ] ; then
+	echo 'Running speed tests'
+	for _tin in ${TEST_DIR}/*.xz ; do
+		echo "Unpacking $_tin..."
+		xz -cd < $_tin > ${TEST_DIR}/test_file
+		# Preread file to cheat benchmark!
+		cat ${TEST_DIR}/test_file > /dev/null
+		echo "Starting benchmarking for $_tin..."
+		$2 ${TEST_DIR}/test_file
+		if [ $? -ne 0 ] ; then
+			echo "Test: $_tin failed"
+			rm ${TEST_DIR}/test_file
+			exit 1
+		fi
+		rm ${TEST_DIR}/test_file
+	done
+fi
+

Added: vendor/libucl/dist/tests/schema/additionalItems.json
===================================================================
--- vendor/libucl/dist/tests/schema/additionalItems.json	                        (rev 0)
+++ vendor/libucl/dist/tests/schema/additionalItems.json	2018-06-19 13:29:00 UTC (rev 11074)
@@ -0,0 +1,82 @@
+[
+    {
+        "description": "additionalItems as schema",
+        "schema": {
+            "items": [{}],
+            "additionalItems": {"type": "integer"}
+        },
+        "tests": [
+            {
+                "description": "additional items match schema",
+                "data": [ null, 2, 3, 4 ],
+                "valid": true
+            },
+            {
+                "description": "additional items do not match schema",
+                "data": [ null, 2, 3, "foo" ],
+                "valid": false
+            }
+        ]
+    },
+    {
+        "description": "items is schema, no additionalItems",
+        "schema": {
+            "items": {},
+            "additionalItems": false
+        },
+        "tests": [
+            {
+                "description": "all items match schema",
+                "data": [ 1, 2, 3, 4, 5 ],
+                "valid": true
+            }
+        ]
+    },
+    {
+        "description": "array of items with no additionalItems",
+        "schema": {
+            "items": [{}, {}, {}],
+            "additionalItems": false
+        },
+        "tests": [
+            {
+                "description": "no additional items present",
+                "data": [ 1, 2, 3 ],
+                "valid": true
+            },
+            {
+                "description": "additional items are not permitted",
+                "data": [ 1, 2, 3, 4 ],
+                "valid": false
+            }
+        ]
+    },
+    {
+        "description": "additionalItems as false without items",
+        "schema": {"additionalItems": false},
+        "tests": [
+            {
+                "description":
+                    "items defaults to empty schema so everything is valid",
+                "data": [ 1, 2, 3, 4, 5 ],
+                "valid": true
+            },
+            {
+                "description": "ignores non-arrays",
+                "data": {"foo" : "bar"},
+                "valid": true
+            }
+        ]
+    },
+    {
+        "description": "additionalItems are allowed by default",
+        "schema": {"items": [{"type": "integer"}]},
+        "tests": [
+            {
+                "description": "only the first item is validated",
+                "data": [1, "foo", false],
+                "valid": true
+            }
+        ]
+    }
+]

Added: vendor/libucl/dist/tests/schema/additionalProperties.json
===================================================================
--- vendor/libucl/dist/tests/schema/additionalProperties.json	                        (rev 0)
+++ vendor/libucl/dist/tests/schema/additionalProperties.json	2018-06-19 13:29:00 UTC (rev 11074)
@@ -0,0 +1,69 @@
+[
+    {
+        "description":
+            "additionalProperties being false does not allow other properties",
+        "schema": {
+            "properties": {"foo": {}, "bar": {}},
+            "patternProperties": { "^v": {} },
+            "additionalProperties": false
+        },
+        "tests": [
+            {
+                "description": "no additional properties is valid",
+                "data": {"foo": 1},
+                "valid": true
+            },
+            {
+                "description": "an additional property is invalid",
+                "data": {"foo" : 1, "bar" : 2, "quux" : "boom"},
+                "valid": false
+            },
+            {
+                "description": "ignores non-objects",
+                "data": [1, 2, 3],
+                "valid": true
+            },
+            {
+                "description": "patternProperties are not additional properties",
+                "data": {"foo":1, "vroom": 2},
+                "valid": true
+            }
+        ]
+    },
+    {
+        "description":
+            "additionalProperties allows a schema which should validate",
+        "schema": {
+            "properties": {"foo": {}, "bar": {}},
+            "additionalProperties": {"type": "boolean"}
+        },
+        "tests": [
+            {
+                "description": "no additional properties is valid",
+                "data": {"foo": 1},
+                "valid": true
+            },
+            {
+                "description": "an additional valid property is valid",
+                "data": {"foo" : 1, "bar" : 2, "quux" : true},
+                "valid": true
+            },
+            {
+                "description": "an additional invalid property is invalid",
+                "data": {"foo" : 1, "bar" : 2, "quux" : 12},
+                "valid": false
+            }
+        ]
+    },
+    {
+        "description": "additionalProperties are allowed by default",
+        "schema": {"properties": {"foo": {}, "bar": {}}},
+        "tests": [
+            {
+                "description": "additional properties are allowed",
+                "data": {"foo": 1, "bar": 2, "quux": true},
+                "valid": true
+            }
+        ]
+    }
+]

Added: vendor/libucl/dist/tests/schema/allOf.json
===================================================================
--- vendor/libucl/dist/tests/schema/allOf.json	                        (rev 0)
+++ vendor/libucl/dist/tests/schema/allOf.json	2018-06-19 13:29:00 UTC (rev 11074)
@@ -0,0 +1,112 @@
+[
+    {
+        "description": "allOf",
+        "schema": {
+            "allOf": [
+                {
+                    "properties": {
+                        "bar": {"type": "integer"}
+                    },
+                    "required": ["bar"]
+                },
+                {
+                    "properties": {
+                        "foo": {"type": "string"}
+                    },
+                    "required": ["foo"]
+                }
+            ]
+        },
+        "tests": [
+            {
+                "description": "allOf",
+                "data": {"foo": "baz", "bar": 2},
+                "valid": true
+            },
+            {
+                "description": "mismatch second",
+                "data": {"foo": "baz"},
+                "valid": false
+            },
+            {
+                "description": "mismatch first",
+                "data": {"bar": 2},
+                "valid": false
+            },
+            {
+                "description": "wrong type",
+                "data": {"foo": "baz", "bar": "quux"},
+                "valid": false
+            }
+        ]
+    },
+    {
+        "description": "allOf with base schema",
+        "schema": {
+            "properties": {"bar": {"type": "integer"}},
+            "required": ["bar"],
+            "allOf" : [
+                {
+                    "properties": {
+                        "foo": {"type": "string"}
+                    },
+                    "required": ["foo"]
+                },
+                {
+                    "properties": {
+                        "baz": {"type": "null"}
+                    },
+                    "required": ["baz"]
+                }
+            ]
+        },
+        "tests": [
+            {
+                "description": "valid",
+                "data": {"foo": "quux", "bar": 2, "baz": null},
+                "valid": true
+            },
+            {
+                "description": "mismatch base schema",
+                "data": {"foo": "quux", "baz": null},
+                "valid": false
+            },
+            {
+                "description": "mismatch first allOf",
+                "data": {"bar": 2, "baz": null},
+                "valid": false
+            },
+            {
+                "description": "mismatch second allOf",
+                "data": {"foo": "quux", "bar": 2},
+                "valid": false
+            },
+            {
+                "description": "mismatch both",
+                "data": {"bar": 2},
+                "valid": false
+            }
+        ]
+    },
+    {
+        "description": "allOf simple types",
+        "schema": {
+            "allOf": [
+                {"maximum": 30},
+                {"minimum": 20}
+            ]
+        },
+        "tests": [
+            {
+                "description": "valid",
+                "data": 25,
+                "valid": true
+            },
+            {
+                "description": "mismatch one",
+                "data": 35,
+                "valid": false
+            }
+        ]
+    }
+]

Added: vendor/libucl/dist/tests/schema/anyOf.json
===================================================================
--- vendor/libucl/dist/tests/schema/anyOf.json	                        (rev 0)
+++ vendor/libucl/dist/tests/schema/anyOf.json	2018-06-19 13:29:00 UTC (rev 11074)
@@ -0,0 +1,68 @@
+[
+    {
+        "description": "anyOf",
+        "schema": {
+            "anyOf": [
+                {
+                    "type": "integer"
+                },
+                {
+                    "minimum": 2
+                }
+            ]
+        },
+        "tests": [
+            {
+                "description": "first anyOf valid",
+                "data": 1,
+                "valid": true
+            },
+            {
+                "description": "second anyOf valid",
+                "data": 2.5,
+                "valid": true
+            },
+            {
+                "description": "both anyOf valid",
+                "data": 3,
+                "valid": true
+            },
+            {
+                "description": "neither anyOf valid",
+                "data": 1.5,
+                "valid": false
+            }
+        ]
+    },
+    {
+        "description": "anyOf with base schema",
+        "schema": {
+            "type": "string",
+            "anyOf" : [
+                {
+                    "maxLength": 2
+                },
+                {
+                    "minLength": 4
+                }
+            ]
+        },
+        "tests": [
+            {
+                "description": "mismatch base schema",
+                "data": 3,
+                "valid": false
+            },
+            {
+                "description": "one anyOf valid",
+                "data": "foobar",
+                "valid": true
+            },
+            {
+                "description": "both anyOf invalid",
+                "data": "foo",
+                "valid": false
+            }
+        ]
+    }
+]

Added: vendor/libucl/dist/tests/schema/definitions.json
===================================================================
--- vendor/libucl/dist/tests/schema/definitions.json	                        (rev 0)
+++ vendor/libucl/dist/tests/schema/definitions.json	2018-06-19 13:29:00 UTC (rev 11074)
@@ -0,0 +1,32 @@
+[
+    {
+        "description": "valid definition",
+        "schema": {"$ref": "http://json-schema.org/draft-04/schema#"},
+        "tests": [
+            {
+                "description": "valid definition schema",
+                "data": {
+                    "definitions": {
+                        "foo": {"type": "integer"}
+                    }
+                },
+                "valid": true
+            }
+        ]
+    },
+    {
+        "description": "invalid definition",
+        "schema": {"$ref": "http://json-schema.org/draft-04/schema#"},
+        "tests": [
+            {
+                "description": "invalid definition schema",
+                "data": {
+                    "definitions": {
+                        "foo": {"type": 1}
+                    }
+                },
+                "valid": false
+            }
+        ]
+    }
+]

Added: vendor/libucl/dist/tests/schema/dependencies.json
===================================================================
--- vendor/libucl/dist/tests/schema/dependencies.json	                        (rev 0)
+++ vendor/libucl/dist/tests/schema/dependencies.json	2018-06-19 13:29:00 UTC (rev 11074)
@@ -0,0 +1,113 @@
+[
+    {
+        "description": "dependencies",
+        "schema": {
+            "dependencies": {"bar": ["foo"]}
+        },
+        "tests": [
+            {
+                "description": "neither",
+                "data": {},
+                "valid": true
+            },
+            {
+                "description": "nondependant",
+                "data": {"foo": 1},
+                "valid": true
+            },
+            {
+                "description": "with dependency",
+                "data": {"foo": 1, "bar": 2},
+                "valid": true
+            },
+            {
+                "description": "missing dependency",
+                "data": {"bar": 2},
+                "valid": false
+            },
+            {
+                "description": "ignores non-objects",
+                "data": "foo",
+                "valid": true
+            }
+        ]
+    },
+    {
+        "description": "multiple dependencies",
+        "schema": {
+            "dependencies": {"quux": ["foo", "bar"]}
+        },
+        "tests": [
+            {
+                "description": "neither",
+                "data": {},
+                "valid": true
+            },
+            {
+                "description": "nondependants",
+                "data": {"foo": 1, "bar": 2},
+                "valid": true
+            },
+            {
+                "description": "with dependencies",
+                "data": {"foo": 1, "bar": 2, "quux": 3},
+                "valid": true
+            },
+            {
+                "description": "missing dependency",
+                "data": {"foo": 1, "quux": 2},
+                "valid": false
+            },
+            {
+                "description": "missing other dependency",
+                "data": {"bar": 1, "quux": 2},
+                "valid": false
+            },
+            {
+                "description": "missing both dependencies",
+                "data": {"quux": 1},
+                "valid": false
+            }
+        ]
+    },
+    {
+        "description": "multiple dependencies subschema",
+        "schema": {
+            "dependencies": {
+                "bar": {
+                    "properties": {
+                        "foo": {"type": "integer"},
+                        "bar": {"type": "integer"}
+                    }
+                }
+            }
+        },
+        "tests": [
+            {
+                "description": "valid",
+                "data": {"foo": 1, "bar": 2},
+                "valid": true
+            },
+            {
+                "description": "no dependency",
+                "data": {"foo": "quux"},
+                "valid": true
+            },
+            {
+                "description": "wrong type",
+                "data": {"foo": "quux", "bar": 2},
+                "valid": false
+            },
+            {
+                "description": "wrong type other",
+                "data": {"foo": 2, "bar": "quux"},
+                "valid": false
+            },
+            {
+                "description": "wrong type both",
+                "data": {"foo": "quux", "bar": "quux"},
+                "valid": false
+            }
+        ]
+    }
+]

Added: vendor/libucl/dist/tests/schema/enum.json
===================================================================
--- vendor/libucl/dist/tests/schema/enum.json	                        (rev 0)
+++ vendor/libucl/dist/tests/schema/enum.json	2018-06-19 13:29:00 UTC (rev 11074)
@@ -0,0 +1,72 @@
+[
+    {
+        "description": "simple enum validation",
+        "schema": {"enum": [1, 2, 3]},
+        "tests": [
+            {
+                "description": "one of the enum is valid",
+                "data": 1,
+                "valid": true
+            },
+            {
+                "description": "something else is invalid",
+                "data": 4,
+                "valid": false
+            }
+        ]
+    },
+    {
+        "description": "heterogeneous enum validation",
+        "schema": {"enum": [6, "foo", [], true, {"foo": 12}]},
+        "tests": [
+            {
+                "description": "one of the enum is valid",
+                "data": [],
+                "valid": true
+            },
+            {
+                "description": "something else is invalid",
+                "data": null,
+                "valid": false
+            },
+            {
+                "description": "objects are deep compared",
+                "data": {"foo": false},
+                "valid": false
+            }
+        ]
+    },
+    {
+        "description": "enums in properties",
+        "schema": {
+           "type":"object",
+		     "properties": {
+		        "foo": {"enum":["foo"]},
+		        "bar": {"enum":["bar"]}
+		     },
+		     "required": ["bar"]
+		  },
+        "tests": [
+            {
+                "description": "both properties are valid",
+                "data": {"foo":"foo", "bar":"bar"},
+                "valid": true
+            },
+            {
+                "description": "missing optional property is valid",
+                "data": {"bar":"bar"},
+                "valid": true
+            },
+            {
+                "description": "missing required property is invalid",
+                "data": {"foo":"foo"},
+                "valid": false
+            },
+            {
+                "description": "missing all properties is invalid",
+                "data": {},
+                "valid": false
+            }
+        ]
+    }
+]

Added: vendor/libucl/dist/tests/schema/items.json
===================================================================
--- vendor/libucl/dist/tests/schema/items.json	                        (rev 0)
+++ vendor/libucl/dist/tests/schema/items.json	2018-06-19 13:29:00 UTC (rev 11074)
@@ -0,0 +1,46 @@
+[
+    {
+        "description": "a schema given for items",
+        "schema": {
+            "items": {"type": "integer"}
+        },
+        "tests": [
+            {
+                "description": "valid items",
+                "data": [ 1, 2, 3 ],
+                "valid": true
+            },
+            {
+                "description": "wrong type of items",
+                "data": [1, "x"],
+                "valid": false
+            },
+            {
+                "description": "ignores non-arrays",
+                "data": {"foo" : "bar"},
+                "valid": true
+            }
+        ]
+    },
+    {
+        "description": "an array of schemas for items",
+        "schema": {
+            "items": [
+                {"type": "integer"},
+                {"type": "string"}
+            ]
+        },
+        "tests": [
+            {
+                "description": "correct types",
+                "data": [ 1, "foo" ],
+                "valid": true
+            },
+            {
+                "description": "wrong types",
+                "data": [ "foo", 1 ],
+                "valid": false
+            }
+        ]
+    }
+]

Added: vendor/libucl/dist/tests/schema/maxItems.json
===================================================================
--- vendor/libucl/dist/tests/schema/maxItems.json	                        (rev 0)
+++ vendor/libucl/dist/tests/schema/maxItems.json	2018-06-19 13:29:00 UTC (rev 11074)
@@ -0,0 +1,28 @@
+[
+    {
+        "description": "maxItems validation",
+        "schema": {"maxItems": 2},
+        "tests": [
+            {
+                "description": "shorter is valid",
+                "data": [1],
+                "valid": true
+            },
+            {
+                "description": "exact length is valid",
+                "data": [1, 2],
+                "valid": true
+            },
+            {
+                "description": "too long is invalid",
+                "data": [1, 2, 3],
+                "valid": false
+            },
+            {
+                "description": "ignores non-arrays",
+                "data": "foobar",
+                "valid": true
+            }
+        ]
+    }
+]

Added: vendor/libucl/dist/tests/schema/maxLength.json
===================================================================
--- vendor/libucl/dist/tests/schema/maxLength.json	                        (rev 0)
+++ vendor/libucl/dist/tests/schema/maxLength.json	2018-06-19 13:29:00 UTC (rev 11074)
@@ -0,0 +1,28 @@
+[
+    {
+        "description": "maxLength validation",
+        "schema": {"maxLength": 2},
+        "tests": [
+            {
+                "description": "shorter is valid",
+                "data": "f",
+                "valid": true
+            },
+            {
+                "description": "exact length is valid",
+                "data": "fo",
+                "valid": true
+            },
+            {
+                "description": "too long is invalid",
+                "data": "foo",
+                "valid": false
+            },
+            {
+                "description": "ignores non-strings",
+                "data": 10,
+                "valid": true
+            }
+        ]
+    }
+]

Added: vendor/libucl/dist/tests/schema/maxProperties.json
===================================================================
--- vendor/libucl/dist/tests/schema/maxProperties.json	                        (rev 0)
+++ vendor/libucl/dist/tests/schema/maxProperties.json	2018-06-19 13:29:00 UTC (rev 11074)
@@ -0,0 +1,28 @@
+[
+    {
+        "description": "maxProperties validation",
+        "schema": {"maxProperties": 2},
+        "tests": [
+            {
+                "description": "shorter is valid",
+                "data": {"foo": 1},
+                "valid": true
+            },
+            {
+                "description": "exact length is valid",
+                "data": {"foo": 1, "bar": 2},
+                "valid": true
+            },
+            {
+                "description": "too long is invalid",
+                "data": {"foo": 1, "bar": 2, "baz": 3},
+                "valid": false
+            },
+            {
+                "description": "ignores non-objects",
+                "data": "foobar",
+                "valid": true
+            }
+        ]
+    }
+]

Added: vendor/libucl/dist/tests/schema/maximum.json
===================================================================
--- vendor/libucl/dist/tests/schema/maximum.json	                        (rev 0)
+++ vendor/libucl/dist/tests/schema/maximum.json	2018-06-19 13:29:00 UTC (rev 11074)
@@ -0,0 +1,42 @@
+[
+    {
+        "description": "maximum validation",
+        "schema": {"maximum": 3.0},
+        "tests": [
+            {
+                "description": "below the maximum is valid",
+                "data": 2.6,
+                "valid": true
+            },
+            {
+                "description": "above the maximum is invalid",
+                "data": 3.5,
+                "valid": false
+            },
+            {
+                "description": "ignores non-numbers",
+                "data": "x",
+                "valid": true
+            }
+        ]
+    },
+    {
+        "description": "exclusiveMaximum validation",
+        "schema": {
+            "maximum": 3.0,
+            "exclusiveMaximum": true
+        },
+        "tests": [
+            {
+                "description": "below the maximum is still valid",
+                "data": 2.2,
+                "valid": true
+            },
+            {
+                "description": "boundary point is invalid",
+                "data": 3.0,
+                "valid": false
+            }
+        ]
+    }
+]

Added: vendor/libucl/dist/tests/schema/minItems.json
===================================================================
--- vendor/libucl/dist/tests/schema/minItems.json	                        (rev 0)
+++ vendor/libucl/dist/tests/schema/minItems.json	2018-06-19 13:29:00 UTC (rev 11074)
@@ -0,0 +1,28 @@
+[
+    {
+        "description": "minItems validation",
+        "schema": {"minItems": 1},
+        "tests": [
+            {
+                "description": "longer is valid",
+                "data": [1, 2],
+                "valid": true
+            },
+            {
+                "description": "exact length is valid",
+                "data": [1],
+                "valid": true
+            },
+            {
+                "description": "too short is invalid",
+                "data": [],
+                "valid": false
+            },
+            {
+                "description": "ignores non-arrays",
+                "data": "",
+                "valid": true
+            }
+        ]
+    }
+]

Added: vendor/libucl/dist/tests/schema/minLength.json
===================================================================
--- vendor/libucl/dist/tests/schema/minLength.json	                        (rev 0)
+++ vendor/libucl/dist/tests/schema/minLength.json	2018-06-19 13:29:00 UTC (rev 11074)
@@ -0,0 +1,28 @@
+[
+    {
+        "description": "minLength validation",
+        "schema": {"minLength": 2},
+        "tests": [
+            {
+                "description": "longer is valid",
+                "data": "foo",
+                "valid": true
+            },
+            {
+                "description": "exact length is valid",
+                "data": "fo",
+                "valid": true
+            },
+            {
+                "description": "too short is invalid",
+                "data": "f",
+                "valid": false
+            },
+            {
+                "description": "ignores non-strings",
+                "data": 1,
+                "valid": true
+            }
+        ]
+    }
+]

Added: vendor/libucl/dist/tests/schema/minProperties.json
===================================================================
--- vendor/libucl/dist/tests/schema/minProperties.json	                        (rev 0)
+++ vendor/libucl/dist/tests/schema/minProperties.json	2018-06-19 13:29:00 UTC (rev 11074)
@@ -0,0 +1,28 @@
+[
+    {
+        "description": "minProperties validation",
+        "schema": {"minProperties": 1},
+        "tests": [
+            {
+                "description": "longer is valid",
+                "data": {"foo": 1, "bar": 2},
+                "valid": true
+            },
+            {
+                "description": "exact length is valid",
+                "data": {"foo": 1},
+                "valid": true
+            },
+            {
+                "description": "too short is invalid",
+                "data": {},
+                "valid": false
+            },
+            {
+                "description": "ignores non-objects",
+                "data": "",
+                "valid": true
+            }
+        ]
+    }
+]

Added: vendor/libucl/dist/tests/schema/minimum.json
===================================================================
--- vendor/libucl/dist/tests/schema/minimum.json	                        (rev 0)
+++ vendor/libucl/dist/tests/schema/minimum.json	2018-06-19 13:29:00 UTC (rev 11074)
@@ -0,0 +1,42 @@
+[
+    {
+        "description": "minimum validation",
+        "schema": {"minimum": 1.1},
+        "tests": [
+            {
+                "description": "above the minimum is valid",
+                "data": 2.6,
+                "valid": true
+            },
+            {
+                "description": "below the minimum is invalid",
+                "data": 0.6,
+                "valid": false
+            },
+            {
+                "description": "ignores non-numbers",
+                "data": "x",
+                "valid": true
+            }
+        ]
+    },
+    {
+        "description": "exclusiveMinimum validation",
+        "schema": {
+            "minimum": 1.1,
+            "exclusiveMinimum": true
+        },
+        "tests": [
+            {
+                "description": "above the minimum is still valid",
+                "data": 1.2,
+                "valid": true
+            },
+            {
+                "description": "boundary point is invalid",
+                "data": 1.1,
+                "valid": false
+            }
+        ]
+    }
+]

Added: vendor/libucl/dist/tests/schema/multipleOf.json
===================================================================
--- vendor/libucl/dist/tests/schema/multipleOf.json	                        (rev 0)
+++ vendor/libucl/dist/tests/schema/multipleOf.json	2018-06-19 13:29:00 UTC (rev 11074)
@@ -0,0 +1,60 @@
+[
+    {
+        "description": "by int",
+        "schema": {"multipleOf": 2},
+        "tests": [
+            {
+                "description": "int by int",
+                "data": 10,
+                "valid": true
+            },
+            {
+                "description": "int by int fail",
+                "data": 7,
+                "valid": false
+            },
+            {
+                "description": "ignores non-numbers",
+                "data": "foo",
+                "valid": true
+            }
+        ]
+    },
+    {
+        "description": "by number",
+        "schema": {"multipleOf": 1.5},
+        "tests": [
+            {
+                "description": "zero is multiple of anything",
+                "data": 0,
+                "valid": true
+            },
+            {
+                "description": "4.5 is multiple of 1.5",
+                "data": 4.5,
+                "valid": true
+            },
+            {
+                "description": "35 is not multiple of 1.5",
+                "data": 35,
+                "valid": false
+            }
+        ]
+    },
+    {
+        "description": "by small number",
+        "schema": {"multipleOf": 0.0001},
+        "tests": [
+            {
+                "description": "0.0075 is multiple of 0.0001",
+                "data": 0.0075,
+                "valid": true
+            },
+            {
+                "description": "0.00751 is not multiple of 0.0001",
+                "data": 0.00751,
+                "valid": false
+            }
+        ]
+    }
+]

Added: vendor/libucl/dist/tests/schema/not.json
===================================================================
--- vendor/libucl/dist/tests/schema/not.json	                        (rev 0)
+++ vendor/libucl/dist/tests/schema/not.json	2018-06-19 13:29:00 UTC (rev 11074)
@@ -0,0 +1,96 @@
+[
+    {
+        "description": "not",
+        "schema": {
+            "not": {"type": "integer"}
+        },
+        "tests": [
+            {
+                "description": "allowed",
+                "data": "foo",
+                "valid": true
+            },
+            {
+                "description": "disallowed",
+                "data": 1,
+                "valid": false
+            }
+        ]
+    },
+    {
+        "description": "not multiple types",
+        "schema": {
+            "not": {"type": ["integer", "boolean"]}
+        },
+        "tests": [
+            {
+                "description": "valid",
+                "data": "foo",
+                "valid": true
+            },
+            {
+                "description": "mismatch",
+                "data": 1,
+                "valid": false
+            },
+            {
+                "description": "other mismatch",
+                "data": true,
+                "valid": false
+            }
+        ]
+    },
+    {
+        "description": "not more complex schema",
+        "schema": {
+            "not": {
+                "type": "object",
+                "properties": {
+                    "foo": {
+                        "type": "string"
+                    }
+                }
+             }
+        },
+        "tests": [
+            {
+                "description": "match",
+                "data": 1,
+                "valid": true
+            },
+            {
+                "description": "other match",
+                "data": {"foo": 1},
+                "valid": true
+            },
+            {
+                "description": "mismatch",
+                "data": {"foo": "bar"},
+                "valid": false
+            }
+        ]
+    },
+    {
+        "description": "forbidden property",
+        "schema": {
+            "properties": {
+                "foo": { 
+                    "not": {}
+                }
+            }
+        },
+        "tests": [
+            {
+                "description": "property present",
+                "data": {"foo": 1, "bar": 2},
+                "valid": false
+            },
+            {
+                "description": "property absent",
+                "data": {"bar": 1, "baz": 2},
+                "valid": true
+            }
+        ]
+    }
+
+]

Added: vendor/libucl/dist/tests/schema/oneOf.json
===================================================================
--- vendor/libucl/dist/tests/schema/oneOf.json	                        (rev 0)
+++ vendor/libucl/dist/tests/schema/oneOf.json	2018-06-19 13:29:00 UTC (rev 11074)
@@ -0,0 +1,68 @@
+[
+    {
+        "description": "oneOf",
+        "schema": {
+            "oneOf": [
+                {
+                    "type": "integer"
+                },
+                {
+                    "minimum": 2
+                }
+            ]
+        },
+        "tests": [
+            {
+                "description": "first oneOf valid",
+                "data": 1,
+                "valid": true
+            },
+            {
+                "description": "second oneOf valid",
+                "data": 2.5,
+                "valid": true
+            },
+            {
+                "description": "both oneOf valid",
+                "data": 3,
+                "valid": false
+            },
+            {
+                "description": "neither oneOf valid",
+                "data": 1.5,
+                "valid": false
+            }
+        ]
+    },
+    {
+        "description": "oneOf with base schema",
+        "schema": {
+            "type": "string",
+            "oneOf" : [
+                {
+                    "minLength": 2
+                },
+                {
+                    "maxLength": 4
+                }
+            ]
+        },
+        "tests": [
+            {
+                "description": "mismatch base schema",
+                "data": 3,
+                "valid": false
+            },
+            {
+                "description": "one oneOf valid",
+                "data": "foobar",
+                "valid": true
+            },
+            {
+                "description": "both oneOf valid",
+                "data": "foo",
+                "valid": false
+            }
+        ]
+    }
+]

Added: vendor/libucl/dist/tests/schema/pattern.json
===================================================================
--- vendor/libucl/dist/tests/schema/pattern.json	                        (rev 0)
+++ vendor/libucl/dist/tests/schema/pattern.json	2018-06-19 13:29:00 UTC (rev 11074)
@@ -0,0 +1,23 @@
+[
+    {
+        "description": "pattern validation",
+        "schema": {"pattern": "^a*$"},
+        "tests": [
+            {
+                "description": "a matching pattern is valid",
+                "data": "aaa",
+                "valid": true
+            },
+            {
+                "description": "a non-matching pattern is invalid",
+                "data": "abc",
+                "valid": false
+            },
+            {
+                "description": "ignores non-strings",
+                "data": true,
+                "valid": true
+            }
+        ]
+    }
+]

Added: vendor/libucl/dist/tests/schema/patternProperties.json
===================================================================
--- vendor/libucl/dist/tests/schema/patternProperties.json	                        (rev 0)
+++ vendor/libucl/dist/tests/schema/patternProperties.json	2018-06-19 13:29:00 UTC (rev 11074)
@@ -0,0 +1,110 @@
+[
+    {
+        "description":
+            "patternProperties validates properties matching a regex",
+        "schema": {
+            "patternProperties": {
+                "f.*o": {"type": "integer"}
+            }
+        },
+        "tests": [
+            {
+                "description": "a single valid match is valid",
+                "data": {"foo": 1},
+                "valid": true
+            },
+            {
+                "description": "multiple valid matches is valid",
+                "data": {"foo": 1, "foooooo" : 2},
+                "valid": true
+            },
+            {
+                "description": "a single invalid match is invalid",
+                "data": {"foo": "bar", "fooooo": 2},
+                "valid": false
+            },
+            {
+                "description": "multiple invalid matches is invalid",
+                "data": {"foo": "bar", "foooooo" : "baz"},
+                "valid": false
+            },
+            {
+                "description": "ignores non-objects",
+                "data": 12,
+                "valid": true
+            }
+        ]
+    },
+    {
+        "description": "multiple simultaneous patternProperties are validated",
+        "schema": {
+            "patternProperties": {
+                "a*": {"type": "integer"},
+                "aaa*": {"maximum": 20}
+            }
+        },
+        "tests": [
+            {
+                "description": "a single valid match is valid",
+                "data": {"a": 21},
+                "valid": true
+            },
+            {
+                "description": "a simultaneous match is valid",
+                "data": {"aaaa": 18},
+                "valid": true
+            },
+            {
+                "description": "multiple matches is valid",
+                "data": {"a": 21, "aaaa": 18},
+                "valid": true
+            },
+            {
+                "description": "an invalid due to one is invalid",
+                "data": {"a": "bar"},
+                "valid": false
+            },
+            {
+                "description": "an invalid due to the other is invalid",
+                "data": {"aaaa": 31},
+                "valid": false
+            },
+            {
+                "description": "an invalid due to both is invalid",
+                "data": {"aaa": "foo", "aaaa": 31},
+                "valid": false
+            }
+        ]
+    },
+    {
+        "description": "regexes are not anchored by default and are case sensitive",
+        "schema": {
+            "patternProperties": {
+                "[0-9]{2,}": { "type": "boolean" },
+                "X_": { "type": "string" }
+            }
+        },
+        "tests": [
+            {
+                "description": "non recognized members are ignored",
+                "data": { "answer 1": "42" },
+                "valid": true
+            },
+            {
+                "description": "recognized members are accounted for",
+                "data": { "a31b": null },
+                "valid": false
+            },
+            {
+                "description": "regexes are case sensitive",
+                "data": { "a_x_3": 3 },
+                "valid": true
+            },
+            {
+                "description": "regexes are case sensitive, 2",
+                "data": { "a_X_3": 3 },
+                "valid": false
+            }
+        ]
+    }
+]

Added: vendor/libucl/dist/tests/schema/properties.json
===================================================================
--- vendor/libucl/dist/tests/schema/properties.json	                        (rev 0)
+++ vendor/libucl/dist/tests/schema/properties.json	2018-06-19 13:29:00 UTC (rev 11074)
@@ -0,0 +1,92 @@
+[
+    {
+        "description": "object properties validation",
+        "schema": {
+            "properties": {
+                "foo": {"type": "integer"},
+                "bar": {"type": "string"}
+            }
+        },
+        "tests": [
+            {
+                "description": "both properties present and valid is valid",
+                "data": {"foo": 1, "bar": "baz"},
+                "valid": true
+            },
+            {
+                "description": "one property invalid is invalid",
+                "data": {"foo": 1, "bar": {}},
+                "valid": false
+            },
+            {
+                "description": "both properties invalid is invalid",
+                "data": {"foo": [], "bar": {}},
+                "valid": false
+            },
+            {
+                "description": "doesn't invalidate other properties",
+                "data": {"quux": []},
+                "valid": true
+            },
+            {
+                "description": "ignores non-objects",
+                "data": [],
+                "valid": true
+            }
+        ]
+    },
+    {
+        "description":
+            "properties, patternProperties, additionalProperties interaction",
+        "schema": {
+            "properties": {
+                "foo": {"type": "array", "maxItems": 3},
+                "bar": {"type": "array"}
+            },
+            "patternProperties": {"f.o": {"minItems": 2}},
+            "additionalProperties": {"type": "integer"}
+        },
+        "tests": [
+            {
+                "description": "property validates property",
+                "data": {"foo": [1, 2]},
+                "valid": true
+            },
+            {
+                "description": "property invalidates property",
+                "data": {"foo": [1, 2, 3, 4]},
+                "valid": false
+            },
+            {
+                "description": "patternProperty invalidates property",
+                "data": {"foo": []},
+                "valid": false
+            },
+            {
+                "description": "patternProperty validates nonproperty",
+                "data": {"fxo": [1, 2]},
+                "valid": true
+            },
+            {
+                "description": "patternProperty invalidates nonproperty",
+                "data": {"fxo": []},
+                "valid": false
+            },
+            {
+                "description": "additionalProperty ignores property",
+                "data": {"bar": []},
+                "valid": true
+            },
+            {
+                "description": "additionalProperty validates others",
+                "data": {"quux": 3},
+                "valid": true
+            },
+            {
+                "description": "additionalProperty invalidates others",
+                "data": {"quux": "foo"},
+                "valid": false
+            }
+        ]
+    }
+]

Added: vendor/libucl/dist/tests/schema/ref.json
===================================================================
--- vendor/libucl/dist/tests/schema/ref.json	                        (rev 0)
+++ vendor/libucl/dist/tests/schema/ref.json	2018-06-19 13:29:00 UTC (rev 11074)
@@ -0,0 +1,146 @@
+[
+    {
+        "description": "root pointer ref",
+        "schema": {
+            "properties": {
+                "foo": {"$ref": "#"}
+            },
+            "additionalProperties": false
+        },
+        "tests": [
+            {
+                "description": "match",
+                "data": {"foo": false},
+                "valid": true
+            },
+            {
+                "description": "recursive match",
+                "data": {"foo": {"foo": false}},
+                "valid": true
+            },
+            {
+                "description": "mismatch",
+                "data": {"bar": false},
+                "valid": false
+            },
+            {
+                "description": "recursive mismatch",
+                "data": {"foo": {"bar": false}},
+                "valid": false
+            }
+        ]
+    },
+    {
+        "description": "relative pointer ref to object",
+        "schema": {
+            "properties": {
+                "foo": {"type": "integer"},
+                "bar": {"$ref": "#/properties/foo"}
+            }
+        },
+        "tests": [
+            {
+                "description": "match",
+                "data": {"bar": 3},
+                "valid": true
+            },
+            {
+                "description": "mismatch",
+                "data": {"bar": true},
+                "valid": false
+            }
+        ]
+    },
+    {
+        "description": "relative pointer ref to array",
+        "schema": {
+            "items": [
+                {"type": "integer"},
+                {"$ref": "#/items/0"}
+            ]
+        },
+        "tests": [
+            {
+                "description": "match array",
+                "data": [1, 2],
+                "valid": true
+            },
+            {
+                "description": "mismatch array",
+                "data": [1, "foo"],
+                "valid": false
+            }
+        ]
+    },
+    {
+        "description": "escaped pointer ref",
+        "schema": {
+            "tilda~field": {"type": "integer"},
+            "slash/field": {"type": "integer"},
+            "percent%field": {"type": "integer"},
+            "properties": {
+                "tilda": {"$ref": "#/tilda~0field"},
+                "slash": {"$ref": "#/slash~1field"},
+                "percent": {"$ref": "#/percent%25field"}
+            }
+        },
+        "tests": [
+            {
+                "description": "slash",
+                "data": {"slash": "aoeu"},
+                "valid": false
+            },
+            {
+                "description": "tilda",
+                "data": {"tilda": "aoeu"},
+                "valid": false
+            },
+            {
+                "description": "percent",
+                "data": {"percent": "aoeu"},
+                "valid": false
+            }
+        ]
+    },
+    {
+        "description": "nested refs",
+        "schema": {
+            "definitions": {
+                "a": {"type": "integer"},
+                "b": {"$ref": "#/definitions/a"},
+                "c": {"$ref": "#/definitions/b"}
+            },
+            "$ref": "#/definitions/c"
+        },
+        "tests": [
+            {
+                "description": "nested ref valid",
+                "data": 5,
+                "valid": true
+            },
+            {
+                "description": "nested ref invalid",
+                "data": "a",
+                "valid": false
+            }
+        ]
+    },
+/*
+    {
+        "description": "remote ref, containing refs itself",
+        "schema": {"$ref": "http://json-schema.org/draft-04/schema#"},
+        "tests": [
+            {
+                "description": "remote ref valid",
+                "data": {"minLength": 1},
+                "valid": true
+            },
+            {
+                "description": "remote ref invalid",
+                "data": {"minLength": -1},
+                "valid": false
+            }
+        ]
+    }
+*/
+]

Added: vendor/libucl/dist/tests/schema/refRemote.json
===================================================================
--- vendor/libucl/dist/tests/schema/refRemote.json	                        (rev 0)
+++ vendor/libucl/dist/tests/schema/refRemote.json	2018-06-19 13:29:00 UTC (rev 11074)
@@ -0,0 +1,74 @@
+[
+    {
+        "description": "remote ref",
+        "schema": {"$ref": "http://localhost:1234/integer.json"},
+        "tests": [
+            {
+                "description": "remote ref valid",
+                "data": 1,
+                "valid": true
+            },
+            {
+                "description": "remote ref invalid",
+                "data": "a",
+                "valid": false
+            }
+        ]
+    },
+    {
+        "description": "fragment within remote ref",
+        "schema": {"$ref": "http://localhost:1234/subSchemas.json#/integer"},
+        "tests": [
+            {
+                "description": "remote fragment valid",
+                "data": 1,
+                "valid": true
+            },
+            {
+                "description": "remote fragment invalid",
+                "data": "a",
+                "valid": false
+            }
+        ]
+    },
+    {
+        "description": "ref within remote ref",
+        "schema": {
+            "$ref": "http://localhost:1234/subSchemas.json#/refToInteger"
+        },
+        "tests": [
+            {
+                "description": "ref within ref valid",
+                "data": 1,
+                "valid": true
+            },
+            {
+                "description": "ref within ref invalid",
+                "data": "a",
+                "valid": false
+            }
+        ]
+    },
+    {
+        "description": "change resolution scope",
+        "schema": {
+            "id": "http://localhost:1234/",
+            "items": {
+                "id": "folder/",
+                "items": {"$ref": "folderInteger.json"}
+            }
+        },
+        "tests": [
+            {
+                "description": "changed scope ref valid",
+                "data": [[1]],
+                "valid": true
+            },
+            {
+                "description": "changed scope ref invalid",
+                "data": [["a"]],
+                "valid": false
+            }
+        ]
+    }
+]

Added: vendor/libucl/dist/tests/schema/required.json
===================================================================
--- vendor/libucl/dist/tests/schema/required.json	                        (rev 0)
+++ vendor/libucl/dist/tests/schema/required.json	2018-06-19 13:29:00 UTC (rev 11074)
@@ -0,0 +1,39 @@
+[
+    {
+        "description": "required validation",
+        "schema": {
+            "properties": {
+                "foo": {},
+                "bar": {}
+            },
+            "required": ["foo"]
+        },
+        "tests": [
+            {
+                "description": "present required property is valid",
+                "data": {"foo": 1},
+                "valid": true
+            },
+            {
+                "description": "non-present required property is invalid",
+                "data": {"bar": 1},
+                "valid": false
+            }
+        ]
+    },
+    {
+        "description": "required default validation",
+        "schema": {
+            "properties": {
+                "foo": {}
+            }
+        },
+        "tests": [
+            {
+                "description": "not required by default",
+                "data": {},
+                "valid": true
+            }
+        ]
+    }
+]

Added: vendor/libucl/dist/tests/schema/type.json
===================================================================
--- vendor/libucl/dist/tests/schema/type.json	                        (rev 0)
+++ vendor/libucl/dist/tests/schema/type.json	2018-06-19 13:29:00 UTC (rev 11074)
@@ -0,0 +1,330 @@
+[
+    {
+        "description": "integer type matches integers",
+        "schema": {"type": "integer"},
+        "tests": [
+            {
+                "description": "an integer is an integer",
+                "data": 1,
+                "valid": true
+            },
+            {
+                "description": "a float is not an integer",
+                "data": 1.1,
+                "valid": false
+            },
+            {
+                "description": "a string is not an integer",
+                "data": "foo",
+                "valid": false
+            },
+            {
+                "description": "an object is not an integer",
+                "data": {},
+                "valid": false
+            },
+            {
+                "description": "an array is not an integer",
+                "data": [],
+                "valid": false
+            },
+            {
+                "description": "a boolean is not an integer",
+                "data": true,
+                "valid": false
+            },
+            {
+                "description": "null is not an integer",
+                "data": null,
+                "valid": false
+            }
+        ]
+    },
+    {
+        "description": "number type matches numbers",
+        "schema": {"type": "number"},
+        "tests": [
+            {
+                "description": "an integer is a number",
+                "data": 1,
+                "valid": true
+            },
+            {
+                "description": "a float is a number",
+                "data": 1.1,
+                "valid": true
+            },
+            {
+                "description": "a string is not a number",
+                "data": "foo",
+                "valid": false
+            },
+            {
+                "description": "an object is not a number",
+                "data": {},
+                "valid": false
+            },
+            {
+                "description": "an array is not a number",
+                "data": [],
+                "valid": false
+            },
+            {
+                "description": "a boolean is not a number",
+                "data": true,
+                "valid": false
+            },
+            {
+                "description": "null is not a number",
+                "data": null,
+                "valid": false
+            }
+        ]
+    },
+    {
+        "description": "string type matches strings",
+        "schema": {"type": "string"},
+        "tests": [
+            {
+                "description": "1 is not a string",
+                "data": 1,
+                "valid": false
+            },
+            {
+                "description": "a float is not a string",
+                "data": 1.1,
+                "valid": false
+            },
+            {
+                "description": "a string is a string",
+                "data": "foo",
+                "valid": true
+            },
+            {
+                "description": "an object is not a string",
+                "data": {},
+                "valid": false
+            },
+            {
+                "description": "an array is not a string",
+                "data": [],
+                "valid": false
+            },
+            {
+                "description": "a boolean is not a string",
+                "data": true,
+                "valid": false
+            },
+            {
+                "description": "null is not a string",
+                "data": null,
+                "valid": false
+            }
+        ]
+    },
+    {
+        "description": "object type matches objects",
+        "schema": {"type": "object"},
+        "tests": [
+            {
+                "description": "an integer is not an object",
+                "data": 1,
+                "valid": false
+            },
+            {
+                "description": "a float is not an object",
+                "data": 1.1,
+                "valid": false
+            },
+            {
+                "description": "a string is not an object",
+                "data": "foo",
+                "valid": false
+            },
+            {
+                "description": "an object is an object",
+                "data": {},
+                "valid": true
+            },
+            {
+                "description": "an array is not an object",
+                "data": [],
+                "valid": false
+            },
+            {
+                "description": "a boolean is not an object",
+                "data": true,
+                "valid": false
+            },
+            {
+                "description": "null is not an object",
+                "data": null,
+                "valid": false
+            }
+        ]
+    },
+    {
+        "description": "array type matches arrays",
+        "schema": {"type": "array"},
+        "tests": [
+            {
+                "description": "an integer is not an array",
+                "data": 1,
+                "valid": false
+            },
+            {
+                "description": "a float is not an array",
+                "data": 1.1,
+                "valid": false
+            },
+            {
+                "description": "a string is not an array",
+                "data": "foo",
+                "valid": false
+            },
+            {
+                "description": "an object is not an array",
+                "data": {},
+                "valid": false
+            },
+            {
+                "description": "an array is not an array",
+                "data": [],
+                "valid": true
+            },
+            {
+                "description": "a boolean is not an array",
+                "data": true,
+                "valid": false
+            },
+            {
+                "description": "null is not an array",
+                "data": null,
+                "valid": false
+            }
+        ]
+    },
+    {
+        "description": "boolean type matches booleans",
+        "schema": {"type": "boolean"},
+        "tests": [
+            {
+                "description": "an integer is not a boolean",
+                "data": 1,
+                "valid": false
+            },
+            {
+                "description": "a float is not a boolean",
+                "data": 1.1,
+                "valid": false
+            },
+            {
+                "description": "a string is not a boolean",
+                "data": "foo",
+                "valid": false
+            },
+            {
+                "description": "an object is not a boolean",
+                "data": {},
+                "valid": false
+            },
+            {
+                "description": "an array is not a boolean",
+                "data": [],
+                "valid": false
+            },
+            {
+                "description": "a boolean is not a boolean",
+                "data": true,
+                "valid": true
+            },
+            {
+                "description": "null is not a boolean",
+                "data": null,
+                "valid": false
+            }
+        ]
+    },
+    {
+        "description": "null type matches only the null object",
+        "schema": {"type": "null"},
+        "tests": [
+            {
+                "description": "an integer is not null",
+                "data": 1,
+                "valid": false
+            },
+            {
+                "description": "a float is not null",
+                "data": 1.1,
+                "valid": false
+            },
+            {
+                "description": "a string is not null",
+                "data": "foo",
+                "valid": false
+            },
+            {
+                "description": "an object is not null",
+                "data": {},
+                "valid": false
+            },
+            {
+                "description": "an array is not null",
+                "data": [],
+                "valid": false
+            },
+            {
+                "description": "a boolean is not null",
+                "data": true,
+                "valid": false
+            },
+            {
+                "description": "null is null",
+                "data": null,
+                "valid": true
+            }
+        ]
+    },
+    {
+        "description": "multiple types can be specified in an array",
+        "schema": {"type": ["integer", "string"]},
+        "tests": [
+            {
+                "description": "an integer is valid",
+                "data": 1,
+                "valid": true
+            },
+            {
+                "description": "a string is valid",
+                "data": "foo",
+                "valid": true
+            },
+            {
+                "description": "a float is invalid",
+                "data": 1.1,
+                "valid": false
+            },
+            {
+                "description": "an object is invalid",
+                "data": {},
+                "valid": false
+            },
+            {
+                "description": "an array is invalid",
+                "data": [],
+                "valid": false
+            },
+            {
+                "description": "a boolean is invalid",
+                "data": true,
+                "valid": false
+            },
+            {
+                "description": "null is invalid",
+                "data": null,
+                "valid": false
+            }
+        ]
+    }
+]

Added: vendor/libucl/dist/tests/schema/uniqueItems.json
===================================================================
--- vendor/libucl/dist/tests/schema/uniqueItems.json	                        (rev 0)
+++ vendor/libucl/dist/tests/schema/uniqueItems.json	2018-06-19 13:29:00 UTC (rev 11074)
@@ -0,0 +1,79 @@
+[
+    {
+        "description": "uniqueItems validation",
+        "schema": {"uniqueItems": true},
+        "tests": [
+            {
+                "description": "unique array of integers is valid",
+                "data": [1, 2],
+                "valid": true
+            },
+            {
+                "description": "non-unique array of integers is invalid",
+                "data": [1, 1],
+                "valid": false
+            },
+            {
+                "description": "numbers are unique if mathematically unequal",
+                "data": [1.0, 1.00, 1],
+                "valid": false
+            },
+            {
+                "description": "unique array of objects is valid",
+                "data": [{"foo": "bar"}, {"foo": "baz"}],
+                "valid": true
+            },
+            {
+                "description": "non-unique array of objects is invalid",
+                "data": [{"foo": "bar"}, {"foo": "bar"}],
+                "valid": false
+            },
+            {
+                "description": "unique array of nested objects is valid",
+                "data": [
+                    {"foo": {"bar" : {"baz" : true}}},
+                    {"foo": {"bar" : {"baz" : false}}}
+                ],
+                "valid": true
+            },
+            {
+                "description": "non-unique array of nested objects is invalid",
+                "data": [
+                    {"foo": {"bar" : {"baz" : true}}},
+                    {"foo": {"bar" : {"baz" : true}}}
+                ],
+                "valid": false
+            },
+            {
+                "description": "unique array of arrays is valid",
+                "data": [["foo"], ["bar"]],
+                "valid": true
+            },
+            {
+                "description": "non-unique array of arrays is invalid",
+                "data": [["foo"], ["foo"]],
+                "valid": false
+            },
+            {
+                "description": "1 and true are unique",
+                "data": [1, true],
+                "valid": true
+            },
+            {
+                "description": "0 and false are unique",
+                "data": [0, false],
+                "valid": true
+            },
+            {
+                "description": "unique heterogeneous types are valid",
+                "data": [{}, [1], true, null, 1],
+                "valid": true
+            },
+            {
+                "description": "non-unique heterogeneous types are invalid",
+                "data": [{}, [1], true, null, {}, 1],
+                "valid": false
+            }
+        ]
+    }
+]

Added: vendor/libucl/dist/tests/schema.test
===================================================================
--- vendor/libucl/dist/tests/schema.test	                        (rev 0)
+++ vendor/libucl/dist/tests/schema.test	2018-06-19 13:29:00 UTC (rev 11074)
@@ -0,0 +1,9 @@
+#!/bin/sh
+
+PROG=${TEST_BINARY_DIR}/test_schema
+rm /tmp/_ucl_test_schema.out ||true
+for i in ${TEST_DIR}/schema/*.json ; do
+	_name=`basename $i`
+	printf "running schema test suite $_name... "
+	$PROG >> /tmp/_ucl_test_schema.out < $i && ( echo "OK" ) || ( echo "Fail" )
+done

Added: vendor/libucl/dist/tests/speed.test
===================================================================
--- vendor/libucl/dist/tests/speed.test	                        (rev 0)
+++ vendor/libucl/dist/tests/speed.test	2018-06-19 13:29:00 UTC (rev 11074)
@@ -0,0 +1,21 @@
+#!/bin/sh
+
+PROG=${TEST_BINARY_DIR}/test_speed
+
+sh -c "xz -c < /dev/null > /dev/null"
+echo 'Running speed tests'
+for _tin in ${TEST_DIR}/*.xz ; do
+	echo "Unpacking $_tin..."
+	xz -cd < $_tin > ${TEST_OUT_DIR}/test_file
+	# Preread file to cheat benchmark!
+	cat ${TEST_OUT_DIR}/test_file > /dev/null
+	echo "Starting benchmarking for $_tin..."
+	$PROG ${TEST_OUT_DIR}/test_file
+	if [ $? -ne 0 ] ; then
+		echo "Test: $_tin failed"
+		rm ${TEST_OUT_DIR}/test_file
+		exit 1
+	fi
+	rm ${TEST_OUT_DIR}/test_file
+done
+

Added: vendor/libucl/dist/tests/test_basic.c
===================================================================
--- vendor/libucl/dist/tests/test_basic.c	                        (rev 0)
+++ vendor/libucl/dist/tests/test_basic.c	2018-06-19 13:29:00 UTC (rev 11074)
@@ -0,0 +1,151 @@
+/* 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"
+
+int
+main (int argc, char **argv)
+{
+	char inbuf[8192], *test_in = NULL;
+	struct ucl_parser *parser = NULL, *parser2 = NULL;
+	ucl_object_t *obj;
+	FILE *in, *out;
+	unsigned char *emitted = NULL;
+	const char *fname_in = NULL, *fname_out = NULL;
+	int ret = 0, inlen, opt, json = 0;
+
+	while ((opt = getopt(argc, argv, "j")) != -1) {
+		switch (opt) {
+		case 'j':
+			json = 1;
+			break;
+		default: /* '?' */
+			fprintf (stderr, "Usage: %s [-j] [in] [out]\n",
+					argv[0]);
+			exit (EXIT_FAILURE);
+		}
+	}
+
+	argc -= optind;
+	argv += optind;
+
+	switch (argc) {
+	case 1:
+		fname_in = argv[0];
+		break;
+	case 2:
+		fname_in = argv[0];
+		fname_out = argv[1];
+		break;
+	}
+
+	if (fname_in != NULL) {
+		in = fopen (fname_in, "r");
+		if (in == NULL) {
+			exit (-errno);
+		}
+	}
+	else {
+		in = stdin;
+	}
+	parser = ucl_parser_new (UCL_PARSER_KEY_LOWERCASE);
+	ucl_parser_register_variable (parser, "ABI", "unknown");
+
+	if (fname_in != NULL) {
+		ucl_parser_set_filevars (parser, fname_in, true);
+	}
+
+	while (!feof (in)) {
+		memset (inbuf, 0, sizeof (inbuf));
+		if (fread (inbuf, 1, sizeof (inbuf) - 1, in) == 0) {
+			break;
+		}
+		inlen = strlen (inbuf);
+		test_in = malloc (inlen);
+		memcpy (test_in, inbuf, inlen);
+		ucl_parser_add_chunk (parser, test_in, inlen);
+	}
+	fclose (in);
+
+	if (fname_out != NULL) {
+		out = fopen (fname_out, "w");
+		if (out == NULL) {
+			exit (-errno);
+		}
+	}
+	else {
+		out = stdout;
+	}
+	if (ucl_parser_get_error (parser) != NULL) {
+		fprintf (out, "Error occurred: %s\n", ucl_parser_get_error(parser));
+		ret = 1;
+		goto end;
+	}
+	obj = ucl_parser_get_object (parser);
+	if (json) {
+		emitted = ucl_object_emit (obj, UCL_EMIT_JSON);
+	}
+	else {
+		emitted = ucl_object_emit (obj, UCL_EMIT_CONFIG);
+	}
+	ucl_parser_free (parser);
+	ucl_object_unref (obj);
+	parser2 = ucl_parser_new (UCL_PARSER_KEY_LOWERCASE);
+	ucl_parser_add_string (parser2, emitted, 0);
+
+	if (ucl_parser_get_error(parser2) != NULL) {
+		fprintf (out, "Error occurred: %s\n", ucl_parser_get_error(parser2));
+		fprintf (out, "%s\n", emitted);
+		ret = 1;
+		goto end;
+	}
+	if (emitted != NULL) {
+		free (emitted);
+	}
+	obj = ucl_parser_get_object (parser2);
+	if (json) {
+		emitted = ucl_object_emit (obj, UCL_EMIT_JSON);
+	}
+	else {
+		emitted = ucl_object_emit (obj, UCL_EMIT_CONFIG);
+	}
+
+	fprintf (out, "%s\n", emitted);
+	ucl_object_unref (obj);
+
+end:
+	if (emitted != NULL) {
+		free (emitted);
+	}
+	if (parser2 != NULL) {
+		ucl_parser_free (parser2);
+	}
+	if (test_in != NULL) {
+		free (test_in);
+	}
+
+	fclose (out);
+
+	return ret;
+}

Added: vendor/libucl/dist/tests/test_generate.c
===================================================================
--- vendor/libucl/dist/tests/test_generate.c	                        (rev 0)
+++ vendor/libucl/dist/tests/test_generate.c	2018-06-19 13:29:00 UTC (rev 11074)
@@ -0,0 +1,150 @@
+/* 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 <stdio.h>
+#include <errno.h>
+#include <assert.h>
+#include "ucl.h"
+
+int
+main (int argc, char **argv)
+{
+	ucl_object_t *obj, *cur, *ar, *ref;
+	const ucl_object_t *found;
+	FILE *out;
+	unsigned char *emitted;
+	const char *fname_out = NULL;
+	int ret = 0;
+
+	switch (argc) {
+	case 2:
+		fname_out = argv[1];
+		break;
+	}
+
+
+	if (fname_out != NULL) {
+		out = fopen (fname_out, "w");
+		if (out == NULL) {
+			exit (-errno);
+		}
+	}
+	else {
+		out = stdout;
+	}
+
+	obj = ucl_object_typed_new (UCL_OBJECT);
+	/* Create some strings */
+	cur = ucl_object_fromstring_common ("  test string    ", 0, UCL_STRING_TRIM);
+	ucl_object_insert_key (obj, cur, "key1", 0, false);
+	cur = ucl_object_fromstring_common ("  test \nstring\n    ", 0, UCL_STRING_TRIM | UCL_STRING_ESCAPE);
+	ucl_object_insert_key (obj, cur, "key2", 0, false);
+	cur = ucl_object_fromstring_common ("  test string    \n", 0, 0);
+	ucl_object_insert_key (obj, cur, "key3", 0, false);
+	/* Array of numbers */
+	ar = ucl_object_typed_new (UCL_ARRAY);
+	cur = ucl_object_fromint (10);
+	ucl_array_append (ar, cur);
+	cur = ucl_object_fromdouble (10.1);
+	ucl_array_append (ar, cur);
+	cur = ucl_object_fromdouble (9.999);
+	ucl_array_prepend (ar, cur);
+
+	/* Removing from an array */
+	cur = ucl_object_fromdouble (1.0);
+	ucl_array_append (ar, cur);
+	cur = ucl_array_delete (ar, cur);
+	assert (ucl_object_todouble (cur) == 1.0);
+	ucl_object_unref (cur);
+	cur = ucl_object_fromdouble (2.0);
+	ucl_array_append (ar, cur);
+	cur = ucl_array_pop_last (ar);
+	assert (ucl_object_todouble (cur) == 2.0);
+	ucl_object_unref (cur);
+	cur = ucl_object_fromdouble (3.0);
+	ucl_array_prepend (ar, cur);
+	cur = ucl_array_pop_first (ar);
+	assert (ucl_object_todouble (cur) == 3.0);
+	ucl_object_unref (cur);
+
+	ucl_object_insert_key (obj, ar, "key4", 0, false);
+	cur = ucl_object_frombool (true);
+	/* Ref object to test refcounts */
+	ref = ucl_object_ref (cur);
+	ucl_object_insert_key (obj, cur, "key4", 0, false);
+	/* Empty strings */
+	cur = ucl_object_fromstring_common ("      ", 0, UCL_STRING_TRIM);
+	ucl_object_insert_key (obj, cur, "key5", 0, false);
+	cur = ucl_object_fromstring_common ("", 0, UCL_STRING_ESCAPE);
+	ucl_object_insert_key (obj, cur, "key6", 0, false);
+	cur = ucl_object_fromstring_common ("   \n", 0, UCL_STRING_ESCAPE);
+	ucl_object_insert_key (obj, cur, "key7", 0, false);
+	/* Numbers and booleans */
+	cur = ucl_object_fromstring_common ("1mb", 0, UCL_STRING_ESCAPE | UCL_STRING_PARSE);
+	ucl_object_insert_key (obj, cur, "key8", 0, false);
+	cur = ucl_object_fromstring_common ("3.14", 0, UCL_STRING_PARSE);
+	ucl_object_insert_key (obj, cur, "key9", 0, false);
+	cur = ucl_object_fromstring_common ("true", 0, UCL_STRING_PARSE);
+	ucl_object_insert_key (obj, cur, "key10", 0, false);
+	cur = ucl_object_fromstring_common ("  off  ", 0, UCL_STRING_PARSE | UCL_STRING_TRIM);
+	ucl_object_insert_key (obj, cur, "key11", 0, false);
+	cur = ucl_object_fromstring_common ("gslin at gslin.org", 0, UCL_STRING_PARSE_INT);
+	ucl_object_insert_key (obj, cur, "key12", 0, false);
+	cur = ucl_object_fromstring_common ("#test", 0, UCL_STRING_PARSE_INT);
+	ucl_object_insert_key (obj, cur, "key13", 0, false);
+	cur = ucl_object_frombool (true);
+	ucl_object_insert_key (obj, cur, "k=3", 0, false);
+
+	/* Try to find using path */
+	/* Should exist */
+	found = ucl_lookup_path (obj, "key4.1");
+	assert (found != NULL && ucl_object_toint (found) == 10);
+	/* . should be ignored */
+	found = ucl_lookup_path (obj, ".key4.1");
+	assert (found != NULL && ucl_object_toint (found) == 10);
+	/* moar dots... */
+	found = ucl_lookup_path (obj, ".key4........1...");
+	assert (found != NULL && ucl_object_toint (found) == 10);
+	/* No such index */
+	found = ucl_lookup_path (obj, ".key4.3");
+	assert (found == NULL);
+	/* No such key */
+	found = ucl_lookup_path (obj, "key9..key1");
+	assert (found == NULL);
+
+	emitted = ucl_object_emit (obj, UCL_EMIT_CONFIG);
+
+	fprintf (out, "%s\n", emitted);
+	ucl_object_unref (obj);
+
+	if (emitted != NULL) {
+		free (emitted);
+	}
+	fclose (out);
+
+	/* Ref should still be accessible */
+	ref->value.iv = 100500;
+	ucl_object_unref (ref);
+
+	return ret;
+}

Added: vendor/libucl/dist/tests/test_schema.c
===================================================================
--- vendor/libucl/dist/tests/test_schema.c	                        (rev 0)
+++ vendor/libucl/dist/tests/test_schema.c	2018-06-19 13:29:00 UTC (rev 11074)
@@ -0,0 +1,158 @@
+/* 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 <errno.h>
+#include <unistd.h>
+#include "ucl.h"
+
+static int
+read_stdin (char **buf)
+{
+	int size = BUFSIZ, remain, ret;
+	char *p;
+
+	*buf = malloc (size);
+	if (*buf == NULL) {
+		return -1;
+	}
+
+	p = *buf;
+	remain = size;
+
+	while ((ret = read (STDIN_FILENO, p, remain)) > 0) {
+		remain -= ret;
+		p += ret;
+		if (remain == 0) {
+			*buf = realloc (*buf, size * 2);
+			if (*buf == NULL) {
+				return -1;
+			}
+			p = *buf + size;
+			remain = size;
+			size *= 2;
+		}
+	}
+
+	return ret;
+}
+
+static bool
+perform_test (const ucl_object_t *schema, const ucl_object_t *obj,
+		struct ucl_schema_error *err)
+{
+	const const ucl_object_t *valid, *data, *description;
+	bool match;
+
+	data = ucl_object_find_key (obj, "data");
+	description = ucl_object_find_key (obj, "description");
+	valid = ucl_object_find_key (obj, "valid");
+
+	if (data == NULL || description == NULL || valid == NULL) {
+		fprintf (stdout, "Bad test case\n");
+		return false;
+	}
+
+	match = ucl_object_validate (schema, data, err);
+	if (match != ucl_object_toboolean (valid)) {
+		fprintf (stdout, "Test case '%s' failed (expected %s): '%s'\n",
+				ucl_object_tostring (description),
+				ucl_object_toboolean (valid) ? "valid" : "invalid",
+						err->msg);
+		return false;
+	}
+
+	return true;
+}
+
+static int
+perform_tests (const ucl_object_t *obj)
+{
+	struct ucl_schema_error err;
+	ucl_object_iter_t iter = NULL;
+	const ucl_object_t *schema, *tests, *description, *test;
+
+	if (obj->type != UCL_OBJECT) {
+		fprintf (stdout, "Bad test case\n");
+		return EXIT_FAILURE;
+	}
+
+	schema = ucl_object_find_key (obj, "schema");
+	tests = ucl_object_find_key (obj, "tests");
+	description = ucl_object_find_key (obj, "description");
+
+	if (schema == NULL || tests == NULL || description == NULL) {
+		fprintf (stdout, "Bad test case\n");
+		return EXIT_FAILURE;
+	}
+
+	memset (&err, 0, sizeof (err));
+
+	while ((test = ucl_iterate_object (tests, &iter, true)) != NULL) {
+		if (!perform_test (schema, test, &err)) {
+			fprintf (stdout, "Test suite '%s' failed\n",
+							ucl_object_tostring (description));
+			return EXIT_FAILURE;
+		}
+	}
+
+	return 0;
+}
+
+int
+main (int argc, char **argv)
+{
+	char *buf = NULL;
+	struct ucl_parser *parser;
+	ucl_object_t *obj = NULL;
+	const ucl_object_t *elt;
+	ucl_object_iter_t iter = NULL;
+	int ret = 0;
+
+	if (read_stdin (&buf) == -1) {
+		exit (EXIT_FAILURE);
+	}
+
+	parser = ucl_parser_new (0);
+
+	ucl_parser_add_string (parser, buf, 0);
+
+	if (ucl_parser_get_error (parser) != NULL) {
+		fprintf (stdout, "Error occurred: %s\n", ucl_parser_get_error (parser));
+		ret = 1;
+		return EXIT_FAILURE;
+	}
+	obj = ucl_parser_get_object (parser);
+	ucl_parser_free (parser);
+
+	while ((elt = ucl_iterate_object (obj, &iter, true)) != NULL) {
+		ret = perform_tests (elt);
+		if (ret != 0) {
+			break;
+		}
+	}
+
+	ucl_object_unref (obj);
+
+	return ret;
+}

Added: vendor/libucl/dist/tests/test_speed.c
===================================================================
--- vendor/libucl/dist/tests/test_speed.c	                        (rev 0)
+++ vendor/libucl/dist/tests/test_speed.c	2018-06-19 13:29:00 UTC (rev 11074)
@@ -0,0 +1,151 @@
+/* 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 <sys/types.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <time.h>
+
+#ifdef __APPLE__
+#ifdef HAVE_MACH_MACH_TIME_H
+#include <mach/mach_time.h>
+#endif
+#endif
+
+#include "ucl.h"
+
+static double
+get_ticks (void)
+{
+	double res;
+
+#ifdef __APPLE__
+	res = mach_absolute_time () / 1000000000.;
+#else
+	struct timespec ts;
+	clock_gettime (CLOCK_MONOTONIC, &ts);
+
+	res = (double)ts.tv_sec + ts.tv_nsec / 1000000000.;
+#endif
+
+	return res;
+}
+
+int
+main (int argc, char **argv)
+{
+	void *map;
+	struct ucl_parser *parser;
+	ucl_object_t *obj;
+	int fin;
+	unsigned char *emitted;
+	struct stat st;
+	const char *fname_in = NULL;
+	int ret = 0;
+	double start, end, seconds;
+
+	switch (argc) {
+	case 2:
+		fname_in = argv[1];
+		break;
+	}
+
+	fin = open (fname_in, O_RDONLY);
+	if (fin == -1) {
+		perror ("open failed");
+		exit (EXIT_FAILURE);
+	}
+	parser = ucl_parser_new (UCL_PARSER_ZEROCOPY);
+
+	(void)fstat (fin, &st);
+	map = mmap (NULL, st.st_size, PROT_READ, MAP_SHARED, fin, 0);
+	if (map == MAP_FAILED) {
+		perror ("mmap failed");
+		exit (EXIT_FAILURE);
+	}
+
+	close (fin);
+
+	start = get_ticks ();
+	ucl_parser_add_chunk (parser, map, st.st_size);
+
+	obj = ucl_parser_get_object (parser);
+	end = get_ticks ();
+
+	seconds = end - start;
+	printf ("ucl: parsed input in %.4f seconds\n", seconds);
+	if (ucl_parser_get_error(parser)) {
+		printf ("Error occurred: %s\n", ucl_parser_get_error(parser));
+		ret = 1;
+		goto err;
+	}
+
+	start = get_ticks ();
+	emitted = ucl_object_emit (obj, UCL_EMIT_CONFIG);
+	end = get_ticks ();
+
+	seconds = end - start;
+	printf ("ucl: emitted config in %.4f seconds\n", seconds);
+
+	free (emitted);
+
+	start = get_ticks ();
+	emitted = ucl_object_emit (obj, UCL_EMIT_JSON);
+	end = get_ticks ();
+
+	seconds = end - start;
+	printf ("ucl: emitted json in %.4f seconds\n", seconds);
+
+	free (emitted);
+
+	start = get_ticks ();
+	emitted = ucl_object_emit (obj, UCL_EMIT_JSON_COMPACT);
+	end = get_ticks ();
+
+	seconds = end - start;
+	printf ("ucl: emitted compact json in %.4f seconds\n", seconds);
+
+	free (emitted);
+
+	start = get_ticks ();
+	emitted = ucl_object_emit (obj, UCL_EMIT_YAML);
+	end = get_ticks ();
+
+	seconds = end - start;
+	printf ("ucl: emitted yaml in %.4f seconds\n", seconds);
+
+	free (emitted);
+
+	ucl_parser_free (parser);
+	ucl_object_unref (obj);
+
+err:
+	munmap (map, st.st_size);
+
+	return ret;
+}

Added: vendor/libucl/dist/uthash/uthash.h
===================================================================
--- vendor/libucl/dist/uthash/uthash.h	                        (rev 0)
+++ vendor/libucl/dist/uthash/uthash.h	2018-06-19 13:29:00 UTC (rev 11074)
@@ -0,0 +1,720 @@
+/*
+Copyright (c) 2003-2013, Troy D. Hanson     http://troydhanson.github.com/uthash/
+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.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
+OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef UTHASH_H
+#define UTHASH_H 
+
+#include <string.h>   /* memcmp,strlen */
+#include <stddef.h>   /* ptrdiff_t */
+#include <stdlib.h>   /* exit() */
+#include "xxhash.h"
+
+/* These macros use decltype or the earlier __typeof GNU extension.
+   As decltype is only available in newer compilers (VS2010 or gcc 4.3+
+   when compiling c++ source) this code uses whatever method is needed
+   or, for VS2008 where neither is available, uses casting workarounds. */
+#ifdef _MSC_VER         /* MS compiler */
+#if _MSC_VER >= 1600 && defined(__cplusplus)  /* VS2010 or newer in C++ mode */
+#define DECLTYPE(x) (decltype(x))
+#else                   /* VS2008 or older (or VS2010 in C mode) */
+#define NO_DECLTYPE
+#define DECLTYPE(x)
+#endif
+#else                   /* GNU, Sun and other compilers */
+#define DECLTYPE(x) (__typeof(x))
+#endif
+
+#ifdef NO_DECLTYPE
+#define DECLTYPE_ASSIGN(dst,src)                                                 \
+do {                                                                             \
+  char **_da_dst = (char**)(&(dst));                                             \
+  *_da_dst = (char*)(src);                                                       \
+} while(0)
+#else 
+#define DECLTYPE_ASSIGN(dst,src)                                                 \
+do {                                                                             \
+  (dst) = DECLTYPE(dst)(src);                                                    \
+} while(0)
+#endif
+
+/* a number of the hash function use uint32_t which isn't defined on win32 */
+#ifdef _MSC_VER
+typedef unsigned int uint32_t;
+typedef unsigned char uint8_t;
+#else
+#include <inttypes.h>   /* uint32_t */
+#endif
+
+#define UTHASH_VERSION 1.9.8
+
+#ifndef uthash_fatal
+#define uthash_fatal(msg) exit(-1)        /* fatal error (out of memory,etc) */
+#endif
+#ifndef uthash_malloc
+#define uthash_malloc(sz) malloc(sz)      /* malloc fcn                      */
+#endif
+#ifndef uthash_free
+#define uthash_free(ptr,sz) free(ptr)     /* free fcn                        */
+#endif
+
+#ifndef uthash_noexpand_fyi
+#define uthash_noexpand_fyi(tbl)          /* can be defined to log noexpand  */
+#endif
+#ifndef uthash_expand_fyi
+#define uthash_expand_fyi(tbl)            /* can be defined to log expands   */
+#endif
+
+/* initial number of buckets */
+#define HASH_INITIAL_NUM_BUCKETS 32      /* initial number of buckets        */
+#define HASH_INITIAL_NUM_BUCKETS_LOG2 5  /* lg2 of initial number of buckets */
+#define HASH_BKT_CAPACITY_THRESH 10      /* expand when bucket count reaches */
+
+/* calculate the element whose hash handle address is hhe */
+#define ELMT_FROM_HH(tbl,hhp) ((void*)(((char*)(hhp)) - ((tbl)->hho)))
+
+#define HASH_FIND(hh,head,keyptr,keylen,out)                                     \
+do {                                                                             \
+  unsigned _hf_bkt,_hf_hashv;                                                    \
+  out=NULL;                                                                      \
+  if (head) {                                                                    \
+     HASH_FCN(keyptr,keylen, (head)->hh.tbl->num_buckets, _hf_hashv, _hf_bkt);   \
+     if (HASH_BLOOM_TEST((head)->hh.tbl, _hf_hashv)) {                           \
+       HASH_FIND_IN_BKT((head)->hh.tbl, hh, (head)->hh.tbl->buckets[ _hf_bkt ],  \
+                        keyptr,keylen,out);                                      \
+     }                                                                           \
+  }                                                                              \
+} while (0)
+
+#ifdef HASH_BLOOM
+#define HASH_BLOOM_BITLEN (1ULL << HASH_BLOOM)
+#define HASH_BLOOM_BYTELEN (HASH_BLOOM_BITLEN/8) + ((HASH_BLOOM_BITLEN%8) ? 1:0)
+#define HASH_BLOOM_MAKE(tbl)                                                     \
+do {                                                                             \
+  (tbl)->bloom_nbits = HASH_BLOOM;                                               \
+  (tbl)->bloom_bv = (uint8_t*)uthash_malloc(HASH_BLOOM_BYTELEN);                 \
+  if (!((tbl)->bloom_bv))  { uthash_fatal( "out of memory"); }                   \
+  memset((tbl)->bloom_bv, 0, HASH_BLOOM_BYTELEN);                                \
+  (tbl)->bloom_sig = HASH_BLOOM_SIGNATURE;                                       \
+} while (0) 
+
+#define HASH_BLOOM_FREE(tbl)                                                     \
+do {                                                                             \
+  uthash_free((tbl)->bloom_bv, HASH_BLOOM_BYTELEN);                              \
+} while (0) 
+
+#define HASH_BLOOM_BITSET(bv,idx) (bv[(idx)/8] |= (1U << ((idx)%8)))
+#define HASH_BLOOM_BITTEST(bv,idx) (bv[(idx)/8] & (1U << ((idx)%8)))
+
+#define HASH_BLOOM_ADD(tbl,hashv)                                                \
+  HASH_BLOOM_BITSET((tbl)->bloom_bv, (hashv & (uint32_t)((1ULL << (tbl)->bloom_nbits) - 1)))
+
+#define HASH_BLOOM_TEST(tbl,hashv)                                               \
+  HASH_BLOOM_BITTEST((tbl)->bloom_bv, (hashv & (uint32_t)((1ULL << (tbl)->bloom_nbits) - 1)))
+
+#else
+#define HASH_BLOOM_MAKE(tbl) 
+#define HASH_BLOOM_FREE(tbl) 
+#define HASH_BLOOM_ADD(tbl,hashv) 
+#define HASH_BLOOM_TEST(tbl,hashv) (1)
+#define HASH_BLOOM_BYTELEN 0
+#endif
+
+#define HASH_MAKE_TABLE(hh,head)                                                 \
+do {                                                                             \
+  (head)->hh.tbl = (UT_hash_table*)uthash_malloc(                                \
+                  sizeof(UT_hash_table));                                        \
+  if (!((head)->hh.tbl))  { uthash_fatal( "out of memory"); }                    \
+  memset((head)->hh.tbl, 0, sizeof(UT_hash_table));                              \
+  (head)->hh.tbl->tail = &((head)->hh);                                          \
+  (head)->hh.tbl->num_buckets = HASH_INITIAL_NUM_BUCKETS;                        \
+  (head)->hh.tbl->log2_num_buckets = HASH_INITIAL_NUM_BUCKETS_LOG2;              \
+  (head)->hh.tbl->hho = (char*)(&(head)->hh) - (char*)(head);                    \
+  (head)->hh.tbl->buckets = (UT_hash_bucket*)uthash_malloc(                      \
+          HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket));               \
+  if (! (head)->hh.tbl->buckets) { uthash_fatal( "out of memory"); }             \
+  memset((head)->hh.tbl->buckets, 0,                                             \
+          HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket));               \
+  HASH_BLOOM_MAKE((head)->hh.tbl);                                               \
+  (head)->hh.tbl->signature = HASH_SIGNATURE;                                    \
+} while(0)
+
+#define HASH_ADD(hh,head,fieldname,keylen_in,add)                                \
+        HASH_ADD_KEYPTR(hh,head,&((add)->fieldname),keylen_in,add)
+
+#define HASH_REPLACE(hh,head,fieldname,keylen_in,add,replaced)                   \
+do {                                                                             \
+  replaced=NULL;                                                                 \
+  HASH_FIND(hh,head,&((add)->fieldname),keylen_in,replaced);                     \
+  if (replaced!=NULL) {                                                          \
+     HASH_DELETE(hh,head,replaced);                                              \
+  };                                                                             \
+  HASH_ADD(hh,head,fieldname,keylen_in,add);                                     \
+} while(0)
+ 
+#define HASH_ADD_KEYPTR(hh,head,keyptr,keylen_in,add)                            \
+do {                                                                             \
+ unsigned _ha_bkt;                                                               \
+ (add)->hh.next = NULL;                                                          \
+ (add)->hh.key = (const char*)keyptr;                                                  \
+ (add)->hh.keylen = (unsigned)keylen_in;                                                   \
+ if (!(head)) {                                                                  \
+    head = (add);                                                                \
+    (head)->hh.prev = NULL;                                                      \
+    HASH_MAKE_TABLE(hh,head);                                                    \
+ } else {                                                                        \
+    (head)->hh.tbl->tail->next = (add);                                          \
+    (add)->hh.prev = ELMT_FROM_HH((head)->hh.tbl, (head)->hh.tbl->tail);         \
+    (head)->hh.tbl->tail = &((add)->hh);                                         \
+ }                                                                               \
+ (head)->hh.tbl->num_items++;                                                    \
+ (add)->hh.tbl = (head)->hh.tbl;                                                 \
+ HASH_FCN(keyptr,keylen_in, (head)->hh.tbl->num_buckets,                         \
+         (add)->hh.hashv, _ha_bkt);                                              \
+ HASH_ADD_TO_BKT((head)->hh.tbl->buckets[_ha_bkt],&(add)->hh);                   \
+ HASH_BLOOM_ADD((head)->hh.tbl,(add)->hh.hashv);                                 \
+ HASH_EMIT_KEY(hh,head,keyptr,keylen_in);                                        \
+ HASH_FSCK(hh,head);                                                             \
+} while(0)
+
+#define HASH_TO_BKT( hashv, num_bkts, bkt )                                      \
+do {                                                                             \
+  bkt = ((hashv) & ((num_bkts) - 1));                                            \
+} while(0)
+
+/* delete "delptr" from the hash table.
+ * "the usual" patch-up process for the app-order doubly-linked-list.
+ * The use of _hd_hh_del below deserves special explanation.
+ * These used to be expressed using (delptr) but that led to a bug
+ * if someone used the same symbol for the head and deletee, like
+ *  HASH_DELETE(hh,users,users);
+ * We want that to work, but by changing the head (users) below
+ * we were forfeiting our ability to further refer to the deletee (users)
+ * in the patch-up process. Solution: use scratch space to
+ * copy the deletee pointer, then the latter references are via that
+ * scratch pointer rather than through the repointed (users) symbol.
+ */
+#define HASH_DELETE(hh,head,delptr)                                              \
+do {                                                                             \
+    unsigned _hd_bkt;                                                            \
+    struct UT_hash_handle *_hd_hh_del;                                           \
+    if ( ((delptr)->hh.prev == NULL) && ((delptr)->hh.next == NULL) )  {         \
+        uthash_free((head)->hh.tbl->buckets,                                     \
+                    (head)->hh.tbl->num_buckets*sizeof(struct UT_hash_bucket) ); \
+        HASH_BLOOM_FREE((head)->hh.tbl);                                         \
+        uthash_free((head)->hh.tbl, sizeof(UT_hash_table));                      \
+        head = NULL;                                                             \
+    } else {                                                                     \
+        _hd_hh_del = &((delptr)->hh);                                            \
+        if ((delptr) == ELMT_FROM_HH((head)->hh.tbl,(head)->hh.tbl->tail)) {     \
+            (head)->hh.tbl->tail =                                               \
+                (UT_hash_handle*)((ptrdiff_t)((delptr)->hh.prev) +               \
+                (head)->hh.tbl->hho);                                            \
+        }                                                                        \
+        if ((delptr)->hh.prev) {                                                 \
+            ((UT_hash_handle*)((ptrdiff_t)((delptr)->hh.prev) +                  \
+                    (head)->hh.tbl->hho))->next = (delptr)->hh.next;             \
+        } else {                                                                 \
+            DECLTYPE_ASSIGN(head,(delptr)->hh.next);                             \
+        }                                                                        \
+        if (_hd_hh_del->next) {                                                  \
+            ((UT_hash_handle*)((ptrdiff_t)_hd_hh_del->next +                     \
+                    (head)->hh.tbl->hho))->prev =                                \
+                    _hd_hh_del->prev;                                            \
+        }                                                                        \
+        HASH_TO_BKT( _hd_hh_del->hashv, (head)->hh.tbl->num_buckets, _hd_bkt);   \
+        HASH_DEL_IN_BKT(hh,(head)->hh.tbl->buckets[_hd_bkt], _hd_hh_del);        \
+        (head)->hh.tbl->num_items--;                                             \
+    }                                                                            \
+    HASH_FSCK(hh,head);                                                          \
+} while (0)
+
+
+/* convenience forms of HASH_FIND/HASH_ADD/HASH_DEL */
+#define HASH_FIND_STR(head,findstr,out)                                          \
+    HASH_FIND(hh,head,findstr,strlen(findstr),out)
+#define HASH_ADD_STR(head,strfield,add)                                          \
+    HASH_ADD(hh,head,strfield,strlen(add->strfield),add)
+#define HASH_REPLACE_STR(head,strfield,add,replaced)                             \
+  HASH_REPLACE(hh,head,strfield,strlen(add->strfield),add,replaced)
+#define HASH_FIND_INT(head,findint,out)                                          \
+    HASH_FIND(hh,head,findint,sizeof(int),out)
+#define HASH_ADD_INT(head,intfield,add)                                          \
+    HASH_ADD(hh,head,intfield,sizeof(int),add)
+#define HASH_REPLACE_INT(head,intfield,add,replaced)                             \
+    HASH_REPLACE(hh,head,intfield,sizeof(int),add,replaced)
+#define HASH_FIND_PTR(head,findptr,out)                                          \
+    HASH_FIND(hh,head,findptr,sizeof(void *),out)
+#define HASH_ADD_PTR(head,ptrfield,add)                                          \
+    HASH_ADD(hh,head,ptrfield,sizeof(void *),add)
+#define HASH_REPLACE_PTR(head,ptrfield,add)                                      \
+    HASH_REPLACE(hh,head,ptrfield,sizeof(void *),add,replaced)
+#define HASH_DEL(head,delptr)                                                    \
+    HASH_DELETE(hh,head,delptr)
+
+/* HASH_FSCK checks hash integrity on every add/delete when HASH_DEBUG is defined.
+ * This is for uthash developer only; it compiles away if HASH_DEBUG isn't defined.
+ */
+#ifdef HASH_DEBUG
+#define HASH_OOPS(...) do { fprintf(stderr,__VA_ARGS__); exit(-1); } while (0)
+#define HASH_FSCK(hh,head)                                                       \
+do {                                                                             \
+    unsigned _bkt_i;                                                             \
+    unsigned _count, _bkt_count;                                                 \
+    char *_prev;                                                                 \
+    struct UT_hash_handle *_thh;                                                 \
+    if (head) {                                                                  \
+        _count = 0;                                                              \
+        for( _bkt_i = 0; _bkt_i < (head)->hh.tbl->num_buckets; _bkt_i++) {       \
+            _bkt_count = 0;                                                      \
+            _thh = (head)->hh.tbl->buckets[_bkt_i].hh_head;                      \
+            _prev = NULL;                                                        \
+            while (_thh) {                                                       \
+               if (_prev != (char*)(_thh->hh_prev)) {                            \
+                   HASH_OOPS("invalid hh_prev %p, actual %p\n",                  \
+                    _thh->hh_prev, _prev );                                      \
+               }                                                                 \
+               _bkt_count++;                                                     \
+               _prev = (char*)(_thh);                                            \
+               _thh = _thh->hh_next;                                             \
+            }                                                                    \
+            _count += _bkt_count;                                                \
+            if ((head)->hh.tbl->buckets[_bkt_i].count !=  _bkt_count) {          \
+               HASH_OOPS("invalid bucket count %d, actual %d\n",                 \
+                (head)->hh.tbl->buckets[_bkt_i].count, _bkt_count);              \
+            }                                                                    \
+        }                                                                        \
+        if (_count != (head)->hh.tbl->num_items) {                               \
+            HASH_OOPS("invalid hh item count %d, actual %d\n",                   \
+                (head)->hh.tbl->num_items, _count );                             \
+        }                                                                        \
+        /* traverse hh in app order; check next/prev integrity, count */         \
+        _count = 0;                                                              \
+        _prev = NULL;                                                            \
+        _thh =  &(head)->hh;                                                     \
+        while (_thh) {                                                           \
+           _count++;                                                             \
+           if (_prev !=(char*)(_thh->prev)) {                                    \
+              HASH_OOPS("invalid prev %p, actual %p\n",                          \
+                    _thh->prev, _prev );                                         \
+           }                                                                     \
+           _prev = (char*)ELMT_FROM_HH((head)->hh.tbl, _thh);                    \
+           _thh = ( _thh->next ?  (UT_hash_handle*)((char*)(_thh->next) +        \
+                                  (head)->hh.tbl->hho) : NULL );                 \
+        }                                                                        \
+        if (_count != (head)->hh.tbl->num_items) {                               \
+            HASH_OOPS("invalid app item count %d, actual %d\n",                  \
+                (head)->hh.tbl->num_items, _count );                             \
+        }                                                                        \
+    }                                                                            \
+} while (0)
+#else
+#define HASH_FSCK(hh,head) 
+#endif
+
+/* When compiled with -DHASH_EMIT_KEYS, length-prefixed keys are emitted to 
+ * the descriptor to which this macro is defined for tuning the hash function.
+ * The app can #include <unistd.h> to get the prototype for write(2). */
+#ifdef HASH_EMIT_KEYS
+#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen)                                   \
+do {                                                                             \
+    unsigned _klen = fieldlen;                                                   \
+    write(HASH_EMIT_KEYS, &_klen, sizeof(_klen));                                \
+    write(HASH_EMIT_KEYS, keyptr, fieldlen);                                     \
+} while (0)
+#else 
+#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen)                    
+#endif
+
+/* default to Jenkin's hash unless overridden e.g. DHASH_FUNCTION=HASH_SAX */
+#ifdef HASH_FUNCTION 
+#define HASH_FCN HASH_FUNCTION
+#else
+#define HASH_FCN HASH_XX
+#endif
+
+#define XX_HASH_PRIME 2654435761U
+
+#define HASH_XX(key,keylen,num_bkts,hashv,bkt)                                  \
+do {                                                                             \
+  hashv = XXH32 (key, keylen, XX_HASH_PRIME);                                    \
+  bkt = (hashv) & (num_bkts-1);                                                  \
+} while (0)
+
+
+
+/* key comparison function; return 0 if keys equal */
+#define HASH_KEYCMP(a,b,len) memcmp(a,b,len) 
+
+/* iterate over items in a known bucket to find desired item */
+#define HASH_FIND_IN_BKT(tbl,hh,head,keyptr,keylen_in,out)                       \
+do {                                                                             \
+ if (head.hh_head) DECLTYPE_ASSIGN(out,ELMT_FROM_HH(tbl,head.hh_head));          \
+ else out=NULL;                                                                  \
+ while (out) {                                                                   \
+    if ((out)->hh.keylen == keylen_in) {                                           \
+        if ((HASH_KEYCMP((out)->hh.key,keyptr,keylen_in)) == 0) break;             \
+    }                                                                            \
+    if ((out)->hh.hh_next) DECLTYPE_ASSIGN(out,ELMT_FROM_HH(tbl,(out)->hh.hh_next)); \
+    else out = NULL;                                                             \
+ }                                                                               \
+} while(0)
+
+/* add an item to a bucket  */
+#define HASH_ADD_TO_BKT(head,addhh)                                              \
+do {                                                                             \
+ head.count++;                                                                   \
+ (addhh)->hh_next = head.hh_head;                                                \
+ (addhh)->hh_prev = NULL;                                                        \
+ if (head.hh_head) { (head).hh_head->hh_prev = (addhh); }                        \
+ (head).hh_head=addhh;                                                           \
+ if (head.count >= ((head.expand_mult+1) * HASH_BKT_CAPACITY_THRESH)             \
+     && (addhh)->tbl->noexpand != 1) {                                           \
+       HASH_EXPAND_BUCKETS((addhh)->tbl);                                        \
+ }                                                                               \
+} while(0)
+
+/* remove an item from a given bucket */
+#define HASH_DEL_IN_BKT(hh,head,hh_del)                                          \
+    (head).count--;                                                              \
+    if ((head).hh_head == hh_del) {                                              \
+      (head).hh_head = hh_del->hh_next;                                          \
+    }                                                                            \
+    if (hh_del->hh_prev) {                                                       \
+        hh_del->hh_prev->hh_next = hh_del->hh_next;                              \
+    }                                                                            \
+    if (hh_del->hh_next) {                                                       \
+        hh_del->hh_next->hh_prev = hh_del->hh_prev;                              \
+    }                                                                
+
+/* Bucket expansion has the effect of doubling the number of buckets
+ * and redistributing the items into the new buckets. Ideally the
+ * items will distribute more or less evenly into the new buckets
+ * (the extent to which this is true is a measure of the quality of
+ * the hash function as it applies to the key domain). 
+ * 
+ * With the items distributed into more buckets, the chain length
+ * (item count) in each bucket is reduced. Thus by expanding buckets
+ * the hash keeps a bound on the chain length. This bounded chain 
+ * length is the essence of how a hash provides constant time lookup.
+ * 
+ * The calculation of tbl->ideal_chain_maxlen below deserves some
+ * explanation. First, keep in mind that we're calculating the ideal
+ * maximum chain length based on the *new* (doubled) bucket count.
+ * In fractions this is just n/b (n=number of items,b=new num buckets).
+ * Since the ideal chain length is an integer, we want to calculate 
+ * ceil(n/b). We don't depend on floating point arithmetic in this
+ * hash, so to calculate ceil(n/b) with integers we could write
+ * 
+ *      ceil(n/b) = (n/b) + ((n%b)?1:0)
+ * 
+ * and in fact a previous version of this hash did just that.
+ * But now we have improved things a bit by recognizing that b is
+ * always a power of two. We keep its base 2 log handy (call it lb),
+ * so now we can write this with a bit shift and logical AND:
+ * 
+ *      ceil(n/b) = (n>>lb) + ( (n & (b-1)) ? 1:0)
+ * 
+ */
+#define HASH_EXPAND_BUCKETS(tbl)                                                 \
+do {                                                                             \
+    unsigned _he_bkt;                                                            \
+    unsigned _he_bkt_i;                                                          \
+    struct UT_hash_handle *_he_thh, *_he_hh_nxt;                                 \
+    UT_hash_bucket *_he_new_buckets, *_he_newbkt;                                \
+    _he_new_buckets = (UT_hash_bucket*)uthash_malloc(                            \
+             2 * tbl->num_buckets * sizeof(struct UT_hash_bucket));              \
+    if (!_he_new_buckets) { uthash_fatal( "out of memory"); }                    \
+    memset(_he_new_buckets, 0,                                                   \
+            2 * tbl->num_buckets * sizeof(struct UT_hash_bucket));               \
+    tbl->ideal_chain_maxlen =                                                    \
+       (tbl->num_items >> (tbl->log2_num_buckets+1)) +                           \
+       ((tbl->num_items & ((tbl->num_buckets*2)-1)) ? 1 : 0);                    \
+    tbl->nonideal_items = 0;                                                     \
+    for(_he_bkt_i = 0; _he_bkt_i < tbl->num_buckets; _he_bkt_i++)                \
+    {                                                                            \
+        _he_thh = tbl->buckets[ _he_bkt_i ].hh_head;                             \
+        while (_he_thh) {                                                        \
+           _he_hh_nxt = _he_thh->hh_next;                                        \
+           HASH_TO_BKT( _he_thh->hashv, tbl->num_buckets*2, _he_bkt);            \
+           _he_newbkt = &(_he_new_buckets[ _he_bkt ]);                           \
+           if (++(_he_newbkt->count) > tbl->ideal_chain_maxlen) {                \
+             tbl->nonideal_items++;                                              \
+             _he_newbkt->expand_mult = _he_newbkt->count /                       \
+                                        tbl->ideal_chain_maxlen;                 \
+           }                                                                     \
+           _he_thh->hh_prev = NULL;                                              \
+           _he_thh->hh_next = _he_newbkt->hh_head;                               \
+           if (_he_newbkt->hh_head) _he_newbkt->hh_head->hh_prev =               \
+                _he_thh;                                                         \
+           _he_newbkt->hh_head = _he_thh;                                        \
+           _he_thh = _he_hh_nxt;                                                 \
+        }                                                                        \
+    }                                                                            \
+    uthash_free( tbl->buckets, tbl->num_buckets*sizeof(struct UT_hash_bucket) ); \
+    tbl->num_buckets *= 2;                                                       \
+    tbl->log2_num_buckets++;                                                     \
+    tbl->buckets = _he_new_buckets;                                              \
+    tbl->ineff_expands = (tbl->nonideal_items > (tbl->num_items >> 1)) ?         \
+        (tbl->ineff_expands+1) : 0;                                              \
+    if (tbl->ineff_expands > 1) {                                                \
+        tbl->noexpand=1;                                                         \
+        uthash_noexpand_fyi(tbl);                                                \
+    }                                                                            \
+    uthash_expand_fyi(tbl);                                                      \
+} while(0)
+
+
+/* This is an adaptation of Simon Tatham's O(n log(n)) mergesort */
+/* Note that HASH_SORT assumes the hash handle name to be hh. 
+ * HASH_SRT was added to allow the hash handle name to be passed in. */
+#define HASH_SORT(head,cmpfcn) HASH_SRT(hh,head,cmpfcn)
+#define HASH_SRT(hh,head,cmpfcn)                                                 \
+do {                                                                             \
+  unsigned _hs_i;                                                                \
+  unsigned _hs_looping,_hs_nmerges,_hs_insize,_hs_psize,_hs_qsize;               \
+  struct UT_hash_handle *_hs_p, *_hs_q, *_hs_e, *_hs_list, *_hs_tail;            \
+  if (head) {                                                                    \
+      _hs_insize = 1;                                                            \
+      _hs_looping = 1;                                                           \
+      _hs_list = &((head)->hh);                                                  \
+      while (_hs_looping) {                                                      \
+          _hs_p = _hs_list;                                                      \
+          _hs_list = NULL;                                                       \
+          _hs_tail = NULL;                                                       \
+          _hs_nmerges = 0;                                                       \
+          while (_hs_p) {                                                        \
+              _hs_nmerges++;                                                     \
+              _hs_q = _hs_p;                                                     \
+              _hs_psize = 0;                                                     \
+              for ( _hs_i = 0; _hs_i  < _hs_insize; _hs_i++ ) {                  \
+                  _hs_psize++;                                                   \
+                  _hs_q = (UT_hash_handle*)((_hs_q->next) ?                      \
+                          ((void*)((char*)(_hs_q->next) +                        \
+                          (head)->hh.tbl->hho)) : NULL);                         \
+                  if (! (_hs_q) ) break;                                         \
+              }                                                                  \
+              _hs_qsize = _hs_insize;                                            \
+              while ((_hs_psize > 0) || ((_hs_qsize > 0) && _hs_q )) {           \
+                  if (_hs_psize == 0) {                                          \
+                      _hs_e = _hs_q;                                             \
+                      _hs_q = (UT_hash_handle*)((_hs_q->next) ?                  \
+                              ((void*)((char*)(_hs_q->next) +                    \
+                              (head)->hh.tbl->hho)) : NULL);                     \
+                      _hs_qsize--;                                               \
+                  } else if ( (_hs_qsize == 0) || !(_hs_q) ) {                   \
+                      _hs_e = _hs_p;                                             \
+                      if (_hs_p){                                                \
+                        _hs_p = (UT_hash_handle*)((_hs_p->next) ?                \
+                                ((void*)((char*)(_hs_p->next) +                  \
+                                (head)->hh.tbl->hho)) : NULL);                   \
+                       }                                                         \
+                      _hs_psize--;                                               \
+                  } else if ((                                                   \
+                      cmpfcn(DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl,_hs_p)), \
+                             DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl,_hs_q))) \
+                             ) <= 0) {                                           \
+                      _hs_e = _hs_p;                                             \
+                      if (_hs_p){                                                \
+                        _hs_p = (UT_hash_handle*)((_hs_p->next) ?                \
+                               ((void*)((char*)(_hs_p->next) +                   \
+                               (head)->hh.tbl->hho)) : NULL);                    \
+                       }                                                         \
+                      _hs_psize--;                                               \
+                  } else {                                                       \
+                      _hs_e = _hs_q;                                             \
+                      _hs_q = (UT_hash_handle*)((_hs_q->next) ?                  \
+                              ((void*)((char*)(_hs_q->next) +                    \
+                              (head)->hh.tbl->hho)) : NULL);                     \
+                      _hs_qsize--;                                               \
+                  }                                                              \
+                  if ( _hs_tail ) {                                              \
+                      _hs_tail->next = ((_hs_e) ?                                \
+                            ELMT_FROM_HH((head)->hh.tbl,_hs_e) : NULL);          \
+                  } else {                                                       \
+                      _hs_list = _hs_e;                                          \
+                  }                                                              \
+                  if (_hs_e) {                                                   \
+                  _hs_e->prev = ((_hs_tail) ?                                    \
+                     ELMT_FROM_HH((head)->hh.tbl,_hs_tail) : NULL);              \
+                  }                                                              \
+                  _hs_tail = _hs_e;                                              \
+              }                                                                  \
+              _hs_p = _hs_q;                                                     \
+          }                                                                      \
+          if (_hs_tail){                                                         \
+            _hs_tail->next = NULL;                                               \
+          }                                                                      \
+          if ( _hs_nmerges <= 1 ) {                                              \
+              _hs_looping=0;                                                     \
+              (head)->hh.tbl->tail = _hs_tail;                                   \
+              DECLTYPE_ASSIGN(head,ELMT_FROM_HH((head)->hh.tbl, _hs_list));      \
+          }                                                                      \
+          _hs_insize *= 2;                                                       \
+      }                                                                          \
+      HASH_FSCK(hh,head);                                                        \
+ }                                                                               \
+} while (0)
+
+/* This function selects items from one hash into another hash. 
+ * The end result is that the selected items have dual presence 
+ * in both hashes. There is no copy of the items made; rather 
+ * they are added into the new hash through a secondary hash 
+ * hash handle that must be present in the structure. */
+#define HASH_SELECT(hh_dst, dst, hh_src, src, cond)                              \
+do {                                                                             \
+  unsigned _src_bkt, _dst_bkt;                                                   \
+  void *_last_elt=NULL, *_elt;                                                   \
+  UT_hash_handle *_src_hh, *_dst_hh, *_last_elt_hh=NULL;                         \
+  ptrdiff_t _dst_hho = ((char*)(&(dst)->hh_dst) - (char*)(dst));                 \
+  if (src) {                                                                     \
+    for(_src_bkt=0; _src_bkt < (src)->hh_src.tbl->num_buckets; _src_bkt++) {     \
+      for(_src_hh = (src)->hh_src.tbl->buckets[_src_bkt].hh_head;                \
+          _src_hh;                                                               \
+          _src_hh = _src_hh->hh_next) {                                          \
+          _elt = ELMT_FROM_HH((src)->hh_src.tbl, _src_hh);                       \
+          if (cond(_elt)) {                                                      \
+            _dst_hh = (UT_hash_handle*)(((char*)_elt) + _dst_hho);               \
+            _dst_hh->key = _src_hh->key;                                         \
+            _dst_hh->keylen = _src_hh->keylen;                                   \
+            _dst_hh->hashv = _src_hh->hashv;                                     \
+            _dst_hh->prev = _last_elt;                                           \
+            _dst_hh->next = NULL;                                                \
+            if (_last_elt_hh) { _last_elt_hh->next = _elt; }                     \
+            if (!dst) {                                                          \
+              DECLTYPE_ASSIGN(dst,_elt);                                         \
+              HASH_MAKE_TABLE(hh_dst,dst);                                       \
+            } else {                                                             \
+              _dst_hh->tbl = (dst)->hh_dst.tbl;                                  \
+            }                                                                    \
+            HASH_TO_BKT(_dst_hh->hashv, _dst_hh->tbl->num_buckets, _dst_bkt);    \
+            HASH_ADD_TO_BKT(_dst_hh->tbl->buckets[_dst_bkt],_dst_hh);            \
+            (dst)->hh_dst.tbl->num_items++;                                      \
+            _last_elt = _elt;                                                    \
+            _last_elt_hh = _dst_hh;                                              \
+          }                                                                      \
+      }                                                                          \
+    }                                                                            \
+  }                                                                              \
+  HASH_FSCK(hh_dst,dst);                                                         \
+} while (0)
+
+#define HASH_CLEAR(hh,head)                                                      \
+do {                                                                             \
+  if (head) {                                                                    \
+    uthash_free((head)->hh.tbl->buckets,                                         \
+                (head)->hh.tbl->num_buckets*sizeof(struct UT_hash_bucket));      \
+    HASH_BLOOM_FREE((head)->hh.tbl);                                             \
+    uthash_free((head)->hh.tbl, sizeof(UT_hash_table));                          \
+    (head)=NULL;                                                                 \
+  }                                                                              \
+} while(0)
+
+#define HASH_OVERHEAD(hh,head)                                                   \
+ (size_t)((((head)->hh.tbl->num_items   * sizeof(UT_hash_handle))   +            \
+           ((head)->hh.tbl->num_buckets * sizeof(UT_hash_bucket))   +            \
+            (sizeof(UT_hash_table))                                 +            \
+            (HASH_BLOOM_BYTELEN)))
+
+#ifdef NO_DECLTYPE
+#define HASH_ITER(hh,head,el,tmp)                                                \
+for((el)=(head), (*(char**)(&(tmp)))=(char*)((head)?(head)->hh.next:NULL);       \
+  el; (el)=(tmp),(*(char**)(&(tmp)))=(char*)((tmp)?(tmp)->hh.next:NULL)) 
+#else
+#define HASH_ITER(hh,head,el,tmp)                                                \
+for((el)=(head),(tmp)=DECLTYPE(el)((head)?(head)->hh.next:NULL);                 \
+  el; (el)=(tmp),(tmp)=DECLTYPE(el)((tmp)?(tmp)->hh.next:NULL))
+#endif
+
+/* obtain a count of items in the hash */
+#define HASH_COUNT(head) HASH_CNT(hh,head) 
+#define HASH_CNT(hh,head) ((head)?((head)->hh.tbl->num_items):0)
+
+typedef struct UT_hash_bucket {
+   struct UT_hash_handle *hh_head;
+   unsigned count;
+
+   /* expand_mult is normally set to 0. In this situation, the max chain length
+    * threshold is enforced at its default value, HASH_BKT_CAPACITY_THRESH. (If
+    * the bucket's chain exceeds this length, bucket expansion is triggered). 
+    * However, setting expand_mult to a non-zero value delays bucket expansion
+    * (that would be triggered by additions to this particular bucket)
+    * until its chain length reaches a *multiple* of HASH_BKT_CAPACITY_THRESH.
+    * (The multiplier is simply expand_mult+1). The whole idea of this
+    * multiplier is to reduce bucket expansions, since they are expensive, in
+    * situations where we know that a particular bucket tends to be overused.
+    * It is better to let its chain length grow to a longer yet-still-bounded
+    * value, than to do an O(n) bucket expansion too often. 
+    */
+   unsigned expand_mult;
+
+} UT_hash_bucket;
+
+/* random signature used only to find hash tables in external analysis */
+#define HASH_SIGNATURE 0xa0111fe1
+#define HASH_BLOOM_SIGNATURE 0xb12220f2
+
+typedef struct UT_hash_table {
+   UT_hash_bucket *buckets;
+   unsigned num_buckets, log2_num_buckets;
+   unsigned num_items;
+   struct UT_hash_handle *tail; /* tail hh in app order, for fast append    */
+   ptrdiff_t hho; /* hash handle offset (byte pos of hash handle in element */
+
+   /* in an ideal situation (all buckets used equally), no bucket would have
+    * more than ceil(#items/#buckets) items. that's the ideal chain length. */
+   unsigned ideal_chain_maxlen;
+
+   /* nonideal_items is the number of items in the hash whose chain position
+    * exceeds the ideal chain maxlen. these items pay the penalty for an uneven
+    * hash distribution; reaching them in a chain traversal takes >ideal steps */
+   unsigned nonideal_items;
+
+   /* ineffective expands occur when a bucket doubling was performed, but 
+    * afterward, more than half the items in the hash had nonideal chain
+    * positions. If this happens on two consecutive expansions we inhibit any
+    * further expansion, as it's not helping; this happens when the hash
+    * function isn't a good fit for the key domain. When expansion is inhibited
+    * the hash will still work, albeit no longer in constant time. */
+   unsigned ineff_expands, noexpand;
+
+   uint32_t signature; /* used only to find hash tables in external analysis */
+#ifdef HASH_BLOOM
+   uint32_t bloom_sig; /* used only to test bloom exists in external analysis */
+   uint8_t *bloom_bv;
+   char bloom_nbits;
+#endif
+
+} UT_hash_table;
+
+typedef struct UT_hash_handle {
+   struct UT_hash_table *tbl;
+   void *prev;                       /* prev element in app order      */
+   void *next;                       /* next element in app order      */
+   struct UT_hash_handle *hh_prev;   /* previous hh in bucket order    */
+   struct UT_hash_handle *hh_next;   /* next hh in bucket order        */
+   const void *key;                  /* ptr to enclosing struct's key  */
+   unsigned keylen;                  /* enclosing struct's key len     */
+   unsigned hashv;                   /* result of hash-fcn(key)        */
+} UT_hash_handle;
+
+#endif /* UTHASH_H */

Added: vendor/libucl/dist/uthash/utlist.h
===================================================================
--- vendor/libucl/dist/uthash/utlist.h	                        (rev 0)
+++ vendor/libucl/dist/uthash/utlist.h	2018-06-19 13:29:00 UTC (rev 11074)
@@ -0,0 +1,757 @@
+/*
+Copyright (c) 2007-2013, Troy D. Hanson   http://troydhanson.github.com/uthash/
+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.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
+OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef UTLIST_H
+#define UTLIST_H
+
+#define UTLIST_VERSION 1.9.8
+
+#include <assert.h>
+
+/* 
+ * This file contains macros to manipulate singly and doubly-linked lists.
+ *
+ * 1. LL_ macros:  singly-linked lists.
+ * 2. DL_ macros:  doubly-linked lists.
+ * 3. CDL_ macros: circular doubly-linked lists.
+ *
+ * To use singly-linked lists, your structure must have a "next" pointer.
+ * To use doubly-linked lists, your structure must "prev" and "next" pointers.
+ * Either way, the pointer to the head of the list must be initialized to NULL.
+ * 
+ * ----------------.EXAMPLE -------------------------
+ * struct item {
+ *      int id;
+ *      struct item *prev, *next;
+ * }
+ *
+ * struct item *list = NULL:
+ *
+ * int main() {
+ *      struct item *item;
+ *      ... allocate and populate item ...
+ *      DL_APPEND(list, item);
+ * }
+ * --------------------------------------------------
+ *
+ * For doubly-linked lists, the append and delete macros are O(1)
+ * For singly-linked lists, append and delete are O(n) but prepend is O(1)
+ * The sort macro is O(n log(n)) for all types of single/double/circular lists.
+ */
+
+/* These macros use decltype or the earlier __typeof GNU extension.
+   As decltype is only available in newer compilers (VS2010 or gcc 4.3+
+   when compiling c++ code), this code uses whatever method is needed
+   or, for VS2008 where neither is available, uses casting workarounds. */
+#ifdef _MSC_VER            /* MS compiler */
+#if _MSC_VER >= 1600 && defined(__cplusplus)  /* VS2010 or newer in C++ mode */
+#define LDECLTYPE(x) decltype(x)
+#else                     /* VS2008 or older (or VS2010 in C mode) */
+#define NO_DECLTYPE
+#define LDECLTYPE(x) char*
+#endif
+#elif defined(__ICCARM__)
+#define NO_DECLTYPE
+#define LDECLTYPE(x) char*
+#else                      /* GNU, Sun and other compilers */
+#define LDECLTYPE(x) __typeof(x)
+#endif
+
+/* for VS2008 we use some workarounds to get around the lack of decltype,
+ * namely, we always reassign our tmp variable to the list head if we need
+ * to dereference its prev/next pointers, and save/restore the real head.*/
+#ifdef NO_DECLTYPE
+#define _SV(elt,list) _tmp = (char*)(list); {char **_alias = (char**)&(list); *_alias = (elt); }
+#define _NEXT(elt,list,next) ((char*)((list)->next))
+#define _NEXTASGN(elt,list,to,next) { char **_alias = (char**)&((list)->next); *_alias=(char*)(to); }
+/* #define _PREV(elt,list,prev) ((char*)((list)->prev)) */
+#define _PREVASGN(elt,list,to,prev) { char **_alias = (char**)&((list)->prev); *_alias=(char*)(to); }
+#define _RS(list) { char **_alias = (char**)&(list); *_alias=_tmp; }
+#define _CASTASGN(a,b) { char **_alias = (char**)&(a); *_alias=(char*)(b); }
+#else 
+#define _SV(elt,list)
+#define _NEXT(elt,list,next) ((elt)->next)
+#define _NEXTASGN(elt,list,to,next) ((elt)->next)=(to)
+/* #define _PREV(elt,list,prev) ((elt)->prev) */
+#define _PREVASGN(elt,list,to,prev) ((elt)->prev)=(to)
+#define _RS(list)
+#define _CASTASGN(a,b) (a)=(b)
+#endif
+
+/******************************************************************************
+ * The sort macro is an adaptation of Simon Tatham's O(n log(n)) mergesort    *
+ * Unwieldy variable names used here to avoid shadowing passed-in variables.  *
+ *****************************************************************************/
+#define LL_SORT(list, cmp)                                                                     \
+    LL_SORT2(list, cmp, next)
+
+#define LL_SORT2(list, cmp, next)                                                              \
+do {                                                                                           \
+  LDECLTYPE(list) _ls_p;                                                                       \
+  LDECLTYPE(list) _ls_q;                                                                       \
+  LDECLTYPE(list) _ls_e;                                                                       \
+  LDECLTYPE(list) _ls_tail;                                                                    \
+  int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping;                       \
+  if (list) {                                                                                  \
+    _ls_insize = 1;                                                                            \
+    _ls_looping = 1;                                                                           \
+    while (_ls_looping) {                                                                      \
+      _CASTASGN(_ls_p,list);                                                                   \
+      list = NULL;                                                                             \
+      _ls_tail = NULL;                                                                         \
+      _ls_nmerges = 0;                                                                         \
+      while (_ls_p) {                                                                          \
+        _ls_nmerges++;                                                                         \
+        _ls_q = _ls_p;                                                                         \
+        _ls_psize = 0;                                                                         \
+        for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) {                                         \
+          _ls_psize++;                                                                         \
+          _SV(_ls_q,list); _ls_q = _NEXT(_ls_q,list,next); _RS(list);                          \
+          if (!_ls_q) break;                                                                   \
+        }                                                                                      \
+        _ls_qsize = _ls_insize;                                                                \
+        while (_ls_psize > 0 || (_ls_qsize > 0 && _ls_q)) {                                    \
+          if (_ls_psize == 0) {                                                                \
+            _ls_e = _ls_q; _SV(_ls_q,list); _ls_q =                                            \
+              _NEXT(_ls_q,list,next); _RS(list); _ls_qsize--;                                  \
+          } else if (_ls_qsize == 0 || !_ls_q) {                                               \
+            _ls_e = _ls_p; _SV(_ls_p,list); _ls_p =                                            \
+              _NEXT(_ls_p,list,next); _RS(list); _ls_psize--;                                  \
+          } else if (cmp(_ls_p,_ls_q) <= 0) {                                                  \
+            _ls_e = _ls_p; _SV(_ls_p,list); _ls_p =                                            \
+              _NEXT(_ls_p,list,next); _RS(list); _ls_psize--;                                  \
+          } else {                                                                             \
+            _ls_e = _ls_q; _SV(_ls_q,list); _ls_q =                                            \
+              _NEXT(_ls_q,list,next); _RS(list); _ls_qsize--;                                  \
+          }                                                                                    \
+          if (_ls_tail) {                                                                      \
+            _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,_ls_e,next); _RS(list);                \
+          } else {                                                                             \
+            _CASTASGN(list,_ls_e);                                                             \
+          }                                                                                    \
+          _ls_tail = _ls_e;                                                                    \
+        }                                                                                      \
+        _ls_p = _ls_q;                                                                         \
+      }                                                                                        \
+      if (_ls_tail) {                                                                          \
+        _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,NULL,next); _RS(list);                     \
+      }                                                                                        \
+      if (_ls_nmerges <= 1) {                                                                  \
+        _ls_looping=0;                                                                         \
+      }                                                                                        \
+      _ls_insize *= 2;                                                                         \
+    }                                                                                          \
+  }                                                                                            \
+} while (0)
+
+
+#define DL_SORT(list, cmp)                                                                     \
+    DL_SORT2(list, cmp, prev, next)
+
+#define DL_SORT2(list, cmp, prev, next)                                                        \
+do {                                                                                           \
+  LDECLTYPE(list) _ls_p;                                                                       \
+  LDECLTYPE(list) _ls_q;                                                                       \
+  LDECLTYPE(list) _ls_e;                                                                       \
+  LDECLTYPE(list) _ls_tail;                                                                    \
+  int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping;                       \
+  if (list) {                                                                                  \
+    _ls_insize = 1;                                                                            \
+    _ls_looping = 1;                                                                           \
+    while (_ls_looping) {                                                                      \
+      _CASTASGN(_ls_p,list);                                                                   \
+      list = NULL;                                                                             \
+      _ls_tail = NULL;                                                                         \
+      _ls_nmerges = 0;                                                                         \
+      while (_ls_p) {                                                                          \
+        _ls_nmerges++;                                                                         \
+        _ls_q = _ls_p;                                                                         \
+        _ls_psize = 0;                                                                         \
+        for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) {                                         \
+          _ls_psize++;                                                                         \
+          _SV(_ls_q,list); _ls_q = _NEXT(_ls_q,list,next); _RS(list);                          \
+          if (!_ls_q) break;                                                                   \
+        }                                                                                      \
+        _ls_qsize = _ls_insize;                                                                \
+        while (_ls_psize > 0 || (_ls_qsize > 0 && _ls_q)) {                                    \
+          if (_ls_psize == 0) {                                                                \
+            _ls_e = _ls_q; _SV(_ls_q,list); _ls_q =                                            \
+              _NEXT(_ls_q,list,next); _RS(list); _ls_qsize--;                                  \
+          } else if (_ls_qsize == 0 || !_ls_q) {                                               \
+            _ls_e = _ls_p; _SV(_ls_p,list); _ls_p =                                            \
+              _NEXT(_ls_p,list,next); _RS(list); _ls_psize--;                                  \
+          } else if (cmp(_ls_p,_ls_q) <= 0) {                                                  \
+            _ls_e = _ls_p; _SV(_ls_p,list); _ls_p =                                            \
+              _NEXT(_ls_p,list,next); _RS(list); _ls_psize--;                                  \
+          } else {                                                                             \
+            _ls_e = _ls_q; _SV(_ls_q,list); _ls_q =                                            \
+              _NEXT(_ls_q,list,next); _RS(list); _ls_qsize--;                                  \
+          }                                                                                    \
+          if (_ls_tail) {                                                                      \
+            _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,_ls_e,next); _RS(list);                \
+          } else {                                                                             \
+            _CASTASGN(list,_ls_e);                                                             \
+          }                                                                                    \
+          _SV(_ls_e,list); _PREVASGN(_ls_e,list,_ls_tail,prev); _RS(list);                     \
+          _ls_tail = _ls_e;                                                                    \
+        }                                                                                      \
+        _ls_p = _ls_q;                                                                         \
+      }                                                                                        \
+      _CASTASGN(list->prev, _ls_tail);                                                         \
+      _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,NULL,next); _RS(list);                       \
+      if (_ls_nmerges <= 1) {                                                                  \
+        _ls_looping=0;                                                                         \
+      }                                                                                        \
+      _ls_insize *= 2;                                                                         \
+    }                                                                                          \
+  }                                                                                            \
+} while (0)
+
+#define CDL_SORT(list, cmp)                                                                    \
+    CDL_SORT2(list, cmp, prev, next)
+
+#define CDL_SORT2(list, cmp, prev, next)                                                       \
+do {                                                                                           \
+  LDECLTYPE(list) _ls_p;                                                                       \
+  LDECLTYPE(list) _ls_q;                                                                       \
+  LDECLTYPE(list) _ls_e;                                                                       \
+  LDECLTYPE(list) _ls_tail;                                                                    \
+  LDECLTYPE(list) _ls_oldhead;                                                                 \
+  LDECLTYPE(list) _tmp;                                                                        \
+  int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping;                       \
+  if (list) {                                                                                  \
+    _ls_insize = 1;                                                                            \
+    _ls_looping = 1;                                                                           \
+    while (_ls_looping) {                                                                      \
+      _CASTASGN(_ls_p,list);                                                                   \
+      _CASTASGN(_ls_oldhead,list);                                                             \
+      list = NULL;                                                                             \
+      _ls_tail = NULL;                                                                         \
+      _ls_nmerges = 0;                                                                         \
+      while (_ls_p) {                                                                          \
+        _ls_nmerges++;                                                                         \
+        _ls_q = _ls_p;                                                                         \
+        _ls_psize = 0;                                                                         \
+        for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) {                                         \
+          _ls_psize++;                                                                         \
+          _SV(_ls_q,list);                                                                     \
+          if (_NEXT(_ls_q,list,next) == _ls_oldhead) {                                         \
+            _ls_q = NULL;                                                                      \
+          } else {                                                                             \
+            _ls_q = _NEXT(_ls_q,list,next);                                                    \
+          }                                                                                    \
+          _RS(list);                                                                           \
+          if (!_ls_q) break;                                                                   \
+        }                                                                                      \
+        _ls_qsize = _ls_insize;                                                                \
+        while (_ls_psize > 0 || (_ls_qsize > 0 && _ls_q)) {                                    \
+          if (_ls_psize == 0) {                                                                \
+            _ls_e = _ls_q; _SV(_ls_q,list); _ls_q =                                            \
+              _NEXT(_ls_q,list,next); _RS(list); _ls_qsize--;                                  \
+            if (_ls_q == _ls_oldhead) { _ls_q = NULL; }                                        \
+          } else if (_ls_qsize == 0 || !_ls_q) {                                               \
+            _ls_e = _ls_p; _SV(_ls_p,list); _ls_p =                                            \
+              _NEXT(_ls_p,list,next); _RS(list); _ls_psize--;                                  \
+            if (_ls_p == _ls_oldhead) { _ls_p = NULL; }                                        \
+          } else if (cmp(_ls_p,_ls_q) <= 0) {                                                  \
+            _ls_e = _ls_p; _SV(_ls_p,list); _ls_p =                                            \
+              _NEXT(_ls_p,list,next); _RS(list); _ls_psize--;                                  \
+            if (_ls_p == _ls_oldhead) { _ls_p = NULL; }                                        \
+          } else {                                                                             \
+            _ls_e = _ls_q; _SV(_ls_q,list); _ls_q =                                            \
+              _NEXT(_ls_q,list,next); _RS(list); _ls_qsize--;                                  \
+            if (_ls_q == _ls_oldhead) { _ls_q = NULL; }                                        \
+          }                                                                                    \
+          if (_ls_tail) {                                                                      \
+            _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,_ls_e,next); _RS(list);                \
+          } else {                                                                             \
+            _CASTASGN(list,_ls_e);                                                             \
+          }                                                                                    \
+          _SV(_ls_e,list); _PREVASGN(_ls_e,list,_ls_tail,prev); _RS(list);                     \
+          _ls_tail = _ls_e;                                                                    \
+        }                                                                                      \
+        _ls_p = _ls_q;                                                                         \
+      }                                                                                        \
+      _CASTASGN(list->prev,_ls_tail);                                                          \
+      _CASTASGN(_tmp,list);                                                                    \
+      _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,_tmp,next); _RS(list);                       \
+      if (_ls_nmerges <= 1) {                                                                  \
+        _ls_looping=0;                                                                         \
+      }                                                                                        \
+      _ls_insize *= 2;                                                                         \
+    }                                                                                          \
+  }                                                                                            \
+} while (0)
+
+/******************************************************************************
+ * singly linked list macros (non-circular)                                   *
+ *****************************************************************************/
+#define LL_PREPEND(head,add)                                                                   \
+    LL_PREPEND2(head,add,next)
+
+#define LL_PREPEND2(head,add,next)                                                             \
+do {                                                                                           \
+  (add)->next = head;                                                                          \
+  head = add;                                                                                  \
+} while (0)
+
+#define LL_CONCAT(head1,head2)                                                                 \
+    LL_CONCAT2(head1,head2,next)
+
+#define LL_CONCAT2(head1,head2,next)                                                           \
+do {                                                                                           \
+  LDECLTYPE(head1) _tmp;                                                                       \
+  if (head1) {                                                                                 \
+    _tmp = head1;                                                                              \
+    while (_tmp->next) { _tmp = _tmp->next; }                                                  \
+    _tmp->next=(head2);                                                                        \
+  } else {                                                                                     \
+    (head1)=(head2);                                                                           \
+  }                                                                                            \
+} while (0)
+
+#define LL_APPEND(head,add)                                                                    \
+    LL_APPEND2(head,add,next)
+
+#define LL_APPEND2(head,add,next)                                                              \
+do {                                                                                           \
+  LDECLTYPE(head) _tmp;                                                                        \
+  (add)->next=NULL;                                                                            \
+  if (head) {                                                                                  \
+    _tmp = head;                                                                               \
+    while (_tmp->next) { _tmp = _tmp->next; }                                                  \
+    _tmp->next=(add);                                                                          \
+  } else {                                                                                     \
+    (head)=(add);                                                                              \
+  }                                                                                            \
+} while (0)
+
+#define LL_DELETE(head,del)                                                                    \
+    LL_DELETE2(head,del,next)
+
+#define LL_DELETE2(head,del,next)                                                              \
+do {                                                                                           \
+  LDECLTYPE(head) _tmp;                                                                        \
+  if ((head) == (del)) {                                                                       \
+    (head)=(head)->next;                                                                       \
+  } else {                                                                                     \
+    _tmp = head;                                                                               \
+    while (_tmp->next && (_tmp->next != (del))) {                                              \
+      _tmp = _tmp->next;                                                                       \
+    }                                                                                          \
+    if (_tmp->next) {                                                                          \
+      _tmp->next = ((del)->next);                                                              \
+    }                                                                                          \
+  }                                                                                            \
+} while (0)
+
+/* Here are VS2008 replacements for LL_APPEND and LL_DELETE */
+#define LL_APPEND_VS2008(head,add)                                                             \
+    LL_APPEND2_VS2008(head,add,next)
+
+#define LL_APPEND2_VS2008(head,add,next)                                                       \
+do {                                                                                           \
+  if (head) {                                                                                  \
+    (add)->next = head;     /* use add->next as a temp variable */                             \
+    while ((add)->next->next) { (add)->next = (add)->next->next; }                             \
+    (add)->next->next=(add);                                                                   \
+  } else {                                                                                     \
+    (head)=(add);                                                                              \
+  }                                                                                            \
+  (add)->next=NULL;                                                                            \
+} while (0)
+
+#define LL_DELETE_VS2008(head,del)                                                             \
+    LL_DELETE2_VS2008(head,del,next)
+
+#define LL_DELETE2_VS2008(head,del,next)                                                       \
+do {                                                                                           \
+  if ((head) == (del)) {                                                                       \
+    (head)=(head)->next;                                                                       \
+  } else {                                                                                     \
+    char *_tmp = (char*)(head);                                                                \
+    while ((head)->next && ((head)->next != (del))) {                                          \
+      head = (head)->next;                                                                     \
+    }                                                                                          \
+    if ((head)->next) {                                                                        \
+      (head)->next = ((del)->next);                                                            \
+    }                                                                                          \
+    {                                                                                          \
+      char **_head_alias = (char**)&(head);                                                    \
+      *_head_alias = _tmp;                                                                     \
+    }                                                                                          \
+  }                                                                                            \
+} while (0)
+#ifdef NO_DECLTYPE
+#undef LL_APPEND
+#define LL_APPEND LL_APPEND_VS2008
+#undef LL_DELETE
+#define LL_DELETE LL_DELETE_VS2008
+#undef LL_DELETE2
+#define LL_DELETE2 LL_DELETE2_VS2008
+#undef LL_APPEND2
+#define LL_APPEND2 LL_APPEND2_VS2008
+#undef LL_CONCAT /* no LL_CONCAT_VS2008 */
+#undef DL_CONCAT /* no DL_CONCAT_VS2008 */
+#endif
+/* end VS2008 replacements */
+
+#define LL_COUNT(head,el,counter)                                                              \
+    LL_COUNT2(head,el,counter,next)                                                            \
+
+#define LL_COUNT2(head,el,counter,next)                                                        \
+{                                                                                              \
+    counter = 0;                                                                               \
+    LL_FOREACH2(head,el,next){ ++counter; }                                                    \
+}
+
+#define LL_FOREACH(head,el)                                                                    \
+    LL_FOREACH2(head,el,next)
+
+#define LL_FOREACH2(head,el,next)                                                              \
+    for(el=head;el;el=(el)->next)
+
+#define LL_FOREACH_SAFE(head,el,tmp)                                                           \
+    LL_FOREACH_SAFE2(head,el,tmp,next)
+
+#define LL_FOREACH_SAFE2(head,el,tmp,next)                                                     \
+  for((el)=(head);(el) && (tmp = (el)->next, 1); (el) = tmp)
+
+#define LL_SEARCH_SCALAR(head,out,field,val)                                                   \
+    LL_SEARCH_SCALAR2(head,out,field,val,next)
+
+#define LL_SEARCH_SCALAR2(head,out,field,val,next)                                             \
+do {                                                                                           \
+    LL_FOREACH2(head,out,next) {                                                               \
+      if ((out)->field == (val)) break;                                                        \
+    }                                                                                          \
+} while(0) 
+
+#define LL_SEARCH(head,out,elt,cmp)                                                            \
+    LL_SEARCH2(head,out,elt,cmp,next)
+
+#define LL_SEARCH2(head,out,elt,cmp,next)                                                      \
+do {                                                                                           \
+    LL_FOREACH2(head,out,next) {                                                               \
+      if ((cmp(out,elt))==0) break;                                                            \
+    }                                                                                          \
+} while(0) 
+
+#define LL_REPLACE_ELEM(head, el, add)                                                         \
+do {                                                                                           \
+ LDECLTYPE(head) _tmp;                                                                         \
+ assert(head != NULL);                                                                         \
+ assert(el != NULL);                                                                           \
+ assert(add != NULL);                                                                          \
+ (add)->next = (el)->next;                                                                     \
+ if ((head) == (el)) {                                                                         \
+  (head) = (add);                                                                              \
+ } else {                                                                                      \
+  _tmp = head;                                                                                 \
+  while (_tmp->next && (_tmp->next != (el))) {                                                 \
+   _tmp = _tmp->next;                                                                          \
+  }                                                                                            \
+  if (_tmp->next) {                                                                            \
+    _tmp->next = (add);                                                                        \
+  }                                                                                            \
+ }                                                                                             \
+} while (0)
+
+#define LL_PREPEND_ELEM(head, el, add)                                                         \
+do {                                                                                           \
+ LDECLTYPE(head) _tmp;                                                                         \
+ assert(head != NULL);                                                                         \
+ assert(el != NULL);                                                                           \
+ assert(add != NULL);                                                                          \
+ (add)->next = (el);                                                                           \
+ if ((head) == (el)) {                                                                         \
+  (head) = (add);                                                                              \
+ } else {                                                                                      \
+  _tmp = head;                                                                                 \
+  while (_tmp->next && (_tmp->next != (el))) {                                                 \
+   _tmp = _tmp->next;                                                                          \
+  }                                                                                            \
+  if (_tmp->next) {                                                                            \
+    _tmp->next = (add);                                                                        \
+  }                                                                                            \
+ }                                                                                             \
+} while (0)                                                                                    \
+
+
+/******************************************************************************
+ * doubly linked list macros (non-circular)                                   *
+ *****************************************************************************/
+#define DL_PREPEND(head,add)                                                                   \
+    DL_PREPEND2(head,add,prev,next)
+
+#define DL_PREPEND2(head,add,prev,next)                                                        \
+do {                                                                                           \
+ (add)->next = head;                                                                           \
+ if (head) {                                                                                   \
+   (add)->prev = (head)->prev;                                                                 \
+   (head)->prev = (add);                                                                       \
+ } else {                                                                                      \
+   (add)->prev = (add);                                                                        \
+ }                                                                                             \
+ (head) = (add);                                                                               \
+} while (0)
+
+#define DL_APPEND(head,add)                                                                    \
+    DL_APPEND2(head,add,prev,next)
+
+#define DL_APPEND2(head,add,prev,next)                                                         \
+do {                                                                                           \
+  if (head) {                                                                                  \
+      (add)->prev = (head)->prev;                                                              \
+      (head)->prev->next = (add);                                                              \
+      (head)->prev = (add);                                                                    \
+      (add)->next = NULL;                                                                      \
+  } else {                                                                                     \
+      (head)=(add);                                                                            \
+      (head)->prev = (head);                                                                   \
+      (head)->next = NULL;                                                                     \
+  }                                                                                            \
+} while (0) 
+
+#define DL_CONCAT(head1,head2)                                                                 \
+    DL_CONCAT2(head1,head2,prev,next)
+
+#define DL_CONCAT2(head1,head2,prev,next)                                                      \
+do {                                                                                           \
+  LDECLTYPE(head1) _tmp;                                                                       \
+  if (head2) {                                                                                 \
+    if (head1) {                                                                               \
+        _tmp = (head2)->prev;                                                                  \
+        (head2)->prev = (head1)->prev;                                                         \
+        (head1)->prev->next = (head2);                                                         \
+        (head1)->prev = _tmp;                                                                  \
+    } else {                                                                                   \
+        (head1)=(head2);                                                                       \
+    }                                                                                          \
+  }                                                                                            \
+} while (0) 
+
+#define DL_DELETE(head,del)                                                                    \
+    DL_DELETE2(head,del,prev,next)
+
+#define DL_DELETE2(head,del,prev,next)                                                         \
+do {                                                                                           \
+  assert((del)->prev != NULL);                                                                 \
+  if ((del)->prev == (del)) {                                                                  \
+      (head)=NULL;                                                                             \
+  } else if ((del)==(head)) {                                                                  \
+      (del)->next->prev = (del)->prev;                                                         \
+      (head) = (del)->next;                                                                    \
+  } else {                                                                                     \
+      (del)->prev->next = (del)->next;                                                         \
+      if ((del)->next) {                                                                       \
+          (del)->next->prev = (del)->prev;                                                     \
+      } else {                                                                                 \
+          (head)->prev = (del)->prev;                                                          \
+      }                                                                                        \
+  }                                                                                            \
+} while (0) 
+
+#define DL_COUNT(head,el,counter)                                                              \
+    DL_COUNT2(head,el,counter,next)                                                            \
+
+#define DL_COUNT2(head,el,counter,next)                                                        \
+{                                                                                              \
+    counter = 0;                                                                               \
+    DL_FOREACH2(head,el,next){ ++counter; }                                                    \
+}
+
+#define DL_FOREACH(head,el)                                                                    \
+    DL_FOREACH2(head,el,next)
+
+#define DL_FOREACH2(head,el,next)                                                              \
+    for(el=head;el;el=(el)->next)
+
+/* this version is safe for deleting the elements during iteration */
+#define DL_FOREACH_SAFE(head,el,tmp)                                                           \
+    DL_FOREACH_SAFE2(head,el,tmp,next)
+
+#define DL_FOREACH_SAFE2(head,el,tmp,next)                                                     \
+  for((el)=(head);(el) && (tmp = (el)->next, 1); (el) = tmp)
+
+/* these are identical to their singly-linked list counterparts */
+#define DL_SEARCH_SCALAR LL_SEARCH_SCALAR
+#define DL_SEARCH LL_SEARCH
+#define DL_SEARCH_SCALAR2 LL_SEARCH_SCALAR2
+#define DL_SEARCH2 LL_SEARCH2
+
+#define DL_REPLACE_ELEM(head, el, add)                                                         \
+do {                                                                                           \
+ assert(head != NULL);                                                                         \
+ assert(el != NULL);                                                                           \
+ assert(add != NULL);                                                                          \
+ if ((head) == (el)) {                                                                         \
+  (head) = (add);                                                                              \
+  (add)->next = (el)->next;                                                                    \
+  if ((el)->next == NULL) {                                                                    \
+   (add)->prev = (add);                                                                        \
+  } else {                                                                                     \
+   (add)->prev = (el)->prev;                                                                   \
+   (add)->next->prev = (add);                                                                  \
+  }                                                                                            \
+ } else {                                                                                      \
+  (add)->next = (el)->next;                                                                    \
+  (add)->prev = (el)->prev;                                                                    \
+  (add)->prev->next = (add);                                                                   \
+  if ((el)->next == NULL) {                                                                    \
+   (head)->prev = (add);                                                                       \
+  } else {                                                                                     \
+   (add)->next->prev = (add);                                                                  \
+  }                                                                                            \
+ }                                                                                             \
+} while (0)
+
+#define DL_PREPEND_ELEM(head, el, add)                                                         \
+do {                                                                                           \
+ assert(head != NULL);                                                                         \
+ assert(el != NULL);                                                                           \
+ assert(add != NULL);                                                                          \
+ (add)->next = (el);                                                                           \
+ (add)->prev = (el)->prev;                                                                     \
+ (el)->prev = (add);                                                                           \
+ if ((head) == (el)) {                                                                         \
+  (head) = (add);                                                                              \
+ } else {                                                                                      \
+  (add)->prev->next = (add);                                                                   \
+ }                                                                                             \
+} while (0)                                                                                    \
+
+
+/******************************************************************************
+ * circular doubly linked list macros                                         *
+ *****************************************************************************/
+#define CDL_PREPEND(head,add)                                                                  \
+    CDL_PREPEND2(head,add,prev,next)
+
+#define CDL_PREPEND2(head,add,prev,next)                                                       \
+do {                                                                                           \
+ if (head) {                                                                                   \
+   (add)->prev = (head)->prev;                                                                 \
+   (add)->next = (head);                                                                       \
+   (head)->prev = (add);                                                                       \
+   (add)->prev->next = (add);                                                                  \
+ } else {                                                                                      \
+   (add)->prev = (add);                                                                        \
+   (add)->next = (add);                                                                        \
+ }                                                                                             \
+(head)=(add);                                                                                  \
+} while (0)
+
+#define CDL_DELETE(head,del)                                                                   \
+    CDL_DELETE2(head,del,prev,next)
+
+#define CDL_DELETE2(head,del,prev,next)                                                        \
+do {                                                                                           \
+  if ( ((head)==(del)) && ((head)->next == (head))) {                                          \
+      (head) = 0L;                                                                             \
+  } else {                                                                                     \
+     (del)->next->prev = (del)->prev;                                                          \
+     (del)->prev->next = (del)->next;                                                          \
+     if ((del) == (head)) (head)=(del)->next;                                                  \
+  }                                                                                            \
+} while (0) 
+
+#define CDL_COUNT(head,el,counter)                                                             \
+    CDL_COUNT2(head,el,counter,next)                                                           \
+
+#define CDL_COUNT2(head, el, counter,next)                                                     \
+{                                                                                              \
+    counter = 0;                                                                               \
+    CDL_FOREACH2(head,el,next){ ++counter; }                                                   \
+}
+
+#define CDL_FOREACH(head,el)                                                                   \
+    CDL_FOREACH2(head,el,next)
+
+#define CDL_FOREACH2(head,el,next)                                                             \
+    for(el=head;el;el=((el)->next==head ? 0L : (el)->next)) 
+
+#define CDL_FOREACH_SAFE(head,el,tmp1,tmp2)                                                    \
+    CDL_FOREACH_SAFE2(head,el,tmp1,tmp2,prev,next)
+
+#define CDL_FOREACH_SAFE2(head,el,tmp1,tmp2,prev,next)                                         \
+  for((el)=(head), ((tmp1)=(head)?((head)->prev):NULL);                                        \
+      (el) && ((tmp2)=(el)->next, 1);                                                          \
+      ((el) = (((el)==(tmp1)) ? 0L : (tmp2))))
+
+#define CDL_SEARCH_SCALAR(head,out,field,val)                                                  \
+    CDL_SEARCH_SCALAR2(head,out,field,val,next)
+
+#define CDL_SEARCH_SCALAR2(head,out,field,val,next)                                            \
+do {                                                                                           \
+    CDL_FOREACH2(head,out,next) {                                                              \
+      if ((out)->field == (val)) break;                                                        \
+    }                                                                                          \
+} while(0) 
+
+#define CDL_SEARCH(head,out,elt,cmp)                                                           \
+    CDL_SEARCH2(head,out,elt,cmp,next)
+
+#define CDL_SEARCH2(head,out,elt,cmp,next)                                                     \
+do {                                                                                           \
+    CDL_FOREACH2(head,out,next) {                                                              \
+      if ((cmp(out,elt))==0) break;                                                            \
+    }                                                                                          \
+} while(0) 
+
+#define CDL_REPLACE_ELEM(head, el, add)                                                        \
+do {                                                                                           \
+ assert(head != NULL);                                                                         \
+ assert(el != NULL);                                                                           \
+ assert(add != NULL);                                                                          \
+ if ((el)->next == (el)) {                                                                     \
+  (add)->next = (add);                                                                         \
+  (add)->prev = (add);                                                                         \
+  (head) = (add);                                                                              \
+ } else {                                                                                      \
+  (add)->next = (el)->next;                                                                    \
+  (add)->prev = (el)->prev;                                                                    \
+  (add)->next->prev = (add);                                                                   \
+  (add)->prev->next = (add);                                                                   \
+  if ((head) == (el)) {                                                                        \
+   (head) = (add);                                                                             \
+  }                                                                                            \
+ }                                                                                             \
+} while (0)
+
+#define CDL_PREPEND_ELEM(head, el, add)                                                        \
+do {                                                                                           \
+ assert(head != NULL);                                                                         \
+ assert(el != NULL);                                                                           \
+ assert(add != NULL);                                                                          \
+ (add)->next = (el);                                                                           \
+ (add)->prev = (el)->prev;                                                                     \
+ (el)->prev = (add);                                                                           \
+ (add)->prev->next = (add);                                                                    \
+ if ((head) == (el)) {                                                                         \
+  (head) = (add);                                                                              \
+ }                                                                                             \
+} while (0)                                                                                    \
+
+#endif /* UTLIST_H */
+

Added: vendor/libucl/dist/uthash/utstring.h
===================================================================
--- vendor/libucl/dist/uthash/utstring.h	                        (rev 0)
+++ vendor/libucl/dist/uthash/utstring.h	2018-06-19 13:29:00 UTC (rev 11074)
@@ -0,0 +1,410 @@
+/*
+Copyright (c) 2008-2013, Troy D. Hanson   http://troydhanson.github.com/uthash/
+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.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
+OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/* a dynamic string implementation using macros 
+ */
+#ifndef UTSTRING_H
+#define UTSTRING_H
+
+#define UTSTRING_VERSION 1.9.8
+
+#ifdef __GNUC__
+#define _UNUSED_ __attribute__ ((__unused__)) 
+#else
+#define _UNUSED_ 
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+
+#ifndef oom
+#define oom() exit(-1)
+#endif
+
+typedef struct {
+    char *d;
+    size_t n; /* allocd size */
+    size_t i; /* index of first unused byte */
+} UT_string;
+
+#define utstring_reserve(s,amt)                            \
+do {                                                       \
+  if (((s)->n - (s)->i) < (size_t)(amt)) {                 \
+     (s)->d = (char*)realloc((s)->d, (s)->n + amt);        \
+     if ((s)->d == NULL) oom();                            \
+     (s)->n += amt;                                        \
+  }                                                        \
+} while(0)
+
+#define utstring_init(s)                                   \
+do {                                                       \
+  (s)->n = 0; (s)->i = 0; (s)->d = NULL;                   \
+  utstring_reserve(s,128);                                 \
+  (s)->d[0] = '\0'; \
+} while(0)
+
+#define utstring_done(s)                                   \
+do {                                                       \
+  if ((s)->d != NULL) free((s)->d);                        \
+  (s)->n = 0;                                              \
+} while(0)
+
+#define utstring_free(s)                                   \
+do {                                                       \
+  utstring_done(s);                                        \
+  free(s);                                                 \
+} while(0)
+
+#define utstring_new(s)                                    \
+do {                                                       \
+   s = (UT_string*)calloc(sizeof(UT_string),1);            \
+   if (!s) oom();                                          \
+   utstring_init(s);                                       \
+} while(0)
+
+#define utstring_renew(s)                                  \
+do {                                                       \
+   if (s) {                                                \
+     utstring_clear(s);                                    \
+   } else {                                                \
+     utstring_new(s);                                      \
+   }                                                       \
+} while(0)
+
+#define utstring_clear(s)                                  \
+do {                                                       \
+  (s)->i = 0;                                              \
+  (s)->d[0] = '\0';                                        \
+} while(0)
+
+#define utstring_bincpy(s,b,l)                             \
+do {                                                       \
+  utstring_reserve((s),(l)+1);                               \
+  if (l) memcpy(&(s)->d[(s)->i], b, l);                    \
+  (s)->i += l;                                               \
+  (s)->d[(s)->i]='\0';                                         \
+} while(0)
+
+#define utstring_concat(dst,src)                                 \
+do {                                                             \
+  utstring_reserve((dst),((src)->i)+1);                          \
+  if ((src)->i) memcpy(&(dst)->d[(dst)->i], (src)->d, (src)->i); \
+  (dst)->i += (src)->i;                                          \
+  (dst)->d[(dst)->i]='\0';                                       \
+} while(0)
+
+#define utstring_len(s) ((unsigned)((s)->i))
+
+#define utstring_body(s) ((s)->d)
+
+_UNUSED_ static void utstring_printf_va(UT_string *s, const char *fmt, va_list ap) {
+   int n;
+   va_list cp;
+   while (1) {
+#ifdef _WIN32
+      cp = ap;
+#else
+      va_copy(cp, ap);
+#endif
+      n = vsnprintf (&s->d[s->i], s->n-s->i, fmt, cp);
+      va_end(cp);
+
+      if ((n > -1) && (n < (int)(s->n-s->i))) {
+        s->i += n;
+        return;
+      }
+
+      /* Else try again with more space. */
+      if (n > -1) utstring_reserve(s,n+1); /* exact */
+      else utstring_reserve(s,(s->n)*2);   /* 2x */
+   }
+}
+#ifdef __GNUC__
+/* support printf format checking (2=the format string, 3=start of varargs) */
+static void utstring_printf(UT_string *s, const char *fmt, ...)
+  __attribute__ (( format( printf, 2, 3) ));
+#endif
+_UNUSED_ static void utstring_printf(UT_string *s, const char *fmt, ...) {
+   va_list ap;
+   va_start(ap,fmt);
+   utstring_printf_va(s,fmt,ap);
+   va_end(ap);
+}
+
+#define utstring_append_len(dst, src, len)                                    \
+do {                                                                           \
+    while ((dst)->n-(dst)->i <= (len)) utstring_reserve((dst),((dst)->n)*2);   \
+    memcpy(&(dst)->d[(dst)->i], (src), (len));                                 \
+    (dst)->i+=(len);                                                           \
+    (dst)->d[(dst)->i]='\0';                                                   \
+} while(0)
+
+#define utstring_append_c(dst, c)                                             \
+do {                                                                           \
+    if ((dst)->n-(dst)->i < 2) utstring_reserve((dst),((dst)->n)*2);            \
+    (dst)->d[(dst)->i++] = (c);                                                \
+    (dst)->d[(dst)->i]='\0';                                                   \
+} while(0)
+
+/*******************************************************************************
+ * begin substring search functions                                            *
+ ******************************************************************************/
+/* Build KMP table from left to right. */
+_UNUSED_ static void _utstring_BuildTable(
+    const char *P_Needle, 
+    ssize_t P_NeedleLen, 
+    long *P_KMP_Table)
+{
+    long i, j;
+
+    i = 0;
+    j = i - 1;
+    P_KMP_Table[i] = j;
+    while (i < P_NeedleLen)
+    {
+        while ( (j > -1) && (P_Needle[i] != P_Needle[j]) )
+        {
+           j = P_KMP_Table[j];
+        }
+        i++;
+        j++;
+        if (i < P_NeedleLen)
+        {
+            if (P_Needle[i] == P_Needle[j])
+            {
+                P_KMP_Table[i] = P_KMP_Table[j];
+            }
+            else
+            {
+                P_KMP_Table[i] = j;
+            }
+        }
+        else
+        {
+            P_KMP_Table[i] = j;
+        }
+    }
+
+    return;
+}
+
+
+/* Build KMP table from right to left. */
+_UNUSED_ static void _utstring_BuildTableR(
+    const char *P_Needle, 
+    ssize_t P_NeedleLen, 
+    long *P_KMP_Table)
+{
+    long i, j;
+
+    i = P_NeedleLen - 1;
+    j = i + 1;
+    P_KMP_Table[i + 1] = j;
+    while (i >= 0)
+    {
+        while ( (j < P_NeedleLen) && (P_Needle[i] != P_Needle[j]) )
+        {
+           j = P_KMP_Table[j + 1];
+        }
+        i--;
+        j--;
+        if (i >= 0)
+        {
+            if (P_Needle[i] == P_Needle[j])
+            {
+                P_KMP_Table[i + 1] = P_KMP_Table[j + 1];
+            }
+            else
+            {
+                P_KMP_Table[i + 1] = j;
+            }
+        }
+        else
+        {
+            P_KMP_Table[i + 1] = j;
+        }
+    }
+
+    return;
+}
+
+
+/* Search data from left to right. ( Multiple search mode. ) */
+_UNUSED_ static long _utstring_find(
+    const char *P_Haystack, 
+    size_t P_HaystackLen, 
+    const char *P_Needle, 
+    size_t P_NeedleLen, 
+    long *P_KMP_Table)
+{
+    long i, j;
+    long V_FindPosition = -1;
+
+    /* Search from left to right. */
+    i = j = 0;
+    while ( (j < (int)P_HaystackLen) && (((P_HaystackLen - j) + i) >= P_NeedleLen) )
+    {
+        while ( (i > -1) && (P_Needle[i] != P_Haystack[j]) )
+        {
+            i = P_KMP_Table[i];
+        }
+        i++;
+        j++;
+        if (i >= (int)P_NeedleLen)
+        {
+            /* Found. */
+            V_FindPosition = j - i;
+            break;
+        }
+    }
+
+    return V_FindPosition;
+}
+
+
+/* Search data from right to left. ( Multiple search mode. ) */
+_UNUSED_ static long _utstring_findR(
+    const char *P_Haystack, 
+    size_t P_HaystackLen, 
+    const char *P_Needle, 
+    size_t P_NeedleLen, 
+    long *P_KMP_Table)
+{
+    long i, j;
+    long V_FindPosition = -1;
+
+    /* Search from right to left. */
+    j = (P_HaystackLen - 1);
+    i = (P_NeedleLen - 1);
+    while ( (j >= 0) && (j >= i) )
+    {
+        while ( (i < (int)P_NeedleLen) && (P_Needle[i] != P_Haystack[j]) )
+        {
+            i = P_KMP_Table[i + 1];
+        }
+        i--;
+        j--;
+        if (i < 0)
+        {
+            /* Found. */
+            V_FindPosition = j + 1;
+            break;
+        }
+    }
+
+    return V_FindPosition;
+}
+
+
+/* Search data from left to right. ( One time search mode. ) */
+_UNUSED_ static long utstring_find(
+    UT_string *s, 
+    long P_StartPosition,   /* Start from 0. -1 means last position. */
+    const char *P_Needle, 
+    ssize_t P_NeedleLen)
+{
+    long V_StartPosition;
+    long V_HaystackLen;
+    long *V_KMP_Table;
+    long V_FindPosition = -1;
+
+    if (P_StartPosition < 0)
+    {
+        V_StartPosition = s->i + P_StartPosition;
+    }
+    else
+    {
+        V_StartPosition = P_StartPosition;
+    }
+    V_HaystackLen = s->i - V_StartPosition;
+    if ( (V_HaystackLen >= P_NeedleLen) && (P_NeedleLen > 0) )
+    {
+        V_KMP_Table = (long *)malloc(sizeof(long) * (P_NeedleLen + 1));
+        if (V_KMP_Table != NULL)
+        {
+            _utstring_BuildTable(P_Needle, P_NeedleLen, V_KMP_Table);
+
+            V_FindPosition = _utstring_find(s->d + V_StartPosition, 
+                                            V_HaystackLen, 
+                                            P_Needle, 
+                                            P_NeedleLen, 
+                                            V_KMP_Table);
+            if (V_FindPosition >= 0)
+            {
+                V_FindPosition += V_StartPosition;
+            }
+
+            free(V_KMP_Table);
+        }
+    }
+
+    return V_FindPosition;
+}
+
+
+/* Search data from right to left. ( One time search mode. ) */
+_UNUSED_ static long utstring_findR(
+    UT_string *s, 
+    long P_StartPosition,   /* Start from 0. -1 means last position. */
+    const char *P_Needle, 
+    ssize_t P_NeedleLen)
+{
+    long V_StartPosition;
+    long V_HaystackLen;
+    long *V_KMP_Table;
+    long V_FindPosition = -1;
+
+    if (P_StartPosition < 0)
+    {
+        V_StartPosition = s->i + P_StartPosition;
+    }
+    else
+    {
+        V_StartPosition = P_StartPosition;
+    }
+    V_HaystackLen = V_StartPosition + 1;
+    if ( (V_HaystackLen >= P_NeedleLen) && (P_NeedleLen > 0) )
+    {
+        V_KMP_Table = (long *)malloc(sizeof(long) * (P_NeedleLen + 1));
+        if (V_KMP_Table != NULL)
+        {
+            _utstring_BuildTableR(P_Needle, P_NeedleLen, V_KMP_Table);
+
+            V_FindPosition = _utstring_findR(s->d, 
+                                             V_HaystackLen, 
+                                             P_Needle, 
+                                             P_NeedleLen, 
+                                             V_KMP_Table);
+
+            free(V_KMP_Table);
+        }
+    }
+
+    return V_FindPosition;
+}
+/*******************************************************************************
+ * end substring search functions                                              *
+ ******************************************************************************/
+
+#endif /* UTSTRING_H */

Added: vendor/libucl/dist/utils/Makefile.am
===================================================================
--- vendor/libucl/dist/utils/Makefile.am	                        (rev 0)
+++ vendor/libucl/dist/utils/Makefile.am	2018-06-19 13:29:00 UTC (rev 11074)
@@ -0,0 +1,19 @@
+common_utils_cflags = -I$(top_srcdir)/include \
+					-I$(top_srcdir)/src \
+					-I$(top_srcdir)/uthash
+common_utils_ldadd = $(top_builddir)/src/libucl.la
+
+ucl_chargen_SOURCES = chargen.c
+ucl_chargen_LDADD = $(common_utils_ldadd)
+ucl_chargen_CFLAGS = $(common_utils_cflags)
+
+ucl_objdump_SOURCES = objdump.c
+ucl_objdump_LDADD = $(common_utils_ldadd)
+ucl_objdump_CFLAGS = $(common_utils_cflags)
+
+if UTILS
+UTL = ucl_chargen ucl_objdump
+else
+UTL =
+endif
+bin_PROGRAMS = $(UTL)

Added: vendor/libucl/dist/utils/chargen.c
===================================================================
--- vendor/libucl/dist/utils/chargen.c	                        (rev 0)
+++ vendor/libucl/dist/utils/chargen.c	2018-06-19 13:29:00 UTC (rev 11074)
@@ -0,0 +1,128 @@
+/* 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.
+ */
+
+/**
+ * @file this utility generates character table for ucl
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <stdbool.h>
+
+static inline int
+print_flag (const char *flag, bool *need_or, char *val)
+{
+	int res;
+	res = sprintf (val, "%s%s", *need_or ? "|" : "", flag);
+
+	*need_or |= true;
+
+	return res;
+}
+
+int
+main (int argc, char **argv)
+{
+	int i, col, r;
+	const char *name = "ucl_chartable";
+	bool need_or;
+	char valbuf[2048];
+
+	col = 0;
+
+	if (argc > 1) {
+		name = argv[1];
+	}
+
+	printf ("static const unsigned int %s[255] = {\n", name);
+
+	for (i = 0; i < 255; i ++) {
+		need_or = false;
+		r = 0;
+		/* UCL_CHARACTER_VALUE_END */
+
+		if (i == ' ' || i == '\t') {
+			r += print_flag ("UCL_CHARACTER_WHITESPACE", &need_or, valbuf + r);
+		}
+		if (isspace (i)) {
+			r += print_flag ("UCL_CHARACTER_WHITESPACE_UNSAFE", &need_or, valbuf + r);
+		}
+		if (isalnum (i) || i >= 0x80 || i == '/' || i == '_') {
+			r += print_flag ("UCL_CHARACTER_KEY_START", &need_or, valbuf + r);
+		}
+		if (isalnum (i) || i == '-' || i == '_' || i == '/' || i == '.' || i >= 0x80) {
+			r += print_flag ("UCL_CHARACTER_KEY", &need_or, valbuf + r);
+		}
+		if (i == 0 || i == '\r' || i == '\n' || i == ']' || i == '}' || i == ';' || i == ',' || i == '#') {
+			r += print_flag ("UCL_CHARACTER_VALUE_END", &need_or, valbuf + r);
+		}
+		else {
+			if (isprint (i) || i >= 0x80) {
+				r += print_flag ("UCL_CHARACTER_VALUE_STR", &need_or, valbuf + r);
+			}
+			if (isdigit (i) || i == '-') {
+				r += print_flag ("UCL_CHARACTER_VALUE_DIGIT_START", &need_or, valbuf + r);
+			}
+			if (isalnum (i) || i == '.' || i == '-' || i == '+') {
+				r += print_flag ("UCL_CHARACTER_VALUE_DIGIT", &need_or, valbuf + r);
+			}
+		}
+		if (i == '"' || i == '\\' || i == '/' || i == 'b' ||
+			i == 'f' || i == 'n' || i == 'r' || i == 't' || i == 'u') {
+			r += print_flag ("UCL_CHARACTER_ESCAPE", &need_or, valbuf + r);
+		}
+		if (i == ' ' || i == '\t' || i == ':' || i == '=') {
+			r += print_flag ("UCL_CHARACTER_KEY_SEP", &need_or, valbuf + r);
+		}
+		if (i == '\n' || i == '\r' || i == '\\' || i == '\b' || i == '\t' ||
+				i == '"' || i == '\f') {
+			r += print_flag ("UCL_CHARACTER_JSON_UNSAFE", &need_or, valbuf + r);
+		}
+		if (i == '\n' || i == '\r' || i == '\\' || i == '\b' || i == '\t' ||
+				i == '"' || i == '\f' || i == '=' || i == ':' || i == '{' || i == '[' || i == ' ') {
+			r += print_flag ("UCL_CHARACTER_UCL_UNSAFE", &need_or, valbuf + r);
+		}
+
+		if (!need_or) {
+			r += print_flag ("UCL_CHARACTER_DENIED", &need_or, valbuf + r);
+		}
+
+		if (isprint (i)) {
+			r += sprintf (valbuf + r, " /* %c */", i);
+		}
+		if (i != 254) {
+			r += sprintf (valbuf + r, ", ");
+		}
+		col += r;
+		if (col > 80) {
+			printf ("\n%s", valbuf);
+			col = r;
+		}
+		else {
+			printf ("%s", valbuf);
+		}
+	}
+	printf ("\n}\n");
+
+	return 0;
+}

Added: vendor/libucl/dist/utils/objdump.c
===================================================================
--- vendor/libucl/dist/utils/objdump.c	                        (rev 0)
+++ vendor/libucl/dist/utils/objdump.c	2018-06-19 13:29:00 UTC (rev 11074)
@@ -0,0 +1,159 @@
+/* Copyright (c) 2013, Dmitriy V. Reshetnikov
+ * 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 <stdio.h>
+#include <errno.h>
+
+#include "ucl.h"
+
+void
+ucl_obj_dump (const ucl_object_t *obj, unsigned int shift)
+{
+	int num = shift * 4 + 5;
+	char *pre = (char *) malloc (num * sizeof(char));
+	const ucl_object_t *cur, *tmp;
+	ucl_object_iter_t it = NULL, it_obj = NULL;
+
+	pre[--num] = 0x00;
+	while (num--)
+		pre[num] = 0x20;
+
+	tmp = obj;
+
+	while ((obj = ucl_iterate_object (tmp, &it, false))) {
+		printf ("%sucl object address: %p\n", pre + 4, obj);
+		if (obj->key != NULL) {
+			printf ("%skey: \"%s\"\n", pre, ucl_object_key (obj));
+		}
+		printf ("%sref: %hd\n", pre, obj->ref);
+		printf ("%slen: %u\n", pre, obj->len);
+		printf ("%sprev: %p\n", pre, obj->prev);
+		printf ("%snext: %p\n", pre, obj->next);
+		if (obj->type == UCL_OBJECT) {
+			printf ("%stype: UCL_OBJECT\n", pre);
+			printf ("%svalue: %p\n", pre, obj->value.ov);
+			while ((cur = ucl_iterate_object (obj, &it_obj, true))) {
+				ucl_obj_dump (cur, shift + 2);
+			}
+		}
+		else if (obj->type == UCL_ARRAY) {
+			printf ("%stype: UCL_ARRAY\n", pre);
+			printf ("%svalue: %p\n", pre, obj->value.av);
+			ucl_obj_dump (obj->value.av, shift + 2);
+		}
+		else if (obj->type == UCL_INT) {
+			printf ("%stype: UCL_INT\n", pre);
+			printf ("%svalue: %jd\n", pre, (intmax_t)ucl_object_toint (obj));
+		}
+		else if (obj->type == UCL_FLOAT) {
+			printf ("%stype: UCL_FLOAT\n", pre);
+			printf ("%svalue: %f\n", pre, ucl_object_todouble (obj));
+		}
+		else if (obj->type == UCL_STRING) {
+			printf ("%stype: UCL_STRING\n", pre);
+			printf ("%svalue: \"%s\"\n", pre, ucl_object_tostring (obj));
+		}
+		else if (obj->type == UCL_BOOLEAN) {
+			printf ("%stype: UCL_BOOLEAN\n", pre);
+			printf ("%svalue: %s\n", pre, ucl_object_tostring_forced (obj));
+		}
+		else if (obj->type == UCL_TIME) {
+			printf ("%stype: UCL_TIME\n", pre);
+			printf ("%svalue: %f\n", pre, ucl_object_todouble (obj));
+		}
+		else if (obj->type == UCL_USERDATA) {
+			printf ("%stype: UCL_USERDATA\n", pre);
+			printf ("%svalue: %p\n", pre, obj->value.ud);
+		}
+	}
+
+	free (pre);
+}
+
+int
+main(int argc, char **argv)
+{
+	const char *fn = NULL;
+	char inbuf[8192];
+	struct ucl_parser *parser;
+	int k, ret = 0, r = 0;
+	ucl_object_t *obj = NULL;
+	const ucl_object_t *par;
+	FILE *in;
+
+	if (argc > 1) {
+		fn = argv[1];
+	}
+
+	if (fn != NULL) {
+		in = fopen (fn, "r");
+		if (in == NULL) {
+			exit (-errno);
+		}
+	}
+	else {
+		in = stdin;
+	}
+
+	parser = ucl_parser_new (0);
+	while (!feof (in) && r < (int)sizeof (inbuf)) {
+		r += fread (inbuf + r, 1, sizeof (inbuf) - r, in);
+	}
+	ucl_parser_add_chunk (parser, inbuf, r);
+	fclose (in);
+	if (ucl_parser_get_error(parser)) {
+		printf ("Error occured: %s\n", ucl_parser_get_error(parser));
+		ret = 1;
+		goto end;
+	}
+
+	obj = ucl_parser_get_object (parser);
+	if (ucl_parser_get_error (parser)) {
+		printf ("Error occured: %s\n", ucl_parser_get_error(parser));
+		ret = 1;
+		goto end;
+	}
+
+	if (argc > 2) {
+		for (k = 2; k < argc; k++) {
+			printf ("search for \"%s\"... ", argv[k]);
+			par = ucl_object_find_key (obj, argv[k]);
+			printf ("%sfound\n", (par == NULL )?"not ":"");
+			ucl_obj_dump (par, 0);
+		}
+	}
+	else {
+		ucl_obj_dump (obj, 0);
+	}
+
+end:
+	if (parser != NULL) {
+		ucl_parser_free (parser);
+	}
+	if (obj != NULL) {
+		ucl_object_unref (obj);
+	}
+
+	return ret;
+}



More information about the Midnightbsd-cvs mailing list