summaryrefslogtreecommitdiffstats
path: root/contrib/libucl
diff options
context:
space:
mode:
authorbapt <bapt@FreeBSD.org>2014-07-19 23:44:57 +0000
committerbapt <bapt@FreeBSD.org>2014-07-19 23:44:57 +0000
commit6f718e3669e9ecc1b1ca407a465d6ee20571a2b8 (patch)
treeba71cc26671c93ed9809f7cadb07734c0bddb4c7 /contrib/libucl
parent6095428430d025abcf6983536297f168bf62b45b (diff)
downloadFreeBSD-src-6f718e3669e9ecc1b1ca407a465d6ee20571a2b8.zip
FreeBSD-src-6f718e3669e9ecc1b1ca407a465d6ee20571a2b8.tar.gz
MFC: r263648, r264789, r266636
This brings: - schema validation - xpath-like interface for ucl objects Adapt pkg(7) to the new libucl API
Diffstat (limited to 'contrib/libucl')
-rw-r--r--contrib/libucl/.gitignore3
-rw-r--r--contrib/libucl/Makefile.am7
-rw-r--r--contrib/libucl/Makefile.unix25
-rw-r--r--contrib/libucl/Makefile.w3217
-rw-r--r--contrib/libucl/README.md27
-rwxr-xr-xcontrib/libucl/autogen.sh2
-rw-r--r--contrib/libucl/cmake/CMakeLists.txt112
-rw-r--r--contrib/libucl/configure.ac163
-rw-r--r--contrib/libucl/doc/Makefile.am8
-rw-r--r--contrib/libucl/doc/api.md127
-rw-r--r--contrib/libucl/doc/libucl.3637
-rw-r--r--contrib/libucl/doc/pandoc.template12
-rw-r--r--contrib/libucl/include/ucl.h657
-rw-r--r--contrib/libucl/libucl.pc.in11
-rw-r--r--contrib/libucl/src/Makefile.am25
-rw-r--r--contrib/libucl/src/tree.h212
-rw-r--r--contrib/libucl/src/ucl_emitter.c71
-rw-r--r--contrib/libucl/src/ucl_hash.c16
-rw-r--r--contrib/libucl/src/ucl_hash.h12
-rw-r--r--contrib/libucl/src/ucl_internal.h74
-rw-r--r--contrib/libucl/src/ucl_parser.c174
-rw-r--r--contrib/libucl/src/ucl_schema.c1014
-rw-r--r--contrib/libucl/src/ucl_util.c951
-rw-r--r--contrib/libucl/stamp-h.in1
-rw-r--r--contrib/libucl/tests/.gitignore8
-rw-r--r--contrib/libucl/tests/10.in1
-rw-r--r--contrib/libucl/tests/5.in1
-rw-r--r--contrib/libucl/tests/5.res1
-rw-r--r--contrib/libucl/tests/7.in0
-rw-r--r--contrib/libucl/tests/7.res1
-rw-r--r--contrib/libucl/tests/Makefile.am33
-rwxr-xr-xcontrib/libucl/tests/basic.test26
-rw-r--r--contrib/libucl/tests/basic/1.in (renamed from contrib/libucl/tests/1.in)0
-rw-r--r--contrib/libucl/tests/basic/1.res (renamed from contrib/libucl/tests/1.res)0
-rw-r--r--contrib/libucl/tests/basic/10.in9
-rw-r--r--contrib/libucl/tests/basic/2.in (renamed from contrib/libucl/tests/2.in)0
-rw-r--r--contrib/libucl/tests/basic/2.res (renamed from contrib/libucl/tests/2.res)0
-rw-r--r--contrib/libucl/tests/basic/3.in (renamed from contrib/libucl/tests/3.in)0
-rw-r--r--contrib/libucl/tests/basic/3.res (renamed from contrib/libucl/tests/3.res)0
-rw-r--r--contrib/libucl/tests/basic/4.in (renamed from contrib/libucl/tests/4.in)0
-rw-r--r--contrib/libucl/tests/basic/4.res (renamed from contrib/libucl/tests/4.res)0
-rw-r--r--contrib/libucl/tests/basic/6.in (renamed from contrib/libucl/tests/6.in)0
-rw-r--r--contrib/libucl/tests/basic/6.res (renamed from contrib/libucl/tests/6.res)0
-rw-r--r--contrib/libucl/tests/basic/8.in (renamed from contrib/libucl/tests/8.in)0
-rw-r--r--contrib/libucl/tests/basic/8.res (renamed from contrib/libucl/tests/8.res)0
-rw-r--r--contrib/libucl/tests/basic/9-comment.inc (renamed from contrib/libucl/tests/9-comment.inc)0
-rw-r--r--contrib/libucl/tests/basic/9-empty.inc (renamed from contrib/libucl/tests/9-empty.inc)0
-rw-r--r--contrib/libucl/tests/basic/9.in (renamed from contrib/libucl/tests/9.in)1
-rw-r--r--contrib/libucl/tests/basic/9.inc (renamed from contrib/libucl/tests/9.inc)0
-rw-r--r--contrib/libucl/tests/basic/9.res (renamed from contrib/libucl/tests/9.res)0
-rwxr-xr-xcontrib/libucl/tests/generate.test13
-rwxr-xr-xcontrib/libucl/tests/run_tests.sh9
-rwxr-xr-xcontrib/libucl/tests/schema.test9
-rw-r--r--contrib/libucl/tests/schema/additionalItems.json82
-rw-r--r--contrib/libucl/tests/schema/additionalProperties.json69
-rw-r--r--contrib/libucl/tests/schema/allOf.json112
-rw-r--r--contrib/libucl/tests/schema/anyOf.json68
-rw-r--r--contrib/libucl/tests/schema/definitions.json32
-rw-r--r--contrib/libucl/tests/schema/dependencies.json113
-rw-r--r--contrib/libucl/tests/schema/enum.json72
-rw-r--r--contrib/libucl/tests/schema/items.json46
-rw-r--r--contrib/libucl/tests/schema/maxItems.json28
-rw-r--r--contrib/libucl/tests/schema/maxLength.json28
-rw-r--r--contrib/libucl/tests/schema/maxProperties.json28
-rw-r--r--contrib/libucl/tests/schema/maximum.json42
-rw-r--r--contrib/libucl/tests/schema/minItems.json28
-rw-r--r--contrib/libucl/tests/schema/minLength.json28
-rw-r--r--contrib/libucl/tests/schema/minProperties.json28
-rw-r--r--contrib/libucl/tests/schema/minimum.json42
-rw-r--r--contrib/libucl/tests/schema/multipleOf.json60
-rw-r--r--contrib/libucl/tests/schema/not.json96
-rw-r--r--contrib/libucl/tests/schema/oneOf.json68
-rw-r--r--contrib/libucl/tests/schema/pattern.json23
-rw-r--r--contrib/libucl/tests/schema/patternProperties.json110
-rw-r--r--contrib/libucl/tests/schema/properties.json92
-rw-r--r--contrib/libucl/tests/schema/ref.json146
-rw-r--r--contrib/libucl/tests/schema/refRemote.json74
-rw-r--r--contrib/libucl/tests/schema/required.json39
-rw-r--r--contrib/libucl/tests/schema/type.json330
-rw-r--r--contrib/libucl/tests/schema/uniqueItems.json79
-rwxr-xr-xcontrib/libucl/tests/speed.test21
-rw-r--r--contrib/libucl/tests/test_basic.c12
-rw-r--r--contrib/libucl/tests/test_generate.c68
-rw-r--r--contrib/libucl/tests/test_schema.c158
-rw-r--r--contrib/libucl/tests/test_speed.c2
-rw-r--r--contrib/libucl/utils/Makefile.am19
-rw-r--r--contrib/libucl/utils/objdump.c10
87 files changed, 6019 insertions, 704 deletions
diff --git a/contrib/libucl/.gitignore b/contrib/libucl/.gitignore
deleted file mode 100644
index ea72388..0000000
--- a/contrib/libucl/.gitignore
+++ /dev/null
@@ -1,3 +0,0 @@
-.cproject
-.project
-.settings
diff --git a/contrib/libucl/Makefile.am b/contrib/libucl/Makefile.am
new file mode 100644
index 0000000..4668b36
--- /dev/null
+++ b/contrib/libucl/Makefile.am
@@ -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 \ No newline at end of file
diff --git a/contrib/libucl/Makefile.unix b/contrib/libucl/Makefile.unix
index 71c7540..121403d 100644
--- a/contrib/libucl/Makefile.unix
+++ b/contrib/libucl/Makefile.unix
@@ -4,7 +4,7 @@ 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 = 8
+PATCH_VERSION = 9
VERSION = "$(MAJOR_VERSION).$(MINOR_VERSION).$(PATCH_VERSION)"
SONAME = libucl.so
SONAME_FULL = $(SONAME).$(MAJOR_VERSION)
@@ -21,8 +21,17 @@ 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)/xxhash.o
+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)
@@ -44,11 +53,13 @@ $(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
+ $(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
@@ -60,13 +71,15 @@ objdump: utils/objdump.c $(OBJDIR)/$(SONAME)
# Tests
-test: $(OBJDIR) $(OBJDIR)/$(SONAME) $(OBJDIR)/test_basic $(OBJDIR)/test_speed $(OBJDIR)/test_generate
+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
+ 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)
diff --git a/contrib/libucl/Makefile.w32 b/contrib/libucl/Makefile.w32
index 895db1d..62ff14a 100644
--- a/contrib/libucl/Makefile.w32
+++ b/contrib/libucl/Makefile.w32
@@ -4,7 +4,7 @@ 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 = 8
+PATCH_VERSION = 9
VERSION = "$(MAJOR_VERSION).$(MINOR_VERSION).$(PATCH_VERSION)"
SONAME = libucl.dll
OBJDIR ?= .obj
@@ -24,8 +24,17 @@ 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)/xxhash.o
+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)
@@ -44,6 +53,8 @@ $(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
diff --git a/contrib/libucl/README.md b/contrib/libucl/README.md
index b5a373a..b6353d10 100644
--- a/contrib/libucl/README.md
+++ b/contrib/libucl/README.md
@@ -1,3 +1,26 @@
+# 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
@@ -262,6 +285,10 @@ Each UCL object can be serialized to one of the three supported formats:
* `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.
diff --git a/contrib/libucl/autogen.sh b/contrib/libucl/autogen.sh
new file mode 100755
index 0000000..68f4a17
--- /dev/null
+++ b/contrib/libucl/autogen.sh
@@ -0,0 +1,2 @@
+#!/bin/sh
+autoreconf -i
diff --git a/contrib/libucl/cmake/CMakeLists.txt b/contrib/libucl/cmake/CMakeLists.txt
new file mode 100644
index 0000000..4551966
--- /dev/null
+++ b/contrib/libucl/cmake/CMakeLists.txt
@@ -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")
diff --git a/contrib/libucl/configure.ac b/contrib/libucl/configure.ac
new file mode 100644
index 0000000..de78e8b
--- /dev/null
+++ b/contrib/libucl/configure.ac
@@ -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
diff --git a/contrib/libucl/doc/Makefile.am b/contrib/libucl/doc/Makefile.am
new file mode 100644
index 0000000..7e57a82
--- /dev/null
+++ b/contrib/libucl/doc/Makefile.am
@@ -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 \ No newline at end of file
diff --git a/contrib/libucl/doc/api.md b/contrib/libucl/doc/api.md
index 0523f53..41e660a 100644
--- a/contrib/libucl/doc/api.md
+++ b/contrib/libucl/doc/api.md
@@ -1,30 +1,75 @@
-Synopsis
-========
+# 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
-===========
+# 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
+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.
+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
+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
+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` objects
+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
+Provide basic utilities to manage `ucl` objects: creating, removing, retaining and releasing reference count and so on.
# Parser functions
@@ -40,6 +85,7 @@ 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
@@ -87,6 +133,14 @@ while this one won't be parsed correctly:
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
@@ -228,7 +282,7 @@ Libucl provides the following functions for emitting UCL objects:
### ucl_object_emit
~~~C
-unsigned char *ucl_object_emit (ucl_object_t *obj, enum ucl_emitter emit_type);
+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.
@@ -236,7 +290,7 @@ Allocate a string that is suitable to fit the underlying UCL object `obj` and fi
### ucl_object_emit_full
~~~C
-bool ucl_object_emit_full (ucl_object_t *obj, enum ucl_emitter emit_type,
+bool ucl_object_emit_full (const ucl_object_t *obj, enum ucl_emitter emit_type,
struct ucl_emitter_functions *emitter);
~~~
@@ -314,19 +368,20 @@ This function is used to convert a string `str` of size `len` to an UCL objects
- `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_NUMBER` - parse passed string and detect number (both float or integer types)
-- `UCL_STRING_PARSE` - parse passed string (and detect booleans and numbers)
+- `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 function
+# 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
-ucl_object_t* ucl_iterate_object (ucl_object_t *obj,
+const ucl_object_t* ucl_iterate_object (const ucl_object_t *obj,
ucl_object_iter_t *iter, bool expand_values);
~~~
@@ -334,7 +389,7 @@ This function accept opaque iterator pointer `iter`. In the first call this iter
~~~C
ucl_object_iter_t it = NULL, it_obj = NULL;
-ucl_object_t *cur, *tmp;
+const ucl_object_t *cur, *tmp;
/* Iterate over the object */
while ((obj = ucl_iterate_object (top, &it, true))) {
@@ -345,4 +400,40 @@ while ((obj = ucl_iterate_object (top, &it, true))) {
ucl_object_tostring_forced (cur));
}
}
-~~~ \ No newline at end of file
+~~~
+
+# 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
diff --git a/contrib/libucl/doc/libucl.3 b/contrib/libucl/doc/libucl.3
new file mode 100644
index 0000000..a185d1a
--- /dev/null
+++ b/contrib/libucl/doc/libucl.3
@@ -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@highsecure.ru>.
diff --git a/contrib/libucl/doc/pandoc.template b/contrib/libucl/doc/pandoc.template
new file mode 100644
index 0000000..e29e20c
--- /dev/null
+++ b/contrib/libucl/doc/pandoc.template
@@ -0,0 +1,12 @@
+% LIBUCL(5) Libucl manual
+% Vsevolod Stakhov <vsevolod@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)
+
diff --git a/contrib/libucl/include/ucl.h b/contrib/libucl/include/ucl.h
index a464a00..3eeea9a 100644
--- a/contrib/libucl/include/ucl.h
+++ b/contrib/libucl/include/ucl.h
@@ -81,6 +81,14 @@ extern "C" {
#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.
@@ -138,7 +146,8 @@ typedef enum ucl_emitter {
*/
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_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;
/**
@@ -150,11 +159,12 @@ typedef enum ucl_string_flags {
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_NUMBER = UCL_STRING_PARSE_INT|UCL_STRING_PARSE_DOUBLE , /**<
+ 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 = 0x20 /**< Treat numbers as bytes */
+ UCL_STRING_PARSE_BYTES = 0x40 /**< Treat numbers as bytes */
} ucl_string_flags_t;
/**
@@ -206,51 +216,33 @@ typedef struct ucl_object_s {
* @param obj CL object
* @return zero terminated key
*/
-UCL_EXTERN char* ucl_copy_key_trash (ucl_object_t *obj);
+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 (ucl_object_t *obj);
+UCL_EXTERN char* ucl_copy_value_trash (const ucl_object_t *obj);
/**
* Creates a new object
* @return new object
*/
-static inline ucl_object_t* ucl_object_new (void) UCL_WARN_UNUSED_RESULT;
-static inline 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_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
*/
-static inline ucl_object_t* ucl_object_typed_new (unsigned int type) UCL_WARN_UNUSED_RESULT;
-static inline ucl_object_t *
-ucl_object_typed_new (unsigned int 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_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
@@ -267,11 +259,7 @@ UCL_EXTERN ucl_object_t * ucl_object_fromstring_common (const char *str, size_t
* @param str NULL terminated string, will be json escaped
* @return new object
*/
-static inline ucl_object_t *
-ucl_object_fromstring (const char *str)
-{
- return ucl_object_fromstring_common (str, 0, UCL_STRING_ESCAPE);
-}
+UCL_EXTERN ucl_object_t *ucl_object_fromstring (const char *str) UCL_WARN_UNUSED_RESULT;
/**
* Create a UCL object from the specified string
@@ -279,68 +267,29 @@ ucl_object_fromstring (const char *str)
* @param len length of a string
* @return new object
*/
-static inline ucl_object_t *
-ucl_object_fromlstring (const char *str, size_t len)
-{
- return ucl_object_fromstring_common (str, len, UCL_STRING_ESCAPE);
-}
+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
*/
-static inline 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_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
*/
-static inline 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_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
*/
-static inline 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;
-}
+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'
@@ -349,10 +298,10 @@ ucl_object_frombool (bool bv)
* @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 new value of top object
+ * @return true if key has been inserted
*/
-UCL_EXTERN ucl_object_t* ucl_object_insert_key (ucl_object_t *top, ucl_object_t *elt,
- const char *key, size_t keylen, bool copy_key) UCL_WARN_UNUSED_RESULT;
+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,
@@ -362,10 +311,10 @@ UCL_EXTERN ucl_object_t* ucl_object_insert_key (ucl_object_t *top, ucl_object_t
* @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 new value of top object
+ * @return true if key has been inserted
*/
-UCL_EXTERN ucl_object_t* ucl_object_replace_key (ucl_object_t *top, ucl_object_t *elt,
- const char *key, size_t keylen, bool copy_key) UCL_WARN_UNUSED_RESULT;
+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,
@@ -373,14 +322,38 @@ UCL_EXTERN ucl_object_t* ucl_object_replace_key (ucl_object_t *top, ucl_object_t
* @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);
+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);
+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,
@@ -390,94 +363,28 @@ UCL_EXTERN bool ucl_object_delete_key (ucl_object_t *top, const char *key);
* @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 new value of top object
+ * @return true if key has been inserted
*/
-UCL_EXTERN ucl_object_t* ucl_object_insert_key_merged (ucl_object_t *top, ucl_object_t *elt,
- const char *key, size_t keylen, bool copy_key) UCL_WARN_UNUSED_RESULT;
+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 new value of top object
- */
-static inline ucl_object_t * ucl_array_append (ucl_object_t *top,
- ucl_object_t *elt) UCL_WARN_UNUSED_RESULT;
-static inline ucl_object_t *
-ucl_array_append (ucl_object_t *top, ucl_object_t *elt)
-{
- ucl_object_t *head;
-
- if (elt == NULL) {
- return NULL;
- }
-
- if (top == NULL) {
- top = ucl_object_typed_new (UCL_ARRAY);
- top->value.av = elt;
- elt->next = NULL;
- elt->prev = elt;
- top->len = 1;
- }
- else {
- 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 top;
-}
+ * @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 new value of top object
- */
-static inline ucl_object_t * ucl_array_prepend (ucl_object_t *top,
- ucl_object_t *elt) UCL_WARN_UNUSED_RESULT;
-static inline ucl_object_t *
-ucl_array_prepend (ucl_object_t *top, ucl_object_t *elt)
-{
- ucl_object_t *head;
-
- if (elt == NULL) {
- return NULL;
- }
-
- if (top == NULL) {
- top = ucl_object_typed_new (UCL_ARRAY);
- top->value.av = elt;
- elt->next = NULL;
- elt->prev = elt;
- top->len = 1;
- }
- else {
- 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 top;
-}
+ * @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
@@ -486,66 +393,22 @@ ucl_array_prepend (ucl_object_t *top, ucl_object_t *elt)
* @param elt element to remove
* @return removed element or NULL if `top` is NULL or not an array
*/
-static inline 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;
-}
+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
*/
-static inline ucl_object_t *
-ucl_array_head (ucl_object_t *top)
-{
- if (top == NULL || top->type != UCL_ARRAY || top->value.av == NULL) {
- return NULL;
- }
- return top->value.av;
-}
+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
*/
-static inline ucl_object_t *
-ucl_array_tail (ucl_object_t *top)
-{
- if (top == NULL || top->type != UCL_ARRAY || top->value.av == NULL) {
- return NULL;
- }
- return top->value.av->prev;
-}
+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
@@ -553,11 +416,16 @@ ucl_array_tail (ucl_object_t *top)
* @param top array ucl object
* @return removed element or NULL if `top` is NULL or not an array
*/
-static inline ucl_object_t *
-ucl_array_pop_last (ucl_object_t *top)
-{
- return ucl_array_delete (top, ucl_array_tail (top));
-}
+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
@@ -565,38 +433,16 @@ ucl_array_pop_last (ucl_object_t *top)
* @param top array ucl object
* @return removed element or NULL if `top` is NULL or not an array
*/
-static inline ucl_object_t *
-ucl_array_pop_first (ucl_object_t *top)
-{
- return ucl_array_delete (top, ucl_array_head (top));
-}
+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 new head if applicable
- */
-static inline ucl_object_t * ucl_elt_append (ucl_object_t *head,
- ucl_object_t *elt) UCL_WARN_UNUSED_RESULT;
-static inline 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;
-}
+ * @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
@@ -604,40 +450,14 @@ ucl_elt_append (ucl_object_t *head, ucl_object_t *elt)
* @param target target double variable
* @return true if conversion was successful
*/
-static inline bool
-ucl_object_todouble_safe (ucl_object_t *obj, double *target)
-{
- if (obj == 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;
-}
+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
*/
-static inline double
-ucl_object_todouble (ucl_object_t *obj)
-{
- double result = 0.;
-
- ucl_object_todouble_safe (obj, &result);
- return result;
-}
+UCL_EXTERN double ucl_object_todouble (const ucl_object_t *obj);
/**
* Converts an object to integer value
@@ -645,40 +465,14 @@ ucl_object_todouble (ucl_object_t *obj)
* @param target target integer variable
* @return true if conversion was successful
*/
-static inline bool
-ucl_object_toint_safe (ucl_object_t *obj, int64_t *target)
-{
- if (obj == 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;
-}
+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
*/
-static inline int64_t
-ucl_object_toint (ucl_object_t *obj)
-{
- int64_t result = 0;
-
- ucl_object_toint_safe (obj, &result);
- return result;
-}
+UCL_EXTERN int64_t ucl_object_toint (const ucl_object_t *obj);
/**
* Converts an object to boolean value
@@ -686,36 +480,14 @@ ucl_object_toint (ucl_object_t *obj)
* @param target target boolean variable
* @return true if conversion was successful
*/
-static inline bool
-ucl_object_toboolean_safe (ucl_object_t *obj, bool *target)
-{
- if (obj == NULL) {
- return false;
- }
- switch (obj->type) {
- case UCL_BOOLEAN:
- *target = (obj->value.iv == true);
- break;
- default:
- return false;
- }
-
- return true;
-}
+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
*/
-static inline bool
-ucl_object_toboolean (ucl_object_t *obj)
-{
- bool result = false;
-
- ucl_object_toboolean_safe (obj, &result);
- return result;
-}
+UCL_EXTERN bool ucl_object_toboolean (const ucl_object_t *obj);
/**
* Converts an object to string value
@@ -723,48 +495,21 @@ ucl_object_toboolean (ucl_object_t *obj)
* @param target target string variable, no need to free value
* @return true if conversion was successful
*/
-static inline bool
-ucl_object_tostring_safe (ucl_object_t *obj, const char **target)
-{
- if (obj == NULL) {
- return false;
- }
-
- switch (obj->type) {
- case UCL_STRING:
- *target = ucl_copy_value_trash (obj);
- break;
- default:
- return false;
- }
-
- return true;
-}
+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
*/
-static inline const char *
-ucl_object_tostring (ucl_object_t *obj)
-{
- const char *result = NULL;
-
- ucl_object_tostring_safe (obj, &result);
- return result;
-}
+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
*/
-static inline const char *
-ucl_object_tostring_forced (ucl_object_t *obj)
-{
- return ucl_copy_value_trash (obj);
-}
+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
@@ -774,37 +519,15 @@ ucl_object_tostring_forced (ucl_object_t *obj)
* @param tlen target length
* @return true if conversion was successful
*/
-static inline bool
-ucl_object_tolstring_safe (ucl_object_t *obj, const char **target, size_t *tlen)
-{
- if (obj == NULL) {
- return false;
- }
- switch (obj->type) {
- case UCL_STRING:
- *target = obj->value.sv;
- *tlen = obj->len;
- break;
- default:
- return false;
- }
-
- return true;
-}
+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
*/
-static inline const char *
-ucl_object_tolstring (ucl_object_t *obj, size_t *tlen)
-{
- const char *result = NULL;
-
- ucl_object_tolstring_safe (obj, &result, tlen);
- return result;
-}
+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
@@ -812,7 +535,8 @@ ucl_object_tolstring (ucl_object_t *obj, size_t *tlen)
* @param key key to search
* @return object matched the specified key or NULL if key is not found
*/
-UCL_EXTERN ucl_object_t * ucl_object_find_key (ucl_object_t *obj, const char *key);
+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
@@ -821,18 +545,24 @@ UCL_EXTERN ucl_object_t * ucl_object_find_key (ucl_object_t *obj, const char *ke
* @param klen length of a key
* @return object matched the specified key or NULL if key is not found
*/
-UCL_EXTERN ucl_object_t *ucl_object_find_keyl (ucl_object_t *obj, const char *key, size_t klen);
+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
*/
-static inline const char *
-ucl_object_key (ucl_object_t *obj)
-{
- return ucl_copy_key_trash (obj);
-}
+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)
@@ -840,39 +570,47 @@ ucl_object_key (ucl_object_t *obj)
* @param len target key length
* @return key pointer
*/
-static inline const char *
-ucl_object_keyl (ucl_object_t *obj, size_t *len)
-{
- *len = obj->keylen;
- return obj->key;
-}
+UCL_EXTERN const char* ucl_object_keyl (const ucl_object_t *obj, size_t *len);
/**
- * Free ucl object
- * @param obj ucl object to free
+ * Increase reference count for an object
+ * @param obj object to ref
*/
-UCL_EXTERN void ucl_object_free (ucl_object_t *obj);
+UCL_EXTERN ucl_object_t* ucl_object_ref (const ucl_object_t *obj);
/**
- * Increase reference count for an object
- * @param obj object to ref
+ * Free ucl object
+ * @param obj ucl object to free
*/
-static inline ucl_object_t *
-ucl_object_ref (ucl_object_t *obj) {
- obj->ref ++;
- return obj;
-}
+UCL_DEPRECATED(UCL_EXTERN void ucl_object_free (ucl_object_t *obj));
/**
* Decrease reference count for an object
* @param obj object to unref
*/
-static inline void
-ucl_object_unref (ucl_object_t *obj) {
- if (obj != NULL && --obj->ref <= 0) {
- ucl_object_free (obj);
- }
-}
+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
*/
@@ -886,7 +624,8 @@ typedef void* ucl_object_iter_t;
* while ((cur = ucl_iterate_object (obj, &it)) != NULL) ...
* @return the next object or NULL
*/
-UCL_EXTERN ucl_object_t* ucl_iterate_object (ucl_object_t *obj, ucl_object_iter_t *iter, bool expand_values);
+UCL_EXTERN const ucl_object_t* ucl_iterate_object (const ucl_object_t *obj,
+ ucl_object_iter_t *iter, bool expand_values);
/** @} */
@@ -928,6 +667,19 @@ UCL_EXTERN void ucl_parser_register_macro (struct ucl_parser *parser, const char
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
@@ -937,6 +689,15 @@ UCL_EXTERN void ucl_parser_register_variable (struct ucl_parser *parser, const c
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
@@ -944,7 +705,18 @@ UCL_EXTERN void ucl_parser_register_variable (struct ucl_parser *parser, const c
* @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);
+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
@@ -953,10 +725,21 @@ UCL_EXTERN bool ucl_parser_add_chunk (struct ucl_parser *parser, const unsigned
* @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);
+UCL_EXTERN bool ucl_parser_add_file (struct ucl_parser *parser,
+ const char *filename);
/**
- * Get a top object for a parser
+ * 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
@@ -1026,7 +809,8 @@ struct ucl_emitter_functions {
* #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 (ucl_object_t *obj, enum ucl_emitter emit_type);
+UCL_EXTERN unsigned char *ucl_object_emit (const ucl_object_t *obj,
+ enum ucl_emitter emit_type);
/**
* Emit object to a string
@@ -1035,10 +819,53 @@ UCL_EXTERN unsigned char *ucl_object_emit (ucl_object_t *obj, enum ucl_emitter e
* #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 (ucl_object_t *obj, enum ucl_emitter emit_type,
+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
diff --git a/contrib/libucl/libucl.pc.in b/contrib/libucl/libucl.pc.in
new file mode 100644
index 0000000..975462d
--- /dev/null
+++ b/contrib/libucl/libucl.pc.in
@@ -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}/
diff --git a/contrib/libucl/src/Makefile.am b/contrib/libucl/src/Makefile.am
new file mode 100644
index 0000000..76391cd
--- /dev/null
+++ b/contrib/libucl/src/Makefile.am
@@ -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
diff --git a/contrib/libucl/src/tree.h b/contrib/libucl/src/tree.h
new file mode 100644
index 0000000..cee9373
--- /dev/null
+++ b/contrib/libucl/src/tree.h
@@ -0,0 +1,212 @@
+/* tree.h -- AVL trees (in the spirit of BSD's 'queue.h') -*- C -*- */
+
+/* Copyright (c) 2005 Ian Piumarta
+ *
+ * All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the 'Software'), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, and/or sell copies of the
+ * Software, and to permit persons to whom the Software is furnished to do so,
+ * provided that the above copyright notice(s) and this permission notice appear
+ * in all copies of the Software and that both the above copyright notice(s) and
+ * this permission notice appear in supporting documentation.
+ *
+ * THE SOFTWARE IS PROVIDED 'AS IS'. USE ENTIRELY AT YOUR OWN RISK.
+ */
+
+/* This file defines an AVL balanced binary tree [Georgii M. Adelson-Velskii and
+ * Evgenii M. Landis, 'An algorithm for the organization of information',
+ * Doklady Akademii Nauk SSSR, 146:263-266, 1962 (Russian). Also in Myron
+ * J. Ricci (trans.), Soviet Math, 3:1259-1263, 1962 (English)].
+ *
+ * An AVL tree is headed by pointers to the root node and to a function defining
+ * the ordering relation between nodes. Each node contains an arbitrary payload
+ * plus three fields per tree entry: the depth of the subtree for which it forms
+ * the root and two pointers to child nodes (singly-linked for minimum space, at
+ * the expense of direct access to the parent node given a pointer to one of the
+ * children). The tree is rebalanced after every insertion or removal. The
+ * tree may be traversed in two directions: forward (in-order left-to-right) and
+ * reverse (in-order, right-to-left).
+ *
+ * Because of the recursive nature of many of the operations on trees it is
+ * necessary to define a number of helper functions for each type of tree node.
+ * The macro TREE_DEFINE(node_tag, entry_name) defines these functions with
+ * unique names according to the node_tag. This macro should be invoked,
+ * thereby defining the necessary functions, once per node tag in the program.
+ *
+ * For details on the use of these macros, see the tree(3) manual page.
+ */
+
+#ifndef __tree_h
+#define __tree_h
+
+
+#define TREE_DELTA_MAX 1
+
+#define TREE_ENTRY(type) \
+ struct { \
+ struct type *avl_left; \
+ struct type *avl_right; \
+ int avl_height; \
+ }
+
+#define TREE_HEAD(name, type) \
+ struct name { \
+ struct type *th_root; \
+ int (*th_cmp)(struct type *lhs, struct type *rhs); \
+ }
+
+#define TREE_INITIALIZER(cmp) { 0, cmp }
+
+#define TREE_DELTA(self, field) \
+ (( (((self)->field.avl_left) ? (self)->field.avl_left->field.avl_height : 0)) \
+ - (((self)->field.avl_right) ? (self)->field.avl_right->field.avl_height : 0))
+
+/* Recursion prevents the following from being defined as macros. */
+
+#define TREE_DEFINE(node, field) \
+ \
+ struct node *TREE_BALANCE_##node##_##field(struct node *); \
+ \
+ struct node *TREE_ROTL_##node##_##field(struct node *self) \
+ { \
+ struct node *r= self->field.avl_right; \
+ self->field.avl_right= r->field.avl_left; \
+ r->field.avl_left= TREE_BALANCE_##node##_##field(self); \
+ return TREE_BALANCE_##node##_##field(r); \
+ } \
+ \
+ struct node *TREE_ROTR_##node##_##field(struct node *self) \
+ { \
+ struct node *l= self->field.avl_left; \
+ self->field.avl_left= l->field.avl_right; \
+ l->field.avl_right= TREE_BALANCE_##node##_##field(self); \
+ return TREE_BALANCE_##node##_##field(l); \
+ } \
+ \
+ struct node *TREE_BALANCE_##node##_##field(struct node *self) \
+ { \
+ int delta= TREE_DELTA(self, field); \
+ \
+ if (delta < -TREE_DELTA_MAX) \
+ { \
+ if (TREE_DELTA(self->field.avl_right, field) > 0) \
+ self->field.avl_right= TREE_ROTR_##node##_##field(self->field.avl_right); \
+ return TREE_ROTL_##node##_##field(self); \
+ } \
+ else if (delta > TREE_DELTA_MAX) \
+ { \
+ if (TREE_DELTA(self->field.avl_left, field) < 0) \
+ self->field.avl_left= TREE_ROTL_##node##_##field(self->field.avl_left); \
+ return TREE_ROTR_##node##_##field(self); \
+ } \
+ self->field.avl_height= 0; \
+ if (self->field.avl_left && (self->field.avl_left->field.avl_height > self->field.avl_height)) \
+ self->field.avl_height= self->field.avl_left->field.avl_height; \
+ if (self->field.avl_right && (self->field.avl_right->field.avl_height > self->field.avl_height)) \
+ self->field.avl_height= self->field.avl_right->field.avl_height; \
+ self->field.avl_height += 1; \
+ return self; \
+ } \
+ \
+ struct node *TREE_INSERT_##node##_##field \
+ (struct node *self, struct node *elm, int (*compare)(struct node *lhs, struct node *rhs)) \
+ { \
+ if (!self) \
+ return elm; \
+ if (compare(elm, self) < 0) \
+ self->field.avl_left= TREE_INSERT_##node##_##field(self->field.avl_left, elm, compare); \
+ else \
+ self->field.avl_right= TREE_INSERT_##node##_##field(self->field.avl_right, elm, compare); \
+ return TREE_BALANCE_##node##_##field(self); \
+ } \
+ \
+ struct node *TREE_FIND_##node##_##field \
+ (struct node *self, struct node *elm, int (*compare)(struct node *lhs, struct node *rhs)) \
+ { \
+ if (!self) \
+ return 0; \
+ if (compare(elm, self) == 0) \
+ return self; \
+ if (compare(elm, self) < 0) \
+ return TREE_FIND_##node##_##field(self->field.avl_left, elm, compare); \
+ else \
+ return TREE_FIND_##node##_##field(self->field.avl_right, elm, compare); \
+ } \
+ \
+ struct node *TREE_MOVE_RIGHT(struct node *self, struct node *rhs) \
+ { \
+ if (!self) \
+ return rhs; \
+ self->field.avl_right= TREE_MOVE_RIGHT(self->field.avl_right, rhs); \
+ return TREE_BALANCE_##node##_##field(self); \
+ } \
+ \
+ struct node *TREE_REMOVE_##node##_##field \
+ (struct node *self, struct node *elm, int (*compare)(struct node *lhs, struct node *rhs)) \
+ { \
+ if (!self) return 0; \
+ \
+ if (compare(elm, self) == 0) \
+ { \
+ struct node *tmp= TREE_MOVE_RIGHT(self->field.avl_left, self->field.avl_right); \
+ self->field.avl_left= 0; \
+ self->field.avl_right= 0; \
+ return tmp; \
+ } \
+ if (compare(elm, self) < 0) \
+ self->field.avl_left= TREE_REMOVE_##node##_##field(self->field.avl_left, elm, compare); \
+ else \
+ self->field.avl_right= TREE_REMOVE_##node##_##field(self->field.avl_right, elm, compare); \
+ return TREE_BALANCE_##node##_##field(self); \
+ } \
+ \
+ void TREE_FORWARD_APPLY_ALL_##node##_##field \
+ (struct node *self, void (*function)(struct node *node, void *data), void *data) \
+ { \
+ if (self) \
+ { \
+ TREE_FORWARD_APPLY_ALL_##node##_##field(self->field.avl_left, function, data); \
+ function(self, data); \
+ TREE_FORWARD_APPLY_ALL_##node##_##field(self->field.avl_right, function, data); \
+ } \
+ } \
+ \
+ void TREE_REVERSE_APPLY_ALL_##node##_##field \
+ (struct node *self, void (*function)(struct node *node, void *data), void *data) \
+ { \
+ if (self) \
+ { \
+ TREE_REVERSE_APPLY_ALL_##node##_##field(self->field.avl_right, function, data); \
+ function(self, data); \
+ TREE_REVERSE_APPLY_ALL_##node##_##field(self->field.avl_left, function, data); \
+ } \
+ }
+
+#define TREE_INSERT(head, node, field, elm) \
+ ((head)->th_root= TREE_INSERT_##node##_##field((head)->th_root, (elm), (head)->th_cmp))
+
+#define TREE_FIND(head, node, field, elm) \
+ (TREE_FIND_##node##_##field((head)->th_root, (elm), (head)->th_cmp))
+
+#define TREE_REMOVE(head, node, field, elm) \
+ ((head)->th_root= TREE_REMOVE_##node##_##field((head)->th_root, (elm), (head)->th_cmp))
+
+#define TREE_DEPTH(head, field) \
+ ((head)->th_root->field.avl_height)
+
+#define TREE_FORWARD_APPLY(head, node, field, function, data) \
+ TREE_FORWARD_APPLY_ALL_##node##_##field((head)->th_root, function, data)
+
+#define TREE_REVERSE_APPLY(head, node, field, function, data) \
+ TREE_REVERSE_APPLY_ALL_##node##_##field((head)->th_root, function, data)
+
+#define TREE_INIT(head, cmp) do { \
+ (head)->th_root= 0; \
+ (head)->th_cmp= (cmp); \
+ } while (0)
+
+
+#endif /* __tree_h */
diff --git a/contrib/libucl/src/ucl_emitter.c b/contrib/libucl/src/ucl_emitter.c
index 51bb09a..04c3d4b 100644
--- a/contrib/libucl/src/ucl_emitter.c
+++ b/contrib/libucl/src/ucl_emitter.c
@@ -21,11 +21,19 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#include <float.h>
-#include <math.h>
+#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
@@ -33,29 +41,29 @@
*/
-static void ucl_obj_write_json (ucl_object_t *obj,
+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 (ucl_object_t *obj,
+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 (ucl_object_t *obj,
+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 (ucl_object_t *obj,
+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 (ucl_object_t *obj,
+static void ucl_elt_array_write_yaml (const ucl_object_t *obj,
struct ucl_emitter_functions *func,
unsigned int tabs,
bool start_tabs,
@@ -136,10 +144,10 @@ ucl_elt_string_write_json (const char *str, size_t size,
* @param buf target buffer
*/
static void
-ucl_elt_obj_write_json (ucl_object_t *obj, struct ucl_emitter_functions *func,
+ucl_elt_obj_write_json (const ucl_object_t *obj, struct ucl_emitter_functions *func,
unsigned int tabs, bool start_tabs, bool compact)
{
- ucl_object_t *cur;
+ const ucl_object_t *cur;
ucl_hash_iter_t it = NULL;
if (start_tabs) {
@@ -188,10 +196,10 @@ ucl_elt_obj_write_json (ucl_object_t *obj, struct ucl_emitter_functions *func,
* @param buf target buffer
*/
static void
-ucl_elt_array_write_json (ucl_object_t *obj, struct ucl_emitter_functions *func,
+ucl_elt_array_write_json (const ucl_object_t *obj, struct ucl_emitter_functions *func,
unsigned int tabs, bool start_tabs, bool compact)
{
- ucl_object_t *cur = obj;
+ const ucl_object_t *cur = obj;
if (start_tabs) {
ucl_add_tabs (func, tabs, compact);
@@ -227,7 +235,7 @@ ucl_elt_array_write_json (ucl_object_t *obj, struct ucl_emitter_functions *func,
* @param buf buffer
*/
static void
-ucl_elt_write_json (ucl_object_t *obj, struct ucl_emitter_functions *func,
+ucl_elt_write_json (const ucl_object_t *obj, struct ucl_emitter_functions *func,
unsigned int tabs, bool start_tabs, bool compact)
{
bool flag;
@@ -287,10 +295,10 @@ ucl_elt_write_json (ucl_object_t *obj, struct ucl_emitter_functions *func,
* @param buf target buffer
*/
static void
-ucl_obj_write_json (ucl_object_t *obj, struct ucl_emitter_functions *func,
+ucl_obj_write_json (const ucl_object_t *obj, struct ucl_emitter_functions *func,
unsigned int tabs, bool start_tabs, bool compact)
{
- ucl_object_t *cur;
+ const ucl_object_t *cur;
bool is_array = (obj->next != NULL);
if (is_array) {
@@ -331,7 +339,8 @@ ucl_obj_write_json (ucl_object_t *obj, struct ucl_emitter_functions *func,
* @return json output (should be freed after using)
*/
static void
-ucl_object_emit_json (ucl_object_t *obj, bool compact, struct ucl_emitter_functions *func)
+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);
}
@@ -342,10 +351,10 @@ ucl_object_emit_json (ucl_object_t *obj, bool compact, struct ucl_emitter_functi
* @param buf target buffer
*/
static void
-ucl_elt_obj_write_config (ucl_object_t *obj, struct ucl_emitter_functions *func,
+ucl_elt_obj_write_config (const ucl_object_t *obj, struct ucl_emitter_functions *func,
unsigned int tabs, bool start_tabs, bool is_top)
{
- ucl_object_t *cur, *cur_obj;
+ const ucl_object_t *cur, *cur_obj;
ucl_hash_iter_t it = NULL;
if (start_tabs) {
@@ -394,10 +403,10 @@ ucl_elt_obj_write_config (ucl_object_t *obj, struct ucl_emitter_functions *func,
* @param buf target buffer
*/
static void
-ucl_elt_array_write_config (ucl_object_t *obj, struct ucl_emitter_functions *func,
+ucl_elt_array_write_config (const ucl_object_t *obj, struct ucl_emitter_functions *func,
unsigned int tabs, bool start_tabs, bool is_top)
{
- ucl_object_t *cur = obj;
+ const ucl_object_t *cur = obj;
if (start_tabs) {
ucl_add_tabs (func, tabs, false);
@@ -419,7 +428,7 @@ ucl_elt_array_write_config (ucl_object_t *obj, struct ucl_emitter_functions *fun
* @param buf buffer
*/
static void
-ucl_elt_write_config (ucl_object_t *obj, struct ucl_emitter_functions *func,
+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;
@@ -484,14 +493,14 @@ ucl_elt_write_config (ucl_object_t *obj, struct ucl_emitter_functions *func,
* @return rcl output (should be freed after using)
*/
static void
-ucl_object_emit_config (ucl_object_t *obj, struct ucl_emitter_functions *func)
+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 (ucl_object_t *obj, struct ucl_emitter_functions *func,
+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);
@@ -510,10 +519,10 @@ ucl_obj_write_yaml (ucl_object_t *obj, struct ucl_emitter_functions *func,
* @param buf target buffer
*/
static void
-ucl_elt_obj_write_yaml (ucl_object_t *obj, struct ucl_emitter_functions *func,
+ucl_elt_obj_write_yaml (const ucl_object_t *obj, struct ucl_emitter_functions *func,
unsigned int tabs, bool start_tabs, bool is_top)
{
- ucl_object_t *cur;
+ const ucl_object_t *cur;
ucl_hash_iter_t it = NULL;
if (start_tabs) {
@@ -558,10 +567,10 @@ ucl_elt_obj_write_yaml (ucl_object_t *obj, struct ucl_emitter_functions *func,
* @param buf target buffer
*/
static void
-ucl_elt_array_write_yaml (ucl_object_t *obj, struct ucl_emitter_functions *func,
+ucl_elt_array_write_yaml (const ucl_object_t *obj, struct ucl_emitter_functions *func,
unsigned int tabs, bool start_tabs, bool is_top)
{
- ucl_object_t *cur = obj;
+ const ucl_object_t *cur = obj;
if (start_tabs) {
ucl_add_tabs (func, tabs, false);
@@ -583,7 +592,7 @@ ucl_elt_array_write_yaml (ucl_object_t *obj, struct ucl_emitter_functions *func,
* @param buf buffer
*/
static void
-ucl_elt_write_yaml (ucl_object_t *obj, struct ucl_emitter_functions *func,
+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;
@@ -648,7 +657,7 @@ ucl_elt_write_yaml (ucl_object_t *obj, struct ucl_emitter_functions *func,
* @return rcl output (should be freed after using)
*/
static void
-ucl_object_emit_yaml (ucl_object_t *obj, struct ucl_emitter_functions *func)
+ucl_object_emit_yaml (const ucl_object_t *obj, struct ucl_emitter_functions *func)
{
ucl_elt_write_yaml (obj, func, 0, false, true, true);
}
@@ -715,7 +724,7 @@ ucl_utstring_append_double (double val, void *ud)
unsigned char *
-ucl_object_emit (ucl_object_t *obj, enum ucl_emitter emit_type)
+ucl_object_emit (const ucl_object_t *obj, enum ucl_emitter emit_type)
{
UT_string *buf = NULL;
unsigned char *res = NULL;
@@ -755,7 +764,7 @@ ucl_object_emit (ucl_object_t *obj, enum ucl_emitter emit_type)
}
bool
-ucl_object_emit_full (ucl_object_t *obj, enum ucl_emitter emit_type,
+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) {
@@ -777,7 +786,7 @@ ucl_object_emit_full (ucl_object_t *obj, enum ucl_emitter emit_type,
unsigned char *
-ucl_object_emit_single_json (ucl_object_t *obj)
+ucl_object_emit_single_json (const ucl_object_t *obj)
{
UT_string *buf = NULL;
unsigned char *res = NULL;
diff --git a/contrib/libucl/src/ucl_hash.c b/contrib/libucl/src/ucl_hash.c
index a3711de..c2e80cb 100644
--- a/contrib/libucl/src/ucl_hash.c
+++ b/contrib/libucl/src/ucl_hash.c
@@ -21,6 +21,7 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
+#include "ucl_internal.h"
#include "ucl_hash.h"
#include "utlist.h"
@@ -39,11 +40,15 @@ ucl_hash_create (void)
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) {
- func (elt->data);
+ 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);
}
@@ -51,7 +56,8 @@ void ucl_hash_destroy (ucl_hash_t* hashlin, ucl_hash_free_func *func)
}
void
-ucl_hash_insert (ucl_hash_t* hashlin, ucl_object_t *obj, const char *key, unsigned keylen)
+ucl_hash_insert (ucl_hash_t* hashlin, const ucl_object_t *obj,
+ const char *key, unsigned keylen)
{
ucl_hash_node_t *node;
@@ -60,7 +66,7 @@ ucl_hash_insert (ucl_hash_t* hashlin, ucl_object_t *obj, const char *key, unsign
HASH_ADD_KEYPTR (hh, hashlin->buckets, key, keylen, node);
}
-void*
+const void*
ucl_hash_iterate (ucl_hash_t *hashlin, ucl_hash_iter_t *iter)
{
ucl_hash_node_t *elt = *iter;
@@ -91,7 +97,7 @@ ucl_hash_iter_has_next (ucl_hash_iter_t iter)
}
-ucl_object_t*
+const ucl_object_t*
ucl_hash_search (ucl_hash_t* hashlin, const char *key, unsigned keylen)
{
ucl_hash_node_t *found;
@@ -108,7 +114,7 @@ ucl_hash_search (ucl_hash_t* hashlin, const char *key, unsigned keylen)
}
void
-ucl_hash_delete (ucl_hash_t* hashlin, ucl_object_t *obj)
+ucl_hash_delete (ucl_hash_t* hashlin, const ucl_object_t *obj)
{
ucl_hash_node_t *found;
diff --git a/contrib/libucl/src/ucl_hash.h b/contrib/libucl/src/ucl_hash.h
index 5c9b851..cbbf005 100644
--- a/contrib/libucl/src/ucl_hash.h
+++ b/contrib/libucl/src/ucl_hash.h
@@ -31,7 +31,7 @@
typedef struct ucl_hash_node_s
{
- ucl_object_t *data;
+ const ucl_object_t *data;
UT_hash_handle hh;
} ucl_hash_node_t;
@@ -62,17 +62,19 @@ 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, ucl_object_t *obj, const char *key, unsigned keylen);
+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, ucl_object_t *obj);
+void ucl_hash_delete (ucl_hash_t* hashlin, const ucl_object_t *obj);
/**
* Searches an element in the hashtable.
*/
-ucl_object_t* ucl_hash_search (ucl_hash_t* hashlin, const char *key, unsigned keylen);
+const ucl_object_t* ucl_hash_search (ucl_hash_t* hashlin, const char *key,
+ unsigned keylen);
/**
@@ -81,7 +83,7 @@ ucl_object_t* ucl_hash_search (ucl_hash_t* hashlin, const char *key, unsigned ke
* @param iter iterator (must be NULL on first iteration)
* @return the next object
*/
-void* ucl_hash_iterate (ucl_hash_t *hashlin, ucl_hash_iter_t *iter);
+const void* ucl_hash_iterate (ucl_hash_t *hashlin, ucl_hash_iter_t *iter);
/**
* Check whether an iterator has next element
diff --git a/contrib/libucl/src/ucl_internal.h b/contrib/libucl/src/ucl_internal.h
index 49c4aae..0e3ecd0 100644
--- a/contrib/libucl/src/ucl_internal.h
+++ b/contrib/libucl/src/ucl_internal.h
@@ -24,18 +24,69 @@
#ifndef UCL_INTERNAL_H_
#define UCL_INTERNAL_H_
-#include <sys/types.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
-#include <sys/mman.h>
+# 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"
@@ -48,6 +99,10 @@
#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
@@ -142,6 +197,8 @@ struct ucl_parser {
struct ucl_chunk *chunks;
struct ucl_pubkey *keys;
struct ucl_variable *variables;
+ ucl_variable_handler var_handler;
+ void *var_data;
UT_string *err;
};
@@ -261,20 +318,21 @@ ucl_maybe_parse_boolean (ucl_object_t *obj, const unsigned char *start, size_t l
* @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);
+ const char *start, const char *end, const char **pos,
+ bool allow_double, bool number_bytes, bool allow_time);
-static inline ucl_object_t *
+static inline const ucl_object_t *
ucl_hash_search_obj (ucl_hash_t* hashlin, ucl_object_t *obj)
{
- return (ucl_object_t *)ucl_hash_search (hashlin, obj->key, obj->keylen);
+ 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, ucl_object_t *obj) UCL_WARN_UNUSED_RESULT;
+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, ucl_object_t *obj)
+ucl_hash_insert_object (ucl_hash_t *hashlin, const ucl_object_t *obj)
{
if (hashlin == NULL) {
hashlin = ucl_hash_create ();
@@ -289,6 +347,6 @@ ucl_hash_insert_object (ucl_hash_t *hashlin, ucl_object_t *obj)
* @param obj
* @return
*/
-unsigned char * ucl_object_emit_single_json (ucl_object_t *obj);
+unsigned char * ucl_object_emit_single_json (const ucl_object_t *obj);
#endif /* UCL_INTERNAL_H_ */
diff --git a/contrib/libucl/src/ucl_parser.c b/contrib/libucl/src/ucl_parser.c
index 12ebad2..b4fe5af 100644
--- a/contrib/libucl/src/ucl_parser.c
+++ b/contrib/libucl/src/ucl_parser.c
@@ -55,34 +55,6 @@ struct ucl_parser_saved_state {
(chunk)->remain --; \
} while (0)
-/**
- * Save parser state
- * @param chunk
- * @param s
- */
-static inline void
-ucl_chunk_save_state (struct ucl_chunk *chunk, struct ucl_parser_saved_state *s)
-{
- s->column = chunk->column;
- s->pos = chunk->pos;
- s->line = chunk->line;
- s->remain = chunk->remain;
-}
-
-/**
- * Restore parser state
- * @param chunk
- * @param s
- */
-static inline void
-ucl_chunk_restore_state (struct ucl_chunk *chunk, struct ucl_parser_saved_state *s)
-{
- chunk->column = s->column;
- chunk->pos = s->pos;
- chunk->line = s->line;
- chunk->remain = s->remain;
-}
-
static inline void
ucl_set_err (struct ucl_chunk *chunk, int code, const char *str, UT_string **err)
{
@@ -264,6 +236,9 @@ ucl_check_variable_safe (struct ucl_parser *parser, const char *ptr, size_t rema
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) {
@@ -286,6 +261,19 @@ ucl_check_variable_safe (struct ucl_parser *parser, const char *ptr, size_t rema
}
}
+ /* 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;
}
@@ -299,7 +287,8 @@ ucl_check_variable_safe (struct ucl_parser *parser, const char *ptr, size_t rema
* @return
*/
static const char *
-ucl_check_variable (struct ucl_parser *parser, const char *ptr, size_t remain, size_t *out_len, bool *vars_found)
+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;
@@ -310,7 +299,8 @@ ucl_check_variable (struct ucl_parser *parser, const char *ptr, size_t remain, s
end = ptr + remain;
while (p < end) {
if (*p == '}') {
- ret = ucl_check_variable_safe (parser, ptr + 1, p - ptr - 1, out_len, true, &found);
+ ret = ucl_check_variable_safe (parser, ptr + 1, p - ptr - 1,
+ out_len, true, &found);
if (found) {
/* {} must be excluded actually */
ret ++;
@@ -356,10 +346,13 @@ static const char *
ucl_expand_single_variable (struct ucl_parser *parser, const char *ptr,
size_t remain, unsigned char **dest)
{
- unsigned char *d = *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 --;
@@ -371,6 +364,7 @@ ucl_expand_single_variable (struct ucl_parser *parser, const char *ptr,
}
else if (*p == '{') {
p ++;
+ strict = true;
ret += 2;
remain -= 2;
}
@@ -387,9 +381,22 @@ ucl_expand_single_variable (struct ucl_parser *parser, const char *ptr,
}
}
if (!found) {
- memcpy (d, ptr, 2);
- d += 2;
- ret --;
+ 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;
@@ -544,6 +551,10 @@ ucl_add_parser_stack (ucl_object_t *obj, struct ucl_parser *parser, bool is_arra
}
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);
@@ -554,12 +565,13 @@ ucl_add_parser_stack (ucl_object_t *obj, struct ucl_parser *parser, bool is_arra
int
ucl_maybe_parse_number (ucl_object_t *obj,
- const char *start, const char *end, const char **pos, bool allow_double, bool number_bytes)
+ 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_date = false, valid_start = false, is_hex = false,
+ is_time = false, valid_start = false, is_hex = false,
is_neg = false;
double dv = 0;
int64_t lv = 0;
@@ -657,7 +669,8 @@ ucl_maybe_parse_number (ucl_object_t *obj,
}
/* Now check endptr */
- if (endptr == NULL || ucl_lex_is_atom_end (*endptr) || *endptr == '\0') {
+ if (endptr == NULL || ucl_lex_is_atom_end (*endptr) || *endptr == '\0' ||
+ ucl_test_character (*endptr, UCL_CHARACTER_WHITESPACE_UNSAFE)) {
p = endptr;
goto set_obj;
}
@@ -678,7 +691,7 @@ ucl_maybe_parse_number (ucl_object_t *obj,
need_double = true;
dv = lv;
}
- is_date = true;
+ is_time = true;
if (p[0] == 'm' || p[0] == 'M') {
dv /= 1000.;
}
@@ -708,7 +721,7 @@ ucl_maybe_parse_number (ucl_object_t *obj,
p ++;
goto set_obj;
}
- else if (end - p >= 3) {
+ else if (allow_time && end - p >= 3) {
if (tolower (p[0]) == 'm' &&
tolower (p[1]) == 'i' &&
tolower (p[2]) == 'n') {
@@ -717,7 +730,7 @@ ucl_maybe_parse_number (ucl_object_t *obj,
need_double = true;
dv = lv;
}
- is_date = true;
+ is_time = true;
dv *= 60.;
p += 3;
goto set_obj;
@@ -737,13 +750,14 @@ ucl_maybe_parse_number (ucl_object_t *obj,
break;
case 'S':
case 's':
- if (p == end - 1 || ucl_lex_is_atom_end (p[1])) {
+ if (allow_time &&
+ (p == end - 1 || ucl_lex_is_atom_end (p[1]))) {
if (!need_double) {
need_double = true;
dv = lv;
}
p ++;
- is_date = true;
+ is_time = true;
goto set_obj;
}
break;
@@ -755,12 +769,13 @@ ucl_maybe_parse_number (ucl_object_t *obj,
case 'W':
case 'Y':
case 'y':
- if (p == end - 1 || ucl_lex_is_atom_end (p[1])) {
+ if (allow_time &&
+ (p == end - 1 || ucl_lex_is_atom_end (p[1]))) {
if (!need_double) {
need_double = true;
dv = lv;
}
- is_date = true;
+ is_time = true;
dv *= ucl_lex_time_multiplier (*p);
p ++;
goto set_obj;
@@ -773,8 +788,8 @@ ucl_maybe_parse_number (ucl_object_t *obj,
return EINVAL;
set_obj:
- if (allow_double && (need_double || is_date)) {
- if (!is_date) {
+ if (allow_double && (need_double || is_time)) {
+ if (!is_time) {
obj->type = UCL_FLOAT;
}
else {
@@ -803,7 +818,8 @@ ucl_lex_number (struct ucl_parser *parser,
const unsigned char *pos;
int ret;
- ret = ucl_maybe_parse_number (obj, chunk->pos, chunk->end, (const char **)&pos, true, false);
+ 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;
@@ -1077,19 +1093,19 @@ ucl_parse_key (struct ucl_parser *parser, struct ucl_chunk *chunk, bool *next_ke
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_free(nobj);
+ 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_free(nobj);
+ ucl_object_unref (nobj);
return false;
}
container = parser->stack->obj->value.ov;
nobj->key = key;
nobj->keylen = keylen;
- tobj = ucl_hash_search_obj (container, nobj);
+ tobj = __DECONST (ucl_object_t *, ucl_hash_search_obj (container, nobj));
if (tobj == NULL) {
container = ucl_hash_insert_object (container, nobj);
nobj->prev = nobj;
@@ -1308,6 +1324,9 @@ ucl_parse_value (struct ucl_parser *parser, struct ucl_chunk *chunk)
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;
@@ -1316,6 +1335,9 @@ ucl_parse_value (struct ucl_parser *parser, struct ucl_chunk *chunk)
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;
@@ -1608,6 +1630,9 @@ ucl_state_machine (struct ucl_parser *parser)
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;
@@ -1673,7 +1698,11 @@ ucl_state_machine (struct ucl_parser *parser)
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);
+ 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;
@@ -1787,6 +1816,9 @@ 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);
@@ -1808,7 +1840,13 @@ ucl_parser_register_macro (struct ucl_parser *parser, const char *macro,
{
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);
@@ -1851,6 +1889,9 @@ ucl_parser_register_variable (struct ucl_parser *parser, const char *var,
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);
@@ -1867,14 +1908,30 @@ ucl_parser_register_variable (struct ucl_parser *parser, const char *var,
}
}
+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;
@@ -1895,3 +1952,18 @@ ucl_parser_add_chunk (struct ucl_parser *parser, const unsigned char *data,
return false;
}
+
+bool
+ucl_parser_add_string (struct ucl_parser *parser, const char *data,
+ size_t len)
+{
+ if (data == NULL) {
+ ucl_create_err (&parser->err, "invalid string added");
+ return false;
+ }
+ if (len == 0) {
+ len = strlen (data);
+ }
+
+ return ucl_parser_add_chunk (parser, (const unsigned char *)data, len);
+}
diff --git a/contrib/libucl/src/ucl_schema.c b/contrib/libucl/src/ucl_schema.c
new file mode 100644
index 0000000..faffe86
--- /dev/null
+++ b/contrib/libucl/src/ucl_schema.c
@@ -0,0 +1,1014 @@
+/*
+ * Copyright (c) 2014, Vsevolod Stakhov
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR ''AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "ucl.h"
+#include "ucl_internal.h"
+#include "tree.h"
+#include "utlist.h"
+#ifdef HAVE_STDARG_H
+#include <stdarg.h>
+#endif
+#ifdef HAVE_STDIO_H
+#include <stdio.h>
+#endif
+#ifdef HAVE_REGEX_H
+#include <regex.h>
+#endif
+#ifdef HAVE_MATH_H
+#include <math.h>
+#endif
+
+static bool ucl_schema_validate (const ucl_object_t *schema,
+ const ucl_object_t *obj, bool try_array,
+ struct ucl_schema_error *err,
+ const ucl_object_t *root);
+
+static bool
+ucl_string_to_type (const char *input, ucl_type_t *res)
+{
+ if (strcasecmp (input, "object") == 0) {
+ *res = UCL_OBJECT;
+ }
+ else if (strcasecmp (input, "array") == 0) {
+ *res = UCL_ARRAY;
+ }
+ else if (strcasecmp (input, "integer") == 0) {
+ *res = UCL_INT;
+ }
+ else if (strcasecmp (input, "number") == 0) {
+ *res = UCL_FLOAT;
+ }
+ else if (strcasecmp (input, "string") == 0) {
+ *res = UCL_STRING;
+ }
+ else if (strcasecmp (input, "boolean") == 0) {
+ *res = UCL_BOOLEAN;
+ }
+ else if (strcasecmp (input, "null") == 0) {
+ *res = UCL_NULL;
+ }
+ else {
+ return false;
+ }
+
+ return true;
+}
+
+static const char *
+ucl_object_type_to_string (ucl_type_t type)
+{
+ const char *res = "unknown";
+
+ switch (type) {
+ case UCL_OBJECT:
+ res = "object";
+ break;
+ case UCL_ARRAY:
+ res = "array";
+ break;
+ case UCL_INT:
+ res = "integer";
+ break;
+ case UCL_FLOAT:
+ case UCL_TIME:
+ res = "number";
+ break;
+ case UCL_STRING:
+ res = "string";
+ break;
+ case UCL_BOOLEAN:
+ res = "boolean";
+ break;
+ case UCL_NULL:
+ case UCL_USERDATA:
+ res = "null";
+ break;
+ }
+
+ return res;
+}
+
+/*
+ * Create validation error
+ */
+static void
+ucl_schema_create_error (struct ucl_schema_error *err,
+ enum ucl_schema_error_code code, const ucl_object_t *obj,
+ const char *fmt, ...)
+{
+ va_list va;
+
+ if (err != NULL) {
+ err->code = code;
+ err->obj = obj;
+ va_start (va, fmt);
+ vsnprintf (err->msg, sizeof (err->msg), fmt, va);
+ va_end (va);
+ }
+}
+
+/*
+ * Check whether we have a pattern specified
+ */
+static const ucl_object_t *
+ucl_schema_test_pattern (const ucl_object_t *obj, const char *pattern)
+{
+ const ucl_object_t *res = NULL;
+#ifdef HAVE_REGEX_H
+ regex_t reg;
+ const ucl_object_t *elt;
+ ucl_object_iter_t iter = NULL;
+
+ if (regcomp (&reg, pattern, REG_EXTENDED | REG_NOSUB) == 0) {
+ while ((elt = ucl_iterate_object (obj, &iter, true)) != NULL) {
+ if (regexec (&reg, ucl_object_key (elt), 0, NULL, 0) == 0) {
+ res = elt;
+ break;
+ }
+ }
+ regfree (&reg);
+ }
+#endif
+ return res;
+}
+
+/*
+ * Check dependencies for an object
+ */
+static bool
+ucl_schema_validate_dependencies (const ucl_object_t *deps,
+ const ucl_object_t *obj, struct ucl_schema_error *err,
+ const ucl_object_t *root)
+{
+ const ucl_object_t *elt, *cur, *cur_dep;
+ ucl_object_iter_t iter = NULL, piter;
+ bool ret = true;
+
+ while (ret && (cur = ucl_iterate_object (deps, &iter, true)) != NULL) {
+ elt = ucl_object_find_key (obj, ucl_object_key (cur));
+ if (elt != NULL) {
+ /* Need to check dependencies */
+ if (cur->type == UCL_ARRAY) {
+ piter = NULL;
+ while (ret && (cur_dep = ucl_iterate_object (cur, &piter, true)) != NULL) {
+ if (ucl_object_find_key (obj, ucl_object_tostring (cur_dep)) == NULL) {
+ ucl_schema_create_error (err, UCL_SCHEMA_MISSING_DEPENDENCY, elt,
+ "dependency %s is missing for key %s",
+ ucl_object_tostring (cur_dep), ucl_object_key (cur));
+ ret = false;
+ break;
+ }
+ }
+ }
+ else if (cur->type == UCL_OBJECT) {
+ ret = ucl_schema_validate (cur, obj, true, err, root);
+ }
+ }
+ }
+
+ return ret;
+}
+
+/*
+ * Validate object
+ */
+static bool
+ucl_schema_validate_object (const ucl_object_t *schema,
+ const ucl_object_t *obj, struct ucl_schema_error *err,
+ const ucl_object_t *root)
+{
+ const ucl_object_t *elt, *prop, *found, *additional_schema = NULL,
+ *required = NULL, *pat, *pelt;
+ ucl_object_iter_t iter = NULL, piter = NULL;
+ bool ret = true, allow_additional = true;
+ int64_t minmax;
+
+ while (ret && (elt = ucl_iterate_object (schema, &iter, true)) != NULL) {
+ if (elt->type == UCL_OBJECT &&
+ strcmp (ucl_object_key (elt), "properties") == 0) {
+ piter = NULL;
+ while (ret && (prop = ucl_iterate_object (elt, &piter, true)) != NULL) {
+ found = ucl_object_find_key (obj, ucl_object_key (prop));
+ if (found) {
+ ret = ucl_schema_validate (prop, found, true, err, root);
+ }
+ }
+ }
+ else if (strcmp (ucl_object_key (elt), "additionalProperties") == 0) {
+ if (elt->type == UCL_BOOLEAN) {
+ if (!ucl_object_toboolean (elt)) {
+ /* Deny additional fields completely */
+ allow_additional = false;
+ }
+ }
+ else if (elt->type == UCL_OBJECT) {
+ /* Define validator for additional fields */
+ additional_schema = elt;
+ }
+ else {
+ ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, elt,
+ "additionalProperties attribute is invalid in schema");
+ ret = false;
+ break;
+ }
+ }
+ else if (strcmp (ucl_object_key (elt), "required") == 0) {
+ if (elt->type == UCL_ARRAY) {
+ required = elt;
+ }
+ else {
+ ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, elt,
+ "required attribute is invalid in schema");
+ ret = false;
+ break;
+ }
+ }
+ else if (strcmp (ucl_object_key (elt), "minProperties") == 0
+ && ucl_object_toint_safe (elt, &minmax)) {
+ if (obj->len < minmax) {
+ ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
+ "object has not enough properties: %u, minimum is: %u",
+ obj->len, (unsigned)minmax);
+ ret = false;
+ break;
+ }
+ }
+ else if (strcmp (ucl_object_key (elt), "maxProperties") == 0
+ && ucl_object_toint_safe (elt, &minmax)) {
+ if (obj->len > minmax) {
+ ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
+ "object has too many properties: %u, maximum is: %u",
+ obj->len, (unsigned)minmax);
+ ret = false;
+ break;
+ }
+ }
+ else if (strcmp (ucl_object_key (elt), "patternProperties") == 0) {
+ piter = NULL;
+ while (ret && (prop = ucl_iterate_object (elt, &piter, true)) != NULL) {
+ found = ucl_schema_test_pattern (obj, ucl_object_key (prop));
+ if (found) {
+ ret = ucl_schema_validate (prop, found, true, err, root);
+ }
+ }
+ }
+ else if (elt->type == UCL_OBJECT &&
+ strcmp (ucl_object_key (elt), "dependencies") == 0) {
+ ret = ucl_schema_validate_dependencies (elt, obj, err, root);
+ }
+ }
+
+ if (ret) {
+ /* Additional properties */
+ if (!allow_additional || additional_schema != NULL) {
+ /* Check if we have exactly the same properties in schema and object */
+ iter = NULL;
+ prop = ucl_object_find_key (schema, "properties");
+ while ((elt = ucl_iterate_object (obj, &iter, true)) != NULL) {
+ found = ucl_object_find_key (prop, ucl_object_key (elt));
+ if (found == NULL) {
+ /* Try patternProperties */
+ piter = NULL;
+ pat = ucl_object_find_key (schema, "patternProperties");
+ while ((pelt = ucl_iterate_object (pat, &piter, true)) != NULL) {
+ found = ucl_schema_test_pattern (obj, ucl_object_key (pelt));
+ if (found != NULL) {
+ break;
+ }
+ }
+ }
+ if (found == NULL) {
+ if (!allow_additional) {
+ ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
+ "object has non-allowed property %s",
+ ucl_object_key (elt));
+ ret = false;
+ break;
+ }
+ else if (additional_schema != NULL) {
+ if (!ucl_schema_validate (additional_schema, elt, true, err, root)) {
+ ret = false;
+ break;
+ }
+ }
+ }
+ }
+ }
+ /* Required properties */
+ if (required != NULL) {
+ iter = NULL;
+ while ((elt = ucl_iterate_object (required, &iter, true)) != NULL) {
+ if (ucl_object_find_key (obj, ucl_object_tostring (elt)) == NULL) {
+ ucl_schema_create_error (err, UCL_SCHEMA_MISSING_PROPERTY, obj,
+ "object has missing property %s",
+ ucl_object_tostring (elt));
+ ret = false;
+ break;
+ }
+ }
+ }
+ }
+
+
+ return ret;
+}
+
+static bool
+ucl_schema_validate_number (const ucl_object_t *schema,
+ const ucl_object_t *obj, struct ucl_schema_error *err)
+{
+ const ucl_object_t *elt, *test;
+ ucl_object_iter_t iter = NULL;
+ bool ret = true, exclusive = false;
+ double constraint, val;
+ const double alpha = 1e-16;
+
+ while (ret && (elt = ucl_iterate_object (schema, &iter, true)) != NULL) {
+ if ((elt->type == UCL_FLOAT || elt->type == UCL_INT) &&
+ strcmp (ucl_object_key (elt), "multipleOf") == 0) {
+ constraint = ucl_object_todouble (elt);
+ if (constraint <= 0) {
+ ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, elt,
+ "multipleOf must be greater than zero");
+ ret = false;
+ break;
+ }
+ val = ucl_object_todouble (obj);
+ if (fabs (remainder (val, constraint)) > alpha) {
+ ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
+ "number %.4f is not multiple of %.4f, remainder is %.7f",
+ val, constraint);
+ ret = false;
+ break;
+ }
+ }
+ else if ((elt->type == UCL_FLOAT || elt->type == UCL_INT) &&
+ strcmp (ucl_object_key (elt), "maximum") == 0) {
+ constraint = ucl_object_todouble (elt);
+ test = ucl_object_find_key (schema, "exclusiveMaximum");
+ if (test && test->type == UCL_BOOLEAN) {
+ exclusive = ucl_object_toboolean (test);
+ }
+ val = ucl_object_todouble (obj);
+ if (val > constraint || (exclusive && val >= constraint)) {
+ ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
+ "number is too big: %.3f, maximum is: %.3f",
+ val, constraint);
+ ret = false;
+ break;
+ }
+ }
+ else if ((elt->type == UCL_FLOAT || elt->type == UCL_INT) &&
+ strcmp (ucl_object_key (elt), "minimum") == 0) {
+ constraint = ucl_object_todouble (elt);
+ test = ucl_object_find_key (schema, "exclusiveMinimum");
+ if (test && test->type == UCL_BOOLEAN) {
+ exclusive = ucl_object_toboolean (test);
+ }
+ val = ucl_object_todouble (obj);
+ if (val < constraint || (exclusive && val <= constraint)) {
+ ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
+ "number is too small: %.3f, minimum is: %.3f",
+ val, constraint);
+ ret = false;
+ break;
+ }
+ }
+ }
+
+ return ret;
+}
+
+static bool
+ucl_schema_validate_string (const ucl_object_t *schema,
+ const ucl_object_t *obj, struct ucl_schema_error *err)
+{
+ const ucl_object_t *elt;
+ ucl_object_iter_t iter = NULL;
+ bool ret = true;
+ int64_t constraint;
+#ifdef HAVE_REGEX_H
+ regex_t re;
+#endif
+
+ while (ret && (elt = ucl_iterate_object (schema, &iter, true)) != NULL) {
+ if (elt->type == UCL_INT &&
+ strcmp (ucl_object_key (elt), "maxLength") == 0) {
+ constraint = ucl_object_toint (elt);
+ if (obj->len > constraint) {
+ ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
+ "string is too big: %.3f, maximum is: %.3f",
+ obj->len, constraint);
+ ret = false;
+ break;
+ }
+ }
+ else if (elt->type == UCL_INT &&
+ strcmp (ucl_object_key (elt), "minLength") == 0) {
+ constraint = ucl_object_toint (elt);
+ if (obj->len < constraint) {
+ ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
+ "string is too short: %.3f, minimum is: %.3f",
+ obj->len, constraint);
+ ret = false;
+ break;
+ }
+ }
+#ifdef HAVE_REGEX_H
+ else if (elt->type == UCL_STRING &&
+ strcmp (ucl_object_key (elt), "pattern") == 0) {
+ if (regcomp (&re, ucl_object_tostring (elt),
+ REG_EXTENDED | REG_NOSUB) != 0) {
+ ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, elt,
+ "cannot compile pattern %s", ucl_object_tostring (elt));
+ ret = false;
+ break;
+ }
+ if (regexec (&re, ucl_object_tostring (obj), 0, NULL, 0) != 0) {
+ ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
+ "string doesn't match regexp %s",
+ ucl_object_tostring (elt));
+ ret = false;
+ }
+ regfree (&re);
+ }
+#endif
+ }
+
+ return ret;
+}
+
+struct ucl_compare_node {
+ const ucl_object_t *obj;
+ TREE_ENTRY(ucl_compare_node) link;
+ struct ucl_compare_node *next;
+};
+
+typedef TREE_HEAD(_tree, ucl_compare_node) ucl_compare_tree_t;
+
+TREE_DEFINE(ucl_compare_node, link)
+
+static int
+ucl_schema_elt_compare (struct ucl_compare_node *n1, struct ucl_compare_node *n2)
+{
+ const ucl_object_t *o1 = n1->obj, *o2 = n2->obj;
+
+ return ucl_object_compare (o1, o2);
+}
+
+static bool
+ucl_schema_array_is_unique (const ucl_object_t *obj, struct ucl_schema_error *err)
+{
+ ucl_compare_tree_t tree = TREE_INITIALIZER (ucl_schema_elt_compare);
+ ucl_object_iter_t iter = NULL;
+ const ucl_object_t *elt;
+ struct ucl_compare_node *node, test, *nodes = NULL, *tmp;
+ bool ret = true;
+
+ while ((elt = ucl_iterate_object (obj, &iter, true)) != NULL) {
+ test.obj = elt;
+ node = TREE_FIND (&tree, ucl_compare_node, link, &test);
+ if (node != NULL) {
+ ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, elt,
+ "duplicate values detected while uniqueItems is true");
+ ret = false;
+ break;
+ }
+ node = calloc (1, sizeof (*node));
+ if (node == NULL) {
+ ucl_schema_create_error (err, UCL_SCHEMA_UNKNOWN, elt,
+ "cannot allocate tree node");
+ ret = false;
+ break;
+ }
+ node->obj = elt;
+ TREE_INSERT (&tree, ucl_compare_node, link, node);
+ LL_PREPEND (nodes, node);
+ }
+
+ LL_FOREACH_SAFE (nodes, node, tmp) {
+ free (node);
+ }
+
+ return ret;
+}
+
+static bool
+ucl_schema_validate_array (const ucl_object_t *schema,
+ const ucl_object_t *obj, struct ucl_schema_error *err,
+ const ucl_object_t *root)
+{
+ const ucl_object_t *elt, *it, *found, *additional_schema = NULL,
+ *first_unvalidated = NULL;
+ ucl_object_iter_t iter = NULL, piter = NULL;
+ bool ret = true, allow_additional = true, need_unique = false;
+ int64_t minmax;
+
+ while (ret && (elt = ucl_iterate_object (schema, &iter, true)) != NULL) {
+ if (strcmp (ucl_object_key (elt), "items") == 0) {
+ if (elt->type == UCL_ARRAY) {
+ found = obj->value.av;
+ while (ret && (it = ucl_iterate_object (elt, &piter, true)) != NULL) {
+ if (found) {
+ ret = ucl_schema_validate (it, found, false, err, root);
+ found = found->next;
+ }
+ }
+ if (found != NULL) {
+ /* The first element that is not validated */
+ first_unvalidated = found;
+ }
+ }
+ else if (elt->type == UCL_OBJECT) {
+ /* Validate all items using the specified schema */
+ while (ret && (it = ucl_iterate_object (obj, &piter, true)) != NULL) {
+ ret = ucl_schema_validate (elt, it, false, err, root);
+ }
+ }
+ else {
+ ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, elt,
+ "items attribute is invalid in schema");
+ ret = false;
+ break;
+ }
+ }
+ else if (strcmp (ucl_object_key (elt), "additionalItems") == 0) {
+ if (elt->type == UCL_BOOLEAN) {
+ if (!ucl_object_toboolean (elt)) {
+ /* Deny additional fields completely */
+ allow_additional = false;
+ }
+ }
+ else if (elt->type == UCL_OBJECT) {
+ /* Define validator for additional fields */
+ additional_schema = elt;
+ }
+ else {
+ ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, elt,
+ "additionalItems attribute is invalid in schema");
+ ret = false;
+ break;
+ }
+ }
+ else if (elt->type == UCL_BOOLEAN &&
+ strcmp (ucl_object_key (elt), "uniqueItems") == 0) {
+ need_unique = ucl_object_toboolean (elt);
+ }
+ else if (strcmp (ucl_object_key (elt), "minItems") == 0
+ && ucl_object_toint_safe (elt, &minmax)) {
+ if (obj->len < minmax) {
+ ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
+ "array has not enough items: %u, minimum is: %u",
+ obj->len, (unsigned)minmax);
+ ret = false;
+ break;
+ }
+ }
+ else if (strcmp (ucl_object_key (elt), "maxItems") == 0
+ && ucl_object_toint_safe (elt, &minmax)) {
+ if (obj->len > minmax) {
+ ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
+ "array has too many items: %u, maximum is: %u",
+ obj->len, (unsigned)minmax);
+ ret = false;
+ break;
+ }
+ }
+ }
+
+ if (ret) {
+ /* Additional properties */
+ if (!allow_additional || additional_schema != NULL) {
+ if (first_unvalidated != NULL) {
+ if (!allow_additional) {
+ ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
+ "array has undefined item");
+ ret = false;
+ }
+ else if (additional_schema != NULL) {
+ elt = first_unvalidated;
+ while (elt) {
+ if (!ucl_schema_validate (additional_schema, elt, false,
+ err, root)) {
+ ret = false;
+ break;
+ }
+ elt = elt->next;
+ }
+ }
+ }
+ }
+ /* Required properties */
+ if (ret && need_unique) {
+ ret = ucl_schema_array_is_unique (obj, err);
+ }
+ }
+
+ return ret;
+}
+
+/*
+ * Returns whether this object is allowed for this type
+ */
+static bool
+ucl_schema_type_is_allowed (const ucl_object_t *type, const ucl_object_t *obj,
+ struct ucl_schema_error *err)
+{
+ ucl_object_iter_t iter = NULL;
+ const ucl_object_t *elt;
+ const char *type_str;
+ ucl_type_t t;
+
+ if (type == NULL) {
+ /* Any type is allowed */
+ return true;
+ }
+
+ if (type->type == UCL_ARRAY) {
+ /* One of allowed types */
+ while ((elt = ucl_iterate_object (type, &iter, true)) != NULL) {
+ if (ucl_schema_type_is_allowed (elt, obj, err)) {
+ return true;
+ }
+ }
+ }
+ else if (type->type == UCL_STRING) {
+ type_str = ucl_object_tostring (type);
+ if (!ucl_string_to_type (type_str, &t)) {
+ ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, type,
+ "Type attribute is invalid in schema");
+ return false;
+ }
+ if (obj->type != t) {
+ /* Some types are actually compatible */
+ if (obj->type == UCL_TIME && t == UCL_FLOAT) {
+ return true;
+ }
+ else if (obj->type == UCL_INT && t == UCL_FLOAT) {
+ return true;
+ }
+ else {
+ ucl_schema_create_error (err, UCL_SCHEMA_TYPE_MISMATCH, obj,
+ "Invalid type of %s, expected %s",
+ ucl_object_type_to_string (obj->type),
+ ucl_object_type_to_string (t));
+ }
+ }
+ else {
+ /* Types are equal */
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/*
+ * Check if object is equal to one of elements of enum
+ */
+static bool
+ucl_schema_validate_enum (const ucl_object_t *en, const ucl_object_t *obj,
+ struct ucl_schema_error *err)
+{
+ ucl_object_iter_t iter = NULL;
+ const ucl_object_t *elt;
+ bool ret = false;
+
+ while ((elt = ucl_iterate_object (en, &iter, true)) != NULL) {
+ if (ucl_object_compare (elt, obj) == 0) {
+ ret = true;
+ break;
+ }
+ }
+
+ if (!ret) {
+ ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
+ "object is not one of enumerated patterns");
+ }
+
+ return ret;
+}
+
+
+/*
+ * Check a single ref component
+ */
+static const ucl_object_t *
+ucl_schema_resolve_ref_component (const ucl_object_t *cur,
+ const char *refc, int len,
+ struct ucl_schema_error *err)
+{
+ const ucl_object_t *res = NULL;
+ char *err_str;
+ int num, i;
+
+ if (cur->type == UCL_OBJECT) {
+ /* Find a key inside an object */
+ res = ucl_object_find_keyl (cur, refc, len);
+ if (res == NULL) {
+ ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, cur,
+ "reference %s is invalid, missing path component", refc);
+ return NULL;
+ }
+ }
+ else if (cur->type == UCL_ARRAY) {
+ /* We must figure out a number inside array */
+ num = strtoul (refc, &err_str, 10);
+ if (err_str != NULL && *err_str != '/' && *err_str != '\0') {
+ ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, cur,
+ "reference %s is invalid, invalid item number", refc);
+ return NULL;
+ }
+ res = cur->value.av;
+ i = 0;
+ while (res != NULL) {
+ if (i == num) {
+ break;
+ }
+ res = res->next;
+ }
+ if (res == NULL) {
+ ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, cur,
+ "reference %s is invalid, item number %d does not exist",
+ refc, num);
+ return NULL;
+ }
+ }
+ else {
+ ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, res,
+ "reference %s is invalid, contains primitive object in the path",
+ refc);
+ return NULL;
+ }
+
+ return res;
+}
+/*
+ * Find reference schema
+ */
+static const ucl_object_t *
+ucl_schema_resolve_ref (const ucl_object_t *root, const char *ref,
+ struct ucl_schema_error *err)
+{
+ const char *p, *c;
+ const ucl_object_t *res = NULL;
+
+
+ if (ref[0] != '#') {
+ ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, root,
+ "reference %s is invalid, not started with #", ref);
+ return NULL;
+ }
+ if (ref[1] == '/') {
+ p = &ref[2];
+ }
+ else if (ref[1] == '\0') {
+ return root;
+ }
+ else {
+ ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, root,
+ "reference %s is invalid, not started with #/", ref);
+ return NULL;
+ }
+
+ c = p;
+ res = root;
+
+ while (*p != '\0') {
+ if (*p == '/') {
+ if (p - c == 0) {
+ ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, res,
+ "reference %s is invalid, empty path component", ref);
+ return NULL;
+ }
+ /* Now we have some url part, so we need to figure out where we are */
+ res = ucl_schema_resolve_ref_component (res, c, p - c, err);
+ if (res == NULL) {
+ return NULL;
+ }
+ c = p + 1;
+ }
+ p ++;
+ }
+
+ if (p - c != 0) {
+ res = ucl_schema_resolve_ref_component (res, c, p - c, err);
+ }
+
+ if (res == NULL || res->type != UCL_OBJECT) {
+ ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, res,
+ "reference %s is invalid, cannot find specified object",
+ ref);
+ return NULL;
+ }
+
+ return res;
+}
+
+static bool
+ucl_schema_validate_values (const ucl_object_t *schema, const ucl_object_t *obj,
+ struct ucl_schema_error *err)
+{
+ const ucl_object_t *elt, *cur;
+ int64_t constraint, i;
+
+ elt = ucl_object_find_key (schema, "maxValues");
+ if (elt != NULL && elt->type == UCL_INT) {
+ constraint = ucl_object_toint (elt);
+ cur = obj;
+ i = 0;
+ while (cur) {
+ if (i > constraint) {
+ ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
+ "object has more values than defined: %ld",
+ (long int)constraint);
+ return false;
+ }
+ i ++;
+ cur = cur->next;
+ }
+ }
+ elt = ucl_object_find_key (schema, "minValues");
+ if (elt != NULL && elt->type == UCL_INT) {
+ constraint = ucl_object_toint (elt);
+ cur = obj;
+ i = 0;
+ while (cur) {
+ if (i >= constraint) {
+ break;
+ }
+ i ++;
+ cur = cur->next;
+ }
+ if (i < constraint) {
+ ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
+ "object has less values than defined: %ld",
+ (long int)constraint);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static bool
+ucl_schema_validate (const ucl_object_t *schema,
+ const ucl_object_t *obj, bool try_array,
+ struct ucl_schema_error *err,
+ const ucl_object_t *root)
+{
+ const ucl_object_t *elt, *cur;
+ ucl_object_iter_t iter = NULL;
+ bool ret;
+
+ if (schema->type != UCL_OBJECT) {
+ ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, schema,
+ "schema is %s instead of object", ucl_object_type_to_string (schema->type));
+ return false;
+ }
+
+ if (try_array) {
+ /*
+ * Special case for multiple values
+ */
+ if (!ucl_schema_validate_values (schema, obj, err)) {
+ return false;
+ }
+ LL_FOREACH (obj, cur) {
+ if (!ucl_schema_validate (schema, cur, false, err, root)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ elt = ucl_object_find_key (schema, "enum");
+ if (elt != NULL && elt->type == UCL_ARRAY) {
+ if (!ucl_schema_validate_enum (elt, obj, err)) {
+ return false;
+ }
+ }
+
+ elt = ucl_object_find_key (schema, "allOf");
+ if (elt != NULL && elt->type == UCL_ARRAY) {
+ iter = NULL;
+ while ((cur = ucl_iterate_object (elt, &iter, true)) != NULL) {
+ ret = ucl_schema_validate (cur, obj, true, err, root);
+ if (!ret) {
+ return false;
+ }
+ }
+ }
+
+ elt = ucl_object_find_key (schema, "anyOf");
+ if (elt != NULL && elt->type == UCL_ARRAY) {
+ iter = NULL;
+ while ((cur = ucl_iterate_object (elt, &iter, true)) != NULL) {
+ ret = ucl_schema_validate (cur, obj, true, err, root);
+ if (ret) {
+ break;
+ }
+ }
+ if (!ret) {
+ return false;
+ }
+ else {
+ /* Reset error */
+ err->code = UCL_SCHEMA_OK;
+ }
+ }
+
+ elt = ucl_object_find_key (schema, "oneOf");
+ if (elt != NULL && elt->type == UCL_ARRAY) {
+ iter = NULL;
+ ret = false;
+ while ((cur = ucl_iterate_object (elt, &iter, true)) != NULL) {
+ if (!ret) {
+ ret = ucl_schema_validate (cur, obj, true, err, root);
+ }
+ else if (ucl_schema_validate (cur, obj, true, err, root)) {
+ ret = false;
+ break;
+ }
+ }
+ if (!ret) {
+ return false;
+ }
+ }
+
+ elt = ucl_object_find_key (schema, "not");
+ if (elt != NULL && elt->type == UCL_OBJECT) {
+ if (ucl_schema_validate (elt, obj, true, err, root)) {
+ return false;
+ }
+ else {
+ /* Reset error */
+ err->code = UCL_SCHEMA_OK;
+ }
+ }
+
+ elt = ucl_object_find_key (schema, "$ref");
+ if (elt != NULL) {
+ cur = ucl_schema_resolve_ref (root, ucl_object_tostring (elt), err);
+ if (cur == NULL) {
+ return false;
+ }
+ if (!ucl_schema_validate (cur, obj, try_array, err, root)) {
+ return false;
+ }
+ }
+
+ elt = ucl_object_find_key (schema, "type");
+ if (!ucl_schema_type_is_allowed (elt, obj, err)) {
+ return false;
+ }
+
+ switch (obj->type) {
+ case UCL_OBJECT:
+ return ucl_schema_validate_object (schema, obj, err, root);
+ break;
+ case UCL_ARRAY:
+ return ucl_schema_validate_array (schema, obj, err, root);
+ break;
+ case UCL_INT:
+ case UCL_FLOAT:
+ return ucl_schema_validate_number (schema, obj, err);
+ break;
+ case UCL_STRING:
+ return ucl_schema_validate_string (schema, obj, err);
+ break;
+ default:
+ break;
+ }
+
+ return true;
+}
+
+bool
+ucl_object_validate (const ucl_object_t *schema,
+ const ucl_object_t *obj, struct ucl_schema_error *err)
+{
+ return ucl_schema_validate (schema, obj, true, err, schema);
+}
diff --git a/contrib/libucl/src/ucl_util.c b/contrib/libucl/src/ucl_util.c
index 34080d4..63f5e62 100644
--- a/contrib/libucl/src/ucl_util.c
+++ b/contrib/libucl/src/ucl_util.c
@@ -25,7 +25,9 @@
#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>
@@ -35,17 +37,36 @@
#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 *mmap(char *addr, size_t length, int prot, int access, int fd, off_t offset)
+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;
@@ -83,7 +104,7 @@ static void *mmap(char *addr, size_t length, int prot, int access, int fd, off_t
return (void *) ((char *) map + offset);
}
-static int munmap(void *map,size_t length)
+static int ucl_munmap(void *map,size_t length)
{
if (!UnmapViewOfFile(map)) {
return(-1);
@@ -91,7 +112,7 @@ static int munmap(void *map,size_t length)
return(0);
}
-static char* realpath(const char *path, char *resolved_path) {
+static char* ucl_realpath(const char *path, char *resolved_path) {
char *p;
char tmp[MAX_PATH + 1];
strncpy(tmp, path, sizeof(tmp)-1);
@@ -102,6 +123,10 @@ static char* realpath(const char *path, char *resolved_path) {
}
return _fullpath(resolved_path, tmp, MAX_PATH);
}
+#else
+#define ucl_mmap mmap
+#define ucl_munmap munmap
+#define ucl_realpath realpath
#endif
/**
@@ -109,35 +134,76 @@ static char* realpath(const char *path, char *resolved_path) {
* 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_free_internal (ucl_object_t *obj, bool allow_rec)
+ucl_object_dtor_free (ucl_object_t *obj)
{
- ucl_object_t *sub, *tmp;
+ 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);
+}
- while (obj != NULL) {
- 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]);
+/*
+ * 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;
- ucl_object_free_internal (sub, false);
+ 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 *)ucl_object_unref);
+ ucl_hash_destroy (obj->value.ov, (ucl_hash_free_func *)dtor);
}
}
tmp = obj->next;
- UCL_FREE (sizeof (ucl_object_t), obj);
+ dtor (obj);
obj = tmp;
if (!allow_rec) {
@@ -149,7 +215,7 @@ ucl_object_free_internal (ucl_object_t *obj, bool allow_rec)
void
ucl_object_free (ucl_object_t *obj)
{
- ucl_object_free_internal (obj, true);
+ ucl_object_free_internal (obj, true, ucl_object_dtor_free);
}
size_t
@@ -158,6 +224,9 @@ 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) {
@@ -188,45 +257,53 @@ ucl_unescape_json_string (char *str, size_t len)
case 'u':
/* Unicode escape */
uval = 0;
- for (i = 0; i < 4; i++) {
- uval <<= 4;
- if (isdigit (h[i])) {
- uval += h[i] - '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;
+ }
}
- else if (h[i] >= 'a' && h[i] <= 'f') {
- uval += h[i] - 'a' + 10;
+ h += 3;
+ len -= 3;
+ /* Encode */
+ if(uval < 0x80) {
+ t[0] = (char)uval;
+ t ++;
}
- else if (h[i] >= 'A' && h[i] <= 'F') {
- uval += h[i] - 'A' + 10;
+ 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++ = '?';
}
- }
- 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++ = '?';
+ *t++ = 'u';
}
break;
default:
@@ -246,41 +323,54 @@ ucl_unescape_json_string (char *str, size_t len)
return (t - str);
}
-UCL_EXTERN char *
-ucl_copy_key_trash (ucl_object_t *obj)
+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) {
- obj->trash_stack[UCL_TRASH_KEY] = malloc (obj->keylen + 1);
- if (obj->trash_stack[UCL_TRASH_KEY] != NULL) {
- memcpy (obj->trash_stack[UCL_TRASH_KEY], obj->key, obj->keylen);
- obj->trash_stack[UCL_TRASH_KEY][obj->keylen] = '\0';
+ 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';
}
- obj->key = obj->trash_stack[UCL_TRASH_KEY];
- obj->flags |= UCL_OBJECT_ALLOCATED_KEY;
+ deconst->key = obj->trash_stack[UCL_TRASH_KEY];
+ deconst->flags |= UCL_OBJECT_ALLOCATED_KEY;
}
return obj->trash_stack[UCL_TRASH_KEY];
}
-UCL_EXTERN char *
-ucl_copy_value_trash (ucl_object_t *obj)
+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 */
- obj->trash_stack[UCL_TRASH_VALUE] = malloc (obj->len + 1);
- if (obj->trash_stack[UCL_TRASH_VALUE] != NULL) {
- memcpy (obj->trash_stack[UCL_TRASH_VALUE], obj->value.sv, obj->len);
- obj->trash_stack[UCL_TRASH_VALUE][obj->len] = '\0';
- obj->value.sv = obj->trash_stack[UCL_TRASH_VALUE];
+ 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 */
- obj->trash_stack[UCL_TRASH_VALUE] = ucl_object_emit_single_json (obj);
- obj->len = strlen (obj->trash_stack[UCL_TRASH_VALUE]);
+ deconst->trash_stack[UCL_TRASH_VALUE] = ucl_object_emit_single_json (obj);
+ deconst->len = strlen (obj->trash_stack[UCL_TRASH_VALUE]);
}
- obj->flags |= UCL_OBJECT_ALLOCATED_VALUE;
+ deconst->flags |= UCL_OBJECT_ALLOCATED_VALUE;
}
return obj->trash_stack[UCL_TRASH_VALUE];
}
@@ -304,6 +394,10 @@ ucl_parser_free (struct ucl_parser *parser)
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);
}
@@ -338,6 +432,10 @@ ucl_parser_free (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;
@@ -360,6 +458,10 @@ ucl_pubkey_add (struct ucl_parser *parser, const unsigned char *key, size_t len)
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) {
@@ -527,7 +629,7 @@ ucl_fetch_file (const unsigned char *filename, unsigned char **buf, size_t *bufl
filename, strerror (errno));
return false;
}
- if ((*buf = mmap (NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0)) == MAP_FAILED) {
+ 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));
@@ -629,12 +731,12 @@ ucl_include_url (const unsigned char *data, size_t len,
urlbuf,
ERR_error_string (ERR_get_error (), NULL));
if (siglen > 0) {
- munmap (sigbuf, siglen);
+ ucl_munmap (sigbuf, siglen);
}
return false;
}
if (siglen > 0) {
- munmap (sigbuf, siglen);
+ ucl_munmap (sigbuf, siglen);
}
#endif
}
@@ -678,7 +780,7 @@ ucl_include_file (const unsigned char *data, size_t len,
int prev_state;
snprintf (filebuf, sizeof (filebuf), "%.*s", (int)len, data);
- if (realpath (filebuf, realbuf) == NULL) {
+ if (ucl_realpath (filebuf, realbuf) == NULL) {
if (!must_exist) {
return true;
}
@@ -706,12 +808,12 @@ ucl_include_file (const unsigned char *data, size_t len,
filebuf,
ERR_error_string (ERR_get_error (), NULL));
if (siglen > 0) {
- munmap (sigbuf, siglen);
+ ucl_munmap (sigbuf, siglen);
}
return false;
}
if (siglen > 0) {
- munmap (sigbuf, siglen);
+ ucl_munmap (sigbuf, siglen);
}
#endif
}
@@ -734,7 +836,7 @@ ucl_include_file (const unsigned char *data, size_t len,
parser->state = prev_state;
if (buflen > 0) {
- munmap (buf, buflen);
+ ucl_munmap (buf, buflen);
}
return res;
@@ -803,7 +905,7 @@ ucl_parser_set_filevars (struct ucl_parser *parser, const char *filename, bool n
if (filename != NULL) {
if (need_expand) {
- if (realpath (filename, realbuf) == NULL) {
+ if (ucl_realpath (filename, realbuf) == NULL) {
return false;
}
}
@@ -834,7 +936,7 @@ ucl_parser_add_file (struct ucl_parser *parser, const char *filename)
bool ret;
char realbuf[PATH_MAX];
- if (realpath (filename, realbuf) == NULL) {
+ if (ucl_realpath (filename, realbuf) == NULL) {
ucl_create_err (&parser->err, "cannot open file %s: %s",
filename,
strerror (errno));
@@ -849,7 +951,7 @@ ucl_parser_add_file (struct ucl_parser *parser, const char *filename)
ret = ucl_parser_add_chunk (parser, buf, len);
if (len > 0) {
- munmap (buf, len);
+ ucl_munmap (buf, len);
}
return ret;
@@ -1014,13 +1116,15 @@ ucl_object_fromstring_common (const char *str, size_t len, enum ucl_string_flags
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_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_BYTES,
+ flags & UCL_STRING_PARSE_TIME);
}
}
}
@@ -1028,21 +1132,22 @@ ucl_object_fromstring_common (const char *str, size_t len, enum ucl_string_flags
return obj;
}
-static ucl_object_t *
+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, *cur;
+ 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 NULL;
+ return false;
}
if (top == NULL) {
- top = ucl_object_new ();
- top->type = UCL_OBJECT;
+ return false;
}
if (top->type != UCL_OBJECT) {
@@ -1052,7 +1157,7 @@ ucl_object_insert_key_common (ucl_object_t *top, ucl_object_t *elt,
}
else {
/* Refuse converting of other object types */
- return top;
+ return false;
}
}
@@ -1078,11 +1183,15 @@ ucl_object_insert_key_common (ucl_object_t *top, ucl_object_t *elt,
ucl_copy_key_trash (elt);
}
- found = ucl_hash_search_obj (top->value.ov, 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) {
@@ -1095,19 +1204,22 @@ ucl_object_insert_key_common (ucl_object_t *top, ucl_object_t *elt,
else if (merge) {
if (found->type != UCL_OBJECT && elt->type == UCL_OBJECT) {
/* Insert old elt to new one */
- elt = ucl_object_insert_key_common (elt, found, found->key, found->keylen, copy_key, false, false);
+ 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 */
- found = ucl_object_insert_key_common (found, elt, elt->key, elt->keylen, copy_key, false, false);
+ 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) {
- ucl_object_ref (cur);
- found = ucl_object_insert_key_common (found, cur, cur->key, cur->keylen, copy_key, false, false);
+ tmp = ucl_object_ref (cur);
+ ucl_object_insert_key_common (found, tmp, cur->key,
+ cur->keylen, copy_key, false, false);
}
ucl_object_unref (elt);
}
@@ -1121,20 +1233,25 @@ ucl_object_insert_key_common (ucl_object_t *top, ucl_object_t *elt,
}
}
- return top;
+ return ret;
}
bool
-ucl_object_delete_keyl(ucl_object_t *top, const char *key, size_t keylen)
+ucl_object_delete_keyl (ucl_object_t *top, const char *key, size_t keylen)
{
ucl_object_t *found;
- found = ucl_object_find_keyl(top, key, keylen);
+ if (top == NULL || key == NULL) {
+ return false;
+ }
+
+ found = __DECONST (ucl_object_t *, ucl_object_find_keyl (top, key, keylen));
- if (found == NULL)
+ if (found == NULL) {
return false;
+ }
- ucl_hash_delete(top->value.ov, found);
+ ucl_hash_delete (top->value.ov, found);
ucl_object_unref (found);
top->len --;
@@ -1142,36 +1259,62 @@ ucl_object_delete_keyl(ucl_object_t *top, const char *key, size_t keylen)
}
bool
-ucl_object_delete_key(ucl_object_t *top, const char *key)
+ucl_object_delete_key (ucl_object_t *top, const char *key)
{
- return ucl_object_delete_keyl(top, key, 0);
+ return ucl_object_delete_keyl (top, key, strlen(key));
}
-ucl_object_t *
+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);
}
-ucl_object_t *
+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);
}
-ucl_object_t *
+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);
}
-ucl_object_t *
-ucl_object_find_keyl (ucl_object_t *obj, const char *key, size_t klen)
+const ucl_object_t *
+ucl_object_find_keyl (const ucl_object_t *obj, const char *key, size_t klen)
{
- ucl_object_t *ret, srch;
+ const ucl_object_t *ret;
+ ucl_object_t srch;
if (obj == NULL || obj->type != UCL_OBJECT || key == NULL) {
return NULL;
@@ -1184,33 +1327,28 @@ ucl_object_find_keyl (ucl_object_t *obj, const char *key, size_t klen)
return ret;
}
-ucl_object_t *
-ucl_object_find_key (ucl_object_t *obj, const char *key)
+const ucl_object_t *
+ucl_object_find_key (const ucl_object_t *obj, const char *key)
{
- size_t klen;
- ucl_object_t *ret, srch;
-
- if (obj == NULL || obj->type != UCL_OBJECT || key == NULL) {
+ if (key == NULL)
return NULL;
- }
-
- klen = strlen (key);
- srch.key = key;
- srch.keylen = klen;
- ret = ucl_hash_search_obj (obj->value.ov, &srch);
- return ret;
+ return ucl_object_find_keyl (obj, key, strlen(key));
}
-ucl_object_t*
-ucl_iterate_object (ucl_object_t *obj, ucl_object_iter_t *iter, bool expand_values)
+const ucl_object_t*
+ucl_iterate_object (const ucl_object_t *obj, ucl_object_iter_t *iter, bool expand_values)
{
- ucl_object_t *elt;
+ const ucl_object_t *elt;
+
+ if (obj == NULL || iter == NULL) {
+ return NULL;
+ }
if (expand_values) {
switch (obj->type) {
case UCL_OBJECT:
- return (ucl_object_t*)ucl_hash_iterate (obj->value.ov, iter);
+ return (const ucl_object_t*)ucl_hash_iterate (obj->value.ov, iter);
break;
case UCL_ARRAY:
elt = *iter;
@@ -1241,9 +1379,580 @@ ucl_iterate_object (ucl_object_t *obj, ucl_object_iter_t *iter, bool expand_valu
else if (elt == obj) {
return NULL;
}
- *iter = elt->next ? elt->next : obj;
+ *iter = __DECONST (void *, elt->next ? elt->next : obj);
return elt;
/* Not reached */
return NULL;
}
+
+const ucl_object_t *
+ucl_lookup_path (const ucl_object_t *top, const char *path_in) {
+ const ucl_object_t *o = NULL, *found;
+ const char *p, *c;
+ char *err_str;
+ unsigned index;
+
+ if (path_in == NULL || top == NULL) {
+ return NULL;
+ }
+
+ found = NULL;
+ p = path_in;
+
+ /* Skip leading dots */
+ while (*p == '.') {
+ p ++;
+ }
+
+ c = p;
+ while (*p != '\0') {
+ p ++;
+ if (*p == '.' || *p == '\0') {
+ if (p > c) {
+ switch (top->type) {
+ case UCL_ARRAY:
+ /* Key should be an int */
+ index = strtoul (c, &err_str, 10);
+ if (err_str != NULL && (*err_str != '.' && *err_str != '\0')) {
+ return NULL;
+ }
+ o = ucl_array_find_index (top, index);
+ break;
+ default:
+ o = ucl_object_find_keyl (top, c, p - c);
+ break;
+ }
+ if (o == NULL) {
+ return NULL;
+ }
+ top = o;
+ }
+ if (*p != '\0') {
+ c = p + 1;
+ }
+ }
+ }
+ found = o;
+
+ return found;
+}
+
+
+ucl_object_t *
+ucl_object_new (void)
+{
+ ucl_object_t *new;
+ new = malloc (sizeof (ucl_object_t));
+ if (new != NULL) {
+ memset (new, 0, sizeof (ucl_object_t));
+ new->ref = 1;
+ new->type = UCL_NULL;
+ }
+ return new;
+}
+
+ucl_object_t *
+ucl_object_typed_new (ucl_type_t type)
+{
+ ucl_object_t *new;
+ new = malloc (sizeof (ucl_object_t));
+ if (new != NULL) {
+ memset (new, 0, sizeof (ucl_object_t));
+ new->ref = 1;
+ new->type = (type <= UCL_NULL ? type : UCL_NULL);
+ }
+ return new;
+}
+
+ucl_type_t
+ucl_object_type (const ucl_object_t *obj)
+{
+ return obj->type;
+}
+
+ucl_object_t*
+ucl_object_fromstring (const char *str)
+{
+ return ucl_object_fromstring_common (str, 0, UCL_STRING_ESCAPE);
+}
+
+ucl_object_t *
+ucl_object_fromlstring (const char *str, size_t len)
+{
+ return ucl_object_fromstring_common (str, len, UCL_STRING_ESCAPE);
+}
+
+ucl_object_t *
+ucl_object_fromint (int64_t iv)
+{
+ ucl_object_t *obj;
+
+ obj = ucl_object_new ();
+ if (obj != NULL) {
+ obj->type = UCL_INT;
+ obj->value.iv = iv;
+ }
+
+ return obj;
+}
+
+ucl_object_t *
+ucl_object_fromdouble (double dv)
+{
+ ucl_object_t *obj;
+
+ obj = ucl_object_new ();
+ if (obj != NULL) {
+ obj->type = UCL_FLOAT;
+ obj->value.dv = dv;
+ }
+
+ return obj;
+}
+
+ucl_object_t*
+ucl_object_frombool (bool bv)
+{
+ ucl_object_t *obj;
+
+ obj = ucl_object_new ();
+ if (obj != NULL) {
+ obj->type = UCL_BOOLEAN;
+ obj->value.iv = bv;
+ }
+
+ return obj;
+}
+
+bool
+ucl_array_append (ucl_object_t *top, ucl_object_t *elt)
+{
+ ucl_object_t *head;
+
+ if (elt == NULL || top == NULL) {
+ return false;
+ }
+
+ head = top->value.av;
+ if (head == NULL) {
+ top->value.av = elt;
+ elt->prev = elt;
+ }
+ else {
+ elt->prev = head->prev;
+ head->prev->next = elt;
+ head->prev = elt;
+ }
+ elt->next = NULL;
+ top->len ++;
+
+ return true;
+}
+
+bool
+ucl_array_prepend (ucl_object_t *top, ucl_object_t *elt)
+{
+ ucl_object_t *head;
+
+ if (elt == NULL || top == NULL) {
+ return false;
+ }
+
+
+ head = top->value.av;
+ if (head == NULL) {
+ top->value.av = elt;
+ elt->prev = elt;
+ }
+ else {
+ elt->prev = head->prev;
+ head->prev = elt;
+ }
+ elt->next = head;
+ top->value.av = elt;
+ top->len ++;
+
+ return true;
+}
+
+ucl_object_t *
+ucl_array_delete (ucl_object_t *top, ucl_object_t *elt)
+{
+ ucl_object_t *head;
+
+ if (top == NULL || top->type != UCL_ARRAY || top->value.av == NULL) {
+ return NULL;
+ }
+ head = top->value.av;
+
+ if (elt->prev == elt) {
+ top->value.av = NULL;
+ }
+ else if (elt == head) {
+ elt->next->prev = elt->prev;
+ top->value.av = elt->next;
+ }
+ else {
+ elt->prev->next = elt->next;
+ if (elt->next) {
+ elt->next->prev = elt->prev;
+ }
+ else {
+ head->prev = elt->prev;
+ }
+ }
+ elt->next = NULL;
+ elt->prev = elt;
+ top->len --;
+
+ return elt;
+}
+
+const ucl_object_t *
+ucl_array_head (const ucl_object_t *top)
+{
+ if (top == NULL || top->type != UCL_ARRAY || top->value.av == NULL) {
+ return NULL;
+ }
+ return top->value.av;
+}
+
+const ucl_object_t *
+ucl_array_tail (const ucl_object_t *top)
+{
+ if (top == NULL || top->type != UCL_ARRAY || top->value.av == NULL) {
+ return NULL;
+ }
+ return top->value.av->prev;
+}
+
+ucl_object_t *
+ucl_array_pop_last (ucl_object_t *top)
+{
+ return ucl_array_delete (top, __DECONST(ucl_object_t *, ucl_array_tail (top)));
+}
+
+ucl_object_t *
+ucl_array_pop_first (ucl_object_t *top)
+{
+ return ucl_array_delete (top, __DECONST(ucl_object_t *, ucl_array_head (top)));
+}
+
+const ucl_object_t *
+ucl_array_find_index (const ucl_object_t *top, unsigned int index)
+{
+ ucl_object_iter_t it = NULL;
+ const ucl_object_t *ret;
+
+ if (top == NULL || top->type != UCL_ARRAY || top->len == 0 ||
+ (index + 1) > top->len) {
+ return NULL;
+ }
+
+ while ((ret = ucl_iterate_object (top, &it, true)) != NULL) {
+ if (index == 0) {
+ return ret;
+ }
+ --index;
+ }
+
+ return NULL;
+}
+
+ucl_object_t *
+ucl_elt_append (ucl_object_t *head, ucl_object_t *elt)
+{
+
+ if (head == NULL) {
+ elt->next = NULL;
+ elt->prev = elt;
+ head = elt;
+ }
+ else {
+ elt->prev = head->prev;
+ head->prev->next = elt;
+ head->prev = elt;
+ elt->next = NULL;
+ }
+
+ return head;
+}
+
+bool
+ucl_object_todouble_safe (const ucl_object_t *obj, double *target)
+{
+ if (obj == NULL || target == NULL) {
+ return false;
+ }
+ switch (obj->type) {
+ case UCL_INT:
+ *target = obj->value.iv; /* Probaly could cause overflow */
+ break;
+ case UCL_FLOAT:
+ case UCL_TIME:
+ *target = obj->value.dv;
+ break;
+ default:
+ return false;
+ }
+
+ return true;
+}
+
+double
+ucl_object_todouble (const ucl_object_t *obj)
+{
+ double result = 0.;
+
+ ucl_object_todouble_safe (obj, &result);
+ return result;
+}
+
+bool
+ucl_object_toint_safe (const ucl_object_t *obj, int64_t *target)
+{
+ if (obj == NULL || target == NULL) {
+ return false;
+ }
+ switch (obj->type) {
+ case UCL_INT:
+ *target = obj->value.iv;
+ break;
+ case UCL_FLOAT:
+ case UCL_TIME:
+ *target = obj->value.dv; /* Loosing of decimal points */
+ break;
+ default:
+ return false;
+ }
+
+ return true;
+}
+
+int64_t
+ucl_object_toint (const ucl_object_t *obj)
+{
+ int64_t result = 0;
+
+ ucl_object_toint_safe (obj, &result);
+ return result;
+}
+
+bool
+ucl_object_toboolean_safe (const ucl_object_t *obj, bool *target)
+{
+ if (obj == NULL || target == NULL) {
+ return false;
+ }
+ switch (obj->type) {
+ case UCL_BOOLEAN:
+ *target = (obj->value.iv == true);
+ break;
+ default:
+ return false;
+ }
+
+ return true;
+}
+
+bool
+ucl_object_toboolean (const ucl_object_t *obj)
+{
+ bool result = false;
+
+ ucl_object_toboolean_safe (obj, &result);
+ return result;
+}
+
+bool
+ucl_object_tostring_safe (const ucl_object_t *obj, const char **target)
+{
+ if (obj == NULL || target == NULL) {
+ return false;
+ }
+
+ switch (obj->type) {
+ case UCL_STRING:
+ *target = ucl_copy_value_trash (obj);
+ break;
+ default:
+ return false;
+ }
+
+ return true;
+}
+
+const char *
+ucl_object_tostring (const ucl_object_t *obj)
+{
+ const char *result = NULL;
+
+ ucl_object_tostring_safe (obj, &result);
+ return result;
+}
+
+const char *
+ucl_object_tostring_forced (const ucl_object_t *obj)
+{
+ return ucl_copy_value_trash (obj);
+}
+
+bool
+ucl_object_tolstring_safe (const ucl_object_t *obj, const char **target, size_t *tlen)
+{
+ if (obj == NULL || target == NULL) {
+ return false;
+ }
+ switch (obj->type) {
+ case UCL_STRING:
+ *target = obj->value.sv;
+ if (tlen != NULL) {
+ *tlen = obj->len;
+ }
+ break;
+ default:
+ return false;
+ }
+
+ return true;
+}
+
+const char *
+ucl_object_tolstring (const ucl_object_t *obj, size_t *tlen)
+{
+ const char *result = NULL;
+
+ ucl_object_tolstring_safe (obj, &result, tlen);
+ return result;
+}
+
+const char *
+ucl_object_key (const ucl_object_t *obj)
+{
+ return ucl_copy_key_trash (obj);
+}
+
+const char *
+ucl_object_keyl (const ucl_object_t *obj, size_t *len)
+{
+ if (len == NULL || obj == NULL) {
+ return NULL;
+ }
+ *len = obj->keylen;
+ return obj->key;
+}
+
+ucl_object_t *
+ucl_object_ref (const ucl_object_t *obj)
+{
+ ucl_object_t *res = NULL;
+
+ if (obj != NULL) {
+ res = __DECONST (ucl_object_t *, obj);
+#ifdef HAVE_ATOMIC_BUILTINS
+ (void)__sync_add_and_fetch (&res->ref, 1);
+#else
+ res->ref ++;
+#endif
+ }
+ return res;
+}
+
+void
+ucl_object_unref (ucl_object_t *obj)
+{
+ if (obj != NULL) {
+#ifdef HAVE_ATOMIC_BUILTINS
+ unsigned int rc = __sync_sub_and_fetch (&obj->ref, 1);
+ if (rc == 0) {
+#else
+ if (--obj->ref == 0) {
+#endif
+ ucl_object_free_internal (obj, true, ucl_object_dtor_unref);
+ }
+ }
+}
+
+int
+ucl_object_compare (const ucl_object_t *o1, const ucl_object_t *o2)
+{
+ const ucl_object_t *it1, *it2;
+ ucl_object_iter_t iter = NULL;
+ int ret = 0;
+
+ if (o1->type != o2->type) {
+ return (o1->type) - (o2->type);
+ }
+
+ switch (o1->type) {
+ case UCL_STRING:
+ if (o1->len == o2->len) {
+ ret = strcmp (ucl_object_tostring(o1), ucl_object_tostring(o2));
+ }
+ else {
+ ret = o1->len - o2->len;
+ }
+ break;
+ case UCL_FLOAT:
+ case UCL_INT:
+ case UCL_TIME:
+ ret = ucl_object_todouble (o1) - ucl_object_todouble (o2);
+ break;
+ case UCL_BOOLEAN:
+ ret = ucl_object_toboolean (o1) - ucl_object_toboolean (o2);
+ break;
+ case UCL_ARRAY:
+ if (o1->len == o2->len) {
+ it1 = o1->value.av;
+ it2 = o2->value.av;
+ /* Compare all elements in both arrays */
+ while (it1 != NULL && it2 != NULL) {
+ ret = ucl_object_compare (it1, it2);
+ if (ret != 0) {
+ break;
+ }
+ it1 = it1->next;
+ it2 = it2->next;
+ }
+ }
+ else {
+ ret = o1->len - o2->len;
+ }
+ break;
+ case UCL_OBJECT:
+ if (o1->len == o2->len) {
+ while ((it1 = ucl_iterate_object (o1, &iter, true)) != NULL) {
+ it2 = ucl_object_find_key (o2, ucl_object_key (it1));
+ if (it2 == NULL) {
+ ret = 1;
+ break;
+ }
+ ret = ucl_object_compare (it1, it2);
+ if (ret != 0) {
+ break;
+ }
+ }
+ }
+ else {
+ ret = o1->len - o2->len;
+ }
+ break;
+ default:
+ ret = 0;
+ break;
+ }
+
+ return ret;
+}
+
+void
+ucl_object_array_sort (ucl_object_t *ar,
+ int (*cmp)(const ucl_object_t *o1, const ucl_object_t *o2))
+{
+ if (cmp == NULL || ar == NULL || ar->type != UCL_ARRAY) {
+ return;
+ }
+
+ DL_SORT (ar->value.av, cmp);
+}
diff --git a/contrib/libucl/stamp-h.in b/contrib/libucl/stamp-h.in
new file mode 100644
index 0000000..9788f70
--- /dev/null
+++ b/contrib/libucl/stamp-h.in
@@ -0,0 +1 @@
+timestamp
diff --git a/contrib/libucl/tests/.gitignore b/contrib/libucl/tests/.gitignore
new file mode 100644
index 0000000..5a48681
--- /dev/null
+++ b/contrib/libucl/tests/.gitignore
@@ -0,0 +1,8 @@
+*.log
+*.trs
+*.plist
+
+test_basic
+test_generate
+test_schema
+test_speed
diff --git a/contrib/libucl/tests/10.in b/contrib/libucl/tests/10.in
deleted file mode 100644
index 560150c..0000000
--- a/contrib/libucl/tests/10.in
+++ /dev/null
@@ -1 +0,0 @@
-a []
diff --git a/contrib/libucl/tests/5.in b/contrib/libucl/tests/5.in
deleted file mode 100644
index 83c831f..0000000
--- a/contrib/libucl/tests/5.in
+++ /dev/null
@@ -1 +0,0 @@
-# test
diff --git a/contrib/libucl/tests/5.res b/contrib/libucl/tests/5.res
deleted file mode 100644
index 8b13789..0000000
--- a/contrib/libucl/tests/5.res
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/contrib/libucl/tests/7.in b/contrib/libucl/tests/7.in
deleted file mode 100644
index e69de29..0000000
--- a/contrib/libucl/tests/7.in
+++ /dev/null
diff --git a/contrib/libucl/tests/7.res b/contrib/libucl/tests/7.res
deleted file mode 100644
index 8b13789..0000000
--- a/contrib/libucl/tests/7.res
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/contrib/libucl/tests/Makefile.am b/contrib/libucl/tests/Makefile.am
new file mode 100644
index 0000000..880036d
--- /dev/null
+++ b/contrib/libucl/tests/Makefile.am
@@ -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 \ No newline at end of file
diff --git a/contrib/libucl/tests/basic.test b/contrib/libucl/tests/basic.test
new file mode 100755
index 0000000..a4a07eb
--- /dev/null
+++ b/contrib/libucl/tests/basic.test
@@ -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
+
+
diff --git a/contrib/libucl/tests/1.in b/contrib/libucl/tests/basic/1.in
index 9c14df6..9c14df6 100644
--- a/contrib/libucl/tests/1.in
+++ b/contrib/libucl/tests/basic/1.in
diff --git a/contrib/libucl/tests/1.res b/contrib/libucl/tests/basic/1.res
index 660af9f..660af9f 100644
--- a/contrib/libucl/tests/1.res
+++ b/contrib/libucl/tests/basic/1.res
diff --git a/contrib/libucl/tests/basic/10.in b/contrib/libucl/tests/basic/10.in
new file mode 100644
index 0000000..e0910c1
--- /dev/null
+++ b/contrib/libucl/tests/basic/10.in
@@ -0,0 +1,9 @@
+section foo bar {
+ key = value
+}
+section foo baz {
+ key = value
+}
+section foo {
+ bar = lol /* removing this line makes parsing successful */
+}
diff --git a/contrib/libucl/tests/2.in b/contrib/libucl/tests/basic/2.in
index a4419a4..a4419a4 100644
--- a/contrib/libucl/tests/2.in
+++ b/contrib/libucl/tests/basic/2.in
diff --git a/contrib/libucl/tests/2.res b/contrib/libucl/tests/basic/2.res
index 72e9570..72e9570 100644
--- a/contrib/libucl/tests/2.res
+++ b/contrib/libucl/tests/basic/2.res
diff --git a/contrib/libucl/tests/3.in b/contrib/libucl/tests/basic/3.in
index b3e3696..b3e3696 100644
--- a/contrib/libucl/tests/3.in
+++ b/contrib/libucl/tests/basic/3.in
diff --git a/contrib/libucl/tests/3.res b/contrib/libucl/tests/basic/3.res
index bb18457..bb18457 100644
--- a/contrib/libucl/tests/3.res
+++ b/contrib/libucl/tests/basic/3.res
diff --git a/contrib/libucl/tests/4.in b/contrib/libucl/tests/basic/4.in
index 2b296ef..2b296ef 100644
--- a/contrib/libucl/tests/4.in
+++ b/contrib/libucl/tests/basic/4.in
diff --git a/contrib/libucl/tests/4.res b/contrib/libucl/tests/basic/4.res
index 58c3599..58c3599 100644
--- a/contrib/libucl/tests/4.res
+++ b/contrib/libucl/tests/basic/4.res
diff --git a/contrib/libucl/tests/6.in b/contrib/libucl/tests/basic/6.in
index 5b46088..5b46088 100644
--- a/contrib/libucl/tests/6.in
+++ b/contrib/libucl/tests/basic/6.in
diff --git a/contrib/libucl/tests/6.res b/contrib/libucl/tests/basic/6.res
index 4b17c4b..4b17c4b 100644
--- a/contrib/libucl/tests/6.res
+++ b/contrib/libucl/tests/basic/6.res
diff --git a/contrib/libucl/tests/8.in b/contrib/libucl/tests/basic/8.in
index 605c0c9..605c0c9 100644
--- a/contrib/libucl/tests/8.in
+++ b/contrib/libucl/tests/basic/8.in
diff --git a/contrib/libucl/tests/8.res b/contrib/libucl/tests/basic/8.res
index 54c904e..54c904e 100644
--- a/contrib/libucl/tests/8.res
+++ b/contrib/libucl/tests/basic/8.res
diff --git a/contrib/libucl/tests/9-comment.inc b/contrib/libucl/tests/basic/9-comment.inc
index 8939db1..8939db1 100644
--- a/contrib/libucl/tests/9-comment.inc
+++ b/contrib/libucl/tests/basic/9-comment.inc
diff --git a/contrib/libucl/tests/9-empty.inc b/contrib/libucl/tests/basic/9-empty.inc
index e69de29..e69de29 100644
--- a/contrib/libucl/tests/9-empty.inc
+++ b/contrib/libucl/tests/basic/9-empty.inc
diff --git a/contrib/libucl/tests/9.in b/contrib/libucl/tests/basic/9.in
index da04e40..4c06e39 100644
--- a/contrib/libucl/tests/9.in
+++ b/contrib/libucl/tests/basic/9.in
@@ -1,5 +1,4 @@
.include "$CURDIR/9.inc"
-.include "$CURDIR/9-empty.inc"
.include "$CURDIR/9-comment.inc"
#.include "$CURDIR/9.inc"
.include "$CURDIR/9.inc"
diff --git a/contrib/libucl/tests/9.inc b/contrib/libucl/tests/basic/9.inc
index c5592b8..c5592b8 100644
--- a/contrib/libucl/tests/9.inc
+++ b/contrib/libucl/tests/basic/9.inc
diff --git a/contrib/libucl/tests/9.res b/contrib/libucl/tests/basic/9.res
index ec3f014..ec3f014 100644
--- a/contrib/libucl/tests/9.res
+++ b/contrib/libucl/tests/basic/9.res
diff --git a/contrib/libucl/tests/generate.test b/contrib/libucl/tests/generate.test
new file mode 100755
index 0000000..ed237a3
--- /dev/null
+++ b/contrib/libucl/tests/generate.test
@@ -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
+
diff --git a/contrib/libucl/tests/run_tests.sh b/contrib/libucl/tests/run_tests.sh
index 6c7751e..bc26160 100755
--- a/contrib/libucl/tests/run_tests.sh
+++ b/contrib/libucl/tests/run_tests.sh
@@ -37,6 +37,15 @@ if [ $# -gt 2 ] ; then
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'
diff --git a/contrib/libucl/tests/schema.test b/contrib/libucl/tests/schema.test
new file mode 100755
index 0000000..0f2c252
--- /dev/null
+++ b/contrib/libucl/tests/schema.test
@@ -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
diff --git a/contrib/libucl/tests/schema/additionalItems.json b/contrib/libucl/tests/schema/additionalItems.json
new file mode 100644
index 0000000..521745c
--- /dev/null
+++ b/contrib/libucl/tests/schema/additionalItems.json
@@ -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
+ }
+ ]
+ }
+]
diff --git a/contrib/libucl/tests/schema/additionalProperties.json b/contrib/libucl/tests/schema/additionalProperties.json
new file mode 100644
index 0000000..eb334c9
--- /dev/null
+++ b/contrib/libucl/tests/schema/additionalProperties.json
@@ -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
+ }
+ ]
+ }
+]
diff --git a/contrib/libucl/tests/schema/allOf.json b/contrib/libucl/tests/schema/allOf.json
new file mode 100644
index 0000000..bbb5f89
--- /dev/null
+++ b/contrib/libucl/tests/schema/allOf.json
@@ -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
+ }
+ ]
+ }
+]
diff --git a/contrib/libucl/tests/schema/anyOf.json b/contrib/libucl/tests/schema/anyOf.json
new file mode 100644
index 0000000..a58714a
--- /dev/null
+++ b/contrib/libucl/tests/schema/anyOf.json
@@ -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
+ }
+ ]
+ }
+]
diff --git a/contrib/libucl/tests/schema/definitions.json b/contrib/libucl/tests/schema/definitions.json
new file mode 100644
index 0000000..cf935a3
--- /dev/null
+++ b/contrib/libucl/tests/schema/definitions.json
@@ -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
+ }
+ ]
+ }
+]
diff --git a/contrib/libucl/tests/schema/dependencies.json b/contrib/libucl/tests/schema/dependencies.json
new file mode 100644
index 0000000..7b9b16a
--- /dev/null
+++ b/contrib/libucl/tests/schema/dependencies.json
@@ -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
+ }
+ ]
+ }
+]
diff --git a/contrib/libucl/tests/schema/enum.json b/contrib/libucl/tests/schema/enum.json
new file mode 100644
index 0000000..f124436
--- /dev/null
+++ b/contrib/libucl/tests/schema/enum.json
@@ -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
+ }
+ ]
+ }
+]
diff --git a/contrib/libucl/tests/schema/items.json b/contrib/libucl/tests/schema/items.json
new file mode 100644
index 0000000..f5e18a1
--- /dev/null
+++ b/contrib/libucl/tests/schema/items.json
@@ -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
+ }
+ ]
+ }
+]
diff --git a/contrib/libucl/tests/schema/maxItems.json b/contrib/libucl/tests/schema/maxItems.json
new file mode 100644
index 0000000..3b53a6b
--- /dev/null
+++ b/contrib/libucl/tests/schema/maxItems.json
@@ -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
+ }
+ ]
+ }
+]
diff --git a/contrib/libucl/tests/schema/maxLength.json b/contrib/libucl/tests/schema/maxLength.json
new file mode 100644
index 0000000..561767b
--- /dev/null
+++ b/contrib/libucl/tests/schema/maxLength.json
@@ -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
+ }
+ ]
+ }
+]
diff --git a/contrib/libucl/tests/schema/maxProperties.json b/contrib/libucl/tests/schema/maxProperties.json
new file mode 100644
index 0000000..d282446
--- /dev/null
+++ b/contrib/libucl/tests/schema/maxProperties.json
@@ -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
+ }
+ ]
+ }
+]
diff --git a/contrib/libucl/tests/schema/maximum.json b/contrib/libucl/tests/schema/maximum.json
new file mode 100644
index 0000000..86c7b89
--- /dev/null
+++ b/contrib/libucl/tests/schema/maximum.json
@@ -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
+ }
+ ]
+ }
+]
diff --git a/contrib/libucl/tests/schema/minItems.json b/contrib/libucl/tests/schema/minItems.json
new file mode 100644
index 0000000..ed51188
--- /dev/null
+++ b/contrib/libucl/tests/schema/minItems.json
@@ -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
+ }
+ ]
+ }
+]
diff --git a/contrib/libucl/tests/schema/minLength.json b/contrib/libucl/tests/schema/minLength.json
new file mode 100644
index 0000000..e9c14b1
--- /dev/null
+++ b/contrib/libucl/tests/schema/minLength.json
@@ -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
+ }
+ ]
+ }
+]
diff --git a/contrib/libucl/tests/schema/minProperties.json b/contrib/libucl/tests/schema/minProperties.json
new file mode 100644
index 0000000..a72c7d2
--- /dev/null
+++ b/contrib/libucl/tests/schema/minProperties.json
@@ -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
+ }
+ ]
+ }
+]
diff --git a/contrib/libucl/tests/schema/minimum.json b/contrib/libucl/tests/schema/minimum.json
new file mode 100644
index 0000000..d5bf000
--- /dev/null
+++ b/contrib/libucl/tests/schema/minimum.json
@@ -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
+ }
+ ]
+ }
+]
diff --git a/contrib/libucl/tests/schema/multipleOf.json b/contrib/libucl/tests/schema/multipleOf.json
new file mode 100644
index 0000000..ca3b761
--- /dev/null
+++ b/contrib/libucl/tests/schema/multipleOf.json
@@ -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
+ }
+ ]
+ }
+]
diff --git a/contrib/libucl/tests/schema/not.json b/contrib/libucl/tests/schema/not.json
new file mode 100644
index 0000000..cbb7f46
--- /dev/null
+++ b/contrib/libucl/tests/schema/not.json
@@ -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
+ }
+ ]
+ }
+
+]
diff --git a/contrib/libucl/tests/schema/oneOf.json b/contrib/libucl/tests/schema/oneOf.json
new file mode 100644
index 0000000..1eaa4e4
--- /dev/null
+++ b/contrib/libucl/tests/schema/oneOf.json
@@ -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
+ }
+ ]
+ }
+]
diff --git a/contrib/libucl/tests/schema/pattern.json b/contrib/libucl/tests/schema/pattern.json
new file mode 100644
index 0000000..befc4b5
--- /dev/null
+++ b/contrib/libucl/tests/schema/pattern.json
@@ -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
+ }
+ ]
+ }
+]
diff --git a/contrib/libucl/tests/schema/patternProperties.json b/contrib/libucl/tests/schema/patternProperties.json
new file mode 100644
index 0000000..18586e5
--- /dev/null
+++ b/contrib/libucl/tests/schema/patternProperties.json
@@ -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
+ }
+ ]
+ }
+]
diff --git a/contrib/libucl/tests/schema/properties.json b/contrib/libucl/tests/schema/properties.json
new file mode 100644
index 0000000..cd1644d
--- /dev/null
+++ b/contrib/libucl/tests/schema/properties.json
@@ -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
+ }
+ ]
+ }
+]
diff --git a/contrib/libucl/tests/schema/ref.json b/contrib/libucl/tests/schema/ref.json
new file mode 100644
index 0000000..1b88b27
--- /dev/null
+++ b/contrib/libucl/tests/schema/ref.json
@@ -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
+ }
+ ]
+ }
+*/
+]
diff --git a/contrib/libucl/tests/schema/refRemote.json b/contrib/libucl/tests/schema/refRemote.json
new file mode 100644
index 0000000..4ca8047
--- /dev/null
+++ b/contrib/libucl/tests/schema/refRemote.json
@@ -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
+ }
+ ]
+ }
+]
diff --git a/contrib/libucl/tests/schema/required.json b/contrib/libucl/tests/schema/required.json
new file mode 100644
index 0000000..612f73f
--- /dev/null
+++ b/contrib/libucl/tests/schema/required.json
@@ -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
+ }
+ ]
+ }
+]
diff --git a/contrib/libucl/tests/schema/type.json b/contrib/libucl/tests/schema/type.json
new file mode 100644
index 0000000..257f051
--- /dev/null
+++ b/contrib/libucl/tests/schema/type.json
@@ -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
+ }
+ ]
+ }
+]
diff --git a/contrib/libucl/tests/schema/uniqueItems.json b/contrib/libucl/tests/schema/uniqueItems.json
new file mode 100644
index 0000000..c1f4ab9
--- /dev/null
+++ b/contrib/libucl/tests/schema/uniqueItems.json
@@ -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
+ }
+ ]
+ }
+]
diff --git a/contrib/libucl/tests/speed.test b/contrib/libucl/tests/speed.test
new file mode 100755
index 0000000..34db653
--- /dev/null
+++ b/contrib/libucl/tests/speed.test
@@ -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
+
diff --git a/contrib/libucl/tests/test_basic.c b/contrib/libucl/tests/test_basic.c
index 641679f..5a977aa 100644
--- a/contrib/libucl/tests/test_basic.c
+++ b/contrib/libucl/tests/test_basic.c
@@ -21,10 +21,8 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#include <stdio.h>
-#include <errno.h>
-#include <unistd.h>
#include "ucl.h"
+#include "ucl_internal.h"
int
main (int argc, char **argv)
@@ -80,7 +78,9 @@ main (int argc, char **argv)
while (!feof (in)) {
memset (inbuf, 0, sizeof (inbuf));
- (void)fread (inbuf, sizeof (inbuf) - 1, 1, in);
+ if (fread (inbuf, 1, sizeof (inbuf) - 1, in) == 0) {
+ break;
+ }
inlen = strlen (inbuf);
test_in = malloc (inlen);
memcpy (test_in, inbuf, inlen);
@@ -97,7 +97,7 @@ main (int argc, char **argv)
else {
out = stdout;
}
- if (ucl_parser_get_error(parser) != NULL) {
+ if (ucl_parser_get_error (parser) != NULL) {
fprintf (out, "Error occurred: %s\n", ucl_parser_get_error(parser));
ret = 1;
goto end;
@@ -112,7 +112,7 @@ main (int argc, char **argv)
ucl_parser_free (parser);
ucl_object_unref (obj);
parser2 = ucl_parser_new (UCL_PARSER_KEY_LOWERCASE);
- ucl_parser_add_chunk (parser2, emitted, strlen (emitted));
+ 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));
diff --git a/contrib/libucl/tests/test_generate.c b/contrib/libucl/tests/test_generate.c
index b2081ba..5c130e6 100644
--- a/contrib/libucl/tests/test_generate.c
+++ b/contrib/libucl/tests/test_generate.c
@@ -29,7 +29,8 @@
int
main (int argc, char **argv)
{
- ucl_object_t *obj, *cur, *ar;
+ ucl_object_t *obj, *cur, *ar, *ref;
+ const ucl_object_t *found;
FILE *out;
unsigned char *emitted;
const char *fname_out = NULL;
@@ -55,62 +56,81 @@ main (int argc, char **argv)
obj = ucl_object_typed_new (UCL_OBJECT);
/* Create some strings */
cur = ucl_object_fromstring_common (" test string ", 0, UCL_STRING_TRIM);
- obj = ucl_object_insert_key (obj, cur, "key1", 0, false);
+ ucl_object_insert_key (obj, cur, "key1", 0, false);
cur = ucl_object_fromstring_common (" test \nstring\n ", 0, UCL_STRING_TRIM | UCL_STRING_ESCAPE);
- obj = ucl_object_insert_key (obj, cur, "key2", 0, false);
+ ucl_object_insert_key (obj, cur, "key2", 0, false);
cur = ucl_object_fromstring_common (" test string \n", 0, 0);
- obj = ucl_object_insert_key (obj, cur, "key3", 0, false);
+ ucl_object_insert_key (obj, cur, "key3", 0, false);
/* Array of numbers */
+ ar = ucl_object_typed_new (UCL_ARRAY);
cur = ucl_object_fromint (10);
- ar = ucl_array_append (NULL, cur);
+ ucl_array_append (ar, cur);
cur = ucl_object_fromdouble (10.1);
- ar = ucl_array_append (ar, cur);
+ ucl_array_append (ar, cur);
cur = ucl_object_fromdouble (9.999);
- ar = ucl_array_prepend (ar, cur);
+ ucl_array_prepend (ar, cur);
/* Removing from an array */
cur = ucl_object_fromdouble (1.0);
- ar = ucl_array_append (ar, cur);
+ 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);
- ar = ucl_array_append (ar, cur);
+ 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);
- ar = ucl_array_prepend (ar, cur);
+ ucl_array_prepend (ar, cur);
cur = ucl_array_pop_first (ar);
assert (ucl_object_todouble (cur) == 3.0);
ucl_object_unref (cur);
- obj = ucl_object_insert_key (obj, ar, "key4", 0, false);
+ ucl_object_insert_key (obj, ar, "key4", 0, false);
cur = ucl_object_frombool (true);
- obj = ucl_object_insert_key (obj, cur, "key4", 0, false);
+ /* 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);
- obj = ucl_object_insert_key (obj, cur, "key5", 0, false);
+ ucl_object_insert_key (obj, cur, "key5", 0, false);
cur = ucl_object_fromstring_common ("", 0, UCL_STRING_ESCAPE);
- obj = ucl_object_insert_key (obj, cur, "key6", 0, false);
+ ucl_object_insert_key (obj, cur, "key6", 0, false);
cur = ucl_object_fromstring_common (" \n", 0, UCL_STRING_ESCAPE);
- obj = ucl_object_insert_key (obj, cur, "key7", 0, false);
+ 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);
- obj = ucl_object_insert_key (obj, cur, "key8", 0, false);
+ ucl_object_insert_key (obj, cur, "key8", 0, false);
cur = ucl_object_fromstring_common ("3.14", 0, UCL_STRING_PARSE);
- obj = ucl_object_insert_key (obj, cur, "key9", 0, false);
+ ucl_object_insert_key (obj, cur, "key9", 0, false);
cur = ucl_object_fromstring_common ("true", 0, UCL_STRING_PARSE);
- obj = ucl_object_insert_key (obj, cur, "key10", 0, false);
+ ucl_object_insert_key (obj, cur, "key10", 0, false);
cur = ucl_object_fromstring_common (" off ", 0, UCL_STRING_PARSE | UCL_STRING_TRIM);
- obj = ucl_object_insert_key (obj, cur, "key11", 0, false);
+ ucl_object_insert_key (obj, cur, "key11", 0, false);
cur = ucl_object_fromstring_common ("gslin@gslin.org", 0, UCL_STRING_PARSE_INT);
- obj = ucl_object_insert_key (obj, cur, "key12", 0, false);
+ ucl_object_insert_key (obj, cur, "key12", 0, false);
cur = ucl_object_fromstring_common ("#test", 0, UCL_STRING_PARSE_INT);
- obj = ucl_object_insert_key (obj, cur, "key13", 0, false);
+ ucl_object_insert_key (obj, cur, "key13", 0, false);
cur = ucl_object_frombool (true);
- obj = ucl_object_insert_key (obj, cur, "k=3", 0, false);
+ 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);
@@ -122,5 +142,9 @@ main (int argc, char **argv)
}
fclose (out);
+ /* Ref should still be accessible */
+ ref->value.iv = 100500;
+ ucl_object_unref (ref);
+
return ret;
}
diff --git a/contrib/libucl/tests/test_schema.c b/contrib/libucl/tests/test_schema.c
new file mode 100644
index 0000000..afce178
--- /dev/null
+++ b/contrib/libucl/tests/test_schema.c
@@ -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;
+}
diff --git a/contrib/libucl/tests/test_speed.c b/contrib/libucl/tests/test_speed.c
index cbf290c..56f2e5a 100644
--- a/contrib/libucl/tests/test_speed.c
+++ b/contrib/libucl/tests/test_speed.c
@@ -32,8 +32,10 @@
#include <time.h>
#ifdef __APPLE__
+#ifdef HAVE_MACH_MACH_TIME_H
#include <mach/mach_time.h>
#endif
+#endif
#include "ucl.h"
diff --git a/contrib/libucl/utils/Makefile.am b/contrib/libucl/utils/Makefile.am
new file mode 100644
index 0000000..61e06a7
--- /dev/null
+++ b/contrib/libucl/utils/Makefile.am
@@ -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) \ No newline at end of file
diff --git a/contrib/libucl/utils/objdump.c b/contrib/libucl/utils/objdump.c
index 8bf6e28..2e00d0a 100644
--- a/contrib/libucl/utils/objdump.c
+++ b/contrib/libucl/utils/objdump.c
@@ -28,11 +28,11 @@
#include "ucl.h"
void
-ucl_obj_dump(ucl_object_t *obj, unsigned int shift)
+ucl_obj_dump (const ucl_object_t *obj, unsigned int shift)
{
int num = shift * 4 + 5;
char *pre = (char *) malloc (num * sizeof(char));
- ucl_object_t *cur, *tmp;
+ const ucl_object_t *cur, *tmp;
ucl_object_iter_t it = NULL, it_obj = NULL;
pre[--num] = 0x00;
@@ -64,7 +64,7 @@ ucl_obj_dump(ucl_object_t *obj, unsigned int shift)
}
else if (obj->type == UCL_INT) {
printf ("%stype: UCL_INT\n", pre);
- printf ("%svalue: %ld\n", pre, ucl_object_toint (obj));
+ printf ("%svalue: %jd\n", pre, (intmax_t)ucl_object_toint (obj));
}
else if (obj->type == UCL_FLOAT) {
printf ("%stype: UCL_FLOAT\n", pre);
@@ -99,7 +99,7 @@ main(int argc, char **argv)
struct ucl_parser *parser;
int k, ret = 0, r = 0;
ucl_object_t *obj = NULL;
- ucl_object_t *par;
+ const ucl_object_t *par;
FILE *in;
if (argc > 1) {
@@ -129,7 +129,7 @@ main(int argc, char **argv)
}
obj = ucl_parser_get_object (parser);
- if (ucl_parser_get_error(parser)) {
+ if (ucl_parser_get_error (parser)) {
printf ("Error occured: %s\n", ucl_parser_get_error(parser));
ret = 1;
goto end;
OpenPOWER on IntegriCloud