summaryrefslogtreecommitdiffstats
path: root/contrib/libucl/src
diff options
context:
space:
mode:
authorbapt <bapt@FreeBSD.org>2014-03-22 17:28:14 +0000
committerbapt <bapt@FreeBSD.org>2014-03-22 17:28:14 +0000
commitb471b8e16d6f2c205ae497402bad0a60c9cd6fa8 (patch)
treeaabc973b2e4479277f8fe9aa0e8ed539826fdca3 /contrib/libucl/src
parent841158cbfbfc6b699dbc10892c6cb878a66a5d7e (diff)
parent6b4d859b54b28a9d46c317ff21676aa37241f6de (diff)
downloadFreeBSD-src-b471b8e16d6f2c205ae497402bad0a60c9cd6fa8.zip
FreeBSD-src-b471b8e16d6f2c205ae497402bad0a60c9cd6fa8.tar.gz
Update to 20140321
This brings schema validation MFC after: 1 week
Diffstat (limited to 'contrib/libucl/src')
-rw-r--r--contrib/libucl/src/Makefile.am23
-rw-r--r--contrib/libucl/src/tree.h212
-rw-r--r--contrib/libucl/src/ucl_emitter.c12
-rw-r--r--contrib/libucl/src/ucl_hash.c1
-rw-r--r--contrib/libucl/src/ucl_internal.h56
-rw-r--r--contrib/libucl/src/ucl_parser.c85
-rw-r--r--contrib/libucl/src/ucl_schema.c1008
-rw-r--r--contrib/libucl/src/ucl_util.c686
8 files changed, 2014 insertions, 69 deletions
diff --git a/contrib/libucl/src/Makefile.am b/contrib/libucl/src/Makefile.am
new file mode 100644
index 0000000..499d640
--- /dev/null
+++ b/contrib/libucl/src/Makefile.am
@@ -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
diff --git a/contrib/libucl/src/tree.h b/contrib/libucl/src/tree.h
new file mode 100644
index 0000000..cee9373
--- /dev/null
+++ b/contrib/libucl/src/tree.h
@@ -0,0 +1,212 @@
+/* tree.h -- AVL trees (in the spirit of BSD's 'queue.h') -*- C -*- */
+
+/* Copyright (c) 2005 Ian Piumarta
+ *
+ * All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the 'Software'), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, and/or sell copies of the
+ * Software, and to permit persons to whom the Software is furnished to do so,
+ * provided that the above copyright notice(s) and this permission notice appear
+ * in all copies of the Software and that both the above copyright notice(s) and
+ * this permission notice appear in supporting documentation.
+ *
+ * THE SOFTWARE IS PROVIDED 'AS IS'. USE ENTIRELY AT YOUR OWN RISK.
+ */
+
+/* This file defines an AVL balanced binary tree [Georgii M. Adelson-Velskii and
+ * Evgenii M. Landis, 'An algorithm for the organization of information',
+ * Doklady Akademii Nauk SSSR, 146:263-266, 1962 (Russian). Also in Myron
+ * J. Ricci (trans.), Soviet Math, 3:1259-1263, 1962 (English)].
+ *
+ * An AVL tree is headed by pointers to the root node and to a function defining
+ * the ordering relation between nodes. Each node contains an arbitrary payload
+ * plus three fields per tree entry: the depth of the subtree for which it forms
+ * the root and two pointers to child nodes (singly-linked for minimum space, at
+ * the expense of direct access to the parent node given a pointer to one of the
+ * children). The tree is rebalanced after every insertion or removal. The
+ * tree may be traversed in two directions: forward (in-order left-to-right) and
+ * reverse (in-order, right-to-left).
+ *
+ * Because of the recursive nature of many of the operations on trees it is
+ * necessary to define a number of helper functions for each type of tree node.
+ * The macro TREE_DEFINE(node_tag, entry_name) defines these functions with
+ * unique names according to the node_tag. This macro should be invoked,
+ * thereby defining the necessary functions, once per node tag in the program.
+ *
+ * For details on the use of these macros, see the tree(3) manual page.
+ */
+
+#ifndef __tree_h
+#define __tree_h
+
+
+#define TREE_DELTA_MAX 1
+
+#define TREE_ENTRY(type) \
+ struct { \
+ struct type *avl_left; \
+ struct type *avl_right; \
+ int avl_height; \
+ }
+
+#define TREE_HEAD(name, type) \
+ struct name { \
+ struct type *th_root; \
+ int (*th_cmp)(struct type *lhs, struct type *rhs); \
+ }
+
+#define TREE_INITIALIZER(cmp) { 0, cmp }
+
+#define TREE_DELTA(self, field) \
+ (( (((self)->field.avl_left) ? (self)->field.avl_left->field.avl_height : 0)) \
+ - (((self)->field.avl_right) ? (self)->field.avl_right->field.avl_height : 0))
+
+/* Recursion prevents the following from being defined as macros. */
+
+#define TREE_DEFINE(node, field) \
+ \
+ struct node *TREE_BALANCE_##node##_##field(struct node *); \
+ \
+ struct node *TREE_ROTL_##node##_##field(struct node *self) \
+ { \
+ struct node *r= self->field.avl_right; \
+ self->field.avl_right= r->field.avl_left; \
+ r->field.avl_left= TREE_BALANCE_##node##_##field(self); \
+ return TREE_BALANCE_##node##_##field(r); \
+ } \
+ \
+ struct node *TREE_ROTR_##node##_##field(struct node *self) \
+ { \
+ struct node *l= self->field.avl_left; \
+ self->field.avl_left= l->field.avl_right; \
+ l->field.avl_right= TREE_BALANCE_##node##_##field(self); \
+ return TREE_BALANCE_##node##_##field(l); \
+ } \
+ \
+ struct node *TREE_BALANCE_##node##_##field(struct node *self) \
+ { \
+ int delta= TREE_DELTA(self, field); \
+ \
+ if (delta < -TREE_DELTA_MAX) \
+ { \
+ if (TREE_DELTA(self->field.avl_right, field) > 0) \
+ self->field.avl_right= TREE_ROTR_##node##_##field(self->field.avl_right); \
+ return TREE_ROTL_##node##_##field(self); \
+ } \
+ else if (delta > TREE_DELTA_MAX) \
+ { \
+ if (TREE_DELTA(self->field.avl_left, field) < 0) \
+ self->field.avl_left= TREE_ROTL_##node##_##field(self->field.avl_left); \
+ return TREE_ROTR_##node##_##field(self); \
+ } \
+ self->field.avl_height= 0; \
+ if (self->field.avl_left && (self->field.avl_left->field.avl_height > self->field.avl_height)) \
+ self->field.avl_height= self->field.avl_left->field.avl_height; \
+ if (self->field.avl_right && (self->field.avl_right->field.avl_height > self->field.avl_height)) \
+ self->field.avl_height= self->field.avl_right->field.avl_height; \
+ self->field.avl_height += 1; \
+ return self; \
+ } \
+ \
+ struct node *TREE_INSERT_##node##_##field \
+ (struct node *self, struct node *elm, int (*compare)(struct node *lhs, struct node *rhs)) \
+ { \
+ if (!self) \
+ return elm; \
+ if (compare(elm, self) < 0) \
+ self->field.avl_left= TREE_INSERT_##node##_##field(self->field.avl_left, elm, compare); \
+ else \
+ self->field.avl_right= TREE_INSERT_##node##_##field(self->field.avl_right, elm, compare); \
+ return TREE_BALANCE_##node##_##field(self); \
+ } \
+ \
+ struct node *TREE_FIND_##node##_##field \
+ (struct node *self, struct node *elm, int (*compare)(struct node *lhs, struct node *rhs)) \
+ { \
+ if (!self) \
+ return 0; \
+ if (compare(elm, self) == 0) \
+ return self; \
+ if (compare(elm, self) < 0) \
+ return TREE_FIND_##node##_##field(self->field.avl_left, elm, compare); \
+ else \
+ return TREE_FIND_##node##_##field(self->field.avl_right, elm, compare); \
+ } \
+ \
+ struct node *TREE_MOVE_RIGHT(struct node *self, struct node *rhs) \
+ { \
+ if (!self) \
+ return rhs; \
+ self->field.avl_right= TREE_MOVE_RIGHT(self->field.avl_right, rhs); \
+ return TREE_BALANCE_##node##_##field(self); \
+ } \
+ \
+ struct node *TREE_REMOVE_##node##_##field \
+ (struct node *self, struct node *elm, int (*compare)(struct node *lhs, struct node *rhs)) \
+ { \
+ if (!self) return 0; \
+ \
+ if (compare(elm, self) == 0) \
+ { \
+ struct node *tmp= TREE_MOVE_RIGHT(self->field.avl_left, self->field.avl_right); \
+ self->field.avl_left= 0; \
+ self->field.avl_right= 0; \
+ return tmp; \
+ } \
+ if (compare(elm, self) < 0) \
+ self->field.avl_left= TREE_REMOVE_##node##_##field(self->field.avl_left, elm, compare); \
+ else \
+ self->field.avl_right= TREE_REMOVE_##node##_##field(self->field.avl_right, elm, compare); \
+ return TREE_BALANCE_##node##_##field(self); \
+ } \
+ \
+ void TREE_FORWARD_APPLY_ALL_##node##_##field \
+ (struct node *self, void (*function)(struct node *node, void *data), void *data) \
+ { \
+ if (self) \
+ { \
+ TREE_FORWARD_APPLY_ALL_##node##_##field(self->field.avl_left, function, data); \
+ function(self, data); \
+ TREE_FORWARD_APPLY_ALL_##node##_##field(self->field.avl_right, function, data); \
+ } \
+ } \
+ \
+ void TREE_REVERSE_APPLY_ALL_##node##_##field \
+ (struct node *self, void (*function)(struct node *node, void *data), void *data) \
+ { \
+ if (self) \
+ { \
+ TREE_REVERSE_APPLY_ALL_##node##_##field(self->field.avl_right, function, data); \
+ function(self, data); \
+ TREE_REVERSE_APPLY_ALL_##node##_##field(self->field.avl_left, function, data); \
+ } \
+ }
+
+#define TREE_INSERT(head, node, field, elm) \
+ ((head)->th_root= TREE_INSERT_##node##_##field((head)->th_root, (elm), (head)->th_cmp))
+
+#define TREE_FIND(head, node, field, elm) \
+ (TREE_FIND_##node##_##field((head)->th_root, (elm), (head)->th_cmp))
+
+#define TREE_REMOVE(head, node, field, elm) \
+ ((head)->th_root= TREE_REMOVE_##node##_##field((head)->th_root, (elm), (head)->th_cmp))
+
+#define TREE_DEPTH(head, field) \
+ ((head)->th_root->field.avl_height)
+
+#define TREE_FORWARD_APPLY(head, node, field, function, data) \
+ TREE_FORWARD_APPLY_ALL_##node##_##field((head)->th_root, function, data)
+
+#define TREE_REVERSE_APPLY(head, node, field, function, data) \
+ TREE_REVERSE_APPLY_ALL_##node##_##field((head)->th_root, function, data)
+
+#define TREE_INIT(head, cmp) do { \
+ (head)->th_root= 0; \
+ (head)->th_cmp= (cmp); \
+ } while (0)
+
+
+#endif /* __tree_h */
diff --git a/contrib/libucl/src/ucl_emitter.c b/contrib/libucl/src/ucl_emitter.c
index 51bb09a..eb314ac 100644
--- a/contrib/libucl/src/ucl_emitter.c
+++ b/contrib/libucl/src/ucl_emitter.c
@@ -21,11 +21,19 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#include <float.h>
-#include <math.h>
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
#include "ucl.h"
#include "ucl_internal.h"
#include "ucl_chartable.h"
+#ifdef HAVE_FLOAT_H
+#include <float.h>
+#endif
+#ifdef HAVE_MATH_H
+#include <math.h>
+#endif
/**
* @file rcl_emitter.c
diff --git a/contrib/libucl/src/ucl_hash.c b/contrib/libucl/src/ucl_hash.c
index a3711de..0ab962a 100644
--- a/contrib/libucl/src/ucl_hash.c
+++ b/contrib/libucl/src/ucl_hash.c
@@ -21,6 +21,7 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
+#include "ucl_internal.h"
#include "ucl_hash.h"
#include "utlist.h"
diff --git a/contrib/libucl/src/ucl_internal.h b/contrib/libucl/src/ucl_internal.h
index 49c4aae..a55d747 100644
--- a/contrib/libucl/src/ucl_internal.h
+++ b/contrib/libucl/src/ucl_internal.h
@@ -24,18 +24,67 @@
#ifndef UCL_INTERNAL_H_
#define UCL_INTERNAL_H_
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#else
+/* Help embedded builds */
+#define HAVE_SYS_TYPES_H
+#define HAVE_SYS_MMAN_H
+#define HAVE_SYS_STAT_H
+#define HAVE_SYS_PARAM_H
+#define HAVE_LIMITS_H
+#define HAVE_FCNTL_H
+#define HAVE_ERRNO_H
+#define HAVE_UNISTD_H
+#define HAVE_CTYPE_H
+#define HAVE_STDIO_H
+#define HAVE_STRING_H
+#define HAVE_FLOAT_H
+#define HAVE_LIBGEN_H
+#define HAVE_MATH_H
+#define HAVE_STDBOOL_H
+#define HAVE_STDINT_H
+#define HAVE_STDARG_H
+#define HAVE_REGEX_H
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
-#ifndef _WIN32
-#include <sys/mman.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 *
diff --git a/contrib/libucl/src/ucl_parser.c b/contrib/libucl/src/ucl_parser.c
index 12ebad2..a067286 100644
--- a/contrib/libucl/src/ucl_parser.c
+++ b/contrib/libucl/src/ucl_parser.c
@@ -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);
+}
diff --git a/contrib/libucl/src/ucl_schema.c b/contrib/libucl/src/ucl_schema.c
new file mode 100644
index 0000000..a74ed0f
--- /dev/null
+++ b/contrib/libucl/src/ucl_schema.c
@@ -0,0 +1,1008 @@
+/*
+ * Copyright (c) 2014, Vsevolod Stakhov
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR ''AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "ucl.h"
+#include "ucl_internal.h"
+#include "tree.h"
+#include "utlist.h"
+#ifdef HAVE_STDARG_H
+#include <stdarg.h>
+#endif
+#ifdef HAVE_STDIO_H
+#include <stdio.h>
+#endif
+#ifdef HAVE_REGEX_H
+#include <regex.h>
+#endif
+#ifdef HAVE_MATH_H
+#include <math.h>
+#endif
+
+static bool ucl_schema_validate (ucl_object_t *schema,
+ ucl_object_t *obj, bool try_array,
+ struct ucl_schema_error *err,
+ ucl_object_t *root);
+
+static bool
+ucl_string_to_type (const char *input, ucl_type_t *res)
+{
+ if (strcasecmp (input, "object") == 0) {
+ *res = UCL_OBJECT;
+ }
+ else if (strcasecmp (input, "array") == 0) {
+ *res = UCL_ARRAY;
+ }
+ else if (strcasecmp (input, "integer") == 0) {
+ *res = UCL_INT;
+ }
+ else if (strcasecmp (input, "number") == 0) {
+ *res = UCL_FLOAT;
+ }
+ else if (strcasecmp (input, "string") == 0) {
+ *res = UCL_STRING;
+ }
+ else if (strcasecmp (input, "boolean") == 0) {
+ *res = UCL_BOOLEAN;
+ }
+ else if (strcasecmp (input, "null") == 0) {
+ *res = UCL_NULL;
+ }
+ else {
+ return false;
+ }
+
+ return true;
+}
+
+static const char *
+ucl_object_type_to_string (ucl_type_t type)
+{
+ const char *res = "unknown";
+
+ switch (type) {
+ case UCL_OBJECT:
+ res = "object";
+ break;
+ case UCL_ARRAY:
+ res = "array";
+ break;
+ case UCL_INT:
+ res = "integer";
+ break;
+ case UCL_FLOAT:
+ case UCL_TIME:
+ res = "number";
+ break;
+ case UCL_STRING:
+ res = "string";
+ break;
+ case UCL_BOOLEAN:
+ res = "boolean";
+ break;
+ case UCL_NULL:
+ case UCL_USERDATA:
+ res = "null";
+ break;
+ }
+
+ return res;
+}
+
+/*
+ * Create validation error
+ */
+static void
+ucl_schema_create_error (struct ucl_schema_error *err,
+ enum ucl_schema_error_code code, ucl_object_t *obj,
+ const char *fmt, ...)
+{
+ va_list va;
+
+ if (err != NULL) {
+ err->code = code;
+ err->obj = obj;
+ va_start (va, fmt);
+ vsnprintf (err->msg, sizeof (err->msg), fmt, va);
+ va_end (va);
+ }
+}
+
+/*
+ * Check whether we have a pattern specified
+ */
+static ucl_object_t *
+ucl_schema_test_pattern (ucl_object_t *obj, const char *pattern)
+{
+ regex_t reg;
+ ucl_object_t *res = NULL, *elt;
+ ucl_object_iter_t iter = NULL;
+
+ if (regcomp (&reg, pattern, REG_EXTENDED | REG_NOSUB) == 0) {
+ while ((elt = ucl_iterate_object (obj, &iter, true)) != NULL) {
+ if (regexec (&reg, ucl_object_key (elt), 0, NULL, 0) == 0) {
+ res = elt;
+ break;
+ }
+ }
+ regfree (&reg);
+ }
+
+ return res;
+}
+
+/*
+ * Check dependencies for an object
+ */
+static bool
+ucl_schema_validate_dependencies (ucl_object_t *deps,
+ ucl_object_t *obj, struct ucl_schema_error *err,
+ ucl_object_t *root)
+{
+ ucl_object_t *elt, *cur, *cur_dep;
+ ucl_object_iter_t iter = NULL, piter;
+ bool ret = true;
+
+ while (ret && (cur = ucl_iterate_object (deps, &iter, true)) != NULL) {
+ elt = ucl_object_find_key (obj, ucl_object_key (cur));
+ if (elt != NULL) {
+ /* Need to check dependencies */
+ if (cur->type == UCL_ARRAY) {
+ piter = NULL;
+ while (ret && (cur_dep = ucl_iterate_object (cur, &piter, true)) != NULL) {
+ if (ucl_object_find_key (obj, ucl_object_tostring (cur_dep)) == NULL) {
+ ucl_schema_create_error (err, UCL_SCHEMA_MISSING_DEPENDENCY, elt,
+ "dependency %s is missing for key %s",
+ ucl_object_tostring (cur_dep), ucl_object_key (cur));
+ ret = false;
+ break;
+ }
+ }
+ }
+ else if (cur->type == UCL_OBJECT) {
+ ret = ucl_schema_validate (cur, obj, true, err, root);
+ }
+ }
+ }
+
+ return ret;
+}
+
+/*
+ * Validate object
+ */
+static bool
+ucl_schema_validate_object (ucl_object_t *schema,
+ ucl_object_t *obj, struct ucl_schema_error *err,
+ ucl_object_t *root)
+{
+ ucl_object_t *elt, *prop, *found, *additional_schema = NULL,
+ *required = NULL, *pat, *pelt;
+ ucl_object_iter_t iter = NULL, piter = NULL;
+ bool ret = true, allow_additional = true;
+ int64_t minmax;
+
+ while (ret && (elt = ucl_iterate_object (schema, &iter, true)) != NULL) {
+ if (elt->type == UCL_OBJECT &&
+ strcmp (ucl_object_key (elt), "properties") == 0) {
+ piter = NULL;
+ while (ret && (prop = ucl_iterate_object (elt, &piter, true)) != NULL) {
+ found = ucl_object_find_key (obj, ucl_object_key (prop));
+ if (found) {
+ ret = ucl_schema_validate (prop, found, true, err, root);
+ }
+ }
+ }
+ else if (strcmp (ucl_object_key (elt), "additionalProperties") == 0) {
+ if (elt->type == UCL_BOOLEAN) {
+ if (!ucl_object_toboolean (elt)) {
+ /* Deny additional fields completely */
+ allow_additional = false;
+ }
+ }
+ else if (elt->type == UCL_OBJECT) {
+ /* Define validator for additional fields */
+ additional_schema = elt;
+ }
+ else {
+ ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, elt,
+ "additionalProperties attribute is invalid in schema");
+ ret = false;
+ break;
+ }
+ }
+ else if (strcmp (ucl_object_key (elt), "required") == 0) {
+ if (elt->type == UCL_ARRAY) {
+ required = elt;
+ }
+ else {
+ ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, elt,
+ "required attribute is invalid in schema");
+ ret = false;
+ break;
+ }
+ }
+ else if (strcmp (ucl_object_key (elt), "minProperties") == 0
+ && ucl_object_toint_safe (elt, &minmax)) {
+ if (obj->len < minmax) {
+ ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
+ "object has not enough properties: %u, minimum is: %u",
+ obj->len, (unsigned)minmax);
+ ret = false;
+ break;
+ }
+ }
+ else if (strcmp (ucl_object_key (elt), "maxProperties") == 0
+ && ucl_object_toint_safe (elt, &minmax)) {
+ if (obj->len > minmax) {
+ ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
+ "object has too many properties: %u, maximum is: %u",
+ obj->len, (unsigned)minmax);
+ ret = false;
+ break;
+ }
+ }
+ else if (strcmp (ucl_object_key (elt), "patternProperties") == 0) {
+ piter = NULL;
+ while (ret && (prop = ucl_iterate_object (elt, &piter, true)) != NULL) {
+ found = ucl_schema_test_pattern (obj, ucl_object_key (prop));
+ if (found) {
+ ret = ucl_schema_validate (prop, found, true, err, root);
+ }
+ }
+ }
+ else if (elt->type == UCL_OBJECT &&
+ strcmp (ucl_object_key (elt), "dependencies") == 0) {
+ ret = ucl_schema_validate_dependencies (elt, obj, err, root);
+ }
+ }
+
+ if (ret) {
+ /* Additional properties */
+ if (!allow_additional || additional_schema != NULL) {
+ /* Check if we have exactly the same properties in schema and object */
+ iter = NULL;
+ prop = ucl_object_find_key (schema, "properties");
+ while ((elt = ucl_iterate_object (obj, &iter, true)) != NULL) {
+ found = ucl_object_find_key (prop, ucl_object_key (elt));
+ if (found == NULL) {
+ /* Try patternProperties */
+ piter = NULL;
+ pat = ucl_object_find_key (schema, "patternProperties");
+ while ((pelt = ucl_iterate_object (pat, &piter, true)) != NULL) {
+ found = ucl_schema_test_pattern (obj, ucl_object_key (pelt));
+ if (found != NULL) {
+ break;
+ }
+ }
+ }
+ if (found == NULL) {
+ if (!allow_additional) {
+ ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
+ "object has non-allowed property %s",
+ ucl_object_key (elt));
+ ret = false;
+ break;
+ }
+ else if (additional_schema != NULL) {
+ if (!ucl_schema_validate (additional_schema, elt, true, err, root)) {
+ ret = false;
+ break;
+ }
+ }
+ }
+ }
+ }
+ /* Required properties */
+ if (required != NULL) {
+ iter = NULL;
+ while ((elt = ucl_iterate_object (required, &iter, true)) != NULL) {
+ if (ucl_object_find_key (obj, ucl_object_tostring (elt)) == NULL) {
+ ucl_schema_create_error (err, UCL_SCHEMA_MISSING_PROPERTY, obj,
+ "object has missing property %s",
+ ucl_object_tostring (elt));
+ ret = false;
+ break;
+ }
+ }
+ }
+ }
+
+
+ return ret;
+}
+
+static bool
+ucl_schema_validate_number (ucl_object_t *schema,
+ ucl_object_t *obj, struct ucl_schema_error *err)
+{
+ ucl_object_t *elt, *test;
+ ucl_object_iter_t iter = NULL;
+ bool ret = true, exclusive = false;
+ double constraint, val;
+ const double alpha = 1e-16;
+
+ while (ret && (elt = ucl_iterate_object (schema, &iter, true)) != NULL) {
+ if ((elt->type == UCL_FLOAT || elt->type == UCL_INT) &&
+ strcmp (ucl_object_key (elt), "multipleOf") == 0) {
+ constraint = ucl_object_todouble (elt);
+ if (constraint <= 0) {
+ ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, elt,
+ "multipleOf must be greater than zero");
+ ret = false;
+ break;
+ }
+ val = ucl_object_todouble (obj);
+ if (fabs (remainder (val, constraint)) > alpha) {
+ ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
+ "number %.4f is not multiple of %.4f, remainder is %.7f",
+ val, constraint);
+ ret = false;
+ break;
+ }
+ }
+ else if ((elt->type == UCL_FLOAT || elt->type == UCL_INT) &&
+ strcmp (ucl_object_key (elt), "maximum") == 0) {
+ constraint = ucl_object_todouble (elt);
+ test = ucl_object_find_key (schema, "exclusiveMaximum");
+ if (test && test->type == UCL_BOOLEAN) {
+ exclusive = ucl_object_toboolean (test);
+ }
+ val = ucl_object_todouble (obj);
+ if (val > constraint || (exclusive && val >= constraint)) {
+ ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
+ "number is too big: %.3f, maximum is: %.3f",
+ val, constraint);
+ ret = false;
+ break;
+ }
+ }
+ else if ((elt->type == UCL_FLOAT || elt->type == UCL_INT) &&
+ strcmp (ucl_object_key (elt), "minimum") == 0) {
+ constraint = ucl_object_todouble (elt);
+ test = ucl_object_find_key (schema, "exclusiveMinimum");
+ if (test && test->type == UCL_BOOLEAN) {
+ exclusive = ucl_object_toboolean (test);
+ }
+ val = ucl_object_todouble (obj);
+ if (val < constraint || (exclusive && val <= constraint)) {
+ ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
+ "number is too small: %.3f, minimum is: %.3f",
+ val, constraint);
+ ret = false;
+ break;
+ }
+ }
+ }
+
+ return ret;
+}
+
+static bool
+ucl_schema_validate_string (ucl_object_t *schema,
+ ucl_object_t *obj, struct ucl_schema_error *err)
+{
+ ucl_object_t *elt;
+ ucl_object_iter_t iter = NULL;
+ bool ret = true;
+ int64_t constraint;
+ regex_t re;
+
+ while (ret && (elt = ucl_iterate_object (schema, &iter, true)) != NULL) {
+ if (elt->type == UCL_INT &&
+ strcmp (ucl_object_key (elt), "maxLength") == 0) {
+ constraint = ucl_object_toint (elt);
+ if (obj->len > constraint) {
+ ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
+ "string is too big: %.3f, maximum is: %.3f",
+ obj->len, constraint);
+ ret = false;
+ break;
+ }
+ }
+ else if (elt->type == UCL_INT &&
+ strcmp (ucl_object_key (elt), "minLength") == 0) {
+ constraint = ucl_object_toint (elt);
+ if (obj->len < constraint) {
+ ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
+ "string is too short: %.3f, minimum is: %.3f",
+ obj->len, constraint);
+ ret = false;
+ break;
+ }
+ }
+ else if (elt->type == UCL_STRING &&
+ strcmp (ucl_object_key (elt), "pattern") == 0) {
+ if (regcomp (&re, ucl_object_tostring (elt),
+ REG_EXTENDED | REG_NOSUB) != 0) {
+ ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, elt,
+ "cannot compile pattern %s", ucl_object_tostring (elt));
+ ret = false;
+ break;
+ }
+ if (regexec (&re, ucl_object_tostring (obj), 0, NULL, 0) != 0) {
+ ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
+ "string doesn't match regexp %s",
+ ucl_object_tostring (elt));
+ ret = false;
+ }
+ regfree (&re);
+ }
+ }
+
+ return ret;
+}
+
+struct ucl_compare_node {
+ ucl_object_t *obj;
+ TREE_ENTRY(ucl_compare_node) link;
+ struct ucl_compare_node *next;
+};
+
+typedef TREE_HEAD(_tree, ucl_compare_node) ucl_compare_tree_t;
+
+TREE_DEFINE(ucl_compare_node, link)
+
+static int
+ucl_schema_elt_compare (struct ucl_compare_node *n1, struct ucl_compare_node *n2)
+{
+ ucl_object_t *o1 = n1->obj, *o2 = n2->obj;
+
+ return ucl_object_compare (o1, o2);
+}
+
+static bool
+ucl_schema_array_is_unique (ucl_object_t *obj, struct ucl_schema_error *err)
+{
+ ucl_compare_tree_t tree = TREE_INITIALIZER (ucl_schema_elt_compare);
+ ucl_object_iter_t iter = NULL;
+ ucl_object_t *elt;
+ struct ucl_compare_node *node, test, *nodes = NULL, *tmp;
+ bool ret = true;
+
+ while ((elt = ucl_iterate_object (obj, &iter, true)) != NULL) {
+ test.obj = elt;
+ node = TREE_FIND (&tree, ucl_compare_node, link, &test);
+ if (node != NULL) {
+ ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, elt,
+ "duplicate values detected while uniqueItems is true");
+ ret = false;
+ break;
+ }
+ node = calloc (1, sizeof (*node));
+ if (node == NULL) {
+ ucl_schema_create_error (err, UCL_SCHEMA_UNKNOWN, elt,
+ "cannot allocate tree node");
+ ret = false;
+ break;
+ }
+ node->obj = elt;
+ TREE_INSERT (&tree, ucl_compare_node, link, node);
+ LL_PREPEND (nodes, node);
+ }
+
+ LL_FOREACH_SAFE (nodes, node, tmp) {
+ free (node);
+ }
+
+ return ret;
+}
+
+static bool
+ucl_schema_validate_array (ucl_object_t *schema,
+ ucl_object_t *obj, struct ucl_schema_error *err,
+ ucl_object_t *root)
+{
+ ucl_object_t *elt, *it, *found, *additional_schema = NULL,
+ *first_unvalidated = NULL;
+ ucl_object_iter_t iter = NULL, piter = NULL;
+ bool ret = true, allow_additional = true, need_unique = false;
+ int64_t minmax;
+
+ while (ret && (elt = ucl_iterate_object (schema, &iter, true)) != NULL) {
+ if (strcmp (ucl_object_key (elt), "items") == 0) {
+ if (elt->type == UCL_ARRAY) {
+ found = obj->value.av;
+ while (ret && (it = ucl_iterate_object (elt, &piter, true)) != NULL) {
+ if (found) {
+ ret = ucl_schema_validate (it, found, false, err, root);
+ found = found->next;
+ }
+ }
+ if (found != NULL) {
+ /* The first element that is not validated */
+ first_unvalidated = found;
+ }
+ }
+ else if (elt->type == UCL_OBJECT) {
+ /* Validate all items using the specified schema */
+ while (ret && (it = ucl_iterate_object (obj, &piter, true)) != NULL) {
+ ret = ucl_schema_validate (elt, it, false, err, root);
+ }
+ }
+ else {
+ ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, elt,
+ "items attribute is invalid in schema");
+ ret = false;
+ break;
+ }
+ }
+ else if (strcmp (ucl_object_key (elt), "additionalItems") == 0) {
+ if (elt->type == UCL_BOOLEAN) {
+ if (!ucl_object_toboolean (elt)) {
+ /* Deny additional fields completely */
+ allow_additional = false;
+ }
+ }
+ else if (elt->type == UCL_OBJECT) {
+ /* Define validator for additional fields */
+ additional_schema = elt;
+ }
+ else {
+ ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, elt,
+ "additionalItems attribute is invalid in schema");
+ ret = false;
+ break;
+ }
+ }
+ else if (elt->type == UCL_BOOLEAN &&
+ strcmp (ucl_object_key (elt), "uniqueItems") == 0) {
+ need_unique = ucl_object_toboolean (elt);
+ }
+ else if (strcmp (ucl_object_key (elt), "minItems") == 0
+ && ucl_object_toint_safe (elt, &minmax)) {
+ if (obj->len < minmax) {
+ ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
+ "array has not enough items: %u, minimum is: %u",
+ obj->len, (unsigned)minmax);
+ ret = false;
+ break;
+ }
+ }
+ else if (strcmp (ucl_object_key (elt), "maxItems") == 0
+ && ucl_object_toint_safe (elt, &minmax)) {
+ if (obj->len > minmax) {
+ ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
+ "array has too many items: %u, maximum is: %u",
+ obj->len, (unsigned)minmax);
+ ret = false;
+ break;
+ }
+ }
+ }
+
+ if (ret) {
+ /* Additional properties */
+ if (!allow_additional || additional_schema != NULL) {
+ if (first_unvalidated != NULL) {
+ if (!allow_additional) {
+ ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
+ "array has undefined item");
+ ret = false;
+ }
+ else if (additional_schema != NULL) {
+ elt = first_unvalidated;
+ while (elt) {
+ if (!ucl_schema_validate (additional_schema, elt, false,
+ err, root)) {
+ ret = false;
+ break;
+ }
+ elt = elt->next;
+ }
+ }
+ }
+ }
+ /* Required properties */
+ if (ret && need_unique) {
+ ret = ucl_schema_array_is_unique (obj, err);
+ }
+ }
+
+ return ret;
+}
+
+/*
+ * Returns whether this object is allowed for this type
+ */
+static bool
+ucl_schema_type_is_allowed (ucl_object_t *type, ucl_object_t *obj,
+ struct ucl_schema_error *err)
+{
+ ucl_object_iter_t iter = NULL;
+ ucl_object_t *elt;
+ const char *type_str;
+ ucl_type_t t;
+
+ if (type == NULL) {
+ /* Any type is allowed */
+ return true;
+ }
+
+ if (type->type == UCL_ARRAY) {
+ /* One of allowed types */
+ while ((elt = ucl_iterate_object (type, &iter, true)) != NULL) {
+ if (ucl_schema_type_is_allowed (elt, obj, err)) {
+ return true;
+ }
+ }
+ }
+ else if (type->type == UCL_STRING) {
+ type_str = ucl_object_tostring (type);
+ if (!ucl_string_to_type (type_str, &t)) {
+ ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, type,
+ "Type attribute is invalid in schema");
+ return false;
+ }
+ if (obj->type != t) {
+ /* Some types are actually compatible */
+ if (obj->type == UCL_TIME && t == UCL_FLOAT) {
+ return true;
+ }
+ else if (obj->type == UCL_INT && t == UCL_FLOAT) {
+ return true;
+ }
+ else {
+ ucl_schema_create_error (err, UCL_SCHEMA_TYPE_MISMATCH, obj,
+ "Invalid type of %s, expected %s",
+ ucl_object_type_to_string (obj->type),
+ ucl_object_type_to_string (t));
+ }
+ }
+ else {
+ /* Types are equal */
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/*
+ * Check if object is equal to one of elements of enum
+ */
+static bool
+ucl_schema_validate_enum (ucl_object_t *en, ucl_object_t *obj,
+ struct ucl_schema_error *err)
+{
+ ucl_object_iter_t iter = NULL;
+ ucl_object_t *elt;
+ bool ret = false;
+
+ while ((elt = ucl_iterate_object (en, &iter, true)) != NULL) {
+ if (ucl_object_compare (elt, obj) == 0) {
+ ret = true;
+ break;
+ }
+ }
+
+ if (!ret) {
+ ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
+ "object is not one of enumerated patterns");
+ }
+
+ return ret;
+}
+
+
+/*
+ * Check a single ref component
+ */
+static ucl_object_t *
+ucl_schema_resolve_ref_component (ucl_object_t *cur,
+ const char *refc, int len,
+ struct ucl_schema_error *err)
+{
+ ucl_object_t *res = NULL;
+ char *err_str;
+ int num, i;
+
+ if (cur->type == UCL_OBJECT) {
+ /* Find a key inside an object */
+ res = ucl_object_find_keyl (cur, refc, len);
+ if (res == NULL) {
+ ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, cur,
+ "reference %s is invalid, missing path component", refc);
+ return NULL;
+ }
+ }
+ else if (cur->type == UCL_ARRAY) {
+ /* We must figure out a number inside array */
+ num = strtoul (refc, &err_str, 10);
+ if (err_str != NULL && *err_str != '/' && *err_str != '\0') {
+ ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, cur,
+ "reference %s is invalid, invalid item number", refc);
+ return NULL;
+ }
+ res = cur->value.av;
+ i = 0;
+ while (res != NULL) {
+ if (i == num) {
+ break;
+ }
+ res = res->next;
+ }
+ if (res == NULL) {
+ ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, cur,
+ "reference %s is invalid, item number %d does not exist",
+ refc, num);
+ return NULL;
+ }
+ }
+ else {
+ ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, res,
+ "reference %s is invalid, contains primitive object in the path",
+ refc);
+ return NULL;
+ }
+
+ return res;
+}
+/*
+ * Find reference schema
+ */
+static ucl_object_t *
+ucl_schema_resolve_ref (ucl_object_t *root, const char *ref,
+ struct ucl_schema_error *err)
+{
+ const char *p, *c;
+ ucl_object_t *res = NULL;
+
+
+ if (ref[0] != '#') {
+ ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, root,
+ "reference %s is invalid, not started with #", ref);
+ return NULL;
+ }
+ if (ref[1] == '/') {
+ p = &ref[2];
+ }
+ else if (ref[1] == '\0') {
+ return root;
+ }
+ else {
+ ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, root,
+ "reference %s is invalid, not started with #/", ref);
+ return NULL;
+ }
+
+ c = p;
+ res = root;
+
+ while (*p != '\0') {
+ if (*p == '/') {
+ if (p - c == 0) {
+ ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, res,
+ "reference %s is invalid, empty path component", ref);
+ return NULL;
+ }
+ /* Now we have some url part, so we need to figure out where we are */
+ res = ucl_schema_resolve_ref_component (res, c, p - c, err);
+ if (res == NULL) {
+ return NULL;
+ }
+ c = p + 1;
+ }
+ p ++;
+ }
+
+ if (p - c != 0) {
+ res = ucl_schema_resolve_ref_component (res, c, p - c, err);
+ }
+
+ if (res == NULL || res->type != UCL_OBJECT) {
+ ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, res,
+ "reference %s is invalid, cannot find specified object",
+ ref);
+ return NULL;
+ }
+
+ return res;
+}
+
+static bool
+ucl_schema_validate_values (ucl_object_t *schema, ucl_object_t *obj,
+ struct ucl_schema_error *err)
+{
+ ucl_object_t *elt, *cur;
+ int64_t constraint, i;
+
+ elt = ucl_object_find_key (schema, "maxValues");
+ if (elt != NULL && elt->type == UCL_INT) {
+ constraint = ucl_object_toint (elt);
+ cur = obj;
+ i = 0;
+ while (cur) {
+ if (i > constraint) {
+ ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
+ "object has more values than defined: %ld",
+ (long int)constraint);
+ return false;
+ }
+ i ++;
+ cur = cur->next;
+ }
+ }
+ elt = ucl_object_find_key (schema, "minValues");
+ if (elt != NULL && elt->type == UCL_INT) {
+ constraint = ucl_object_toint (elt);
+ cur = obj;
+ i = 0;
+ while (cur) {
+ if (i >= constraint) {
+ break;
+ }
+ i ++;
+ cur = cur->next;
+ }
+ if (i < constraint) {
+ ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
+ "object has less values than defined: %ld",
+ (long int)constraint);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static bool
+ucl_schema_validate (ucl_object_t *schema,
+ ucl_object_t *obj, bool try_array,
+ struct ucl_schema_error *err,
+ ucl_object_t *root)
+{
+ ucl_object_t *elt, *cur;
+ ucl_object_iter_t iter = NULL;
+ bool ret;
+
+ if (schema->type != UCL_OBJECT) {
+ ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, schema,
+ "schema is %s instead of object", ucl_object_type_to_string (schema->type));
+ return false;
+ }
+
+ if (try_array) {
+ /*
+ * Special case for multiple values
+ */
+ if (!ucl_schema_validate_values (schema, obj, err)) {
+ return false;
+ }
+ LL_FOREACH (obj, cur) {
+ if (!ucl_schema_validate (schema, cur, false, err, root)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ elt = ucl_object_find_key (schema, "enum");
+ if (elt != NULL && elt->type == UCL_ARRAY) {
+ if (!ucl_schema_validate_enum (elt, obj, err)) {
+ return false;
+ }
+ }
+
+ elt = ucl_object_find_key (schema, "allOf");
+ if (elt != NULL && elt->type == UCL_ARRAY) {
+ iter = NULL;
+ while ((cur = ucl_iterate_object (elt, &iter, true)) != NULL) {
+ ret = ucl_schema_validate (cur, obj, true, err, root);
+ if (!ret) {
+ return false;
+ }
+ }
+ }
+
+ elt = ucl_object_find_key (schema, "anyOf");
+ if (elt != NULL && elt->type == UCL_ARRAY) {
+ iter = NULL;
+ while ((cur = ucl_iterate_object (elt, &iter, true)) != NULL) {
+ ret = ucl_schema_validate (cur, obj, true, err, root);
+ if (ret) {
+ break;
+ }
+ }
+ if (!ret) {
+ return false;
+ }
+ else {
+ /* Reset error */
+ err->code = UCL_SCHEMA_OK;
+ }
+ }
+
+ elt = ucl_object_find_key (schema, "oneOf");
+ if (elt != NULL && elt->type == UCL_ARRAY) {
+ iter = NULL;
+ ret = false;
+ while ((cur = ucl_iterate_object (elt, &iter, true)) != NULL) {
+ if (!ret) {
+ ret = ucl_schema_validate (cur, obj, true, err, root);
+ }
+ else if (ucl_schema_validate (cur, obj, true, err, root)) {
+ ret = false;
+ break;
+ }
+ }
+ if (!ret) {
+ return false;
+ }
+ }
+
+ elt = ucl_object_find_key (schema, "not");
+ if (elt != NULL && elt->type == UCL_OBJECT) {
+ if (ucl_schema_validate (elt, obj, true, err, root)) {
+ return false;
+ }
+ else {
+ /* Reset error */
+ err->code = UCL_SCHEMA_OK;
+ }
+ }
+
+ elt = ucl_object_find_key (schema, "$ref");
+ if (elt != NULL) {
+ cur = ucl_schema_resolve_ref (root, ucl_object_tostring (elt), err);
+ if (cur == NULL) {
+ return false;
+ }
+ if (!ucl_schema_validate (cur, obj, try_array, err, root)) {
+ return false;
+ }
+ }
+
+ elt = ucl_object_find_key (schema, "type");
+ if (!ucl_schema_type_is_allowed (elt, obj, err)) {
+ return false;
+ }
+
+ switch (obj->type) {
+ case UCL_OBJECT:
+ return ucl_schema_validate_object (schema, obj, err, root);
+ break;
+ case UCL_ARRAY:
+ return ucl_schema_validate_array (schema, obj, err, root);
+ break;
+ case UCL_INT:
+ case UCL_FLOAT:
+ return ucl_schema_validate_number (schema, obj, err);
+ break;
+ case UCL_STRING:
+ return ucl_schema_validate_string (schema, obj, err);
+ break;
+ default:
+ break;
+ }
+
+ return true;
+}
+
+bool
+ucl_object_validate (ucl_object_t *schema,
+ ucl_object_t *obj, struct ucl_schema_error *err)
+{
+ return ucl_schema_validate (schema, obj, true, err, schema);
+}
diff --git a/contrib/libucl/src/ucl_util.c b/contrib/libucl/src/ucl_util.c
index 34080d4..f96c23c 100644
--- a/contrib/libucl/src/ucl_util.c
+++ b/contrib/libucl/src/ucl_util.c
@@ -25,7 +25,9 @@
#include "ucl_internal.h"
#include "ucl_chartable.h"
+#ifdef HAVE_LIBGEN_H
#include <libgen.h> /* For dirname */
+#endif
#ifdef HAVE_OPENSSL
#include <openssl/err.h>
@@ -35,17 +37,36 @@
#include <openssl/evp.h>
#endif
+#ifdef CURL_FOUND
+#include <curl/curl.h>
+#endif
+#ifdef HAVE_FETCH_H
+#include <fetch.h>
+#endif
+
#ifdef _WIN32
#include <windows.h>
+#ifndef PROT_READ
#define PROT_READ 1
+#endif
+#ifndef PROT_WRITE
#define PROT_WRITE 2
+#endif
+#ifndef PROT_READWRITE
#define PROT_READWRITE 3
+#endif
+#ifndef MAP_SHARED
#define MAP_SHARED 1
+#endif
+#ifndef MAP_PRIVATE
#define MAP_PRIVATE 2
+#endif
+#ifndef MAP_FAILED
#define MAP_FAILED ((void *) -1)
+#endif
-static void *mmap(char *addr, size_t length, int prot, int access, int fd, off_t offset)
+static void *ucl_mmap(char *addr, size_t length, int prot, int access, int fd, off_t offset)
{
void *map = NULL;
HANDLE handle = INVALID_HANDLE_VALUE;
@@ -83,7 +104,7 @@ static void *mmap(char *addr, size_t length, int prot, int access, int fd, off_t
return (void *) ((char *) map + offset);
}
-static int munmap(void *map,size_t length)
+static int ucl_munmap(void *map,size_t length)
{
if (!UnmapViewOfFile(map)) {
return(-1);
@@ -91,7 +112,7 @@ static int munmap(void *map,size_t length)
return(0);
}
-static char* realpath(const char *path, char *resolved_path) {
+static char* ucl_realpath(const char *path, char *resolved_path) {
char *p;
char tmp[MAX_PATH + 1];
strncpy(tmp, path, sizeof(tmp)-1);
@@ -102,6 +123,10 @@ static char* realpath(const char *path, char *resolved_path) {
}
return _fullpath(resolved_path, tmp, MAX_PATH);
}
+#else
+#define ucl_mmap mmap
+#define ucl_munmap munmap
+#define ucl_realpath realpath
#endif
/**
@@ -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);
+}
OpenPOWER on IntegriCloud