diff options
author | bapt <bapt@FreeBSD.org> | 2014-03-22 17:28:14 +0000 |
---|---|---|
committer | bapt <bapt@FreeBSD.org> | 2014-03-22 17:28:14 +0000 |
commit | b471b8e16d6f2c205ae497402bad0a60c9cd6fa8 (patch) | |
tree | aabc973b2e4479277f8fe9aa0e8ed539826fdca3 /contrib/libucl/src | |
parent | 841158cbfbfc6b699dbc10892c6cb878a66a5d7e (diff) | |
parent | 6b4d859b54b28a9d46c317ff21676aa37241f6de (diff) | |
download | FreeBSD-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.am | 23 | ||||
-rw-r--r-- | contrib/libucl/src/tree.h | 212 | ||||
-rw-r--r-- | contrib/libucl/src/ucl_emitter.c | 12 | ||||
-rw-r--r-- | contrib/libucl/src/ucl_hash.c | 1 | ||||
-rw-r--r-- | contrib/libucl/src/ucl_internal.h | 56 | ||||
-rw-r--r-- | contrib/libucl/src/ucl_parser.c | 85 | ||||
-rw-r--r-- | contrib/libucl/src/ucl_schema.c | 1008 | ||||
-rw-r--r-- | contrib/libucl/src/ucl_util.c | 686 |
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 (®, pattern, REG_EXTENDED | REG_NOSUB) == 0) { + while ((elt = ucl_iterate_object (obj, &iter, true)) != NULL) { + if (regexec (®, ucl_object_key (elt), 0, NULL, 0) == 0) { + res = elt; + break; + } + } + regfree (®); + } + + 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); +} |