diff options
Diffstat (limited to 'src/ucl_util.c')
-rw-r--r-- | src/ucl_util.c | 901 |
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, ¶ms); + } + else if (data != NULL) { + /* Try to load a file */ + return ucl_include_file (data, len, parser, ¶ms); + } } - 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, ¶ms); + } + + 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, ¶ms))) { + 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; } } |