summaryrefslogtreecommitdiff
path: root/src/ucl_util.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/ucl_util.c')
-rw-r--r--src/ucl_util.c901
1 files changed, 795 insertions, 106 deletions
diff --git a/src/ucl_util.c b/src/ucl_util.c
index 41e012bf15bbc..730a5c4afd57d 100644
--- a/src/ucl_util.c
+++ b/src/ucl_util.c
@@ -1,4 +1,5 @@
/* Copyright (c) 2013, Vsevolod Stakhov
+ * Copyright (c) 2015 Allan Jude <allanjude@freebsd.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -25,6 +26,7 @@
#include "ucl_internal.h"
#include "ucl_chartable.h"
#include "kvec.h"
+#include <stdarg.h>
#ifndef _WIN32
#include <glob.h>
@@ -230,11 +232,13 @@ ucl_object_free_internal (ucl_object_t *obj, bool allow_rec, ucl_object_dtor dto
kv_destroy (*vec);
UCL_FREE (sizeof (*vec), vec);
}
+ obj->value.av = NULL;
}
else if (obj->type == UCL_OBJECT) {
if (obj->value.ov != NULL) {
ucl_hash_destroy (obj->value.ov, (ucl_hash_free_func *)dtor);
}
+ obj->value.ov = NULL;
}
tmp = obj->next;
dtor (obj);
@@ -266,6 +270,17 @@ ucl_unescape_json_string (char *str, size_t len)
while (len) {
if (*h == '\\') {
h ++;
+
+ if (len == 1) {
+ /*
+ * If \ is last, then do not try to go further
+ * Issue: #74
+ */
+ len --;
+ *t++ = '\\';
+ continue;
+ }
+
switch (*h) {
case 'n':
*t++ = '\n';
@@ -350,7 +365,10 @@ ucl_unescape_json_string (char *str, size_t len)
else {
*t++ = *h++;
}
- len --;
+
+ if (len > 0) {
+ len --;
+ }
}
*t = '\0';
@@ -392,11 +410,24 @@ ucl_copy_value_trash (const ucl_object_t *obj)
if (obj->type == UCL_STRING) {
/* Special case for strings */
- deconst->trash_stack[UCL_TRASH_VALUE] = malloc (obj->len + 1);
- if (deconst->trash_stack[UCL_TRASH_VALUE] != NULL) {
- memcpy (deconst->trash_stack[UCL_TRASH_VALUE], obj->value.sv, obj->len);
- deconst->trash_stack[UCL_TRASH_VALUE][obj->len] = '\0';
- deconst->value.sv = obj->trash_stack[UCL_TRASH_VALUE];
+ if (obj->flags & UCL_OBJECT_BINARY) {
+ deconst->trash_stack[UCL_TRASH_VALUE] = malloc (obj->len);
+ if (deconst->trash_stack[UCL_TRASH_VALUE] != NULL) {
+ memcpy (deconst->trash_stack[UCL_TRASH_VALUE],
+ obj->value.sv,
+ obj->len);
+ deconst->value.sv = obj->trash_stack[UCL_TRASH_VALUE];
+ }
+ }
+ else {
+ deconst->trash_stack[UCL_TRASH_VALUE] = malloc (obj->len + 1);
+ if (deconst->trash_stack[UCL_TRASH_VALUE] != NULL) {
+ memcpy (deconst->trash_stack[UCL_TRASH_VALUE],
+ obj->value.sv,
+ obj->len);
+ deconst->trash_stack[UCL_TRASH_VALUE][obj->len] = '\0';
+ deconst->value.sv = obj->trash_stack[UCL_TRASH_VALUE];
+ }
}
}
else {
@@ -406,10 +437,11 @@ ucl_copy_value_trash (const ucl_object_t *obj)
}
deconst->flags |= UCL_OBJECT_ALLOCATED_VALUE;
}
+
return obj->trash_stack[UCL_TRASH_VALUE];
}
-UCL_EXTERN ucl_object_t*
+ucl_object_t*
ucl_parser_get_object (struct ucl_parser *parser)
{
if (parser->state != UCL_STATE_ERROR && parser->top_obj != NULL) {
@@ -419,7 +451,7 @@ ucl_parser_get_object (struct ucl_parser *parser)
return NULL;
}
-UCL_EXTERN void
+void
ucl_parser_free (struct ucl_parser *parser)
{
struct ucl_stack *stack, *stmp;
@@ -427,6 +459,7 @@ ucl_parser_free (struct ucl_parser *parser)
struct ucl_chunk *chunk, *ctmp;
struct ucl_pubkey *key, *ktmp;
struct ucl_variable *var, *vtmp;
+ ucl_object_t *tr, *trtmp;
if (parser == NULL) {
return;
@@ -436,6 +469,10 @@ ucl_parser_free (struct ucl_parser *parser)
ucl_object_unref (parser->top_obj);
}
+ if (parser->includepaths != NULL) {
+ ucl_object_unref (parser->includepaths);
+ }
+
LL_FOREACH_SAFE (parser->stack, stack, stmp) {
free (stack);
}
@@ -455,6 +492,9 @@ ucl_parser_free (struct ucl_parser *parser)
free (var->var);
UCL_FREE (sizeof (struct ucl_variable), var);
}
+ LL_FOREACH_SAFE (parser->trash_objs, tr, trtmp) {
+ ucl_object_free_internal (tr, false, ucl_object_dtor_free);
+ }
if (parser->err != NULL) {
utstring_free (parser->err);
@@ -467,29 +507,61 @@ ucl_parser_free (struct ucl_parser *parser)
UCL_FREE (sizeof (struct ucl_parser), parser);
}
-UCL_EXTERN const char *
+const char *
ucl_parser_get_error(struct ucl_parser *parser)
{
if (parser == NULL) {
return NULL;
}
- if (parser->err == NULL)
+ if (parser->err == NULL) {
return NULL;
+ }
- return utstring_body(parser->err);
+ return utstring_body (parser->err);
}
-UCL_EXTERN void
+int
+ucl_parser_get_error_code(struct ucl_parser *parser)
+{
+ if (parser == NULL) {
+ return 0;
+ }
+
+ return parser->err_code;
+}
+
+unsigned
+ucl_parser_get_column(struct ucl_parser *parser)
+{
+ if (parser == NULL || parser->chunks == NULL) {
+ return 0;
+ }
+
+ return parser->chunks->column;
+}
+
+unsigned
+ucl_parser_get_linenum(struct ucl_parser *parser)
+{
+ if (parser == NULL || parser->chunks == NULL) {
+ return 0;
+ }
+
+ return parser->chunks->line;
+}
+
+void
ucl_parser_clear_error(struct ucl_parser *parser)
{
if (parser != NULL && parser->err != NULL) {
utstring_free(parser->err);
parser->err = NULL;
+ parser->err_code = 0;
}
}
-UCL_EXTERN bool
+bool
ucl_pubkey_add (struct ucl_parser *parser, const unsigned char *key, size_t len)
{
#ifndef HAVE_OPENSSL
@@ -738,6 +810,20 @@ ucl_sig_check (const unsigned char *data, size_t datalen,
}
#endif
+struct ucl_include_params {
+ bool check_signature;
+ bool must_exist;
+ bool use_glob;
+ bool use_prefix;
+ bool soft_fail;
+ bool allow_glob;
+ unsigned priority;
+ enum ucl_duplicate_strategy strat;
+ enum ucl_parse_type parse_type;
+ const char *prefix;
+ const char *target;
+};
+
/**
* Include an url to configuration
* @param data
@@ -748,8 +834,8 @@ ucl_sig_check (const unsigned char *data, size_t datalen,
*/
static bool
ucl_include_url (const unsigned char *data, size_t len,
- struct ucl_parser *parser, bool check_signature, bool must_exist,
- unsigned priority)
+ struct ucl_parser *parser,
+ struct ucl_include_params *params)
{
bool res;
@@ -761,11 +847,11 @@ ucl_include_url (const unsigned char *data, size_t len,
snprintf (urlbuf, sizeof (urlbuf), "%.*s", (int)len, data);
- if (!ucl_fetch_url (urlbuf, &buf, &buflen, &parser->err, must_exist)) {
- return (!must_exist || false);
+ if (!ucl_fetch_url (urlbuf, &buf, &buflen, &parser->err, params->must_exist)) {
+ return (!params->must_exist || false);
}
- if (check_signature) {
+ if (params->check_signature) {
#if (defined(HAVE_OPENSSL) && OPENSSL_VERSION_NUMBER >= 0x10000000L)
unsigned char *sigbuf = NULL;
size_t siglen = 0;
@@ -792,7 +878,8 @@ ucl_include_url (const unsigned char *data, size_t len,
prev_state = parser->state;
parser->state = UCL_STATE_INIT;
- res = ucl_parser_add_chunk_priority (parser, buf, buflen, priority);
+ res = ucl_parser_add_chunk_full (parser, buf, buflen, params->priority,
+ params->strat, params->parse_type);
if (res == true) {
/* Remove chunk from the stack */
chunk = parser->chunks;
@@ -821,22 +908,27 @@ ucl_include_url (const unsigned char *data, size_t len,
*/
static bool
ucl_include_file_single (const unsigned char *data, size_t len,
- struct ucl_parser *parser, bool check_signature, bool must_exist,
- unsigned priority)
+ struct ucl_parser *parser, struct ucl_include_params *params)
{
bool res;
struct ucl_chunk *chunk;
unsigned char *buf = NULL;
- char *old_curfile;
- size_t buflen;
+ char *old_curfile, *ext;
+ size_t buflen = 0;
char filebuf[PATH_MAX], realbuf[PATH_MAX];
int prev_state;
struct ucl_variable *cur_var, *tmp_var, *old_curdir = NULL,
*old_filename = NULL;
+ ucl_object_t *nest_obj = NULL, *old_obj = NULL, *new_obj = NULL;
+ ucl_hash_t *container = NULL;
+ struct ucl_stack *st = NULL;
snprintf (filebuf, sizeof (filebuf), "%.*s", (int)len, data);
if (ucl_realpath (filebuf, realbuf) == NULL) {
- if (!must_exist) {
+ if (params->soft_fail) {
+ return false;
+ }
+ if (!params->must_exist) {
return true;
}
ucl_create_err (&parser->err, "cannot open file %s: %s",
@@ -847,16 +939,23 @@ ucl_include_file_single (const unsigned char *data, size_t len,
if (parser->cur_file && strcmp (realbuf, parser->cur_file) == 0) {
/* We are likely including the file itself */
+ if (params->soft_fail) {
+ return false;
+ }
+
ucl_create_err (&parser->err, "trying to include the file %s from itself",
realbuf);
return false;
}
- if (!ucl_fetch_file (realbuf, &buf, &buflen, &parser->err, must_exist)) {
- return (!must_exist || false);
+ if (!ucl_fetch_file (realbuf, &buf, &buflen, &parser->err, params->must_exist)) {
+ if (params->soft_fail) {
+ return false;
+ }
+ return (!params->must_exist || false);
}
- if (check_signature) {
+ if (params->check_signature) {
#if (defined(HAVE_OPENSSL) && OPENSSL_VERSION_NUMBER >= 0x10000000L)
unsigned char *sigbuf = NULL;
size_t siglen = 0;
@@ -900,14 +999,128 @@ ucl_include_file_single (const unsigned char *data, size_t len,
prev_state = parser->state;
parser->state = UCL_STATE_INIT;
- res = ucl_parser_add_chunk_priority (parser, buf, buflen, priority);
- if (!res && !must_exist) {
+ if (params->use_prefix && params->prefix == NULL) {
+ /* Auto generate a key name based on the included filename */
+ params->prefix = basename (realbuf);
+ ext = strrchr (params->prefix, '.');
+ if (ext != NULL && (strcmp (ext, ".conf") == 0 || strcmp (ext, ".ucl") == 0)) {
+ /* Strip off .conf or .ucl */
+ *ext = '\0';
+ }
+ }
+ if (params->prefix != NULL) {
+ /* This is a prefixed include */
+ container = parser->stack->obj->value.ov;
+
+ old_obj = __DECONST (ucl_object_t *, ucl_hash_search (container,
+ params->prefix, strlen (params->prefix)));
+
+ if (strcasecmp (params->target, "array") == 0 && old_obj == NULL) {
+ /* Create an array with key: prefix */
+ old_obj = ucl_object_new_full (UCL_ARRAY, params->priority);
+ old_obj->key = params->prefix;
+ old_obj->keylen = strlen (params->prefix);
+ ucl_copy_key_trash(old_obj);
+ old_obj->prev = old_obj;
+ old_obj->next = NULL;
+
+ container = ucl_hash_insert_object (container, old_obj,
+ parser->flags & UCL_PARSER_KEY_LOWERCASE);
+ parser->stack->obj->len ++;
+
+ nest_obj = ucl_object_new_full (UCL_OBJECT, params->priority);
+ nest_obj->prev = nest_obj;
+ nest_obj->next = NULL;
+
+ ucl_array_append (old_obj, nest_obj);
+ }
+ else if (old_obj == NULL) {
+ /* Create an object with key: prefix */
+ nest_obj = ucl_object_new_full (UCL_OBJECT, params->priority);
+ nest_obj->key = params->prefix;
+ nest_obj->keylen = strlen (params->prefix);
+ ucl_copy_key_trash(nest_obj);
+ nest_obj->prev = nest_obj;
+ nest_obj->next = NULL;
+
+ container = ucl_hash_insert_object (container, nest_obj,
+ parser->flags & UCL_PARSER_KEY_LOWERCASE);
+ parser->stack->obj->len ++;
+ }
+ else if (strcasecmp (params->target, "array") == 0 ||
+ ucl_object_type(old_obj) == UCL_ARRAY) {
+ if (ucl_object_type(old_obj) == UCL_ARRAY) {
+ /* Append to the existing array */
+ nest_obj = ucl_object_new_full (UCL_OBJECT, params->priority);
+ nest_obj->prev = nest_obj;
+ nest_obj->next = NULL;
+
+ ucl_array_append (old_obj, nest_obj);
+ }
+ else {
+ /* Convert the object to an array */
+ new_obj = ucl_object_typed_new (UCL_ARRAY);
+ new_obj->key = old_obj->key;
+ new_obj->keylen = old_obj->keylen;
+ new_obj->flags |= UCL_OBJECT_MULTIVALUE;
+ new_obj->prev = new_obj;
+ new_obj->next = NULL;
+
+ nest_obj = ucl_object_new_full (UCL_OBJECT, params->priority);
+ nest_obj->prev = nest_obj;
+ nest_obj->next = NULL;
+
+ ucl_array_append (new_obj, old_obj);
+ ucl_array_append (new_obj, nest_obj);
+ ucl_hash_replace (container, old_obj, new_obj);
+ }
+ }
+ else {
+ if (ucl_object_type (old_obj) == UCL_OBJECT) {
+ /* Append to existing Object*/
+ nest_obj = old_obj;
+ }
+ else {
+ /* The key is not an object */
+ ucl_create_err (&parser->err,
+ "Conflicting type for key: %s",
+ params->prefix);
+ return false;
+ }
+ }
+
+ /* Put all of the content of the include inside that object */
+ parser->stack->obj->value.ov = container;
+
+ if (nest_obj != NULL) {
+ st = UCL_ALLOC (sizeof (struct ucl_stack));
+ if (st == NULL) {
+ ucl_create_err (&parser->err, "cannot allocate memory for an object");
+ ucl_object_unref (nest_obj);
+ return NULL;
+ }
+ st->obj = nest_obj;
+ st->level = parser->stack->level;
+ LL_PREPEND (parser->stack, st);
+ parser->cur_obj = nest_obj;
+ }
+ }
+
+ res = ucl_parser_add_chunk_full (parser, buf, buflen, params->priority,
+ params->strat, params->parse_type);
+ if (!res && !params->must_exist) {
/* Free error */
utstring_free (parser->err);
parser->err = NULL;
parser->state = UCL_STATE_AFTER_VALUE;
}
+ /* Stop nesting the include, take 1 level off the stack */
+ if (params->prefix != NULL && nest_obj != NULL) {
+ parser->stack = st->next;
+ UCL_FREE (sizeof (struct ucl_stack), st);
+ }
+
/* Remove chunk from the stack */
chunk = parser->chunks;
if (chunk != NULL) {
@@ -917,6 +1130,10 @@ ucl_include_file_single (const unsigned char *data, size_t len,
}
/* Restore old file vars */
+ if (parser->cur_file) {
+ free (parser->cur_file);
+ }
+
parser->cur_file = old_curfile;
DL_FOREACH_SAFE (parser->variables, cur_var, tmp_var) {
if (strcmp (cur_var->var, "CURDIR") == 0 && old_curdir) {
@@ -938,9 +1155,6 @@ ucl_include_file_single (const unsigned char *data, size_t len,
if (old_curdir) {
DL_APPEND (parser->variables, old_curdir);
}
- if (old_curfile) {
- free (old_curfile);
- }
parser->state = prev_state;
@@ -961,8 +1175,7 @@ ucl_include_file_single (const unsigned char *data, size_t len,
*/
static bool
ucl_include_file (const unsigned char *data, size_t len,
- struct ucl_parser *parser, bool check_signature, bool must_exist,
- bool allow_glob, unsigned priority)
+ struct ucl_parser *parser, struct ucl_include_params *params)
{
const unsigned char *p = data, *end = data + len;
bool need_glob = false;
@@ -971,9 +1184,8 @@ ucl_include_file (const unsigned char *data, size_t len,
size_t i;
#ifndef _WIN32
- if (!allow_glob) {
- return ucl_include_file_single (data, len, parser, check_signature,
- must_exist, priority);
+ if (!params->allow_glob) {
+ return ucl_include_file_single (data, len, parser, params);
}
else {
/* Check for special symbols in a filename */
@@ -987,14 +1199,17 @@ ucl_include_file (const unsigned char *data, size_t len,
if (need_glob) {
glob_t globbuf;
memset (&globbuf, 0, sizeof (globbuf));
- ucl_strlcpy (glob_pattern, (const char *)data, sizeof (glob_pattern));
+ ucl_strlcpy (glob_pattern, (const char *)data,
+ (len + 1 < sizeof (glob_pattern) ? len + 1 : sizeof (glob_pattern)));
if (glob (glob_pattern, 0, NULL, &globbuf) != 0) {
- return (!must_exist || false);
+ return (!params->must_exist || false);
}
for (i = 0; i < globbuf.gl_pathc; i ++) {
if (!ucl_include_file_single ((unsigned char *)globbuf.gl_pathv[i],
- strlen (globbuf.gl_pathv[i]), parser, check_signature,
- must_exist, priority)) {
+ strlen (globbuf.gl_pathv[i]), parser, params)) {
+ if (params->soft_fail) {
+ continue;
+ }
globfree (&globbuf);
return false;
}
@@ -1002,22 +1217,20 @@ ucl_include_file (const unsigned char *data, size_t len,
}
globfree (&globbuf);
- if (cnt == 0 && must_exist) {
+ if (cnt == 0 && params->must_exist) {
ucl_create_err (&parser->err, "cannot match any files for pattern %s",
glob_pattern);
return false;
}
}
else {
- return ucl_include_file_single (data, len, parser, check_signature,
- must_exist, priority);
+ return ucl_include_file_single (data, len, parser, params);
}
}
#else
/* Win32 compilers do not support globbing. Therefore, for Win32,
treat allow_glob/need_glob as a NOOP and just return */
- return ucl_include_file_single (data, len, parser, check_signature,
- must_exist, priority);
+ return ucl_include_file_single (data, len, parser, params);
#endif
return true;
@@ -1039,52 +1252,123 @@ ucl_include_common (const unsigned char *data, size_t len,
bool default_try,
bool default_sign)
{
- bool try_load, allow_glob, allow_url, need_sign;
- unsigned priority;
+ bool allow_url, search;
+ const char *duplicate;
const ucl_object_t *param;
- ucl_object_iter_t it = NULL;
+ ucl_object_iter_t it = NULL, ip = NULL;
+ char ipath[PATH_MAX];
+ struct ucl_include_params params;
/* Default values */
- try_load = default_try;
- allow_glob = false;
- allow_url = true;
- need_sign = default_sign;
- priority = 0;
+ params.soft_fail = default_try;
+ params.allow_glob = false;
+ params.check_signature = default_sign;
+ params.use_prefix = false;
+ params.target = "object";
+ params.prefix = NULL;
+ params.priority = 0;
+ params.parse_type = UCL_PARSE_UCL;
+ params.strat = UCL_DUPLICATE_APPEND;
+ params.must_exist = !default_try;
+
+ search = false;
/* Process arguments */
if (args != NULL && args->type == UCL_OBJECT) {
while ((param = ucl_iterate_object (args, &it, true)) != NULL) {
if (param->type == UCL_BOOLEAN) {
- if (strcmp (param->key, "try") == 0) {
- try_load = ucl_object_toboolean (param);
+ if (strncmp (param->key, "try", param->keylen) == 0) {
+ params.must_exist = !ucl_object_toboolean (param);
+ }
+ else if (strncmp (param->key, "sign", param->keylen) == 0) {
+ params.check_signature = ucl_object_toboolean (param);
+ }
+ else if (strncmp (param->key, "glob", param->keylen) == 0) {
+ params.allow_glob = ucl_object_toboolean (param);
+ }
+ else if (strncmp (param->key, "url", param->keylen) == 0) {
+ allow_url = ucl_object_toboolean (param);
+ }
+ else if (strncmp (param->key, "prefix", param->keylen) == 0) {
+ params.use_prefix = ucl_object_toboolean (param);
+ }
+ }
+ else if (param->type == UCL_STRING) {
+ if (strncmp (param->key, "key", param->keylen) == 0) {
+ params.prefix = ucl_object_tostring (param);
}
- else if (strcmp (param->key, "sign") == 0) {
- need_sign = ucl_object_toboolean (param);
+ else if (strncmp (param->key, "target", param->keylen) == 0) {
+ params.target = ucl_object_tostring (param);
}
- else if (strcmp (param->key, "glob") == 0) {
- allow_glob = ucl_object_toboolean (param);
+ else if (strncmp (param->key, "duplicate", param->keylen) == 0) {
+ duplicate = ucl_object_tostring (param);
+
+ if (strcmp (duplicate, "append") == 0) {
+ params.strat = UCL_DUPLICATE_APPEND;
+ }
+ else if (strcmp (duplicate, "merge") == 0) {
+ params.strat = UCL_DUPLICATE_MERGE;
+ }
+ else if (strcmp (duplicate, "rewrite") == 0) {
+ params.strat = UCL_DUPLICATE_REWRITE;
+ }
+ else if (strcmp (duplicate, "error") == 0) {
+ params.strat = UCL_DUPLICATE_ERROR;
+ }
}
- else if (strcmp (param->key, "url") == 0) {
- allow_url = ucl_object_toboolean (param);
+ }
+ else if (param->type == UCL_ARRAY) {
+ if (strncmp (param->key, "path", param->keylen) == 0) {
+ ucl_set_include_path (parser, __DECONST(ucl_object_t *, param));
}
}
else if (param->type == UCL_INT) {
- if (strcmp (param->key, "priority") == 0) {
- priority = ucl_object_toint (param);
+ if (strncmp (param->key, "priority", param->keylen) == 0) {
+ params.priority = ucl_object_toint (param);
}
}
}
}
- if (*data == '/' || *data == '.') {
- /* Try to load a file */
- return ucl_include_file (data, len, parser, need_sign, !try_load,
- allow_glob, priority);
+ if (parser->includepaths == NULL) {
+ if (allow_url && ucl_strnstr (data, "://", len) != NULL) {
+ /* Globbing is not used for URL's */
+ return ucl_include_url (data, len, parser, &params);
+ }
+ else if (data != NULL) {
+ /* Try to load a file */
+ return ucl_include_file (data, len, parser, &params);
+ }
}
- else if (allow_url) {
- /* Globbing is not used for URL's */
- return ucl_include_url (data, len, parser, need_sign, !try_load,
- priority);
+ else {
+ if (allow_url && ucl_strnstr (data, "://", len) != NULL) {
+ /* Globbing is not used for URL's */
+ return ucl_include_url (data, len, parser, &params);
+ }
+
+ ip = ucl_object_iterate_new (parser->includepaths);
+ while ((param = ucl_object_iterate_safe (ip, true)) != NULL) {
+ if (ucl_object_type(param) == UCL_STRING) {
+ snprintf (ipath, sizeof (ipath), "%s/%.*s", ucl_object_tostring(param),
+ (int)len, data);
+ if ((search = ucl_include_file (ipath, strlen (ipath),
+ parser, &params))) {
+ if (!params.allow_glob) {
+ break;
+ }
+ }
+ }
+ }
+ ucl_object_iterate_free (ip);
+ if (search == true) {
+ return true;
+ }
+ else {
+ ucl_create_err (&parser->err,
+ "cannot find file: %.*s in search path",
+ (int)len, data);
+ return false;
+ }
}
return false;
@@ -1094,11 +1378,11 @@ ucl_include_common (const unsigned char *data, size_t len,
* Handle include macro
* @param data include data
* @param len length of data
+ * @param args UCL object representing arguments to the macro
* @param ud user data
- * @param err error ptr
* @return
*/
-UCL_EXTERN bool
+bool
ucl_include_handler (const unsigned char *data, size_t len,
const ucl_object_t *args, void* ud)
{
@@ -1111,11 +1395,11 @@ ucl_include_handler (const unsigned char *data, size_t len,
* Handle includes macro
* @param data include data
* @param len length of data
+ * @param args UCL object representing arguments to the macro
* @param ud user data
- * @param err error ptr
* @return
*/
-UCL_EXTERN bool
+bool
ucl_includes_handler (const unsigned char *data, size_t len,
const ucl_object_t *args, void* ud)
{
@@ -1124,8 +1408,15 @@ ucl_includes_handler (const unsigned char *data, size_t len,
return ucl_include_common (data, len, args, parser, false, true);
}
-
-UCL_EXTERN bool
+/**
+ * Handle tryinclude macro
+ * @param data include data
+ * @param len length of data
+ * @param args UCL object representing arguments to the macro
+ * @param ud user data
+ * @return
+ */
+bool
ucl_try_include_handler (const unsigned char *data, size_t len,
const ucl_object_t *args, void* ud)
{
@@ -1134,7 +1425,251 @@ ucl_try_include_handler (const unsigned char *data, size_t len,
return ucl_include_common (data, len, args, parser, true, false);
}
-UCL_EXTERN bool
+/**
+ * Handle priority macro
+ * @param data include data
+ * @param len length of data
+ * @param args UCL object representing arguments to the macro
+ * @param ud user data
+ * @return
+ */
+bool
+ucl_priority_handler (const unsigned char *data, size_t len,
+ const ucl_object_t *args, void* ud)
+{
+ struct ucl_parser *parser = ud;
+ unsigned priority = 255;
+ const ucl_object_t *param;
+ bool found = false;
+ char *value = NULL, *leftover = NULL;
+ ucl_object_iter_t it = NULL;
+
+ if (parser == NULL) {
+ return false;
+ }
+
+ /* Process arguments */
+ if (args != NULL && args->type == UCL_OBJECT) {
+ while ((param = ucl_iterate_object (args, &it, true)) != NULL) {
+ if (param->type == UCL_INT) {
+ if (strncmp (param->key, "priority", param->keylen) == 0) {
+ priority = ucl_object_toint (param);
+ found = true;
+ }
+ }
+ }
+ }
+
+ if (len > 0) {
+ value = malloc(len + 1);
+ ucl_strlcpy(value, (const char *)data, len + 1);
+ priority = strtol(value, &leftover, 10);
+ if (*leftover != '\0') {
+ ucl_create_err (&parser->err, "Invalid priority value in macro: %s",
+ value);
+ free(value);
+ return false;
+ }
+ free(value);
+ found = true;
+ }
+
+ if (found == true) {
+ parser->chunks->priority = priority;
+ return true;
+ }
+
+ ucl_create_err (&parser->err, "Unable to parse priority macro");
+ return false;
+}
+
+/**
+ * Handle load macro
+ * @param data include data
+ * @param len length of data
+ * @param args UCL object representing arguments to the macro
+ * @param ud user data
+ * @return
+ */
+bool
+ucl_load_handler (const unsigned char *data, size_t len,
+ const ucl_object_t *args, void* ud)
+{
+ struct ucl_parser *parser = ud;
+ const ucl_object_t *param;
+ ucl_object_t *obj, *old_obj;
+ ucl_object_iter_t it = NULL;
+ bool try_load, multiline, test;
+ const char *target, *prefix;
+ char *load_file, *tmp;
+ unsigned char *buf;
+ size_t buflen;
+ unsigned priority;
+ int64_t iv;
+ ucl_hash_t *container = NULL;
+ enum ucl_string_flags flags;
+
+ /* Default values */
+ try_load = false;
+ multiline = false;
+ test = false;
+ target = "string";
+ prefix = NULL;
+ load_file = NULL;
+ buf = NULL;
+ buflen = 0;
+ priority = 0;
+ obj = NULL;
+ old_obj = NULL;
+ flags = 0;
+
+ if (parser == NULL) {
+ return false;
+ }
+
+ /* Process arguments */
+ if (args != NULL && args->type == UCL_OBJECT) {
+ while ((param = ucl_iterate_object (args, &it, true)) != NULL) {
+ if (param->type == UCL_BOOLEAN) {
+ if (strncmp (param->key, "try", param->keylen) == 0) {
+ try_load = ucl_object_toboolean (param);
+ }
+ else if (strncmp (param->key, "multiline", param->keylen) == 0) {
+ multiline = ucl_object_toboolean (param);
+ }
+ else if (strncmp (param->key, "escape", param->keylen) == 0) {
+ test = ucl_object_toboolean (param);
+ if (test) {
+ flags |= UCL_STRING_ESCAPE;
+ }
+ }
+ else if (strncmp (param->key, "trim", param->keylen) == 0) {
+ test = ucl_object_toboolean (param);
+ if (test) {
+ flags |= UCL_STRING_TRIM;
+ }
+ }
+ }
+ else if (param->type == UCL_STRING) {
+ if (strncmp (param->key, "key", param->keylen) == 0) {
+ prefix = ucl_object_tostring (param);
+ }
+ else if (strncmp (param->key, "target", param->keylen) == 0) {
+ target = ucl_object_tostring (param);
+ }
+ }
+ else if (param->type == UCL_INT) {
+ if (strncmp (param->key, "priority", param->keylen) == 0) {
+ priority = ucl_object_toint (param);
+ }
+ }
+ }
+ }
+
+ if (prefix == NULL || strlen(prefix) == 0) {
+ ucl_create_err (&parser->err, "No Key specified in load macro");
+ return false;
+ }
+
+ if (len > 0) {
+ asprintf (&load_file, "%.*s", (int)len, data);
+ if (!ucl_fetch_file (load_file, &buf, &buflen, &parser->err, !try_load)) {
+ return (try_load || false);
+ }
+
+ container = parser->stack->obj->value.ov;
+ old_obj = __DECONST (ucl_object_t *, ucl_hash_search (container, prefix, strlen (prefix)));
+ if (old_obj != NULL) {
+ ucl_create_err (&parser->err, "Key %s already exists", prefix);
+ return false;
+ }
+
+ if (strcasecmp (target, "string") == 0) {
+ obj = ucl_object_fromstring_common (buf, buflen, flags);
+ ucl_copy_value_trash (obj);
+ if (multiline) {
+ obj->flags |= UCL_OBJECT_MULTILINE;
+ }
+ }
+ else if (strcasecmp (target, "int") == 0) {
+ asprintf(&tmp, "%.*s", (int)buflen, buf);
+ iv = strtoll(tmp, NULL, 10);
+ obj = ucl_object_fromint(iv);
+ }
+
+ if (buflen > 0) {
+ ucl_munmap (buf, buflen);
+ }
+
+ if (obj != NULL) {
+ obj->key = prefix;
+ obj->keylen = strlen (prefix);
+ ucl_copy_key_trash(obj);
+ obj->prev = obj;
+ obj->next = NULL;
+ ucl_object_set_priority (obj, priority);
+ container = ucl_hash_insert_object (container, obj,
+ parser->flags & UCL_PARSER_KEY_LOWERCASE);
+ parser->stack->obj->value.ov = container;
+ }
+ return true;
+ }
+
+ ucl_create_err (&parser->err, "Unable to parse load macro");
+ return false;
+}
+
+bool
+ucl_inherit_handler (const unsigned char *data, size_t len,
+ const ucl_object_t *args, const ucl_object_t *ctx, void* ud)
+{
+ const ucl_object_t *parent, *cur;
+ ucl_object_t *target, *copy;
+ ucl_object_iter_t it = NULL;
+ bool replace = false;
+ struct ucl_parser *parser = ud;
+
+ parent = ucl_object_find_keyl (ctx, data, len);
+
+ /* Some sanity checks */
+ if (parent == NULL || ucl_object_type (parent) != UCL_OBJECT) {
+ ucl_create_err (&parser->err, "Unable to find inherited object %*.s",
+ (int)len, data);
+ return false;
+ }
+
+ if (parser->stack == NULL || parser->stack->obj == NULL ||
+ ucl_object_type (parser->stack->obj) != UCL_OBJECT) {
+ ucl_create_err (&parser->err, "Invalid inherit context");
+ return false;
+ }
+
+ target = parser->stack->obj;
+
+ if (args && (cur = ucl_object_find_key (args, "replace")) != NULL) {
+ replace = ucl_object_toboolean (cur);
+ }
+
+ while ((cur = ucl_iterate_object (parent, &it, true))) {
+ /* We do not replace existing keys */
+ if (!replace && ucl_object_find_keyl (target, cur->key, cur->keylen)) {
+ continue;
+ }
+
+ copy = ucl_object_copy (cur);
+
+ if (!replace) {
+ copy->flags |= UCL_OBJECT_INHERITED;
+ }
+
+ ucl_object_insert_key (target, copy, copy->key,
+ copy->keylen, false);
+ }
+
+ return true;
+}
+
+bool
ucl_parser_set_filevars (struct ucl_parser *parser, const char *filename, bool need_expand)
{
char realbuf[PATH_MAX], *curdir;
@@ -1164,8 +1699,9 @@ ucl_parser_set_filevars (struct ucl_parser *parser, const char *filename, bool n
return true;
}
-UCL_EXTERN bool
-ucl_parser_add_file (struct ucl_parser *parser, const char *filename)
+bool
+ucl_parser_add_file_priority (struct ucl_parser *parser, const char *filename,
+ unsigned priority)
{
unsigned char *buf;
size_t len;
@@ -1188,7 +1724,7 @@ ucl_parser_add_file (struct ucl_parser *parser, const char *filename)
}
parser->cur_file = strdup (realbuf);
ucl_parser_set_filevars (parser, realbuf, false);
- ret = ucl_parser_add_chunk (parser, buf, len);
+ ret = ucl_parser_add_chunk_priority (parser, buf, len, priority);
if (len > 0) {
ucl_munmap (buf, len);
@@ -1197,8 +1733,20 @@ ucl_parser_add_file (struct ucl_parser *parser, const char *filename)
return ret;
}
-UCL_EXTERN bool
-ucl_parser_add_fd (struct ucl_parser *parser, int fd)
+bool
+ucl_parser_add_file (struct ucl_parser *parser, const char *filename)
+{
+ if (parser == NULL) {
+ return false;
+ }
+
+ return ucl_parser_add_file_priority(parser, filename,
+ parser->default_priority);
+}
+
+bool
+ucl_parser_add_fd_priority (struct ucl_parser *parser, int fd,
+ unsigned priority)
{
unsigned char *buf;
size_t len;
@@ -1221,7 +1769,7 @@ ucl_parser_add_fd (struct ucl_parser *parser, int fd)
}
parser->cur_file = NULL;
len = st.st_size;
- ret = ucl_parser_add_chunk (parser, buf, len);
+ ret = ucl_parser_add_chunk_priority (parser, buf, len, priority);
if (len > 0) {
ucl_munmap (buf, len);
@@ -1230,6 +1778,16 @@ ucl_parser_add_fd (struct ucl_parser *parser, int fd)
return ret;
}
+bool
+ucl_parser_add_fd (struct ucl_parser *parser, int fd)
+{
+ if (parser == NULL) {
+ return false;
+ }
+
+ return ucl_parser_add_fd_priority(parser, fd, parser->default_priority);
+}
+
size_t
ucl_strlcpy (char *dst, const char *src, size_t siz)
{
@@ -1285,6 +1843,51 @@ ucl_strlcpy_tolower (char *dst, const char *src, size_t siz)
return (s - src); /* count does not include NUL */
}
+/*
+ * Find the first occurrence of find in s
+ */
+char *
+ucl_strnstr (const char *s, const char *find, int len)
+{
+ char c, sc;
+ int mlen;
+
+ if ((c = *find++) != 0) {
+ mlen = strlen (find);
+ do {
+ do {
+ if ((sc = *s++) == 0 || len-- == 0)
+ return (NULL);
+ } while (sc != c);
+ } while (strncmp (s, find, mlen) != 0);
+ s--;
+ }
+ return ((char *)s);
+}
+
+/*
+ * Find the first occurrence of find in s, ignore case.
+ */
+char *
+ucl_strncasestr (const char *s, const char *find, int len)
+{
+ char c, sc;
+ int mlen;
+
+ if ((c = *find++) != 0) {
+ c = tolower (c);
+ mlen = strlen (find);
+ do {
+ do {
+ if ((sc = *s++) == 0 || len-- == 0)
+ return (NULL);
+ } while (tolower (sc) != c);
+ } while (strncasecmp (s, find, mlen) != 0);
+ s--;
+ }
+ return ((char *)s);
+}
+
ucl_object_t *
ucl_object_fromstring_common (const char *str, size_t len, enum ucl_string_flags flags)
{
@@ -1539,7 +2142,7 @@ ucl_object_delete_keyl (ucl_object_t *top, const char *key, size_t keylen)
bool
ucl_object_delete_key (ucl_object_t *top, const char *key)
{
- return ucl_object_delete_keyl (top, key, strlen(key));
+ return ucl_object_delete_keyl (top, key, strlen (key));
}
ucl_object_t*
@@ -1564,7 +2167,7 @@ ucl_object_pop_keyl (ucl_object_t *top, const char *key, size_t keylen)
ucl_object_t*
ucl_object_pop_key (ucl_object_t *top, const char *key)
{
- return ucl_object_pop_keyl (top, key, strlen(key));
+ return ucl_object_pop_keyl (top, key, strlen (key));
}
bool
@@ -1642,10 +2245,45 @@ ucl_object_find_keyl (const ucl_object_t *obj, const char *key, size_t klen)
const ucl_object_t *
ucl_object_find_key (const ucl_object_t *obj, const char *key)
{
- if (key == NULL)
+ if (key == NULL) {
return NULL;
+ }
- return ucl_object_find_keyl (obj, key, strlen(key));
+ return ucl_object_find_keyl (obj, key, strlen (key));
+}
+
+const ucl_object_t*
+ucl_object_find_any_key (const ucl_object_t *obj,
+ const char *key, ...)
+{
+ va_list ap;
+ const ucl_object_t *ret = NULL;
+ const char *nk = NULL;
+
+ if (obj == NULL || key == NULL) {
+ return NULL;
+ }
+
+ ret = ucl_object_find_keyl (obj, key, strlen (key));
+
+ if (ret == NULL) {
+ va_start (ap, key);
+
+ while (ret == NULL) {
+ nk = va_arg (ap, const char *);
+
+ if (nk == NULL) {
+ break;
+ }
+ else {
+ ret = ucl_object_find_keyl (obj, nk, strlen (nk));
+ }
+ }
+
+ va_end (ap);
+ }
+
+ return ret;
}
const ucl_object_t*
@@ -1792,6 +2430,12 @@ ucl_object_iterate_free (ucl_object_iter_t it)
const ucl_object_t *
ucl_lookup_path (const ucl_object_t *top, const char *path_in) {
+ return ucl_lookup_path_char (top, path_in, '.');
+}
+
+
+const ucl_object_t *
+ucl_lookup_path_char (const ucl_object_t *top, const char *path_in, const char sep) {
const ucl_object_t *o = NULL, *found;
const char *p, *c;
char *err_str;
@@ -1805,20 +2449,20 @@ ucl_lookup_path (const ucl_object_t *top, const char *path_in) {
p = path_in;
/* Skip leading dots */
- while (*p == '.') {
+ while (*p == sep) {
p ++;
}
c = p;
while (*p != '\0') {
p ++;
- if (*p == '.' || *p == '\0') {
+ if (*p == sep || *p == '\0') {
if (p > c) {
switch (top->type) {
case UCL_ARRAY:
/* Key should be an int */
index = strtoul (c, &err_str, 10);
- if (err_str != NULL && (*err_str != '.' && *err_str != '\0')) {
+ if (err_str != NULL && (*err_str != sep && *err_str != '\0')) {
return NULL;
}
o = ucl_array_find_index (top, index);
@@ -1913,6 +2557,10 @@ ucl_object_new_userdata (ucl_userdata_dtor dtor, ucl_userdata_emitter emitter)
ucl_type_t
ucl_object_type (const ucl_object_t *obj)
{
+ if (obj == NULL) {
+ return UCL_NULL;
+ }
+
return obj->type;
}
@@ -1981,6 +2629,11 @@ ucl_array_append (ucl_object_t *top, ucl_object_t *elt)
if (vec == NULL) {
vec = UCL_ALLOC (sizeof (*vec));
+
+ if (vec == NULL) {
+ return false;
+ }
+
kv_init (*vec);
top->value.av = (void *)vec;
}
@@ -2021,14 +2674,23 @@ bool
ucl_array_merge (ucl_object_t *top, ucl_object_t *elt, bool copy)
{
unsigned i;
+ ucl_object_t *cp = NULL;
ucl_object_t **obj;
- UCL_ARRAY_GET (v1, top);
- UCL_ARRAY_GET (v2, elt);
if (elt == NULL || top == NULL || top->type != UCL_ARRAY || elt->type != UCL_ARRAY) {
return false;
}
+ if (copy) {
+ cp = ucl_object_copy (elt);
+ }
+ else {
+ cp = ucl_object_ref (elt);
+ }
+
+ UCL_ARRAY_GET (v1, top);
+ UCL_ARRAY_GET (v2, cp);
+
kv_concat (ucl_object_t *, *v1, *v2);
for (i = v2->n; i < v1->n; i ++) {
@@ -2036,14 +2698,7 @@ ucl_array_merge (ucl_object_t *top, ucl_object_t *elt, bool copy)
if (*obj == NULL) {
continue;
}
-
top->len ++;
- if (copy) {
- *obj = ucl_object_copy (*obj);
- }
- else {
- ucl_object_ref (*obj);
- }
}
return true;
@@ -2056,6 +2711,10 @@ ucl_array_delete (ucl_object_t *top, ucl_object_t *elt)
ucl_object_t *ret = NULL;
unsigned i;
+ if (vec == NULL) {
+ return NULL;
+ }
+
for (i = 0; i < vec->n; i ++) {
if (kv_A (*vec, i) == elt) {
kv_del (ucl_object_t *, *vec, i);
@@ -2073,7 +2732,8 @@ ucl_array_head (const ucl_object_t *top)
{
UCL_ARRAY_GET (vec, top);
- if (top == NULL || top->type != UCL_ARRAY || top->value.av == NULL) {
+ if (vec == NULL || top == NULL || top->type != UCL_ARRAY ||
+ top->value.av == NULL) {
return NULL;
}
@@ -2136,6 +2796,25 @@ ucl_array_find_index (const ucl_object_t *top, unsigned int index)
return NULL;
}
+unsigned int
+ucl_array_index_of (ucl_object_t *top, ucl_object_t *elt)
+{
+ UCL_ARRAY_GET (vec, top);
+ unsigned i;
+
+ if (vec == NULL) {
+ return (unsigned int)(-1);
+ }
+
+ for (i = 0; i < vec->n; i ++) {
+ if (kv_A (*vec, i) == elt) {
+ return i;
+ }
+ }
+
+ return (unsigned int)(-1);
+}
+
ucl_object_t *
ucl_array_replace_index (ucl_object_t *top, ucl_object_t *elt,
unsigned int index)
@@ -2265,7 +2944,9 @@ ucl_object_tostring_safe (const ucl_object_t *obj, const char **target)
switch (obj->type) {
case UCL_STRING:
- *target = ucl_copy_value_trash (obj);
+ if (!(obj->flags & UCL_OBJECT_BINARY)) {
+ *target = ucl_copy_value_trash (obj);
+ }
break;
default:
return false;
@@ -2286,7 +2967,12 @@ ucl_object_tostring (const ucl_object_t *obj)
const char *
ucl_object_tostring_forced (const ucl_object_t *obj)
{
- return ucl_copy_value_trash (obj);
+ /* TODO: For binary strings we might encode string here */
+ if (!(obj->flags & UCL_OBJECT_BINARY)) {
+ return ucl_copy_value_trash (obj);
+ }
+
+ return NULL;
}
bool
@@ -2533,7 +3219,7 @@ ucl_object_compare (const ucl_object_t *o1, const ucl_object_t *o2)
void
ucl_object_array_sort (ucl_object_t *ar,
- int (*cmp)(const ucl_object_t *o1, const ucl_object_t *o2))
+ int (*cmp)(const ucl_object_t **o1, const ucl_object_t **o2))
{
UCL_ARRAY_GET (vec, ar);
@@ -2563,6 +3249,9 @@ ucl_object_set_priority (ucl_object_t *obj,
{
if (obj != NULL) {
priority &= (0x1 << PRIOBITS) - 1;
- obj->flags |= priority << ((sizeof (obj->flags) * NBBY) - PRIOBITS);
+ priority <<= ((sizeof (obj->flags) * NBBY) - PRIOBITS);
+ priority |= obj->flags & ((1 << ((sizeof (obj->flags) * NBBY) -
+ PRIOBITS)) - 1);
+ obj->flags = priority;
}
}