Update to 20140321

This brings schema validation

MFC after:	1 week
This commit is contained in:
Baptiste Daroussin 2014-03-22 17:28:14 +00:00
commit 97bd480fe3
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=263648
85 changed files with 5501 additions and 491 deletions

View file

@ -1,3 +0,0 @@
.cproject
.project
.settings

View file

@ -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

View file

@ -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)

View file

@ -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

View file

@ -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, schema definition can be in UCL format instead of JSON that sinplifies 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 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 input is validated properly.
## Performance
Are UCL parser and emitter fast enough? Well, there are some numbers.

2
contrib/libucl/autogen.sh Executable file
View file

@ -0,0 +1,2 @@
#!/bin/sh
autoreconf -i

View file

@ -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")

124
contrib/libucl/configure.ac Normal file
View file

@ -0,0 +1,124 @@
m4_define([maj_ver], [0])
m4_define([med_ver], [3])
m4_define([min_ver], [0])
m4_define([so_version], [maj_ver:med_ver])
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)]))
AC_ARG_ENABLE([signatures], AS_HELP_STRING([--enable-signatures],
[Enable signatures check (requires openssl)]))
AC_ARG_ENABLE([utils],
[--enable-utils Build and install utils],
[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).])
LIBSSL_LIB="-lcrypto"
LIBS_EXTRA="${LIBS_EXTRA} -lcrypto"
], [AC_MSG_ERROR([unable to find the EVP_MD_CTX_create() function])])
])
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])])
AC_CHECK_HEADER([regex.h], [
AC_DEFINE(HAVE_REGEX_H, 1, [Define to 1 if you have the <regex.h> header file.])
],
[AC_MSG_ERROR([unable to find the regex.h header])],
[#include <sys/types.h>])
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_CONFIG_FILES(Makefile \
src/Makefile \
tests/Makefile \
utils/Makefile \
doc/Makefile \
libucl.pc)
AC_CONFIG_FILES([stamp-h], [echo timestamp > stamp-h])
AC_OUTPUT

View file

@ -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

View file

@ -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
@ -314,13 +368,14 @@ 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`.
@ -345,4 +400,40 @@ while ((obj = ucl_iterate_object (top, &it, true))) {
ucl_object_tostring_forced (cur));
}
}
~~~
~~~
# Validation functions
Currently, there is only one validation function called `ucl_object_validate`. It performs validation of object using the specified schema. This function is defined as following:
## ucl_object_validate
~~~C
bool ucl_object_validate (ucl_object_t *schema,
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).

634
contrib/libucl/doc/libucl.3 Normal file
View file

@ -0,0 +1,634 @@
.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\ (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\ (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]
ucl_object_t*\ ucl_iterate_object\ (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;
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\ (ucl_object_t\ *schema,
\ \ \ \ 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>.

View file

@ -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)

View file

@ -138,7 +138,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 +151,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;
/**
@ -219,38 +221,14 @@ UCL_EXTERN char* ucl_copy_value_trash (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 (unsigned int type) UCL_WARN_UNUSED_RESULT;
/**
* Convert any string to an ucl object making the specified transformations
@ -267,11 +245,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);
/**
* Create a UCL object from the specified string
@ -279,68 +253,28 @@ 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);
/**
* 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);
/**
* 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);
/**
* 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);
/**
* Insert a object 'elt' to the hash 'top' and associate it with key 'key'
@ -382,6 +316,28 @@ UCL_EXTERN bool ucl_object_delete_keyl (ucl_object_t *top, const char *key, size
*/
UCL_EXTERN bool ucl_object_delete_key (ucl_object_t *top, const char *key);
/**
* Delete key from `top` object returning the object deleted. This object is not
* released
* @param top object
* @param key key to remove
* @param keylen length of the key (or 0 for NULL terminated keys)
* @return removed object or NULL if object has not been found
*/
UCL_EXTERN ucl_object_t* ucl_object_pop_keyl (ucl_object_t *top, const char *key,
size_t keylen) UCL_WARN_UNUSED_RESULT;
/**
* Delete key from `top` object returning the object deleted. This object is not
* released
* @param top object
* @param key key to remove
* @return removed object or NULL if object has not been found
*/
UCL_EXTERN ucl_object_t* ucl_object_pop_key (ucl_object_t *top, const char *key)
UCL_WARN_UNUSED_RESULT;
/**
* Insert a object 'elt' to the hash 'top' and associate it with key 'key', if the specified key exist,
* try to merge its content
@ -401,41 +357,8 @@ UCL_EXTERN ucl_object_t* ucl_object_insert_key_merged (ucl_object_t *top, ucl_ob
* @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_EXTERN 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;
}
/**
* Append an element to the start of array object
@ -443,41 +366,8 @@ ucl_array_append (ucl_object_t *top, ucl_object_t *elt)
* @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_EXTERN 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;
}
/**
* Removes an element `elt` from the array `top`. Caller must unref the returned object when it is not
@ -486,66 +376,21 @@ 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 ucl_object_t* ucl_array_head (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 ucl_object_t* ucl_array_tail (ucl_object_t *top);
/**
* Removes the last element from the array `top`. Caller must unref the returned object when it is not
@ -553,11 +398,7 @@ 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);
/**
* Removes the first element from the array `top`. Caller must unref the returned object when it is not
@ -565,11 +406,7 @@ 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
@ -577,26 +414,8 @@ ucl_array_pop_first (ucl_object_t *top)
* @param elt new element
* @return new head if applicable
*/
static inline ucl_object_t * ucl_elt_append (ucl_object_t *head,
UCL_EXTERN 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;
}
/**
* Converts an object to double value
@ -604,40 +423,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 (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 (ucl_object_t *obj);
/**
* Converts an object to integer value
@ -645,40 +438,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 (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 (ucl_object_t *obj);
/**
* Converts an object to boolean value
@ -686,36 +453,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 (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 (ucl_object_t *obj);
/**
* Converts an object to string value
@ -723,48 +468,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 (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 (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 (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 +492,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 (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 (ucl_object_t *obj, size_t *tlen);
/**
* Return object identified by a key in the specified object
@ -812,7 +508,7 @@ 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 ucl_object_t* ucl_object_find_key (ucl_object_t *obj, const char *key);
/**
* Return object identified by a fixed size key in the specified object
@ -821,18 +517,14 @@ 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 ucl_object_t* ucl_object_find_keyl (ucl_object_t *obj, const char *key, size_t klen);
/**
* 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 (ucl_object_t *obj);
/**
* Returns a key of an object as a fixed size string (may be more efficient)
@ -840,12 +532,7 @@ 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 (ucl_object_t *obj, size_t *len);
/**
* Free ucl object
@ -857,22 +544,34 @@ UCL_EXTERN void ucl_object_free (ucl_object_t *obj);
* Increase reference count for an object
* @param obj object to ref
*/
static inline ucl_object_t *
ucl_object_ref (ucl_object_t *obj) {
obj->ref ++;
return obj;
}
UCL_EXTERN ucl_object_t* ucl_object_ref (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 (ucl_object_t *o1, 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)(ucl_object_t *o1, ucl_object_t *o2));
/**
* Opaque iterator object
*/
@ -944,7 +643,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
@ -1039,6 +749,48 @@ UCL_EXTERN bool ucl_object_emit_full (ucl_object_t *obj, enum ucl_emitter emit_t
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 */
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 (ucl_object_t *schema,
ucl_object_t *obj, struct ucl_schema_error *err);
/** @} */
#ifdef __cplusplus
}
#endif

View file

@ -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}/

View file

@ -0,0 +1,23 @@
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@ \
@CURL_LIBS@
include_HEADERS= $(top_srcdir)/include/ucl.h
noinst_HEADERS= ucl_internal.h \
xxhash.h \
ucl_hash.h \
ucl_chartable.h \
tree.h

212
contrib/libucl/src/tree.h Normal file
View file

@ -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 */

View file

@ -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

View file

@ -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"

View file

@ -24,18 +24,67 @@
#ifndef UCL_INTERNAL_H_
#define UCL_INTERNAL_H_
#include <sys/types.h>
#ifndef _WIN32
#include <sys/mman.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
#define HAVE_REGEX_H
#endif
#include <sys/stat.h>
#include <sys/param.h>
#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"
@ -261,7 +310,8 @@ 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 *

View file

@ -544,6 +544,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 +558,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 +662,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 +684,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 +714,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 +723,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 +743,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 +762,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 +781,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 +811,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;
@ -1308,6 +1317,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 +1328,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 +1623,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 +1691,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 +1809,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 +1833,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 +1882,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);
@ -1873,8 +1907,16 @@ ucl_parser_add_chunk (struct ucl_parser *parser, const unsigned char *data,
{
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 +1937,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);
}

File diff suppressed because it is too large Load diff

View file

@ -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
/**
@ -158,6 +183,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 +216,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:
@ -249,6 +285,9 @@ ucl_unescape_json_string (char *str, size_t len)
UCL_EXTERN char *
ucl_copy_key_trash (ucl_object_t *obj)
{
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) {
@ -265,6 +304,9 @@ ucl_copy_key_trash (ucl_object_t *obj)
UCL_EXTERN char *
ucl_copy_value_trash (ucl_object_t *obj)
{
if (obj == NULL) {
return NULL;
}
if (obj->trash_stack[UCL_TRASH_VALUE] == NULL) {
if (obj->type == UCL_STRING) {
/* Special case for strings */
@ -304,6 +346,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 +384,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 +410,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 +581,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 +683,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 +732,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 +760,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 +788,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 +857,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 +888,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 +903,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 +1068,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);
}
}
}
@ -1083,6 +1139,7 @@ ucl_object_insert_key_common (ucl_object_t *top, ucl_object_t *elt,
if (!found) {
top->value.ov = ucl_hash_insert_object (top->value.ov, elt);
DL_APPEND (found, elt);
top->len ++;
}
else {
if (replace) {
@ -1129,10 +1186,15 @@ ucl_object_delete_keyl(ucl_object_t *top, const char *key, size_t keylen)
{
ucl_object_t *found;
if (top == NULL || key == NULL) {
return false;
}
found = ucl_object_find_keyl(top, key, keylen);
if (found == NULL)
if (found == NULL) {
return false;
}
ucl_hash_delete(top->value.ov, found);
ucl_object_unref (found);
@ -1147,6 +1209,31 @@ ucl_object_delete_key(ucl_object_t *top, const char *key)
return ucl_object_delete_keyl(top, key, 0);
}
ucl_object_t*
ucl_object_pop_keyl (ucl_object_t *top, const char *key, size_t keylen)
{
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 found;
}
ucl_object_t*
ucl_object_pop_key (ucl_object_t *top, const char *key)
{
return ucl_object_pop_keyl (top, key, 0);
}
ucl_object_t *
ucl_object_insert_key (ucl_object_t *top, ucl_object_t *elt,
const char *key, size_t keylen, bool copy_key)
@ -1207,6 +1294,10 @@ ucl_iterate_object (ucl_object_t *obj, ucl_object_iter_t *iter, bool expand_valu
{
ucl_object_t *elt;
if (obj == NULL || iter == NULL) {
return NULL;
}
if (expand_values) {
switch (obj->type) {
case UCL_OBJECT:
@ -1247,3 +1338,498 @@ ucl_iterate_object (ucl_object_t *obj, ucl_object_iter_t *iter, bool expand_valu
/* Not reached */
return NULL;
}
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 (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_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;
}
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;
}
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;
}
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_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_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_object_t *
ucl_array_pop_last (ucl_object_t *top)
{
return ucl_array_delete (top, ucl_array_tail (top));
}
ucl_object_t *
ucl_array_pop_first (ucl_object_t *top)
{
return ucl_array_delete (top, ucl_array_head (top));
}
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 (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 (ucl_object_t *obj)
{
double result = 0.;
ucl_object_todouble_safe (obj, &result);
return result;
}
bool
ucl_object_toint_safe (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 (ucl_object_t *obj)
{
int64_t result = 0;
ucl_object_toint_safe (obj, &result);
return result;
}
bool
ucl_object_toboolean_safe (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 (ucl_object_t *obj)
{
bool result = false;
ucl_object_toboolean_safe (obj, &result);
return result;
}
bool
ucl_object_tostring_safe (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 (ucl_object_t *obj)
{
const char *result = NULL;
ucl_object_tostring_safe (obj, &result);
return result;
}
const char *
ucl_object_tostring_forced (ucl_object_t *obj)
{
return ucl_copy_value_trash (obj);
}
bool
ucl_object_tolstring_safe (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 (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 (ucl_object_t *obj)
{
return ucl_copy_key_trash (obj);
}
const char *
ucl_object_keyl (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 (ucl_object_t *obj)
{
if (obj != NULL) {
obj->ref ++;
}
return obj;
}
void
ucl_object_unref (ucl_object_t *obj)
{
if (obj != NULL && --obj->ref <= 0) {
ucl_object_free (obj);
}
}
int
ucl_object_compare (ucl_object_t *o1, ucl_object_t *o2)
{
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)(ucl_object_t *o1, ucl_object_t *o2))
{
if (cmp == NULL || ar == NULL || ar->type != UCL_ARRAY) {
return;
}
DL_SORT (ar->value.av, cmp);
}

View file

@ -0,0 +1 @@
timestamp

8
contrib/libucl/tests/.gitignore vendored Normal file
View file

@ -0,0 +1,8 @@
*.log
*.trs
*.plist
test_basic
test_generate
test_schema
test_speed

View file

@ -1 +0,0 @@
a []

View file

@ -1 +0,0 @@
# test

View file

@ -1 +0,0 @@

View file

@ -1 +0,0 @@

View file

@ -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

26
contrib/libucl/tests/basic.test Executable file
View file

@ -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

View file

@ -0,0 +1,9 @@
section foo bar {
key = value
}
section foo baz {
key = value
}
section foo {
bar = lol /* removing this line makes parsing successful */
}

View file

@ -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"

View file

@ -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

View file

@ -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'

View file

@ -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

View file

@ -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
}
]
}
]

View file

@ -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
}
]
}
]

View file

@ -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
}
]
}
]

View file

@ -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
}
]
}
]

View file

@ -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
}
]
}
]

View file

@ -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
}
]
}
]

View file

@ -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
}
]
}
]

View file

@ -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
}
]
}
]

View file

@ -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
}
]
}
]

View file

@ -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
}
]
}
]

View file

@ -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
}
]
}
]

View file

@ -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
}
]
}
]

View file

@ -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
}
]
}
]

View file

@ -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
}
]
}
]

View file

@ -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
}
]
}
]

View file

@ -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
}
]
}
]

View file

@ -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
}
]
}
]

View file

@ -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
}
]
}
]

View file

@ -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
}
]
}
]

View file

@ -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
}
]
}
]

View file

@ -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
}
]
}
]

View file

@ -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
}
]
}
]

View file

@ -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
}
]
}
*/
]

View file

@ -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
}
]
}
]

View file

@ -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
}
]
}
]

View file

@ -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
}
]
}
]

View file

@ -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
}
]
}
]

21
contrib/libucl/tests/speed.test Executable file
View file

@ -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

View file

@ -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)
@ -97,7 +95,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 +110,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));

View file

@ -0,0 +1,157 @@
/* 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 (ucl_object_t *schema, ucl_object_t *obj,
struct ucl_schema_error *err)
{
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 (ucl_object_t *obj)
{
struct ucl_schema_error err;
ucl_object_iter_t iter = NULL;
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, *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;
}

View file

@ -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"

View file

@ -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)

View file

@ -8,6 +8,7 @@ SHLIB_MAJOR= 1
SRCS= ucl_emitter.c \
ucl_hash.c \
ucl_parser.c \
ucl_schema.c \
ucl_util.c \
xxhash.c