From bb4a6a9ae9a22fa3a9d5d6d80f8db6f1fd345c0a Mon Sep 17 00:00:00 2001 From: Vsevolod Stakhov Date: Tue, 13 Oct 2015 17:05:19 +0100 Subject: [PATCH] Sync with libucl. --- contrib/libucl/tree.h | 27 +- contrib/libucl/ucl.h | 41 +- contrib/libucl/ucl_emitter.c | 25 +- contrib/libucl/ucl_internal.h | 37 +- contrib/libucl/ucl_msgpack.c | 1285 ++++++++++++++++++++++++++++++++- contrib/libucl/ucl_parser.c | 237 ++++-- contrib/libucl/ucl_util.c | 218 +++--- 7 files changed, 1688 insertions(+), 182 deletions(-) diff --git a/contrib/libucl/tree.h b/contrib/libucl/tree.h index cee937369..404b4a867 100644 --- a/contrib/libucl/tree.h +++ b/contrib/libucl/tree.h @@ -44,6 +44,13 @@ #define TREE_DELTA_MAX 1 +#ifndef _HU_FUNCTION +# if defined(__GNUC__) || defined(__clang__) +# define _HU_FUNCTION(x) __attribute__((__unused__)) x +# else +# define _HU_FUNCTION(x) x +# endif +#endif #define TREE_ENTRY(type) \ struct { \ @@ -68,9 +75,9 @@ #define TREE_DEFINE(node, field) \ \ - struct node *TREE_BALANCE_##node##_##field(struct node *); \ + static struct node *_HU_FUNCTION(TREE_BALANCE_##node##_##field)(struct node *); \ \ - struct node *TREE_ROTL_##node##_##field(struct node *self) \ + static struct node *_HU_FUNCTION(TREE_ROTL_##node##_##field)(struct node *self) \ { \ struct node *r= self->field.avl_right; \ self->field.avl_right= r->field.avl_left; \ @@ -78,7 +85,7 @@ return TREE_BALANCE_##node##_##field(r); \ } \ \ - struct node *TREE_ROTR_##node##_##field(struct node *self) \ + static struct node *_HU_FUNCTION(TREE_ROTR_##node##_##field)(struct node *self) \ { \ struct node *l= self->field.avl_left; \ self->field.avl_left= l->field.avl_right; \ @@ -86,7 +93,7 @@ return TREE_BALANCE_##node##_##field(l); \ } \ \ - struct node *TREE_BALANCE_##node##_##field(struct node *self) \ + static struct node *_HU_FUNCTION(TREE_BALANCE_##node##_##field)(struct node *self) \ { \ int delta= TREE_DELTA(self, field); \ \ @@ -111,7 +118,7 @@ return self; \ } \ \ - struct node *TREE_INSERT_##node##_##field \ + static struct node *_HU_FUNCTION(TREE_INSERT_##node##_##field) \ (struct node *self, struct node *elm, int (*compare)(struct node *lhs, struct node *rhs)) \ { \ if (!self) \ @@ -123,7 +130,7 @@ return TREE_BALANCE_##node##_##field(self); \ } \ \ - struct node *TREE_FIND_##node##_##field \ + static struct node *_HU_FUNCTION(TREE_FIND_##node##_##field) \ (struct node *self, struct node *elm, int (*compare)(struct node *lhs, struct node *rhs)) \ { \ if (!self) \ @@ -136,7 +143,7 @@ return TREE_FIND_##node##_##field(self->field.avl_right, elm, compare); \ } \ \ - struct node *TREE_MOVE_RIGHT(struct node *self, struct node *rhs) \ + static struct node *_HU_FUNCTION(TREE_MOVE_RIGHT)(struct node *self, struct node *rhs) \ { \ if (!self) \ return rhs; \ @@ -144,7 +151,7 @@ return TREE_BALANCE_##node##_##field(self); \ } \ \ - struct node *TREE_REMOVE_##node##_##field \ + static struct node *_HU_FUNCTION(TREE_REMOVE_##node##_##field) \ (struct node *self, struct node *elm, int (*compare)(struct node *lhs, struct node *rhs)) \ { \ if (!self) return 0; \ @@ -163,7 +170,7 @@ return TREE_BALANCE_##node##_##field(self); \ } \ \ - void TREE_FORWARD_APPLY_ALL_##node##_##field \ + static void _HU_FUNCTION(TREE_FORWARD_APPLY_ALL_##node##_##field) \ (struct node *self, void (*function)(struct node *node, void *data), void *data) \ { \ if (self) \ @@ -174,7 +181,7 @@ } \ } \ \ - void TREE_REVERSE_APPLY_ALL_##node##_##field \ + static void _HU_FUNCTION(TREE_REVERSE_APPLY_ALL_##node##_##field) \ (struct node *self, void (*function)(struct node *node, void *data), void *data) \ { \ if (self) \ diff --git a/contrib/libucl/ucl.h b/contrib/libucl/ucl.h index 2e0183671..bd209295b 100644 --- a/contrib/libucl/ucl.h +++ b/contrib/libucl/ucl.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2013, Vsevolod Stakhov +/* Copyright (c) 2013-2015, Vsevolod Stakhov * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -182,9 +182,29 @@ typedef enum ucl_object_flags { UCL_OBJECT_EPHEMERAL = (1 << 3), /**< Temporary object that does not need to be freed really */ UCL_OBJECT_MULTILINE = (1 << 4), /**< String should be displayed as multiline string */ UCL_OBJECT_MULTIVALUE = (1 << 5), /**< Object is a key with multiple values */ - UCL_OBJECT_INHERITED = (1 << 6) /**< Object has been inherited from another */ + UCL_OBJECT_INHERITED = (1 << 6), /**< Object has been inherited from another */ + UCL_OBJECT_BINARY = (1 << 7) /**< Object contains raw binary data */ } ucl_object_flags_t; +/** + * Duplicate policy types + */ +enum ucl_duplicate_strategy { + UCL_DUPLICATE_APPEND = 0, /**< Default policy to merge based on priorities */ + UCL_DUPLICATE_MERGE, /**< Merge new object with old one */ + UCL_DUPLICATE_REWRITE, /**< Rewrite old keys */ + UCL_DUPLICATE_ERROR /**< Stop parsing on duplicate found */ +}; + +/** + * Input format type + */ +enum ucl_parse_type { + UCL_PARSE_UCL = 0, /**< Default ucl format */ + UCL_PARSE_MSGPACK, /**< Message pack input format */ + UCL_PARSE_CSEXP /**< Canonical S-expressions */ +}; + /** * UCL object structure. Please mention that the most of fields should not be touched by * UCL users. In future, this structure may be converted to private one. @@ -195,7 +215,7 @@ typedef struct ucl_object_s { */ union { int64_t iv; /**< Int value of an object */ - const char *sv; /**< String value of an object */ + const char *sv; /**< String value of an object */ double dv; /**< Double value of an object */ void *av; /**< Array */ void *ov; /**< Object */ @@ -916,6 +936,21 @@ UCL_EXTERN bool ucl_parser_add_chunk (struct ucl_parser *parser, UCL_EXTERN bool ucl_parser_add_chunk_priority (struct ucl_parser *parser, const unsigned char *data, size_t len, unsigned priority); +/** + * Full version of ucl_add_chunk with priority and duplicate strategy + * @param parser parser structure + * @param data the pointer to the beginning of a chunk + * @param len the length of a chunk + * @param priority the desired priority of a chunk (only 4 least significant bits + * are considered for this parameter) + * @param strat duplicates merging strategy + * @param parse_type input format + * @return true if chunk has been added and false in case of error + */ +UCL_EXTERN bool ucl_parser_add_chunk_full (struct ucl_parser *parser, + const unsigned char *data, size_t len, unsigned priority, + enum ucl_duplicate_strategy strat, enum ucl_parse_type parse_type); + /** * Load ucl object from a string * @param parser parser structure diff --git a/contrib/libucl/ucl_emitter.c b/contrib/libucl/ucl_emitter.c index 12cd31c7d..8bfbf09b8 100644 --- a/contrib/libucl/ucl_emitter.c +++ b/contrib/libucl/ucl_emitter.c @@ -498,7 +498,14 @@ ucl_emit_msgpack_elt (struct ucl_emitter_context *ctx, case UCL_STRING: ucl_emitter_print_key_msgpack (print_key, ctx, obj); - ucl_emitter_print_string_msgpack (ctx, obj->value.sv, obj->len); + + if (obj->flags & UCL_OBJECT_BINARY) { + ucl_emitter_print_binary_string_msgpack (ctx, obj->value.sv, + obj->len); + } + else { + ucl_emitter_print_string_msgpack (ctx, obj->value.sv, obj->len); + } break; case UCL_NULL: @@ -509,27 +516,31 @@ ucl_emit_msgpack_elt (struct ucl_emitter_context *ctx, case UCL_OBJECT: ucl_emitter_print_key_msgpack (print_key, ctx, obj); ucl_emit_msgpack_start_obj (ctx, obj, print_key); - it = ucl_object_iterate_new (obj); + it = NULL; - while ((cur = ucl_object_iterate_safe (it, true)) != NULL) { + while ((cur = ucl_iterate_object (obj, &it, true)) != NULL) { LL_FOREACH (cur, celt) { ucl_emit_msgpack_elt (ctx, celt, false, true); + /* XXX: + * in msgpack the length of objects is encoded within a single elt + * so in case of multi-value keys we are using merely the first + * element ignoring others + */ + break; } } - ucl_object_iterate_free (it); break; case UCL_ARRAY: ucl_emitter_print_key_msgpack (print_key, ctx, obj); ucl_emit_msgpack_start_array (ctx, obj, print_key); - it = ucl_object_iterate_new (obj); + it = NULL; - while ((cur = ucl_object_iterate_safe (it, true)) != NULL) { + while ((cur = ucl_iterate_object (obj, &it, true)) != NULL) { ucl_emit_msgpack_elt (ctx, cur, false, false); } - ucl_object_iterate_free (it); break; case UCL_USERDATA: diff --git a/contrib/libucl/ucl_internal.h b/contrib/libucl/ucl_internal.h index 885e8c57f..da7df1270 100644 --- a/contrib/libucl/ucl_internal.h +++ b/contrib/libucl/ucl_internal.h @@ -157,7 +157,7 @@ struct ucl_macro { struct ucl_stack { ucl_object_t *obj; struct ucl_stack *next; - int level; + uint64_t level; }; struct ucl_chunk { @@ -168,6 +168,8 @@ struct ucl_chunk { unsigned int line; unsigned int column; unsigned priority; + enum ucl_duplicate_strategy strategy; + enum ucl_parse_type parse_type; struct ucl_chunk *next; }; @@ -303,6 +305,8 @@ ucl_create_err (UT_string **err, const char *fmt, ...) __attribute__ (( format( printf, 2, 3) )); #endif +#undef UCL_FATAL_ERRORS + static inline void ucl_create_err (UT_string **err, const char *fmt, ...) @@ -314,6 +318,10 @@ ucl_create_err (UT_string **err, const char *fmt, ...) utstring_printf_va (*err, fmt, ap); va_end (ap); } + +#ifdef UCL_FATAL_ERRORS + assert (0); +#endif } /** @@ -477,6 +485,15 @@ void ucl_emitter_print_bool_msgpack (struct ucl_emitter_context *ctx, void ucl_emitter_print_string_msgpack (struct ucl_emitter_context *ctx, const char *s, size_t len); +/** + * Print binary string to the msgpack output + * @param ctx + * @param s + * @param len + */ +void ucl_emitter_print_binary_string_msgpack (struct ucl_emitter_context *ctx, + const char *s, size_t len); + /** * Print array preamble for msgpack * @param ctx @@ -498,7 +515,7 @@ void ucl_emitter_print_object_msgpack (struct ucl_emitter_context *ctx, */ void ucl_emitter_print_null_msgpack (struct ucl_emitter_context *ctx); /** - * Print object's key if needed to the msgpakc output + * Print object's key if needed to the msgpack output * @param print_key * @param ctx * @param obj @@ -507,4 +524,20 @@ void ucl_emitter_print_key_msgpack (bool print_key, struct ucl_emitter_context *ctx, const ucl_object_t *obj); +/** + * Add new element to an object using the current merge strategy and priority + * @param parser + * @param nobj + * @return + */ +bool ucl_parser_process_object_element (struct ucl_parser *parser, + ucl_object_t *nobj); + +/** + * Parse msgpack chunk + * @param parser + * @return + */ +bool ucl_parse_msgpack (struct ucl_parser *parser); + #endif /* UCL_INTERNAL_H_ */ diff --git a/contrib/libucl/ucl_msgpack.c b/contrib/libucl/ucl_msgpack.c index 5c315430c..e8ebfba8a 100644 --- a/contrib/libucl/ucl_msgpack.c +++ b/contrib/libucl/ucl_msgpack.c @@ -85,10 +85,16 @@ #define TO_BE16 SWAP_LE_BE16 #define TO_BE32 SWAP_LE_BE32 #define TO_BE64 SWAP_LE_BE64 +#define FROM_BE16 SWAP_LE_BE16 +#define FROM_BE32 SWAP_LE_BE32 +#define FROM_BE64 SWAP_LE_BE64 #else #define TO_BE16(val) (uint16_t)(val) #define TO_BE32(val) (uint32_t)(val) #define TO_BE64(val) (uint64_t)(val) +#define FROM_BE16(val) (uint16_t)(val) +#define FROM_BE32(val) (uint32_t)(val) +#define FROM_BE64(val) (uint64_t)(val) #endif void @@ -139,9 +145,9 @@ ucl_emitter_print_int_msgpack (struct ucl_emitter_context *ctx, int64_t val) /* Bithack abs */ uval = ((val ^ (val >> 63)) - (val >> 63)); - if (val >= -(1 << 5)) { + if (val > -(1 << 5)) { len = 1; - buf[0] = mask_negative | (uval & 0xff); + buf[0] = (mask_negative | uval) & 0xff; } else if (uval <= 0xff) { len = 2; @@ -240,6 +246,39 @@ ucl_emitter_print_string_msgpack (struct ucl_emitter_context *ctx, func->ucl_emitter_append_len (s, len, func->ud); } +void +ucl_emitter_print_binary_string_msgpack (struct ucl_emitter_context *ctx, + const char *s, size_t len) +{ + const struct ucl_emitter_functions *func = ctx->func; + const unsigned char l8_ch = 0xc4, l16_ch = 0xc5, l32_ch = 0xc6; + unsigned char buf[5]; + unsigned blen; + + if (len <= 0xff) { + blen = 2; + buf[0] = l8_ch; + buf[1] = len & 0xff; + } + else if (len <= 0xffff) { + uint16_t bl = TO_BE16 (len); + + blen = 3; + buf[0] = l16_ch; + memcpy (&buf[1], &bl, sizeof (bl)); + } + else { + uint32_t bl = TO_BE32 (len); + + blen = 5; + buf[0] = l32_ch; + memcpy (&buf[1], &bl, sizeof (bl)); + } + + func->ucl_emitter_append_len (buf, blen, func->ud); + func->ucl_emitter_append_len (s, len, func->ud); +} + void ucl_emitter_print_null_msgpack (struct ucl_emitter_context *ctx) { @@ -317,3 +356,1245 @@ ucl_emitter_print_object_msgpack (struct ucl_emitter_context *ctx, size_t len) func->ucl_emitter_append_len (buf, blen, func->ud); } + + +enum ucl_msgpack_format { + msgpack_positive_fixint = 0, + msgpack_fixmap, + msgpack_fixarray, + msgpack_fixstr, + msgpack_nil, + msgpack_false, + msgpack_true, + msgpack_bin8, + msgpack_bin16, + msgpack_bin32, + msgpack_ext8, + msgpack_ext16, + msgpack_ext32, + msgpack_float32, + msgpack_float64, + msgpack_uint8, + msgpack_uint16, + msgpack_uint32, + msgpack_uint64, + msgpack_int8, + msgpack_int16, + msgpack_int32, + msgpack_int64, + msgpack_fixext1, + msgpack_fixext2, + msgpack_fixext4, + msgpack_fixext8, + msgpack_fixext16, + msgpack_str8, + msgpack_str16, + msgpack_str32, + msgpack_array16, + msgpack_array32, + msgpack_map16, + msgpack_map32, + msgpack_negative_fixint, + msgpack_invalid +}; + +typedef ssize_t (*ucl_msgpack_parse_function)(struct ucl_parser *parser, + struct ucl_stack *container, size_t len, enum ucl_msgpack_format fmt, + const unsigned char *pos, size_t remain); + +static ssize_t ucl_msgpack_parse_map (struct ucl_parser *parser, + struct ucl_stack *container, size_t len, enum ucl_msgpack_format fmt, + const unsigned char *pos, size_t remain); +static ssize_t ucl_msgpack_parse_array (struct ucl_parser *parser, + struct ucl_stack *container, size_t len, enum ucl_msgpack_format fmt, + const unsigned char *pos, size_t remain); +static ssize_t ucl_msgpack_parse_string (struct ucl_parser *parser, + struct ucl_stack *container, size_t len, enum ucl_msgpack_format fmt, + const unsigned char *pos, size_t remain); +static ssize_t ucl_msgpack_parse_int (struct ucl_parser *parser, + struct ucl_stack *container, size_t len, enum ucl_msgpack_format fmt, + const unsigned char *pos, size_t remain); +static ssize_t ucl_msgpack_parse_float (struct ucl_parser *parser, + struct ucl_stack *container, size_t len, enum ucl_msgpack_format fmt, + const unsigned char *pos, size_t remain); +static ssize_t ucl_msgpack_parse_bool (struct ucl_parser *parser, + struct ucl_stack *container, size_t len, enum ucl_msgpack_format fmt, + const unsigned char *pos, size_t remain); +static ssize_t ucl_msgpack_parse_null (struct ucl_parser *parser, + struct ucl_stack *container, size_t len, enum ucl_msgpack_format fmt, + const unsigned char *pos, size_t remain); +static ssize_t ucl_msgpack_parse_ignore (struct ucl_parser *parser, + struct ucl_stack *container, size_t len, enum ucl_msgpack_format fmt, + const unsigned char *pos, size_t remain); + +#define MSGPACK_FLAG_FIXED (1 << 0) +#define MSGPACK_FLAG_CONTAINER (1 << 1) +#define MSGPACK_FLAG_TYPEVALUE (1 << 2) +#define MSGPACK_FLAG_EXT (1 << 3) +#define MSGPACK_FLAG_ASSOC (1 << 4) +#define MSGPACK_FLAG_KEY (1 << 5) +#define MSGPACK_CONTAINER_BIT (1ULL << 62) + +/* + * Search tree packed in array + */ +struct ucl_msgpack_parser { + uint8_t prefix; /* Prefix byte */ + uint8_t prefixlen; /* Length of prefix in bits */ + uint8_t fmt; /* The desired format */ + uint8_t len; /* Length of the object + (either length bytes + or length of value in case + of fixed objects */ + uint8_t flags; /* Flags of the specified type */ + ucl_msgpack_parse_function func; /* Parser function */ +} parsers[] = { + { + 0xa0, + 3, + msgpack_fixstr, + 0, + MSGPACK_FLAG_FIXED|MSGPACK_FLAG_KEY, + ucl_msgpack_parse_string + }, + { + 0x0, + 1, + msgpack_positive_fixint, + 0, + MSGPACK_FLAG_FIXED|MSGPACK_FLAG_TYPEVALUE, + ucl_msgpack_parse_int + }, + { + 0xe0, + 3, + msgpack_negative_fixint, + 0, + MSGPACK_FLAG_FIXED|MSGPACK_FLAG_TYPEVALUE, + ucl_msgpack_parse_int + }, + { + 0x80, + 4, + msgpack_fixmap, + 0, + MSGPACK_FLAG_FIXED|MSGPACK_FLAG_CONTAINER|MSGPACK_FLAG_ASSOC, + ucl_msgpack_parse_map + }, + { + 0x90, + 4, + msgpack_fixarray, + 0, + MSGPACK_FLAG_FIXED|MSGPACK_FLAG_CONTAINER, + ucl_msgpack_parse_array + }, + { + 0xd9, + 8, + msgpack_str8, + 1, + MSGPACK_FLAG_KEY, + ucl_msgpack_parse_string + }, + { + 0xc4, + 8, + msgpack_bin8, + 1, + MSGPACK_FLAG_KEY, + ucl_msgpack_parse_string + }, + { + 0xcf, + 8, + msgpack_uint64, + 8, + MSGPACK_FLAG_FIXED, + ucl_msgpack_parse_int + }, + { + 0xd3, + 8, + msgpack_int64, + 8, + MSGPACK_FLAG_FIXED, + ucl_msgpack_parse_int + }, + { + 0xce, + 8, + msgpack_uint32, + 4, + MSGPACK_FLAG_FIXED, + ucl_msgpack_parse_int + }, + { + 0xd2, + 8, + msgpack_int32, + 4, + MSGPACK_FLAG_FIXED, + ucl_msgpack_parse_int + }, + { + 0xcb, + 8, + msgpack_float64, + 8, + MSGPACK_FLAG_FIXED, + ucl_msgpack_parse_float + }, + { + 0xca, + 8, + msgpack_float32, + 4, + MSGPACK_FLAG_FIXED, + ucl_msgpack_parse_float + }, + { + 0xc2, + 8, + msgpack_false, + 1, + MSGPACK_FLAG_FIXED | MSGPACK_FLAG_TYPEVALUE, + ucl_msgpack_parse_bool + }, + { + 0xc3, + 8, + msgpack_true, + 1, + MSGPACK_FLAG_FIXED | MSGPACK_FLAG_TYPEVALUE, + ucl_msgpack_parse_bool + }, + { + 0xcc, + 8, + msgpack_uint8, + 1, + MSGPACK_FLAG_FIXED, + ucl_msgpack_parse_int + }, + { + 0xcd, + 8, + msgpack_uint16, + 2, + MSGPACK_FLAG_FIXED, + ucl_msgpack_parse_int + }, + { + 0xd0, + 8, + msgpack_int8, + 1, + MSGPACK_FLAG_FIXED, + ucl_msgpack_parse_int + }, + { + 0xd1, + 8, + msgpack_int16, + 2, + MSGPACK_FLAG_FIXED, + ucl_msgpack_parse_int + }, + { + 0xc0, + 8, + msgpack_nil, + 0, + MSGPACK_FLAG_FIXED | MSGPACK_FLAG_TYPEVALUE, + ucl_msgpack_parse_null + }, + { + 0xda, + 8, + msgpack_str16, + 2, + MSGPACK_FLAG_KEY, + ucl_msgpack_parse_string + }, + { + 0xdb, + 8, + msgpack_str32, + 4, + MSGPACK_FLAG_KEY, + ucl_msgpack_parse_string + }, + { + 0xc5, + 8, + msgpack_bin16, + 2, + MSGPACK_FLAG_KEY, + ucl_msgpack_parse_string + }, + { + 0xc6, + 8, + msgpack_bin32, + 4, + MSGPACK_FLAG_KEY, + ucl_msgpack_parse_string + }, + { + 0xdc, + 8, + msgpack_array16, + 2, + MSGPACK_FLAG_CONTAINER, + ucl_msgpack_parse_array + }, + { + 0xdd, + 8, + msgpack_array32, + 4, + MSGPACK_FLAG_CONTAINER, + ucl_msgpack_parse_array + }, + { + 0xde, + 8, + msgpack_map16, + 2, + MSGPACK_FLAG_CONTAINER|MSGPACK_FLAG_ASSOC, + ucl_msgpack_parse_map + }, + { + 0xdf, + 8, + msgpack_map32, + 4, + MSGPACK_FLAG_CONTAINER|MSGPACK_FLAG_ASSOC, + ucl_msgpack_parse_map + }, + { + 0xc7, + 8, + msgpack_ext8, + 1, + MSGPACK_FLAG_EXT, + ucl_msgpack_parse_ignore + }, + { + 0xc8, + 8, + msgpack_ext16, + 2, + MSGPACK_FLAG_EXT, + ucl_msgpack_parse_ignore + }, + { + 0xc9, + 8, + msgpack_ext32, + 4, + MSGPACK_FLAG_EXT, + ucl_msgpack_parse_ignore + }, + { + 0xd4, + 8, + msgpack_fixext1, + 1, + MSGPACK_FLAG_FIXED | MSGPACK_FLAG_EXT, + ucl_msgpack_parse_ignore + }, + { + 0xd5, + 8, + msgpack_fixext2, + 2, + MSGPACK_FLAG_FIXED | MSGPACK_FLAG_EXT, + ucl_msgpack_parse_ignore + }, + { + 0xd6, + 8, + msgpack_fixext4, + 4, + MSGPACK_FLAG_FIXED | MSGPACK_FLAG_EXT, + ucl_msgpack_parse_ignore + }, + { + 0xd7, + 8, + msgpack_fixext8, + 8, + MSGPACK_FLAG_FIXED | MSGPACK_FLAG_EXT, + ucl_msgpack_parse_ignore + }, + { + 0xd8, + 8, + msgpack_fixext16, + 16, + MSGPACK_FLAG_FIXED | MSGPACK_FLAG_EXT, + ucl_msgpack_parse_ignore + } +}; + +#undef MSGPACK_DEBUG_PARSER + +static inline struct ucl_msgpack_parser * +ucl_msgpack_get_parser_from_type (unsigned char t) +{ + unsigned int i, shift, mask; + + for (i = 0; i < sizeof (parsers) / sizeof (parsers[0]); i ++) { + shift = CHAR_BIT - parsers[i].prefixlen; + mask = parsers[i].prefix >> shift; + + if (mask == (t >> shift)) { + return &parsers[i]; + } + } + + return NULL; +} + +static inline struct ucl_stack * +ucl_msgpack_get_container (struct ucl_parser *parser, + struct ucl_msgpack_parser *obj_parser, uint64_t len) +{ + struct ucl_stack *stack; + + assert (obj_parser != NULL); + + if (obj_parser->flags & MSGPACK_FLAG_CONTAINER) { + assert ((len & MSGPACK_CONTAINER_BIT) == 0); + /* + * Insert new container to the stack + */ + if (parser->stack == NULL) { + parser->stack = calloc (1, sizeof (struct ucl_stack)); + + if (parser->stack == NULL) { + ucl_create_err (&parser->err, "no memory"); + return NULL; + } + } + else { + stack = calloc (1, sizeof (struct ucl_stack)); + + if (stack == NULL) { + ucl_create_err (&parser->err, "no memory"); + return NULL; + } + + stack->next = parser->stack; + parser->stack = stack; + } + + parser->stack->level = len | MSGPACK_CONTAINER_BIT; + +#ifdef MSGPACK_DEBUG_PARSER + stack = parser->stack; + while (stack) { + fprintf(stderr, "+"); + stack = stack->next; + } + + fprintf(stderr, "%s -> %d\n", obj_parser->flags & MSGPACK_FLAG_ASSOC ? "object" : "array", (int)len); +#endif + } + else { + /* + * Get the current stack top + */ + if (parser->stack) { + return parser->stack; + } + else { + ucl_create_err (&parser->err, "bad top level object for msgpack"); + return NULL; + } + } + + return parser->stack; +} + +static bool +ucl_msgpack_is_container_finished (struct ucl_stack *container) +{ + uint64_t level; + + assert (container != NULL); + + if (container->level & MSGPACK_CONTAINER_BIT) { + level = container->level & ~MSGPACK_CONTAINER_BIT; + + if (level == 0) { + return true; + } + } + + return false; +} + +static bool +ucl_msgpack_insert_object (struct ucl_parser *parser, + const unsigned char *key, + size_t keylen, ucl_object_t *obj) +{ + uint64_t level; + struct ucl_stack *container; + + container = parser->stack; + assert (container != NULL); + assert (container->level > 0); + assert (obj != NULL); + assert (container->obj != NULL); + + if (container->obj->type == UCL_ARRAY) { + ucl_array_append (container->obj, obj); + } + else if (container->obj->type == UCL_OBJECT) { + if (key == NULL || keylen == 0) { + ucl_create_err (&parser->err, "cannot insert object with no key"); + return false; + } + + obj->key = key; + obj->keylen = keylen; + + if (!(parser->flags & UCL_PARSER_ZEROCOPY)) { + ucl_copy_key_trash (obj); + } + + ucl_parser_process_object_element (parser, obj); + } + else { + ucl_create_err (&parser->err, "bad container type"); + return false; + } + + if (container->level & MSGPACK_CONTAINER_BIT) { + level = container->level & ~MSGPACK_CONTAINER_BIT; + container->level = (level - 1) | MSGPACK_CONTAINER_BIT; + } + + return true; +} + +static struct ucl_stack * +ucl_msgpack_get_next_container (struct ucl_parser *parser) +{ + struct ucl_stack *cur = NULL; + uint64_t level; + + cur = parser->stack; + + if (cur == NULL) { + return NULL; + } + + if (cur->level & MSGPACK_CONTAINER_BIT) { + level = cur->level & ~MSGPACK_CONTAINER_BIT; + + if (level == 0) { + /* We need to switch to the previous container */ + parser->stack = cur->next; + parser->cur_obj = cur->obj; + free (cur); + +#ifdef MSGPACK_DEBUG_PARSER + cur = parser->stack; + while (cur) { + fprintf(stderr, "-"); + cur = cur->next; + } + fprintf(stderr, "-%s -> %d\n", parser->cur_obj->type == UCL_OBJECT ? "object" : "array", (int)parser->cur_obj->len); +#endif + + return ucl_msgpack_get_next_container (parser); + } + } + + /* + * For UCL containers we don't know length, so we just insert the whole + * message pack blob into the top level container + */ + + assert (cur->obj != NULL); + + return cur; +} + +#define CONSUME_RET do { \ + if (ret != -1) { \ + p += ret; \ + remain -= ret; \ + obj_parser = NULL; \ + assert (remain >= 0); \ + } \ + else { \ + ucl_create_err (&parser->err, \ + "cannot parse type %d of len %u", \ + (int)obj_parser->fmt, \ + (unsigned)len); \ + return false; \ + } \ +} while(0) + +#define GET_NEXT_STATE do { \ + container = ucl_msgpack_get_next_container (parser); \ + if (container == NULL) { \ + ucl_create_err (&parser->err, \ + "empty container"); \ + return false; \ + } \ + next_state = container->obj->type == UCL_OBJECT ? \ + read_assoc_key : read_array_value; \ +} while(0) + +static bool +ucl_msgpack_consume (struct ucl_parser *parser) +{ + const unsigned char *p, *end, *key = NULL; + struct ucl_stack *container; + enum e_msgpack_parser_state { + read_type, + start_assoc, + start_array, + read_assoc_key, + read_assoc_value, + finish_assoc_value, + read_array_value, + finish_array_value, + error_state + } state = read_type, next_state = error_state; + struct ucl_msgpack_parser *obj_parser; + uint64_t len; + ssize_t ret, remain, keylen = 0; +#ifdef MSGPACK_DEBUG_PARSER + uint64_t i; + enum e_msgpack_parser_state hist[256]; +#endif + + p = parser->chunks->begin; + remain = parser->chunks->remain; + end = p + remain; + + + while (p < end) { +#ifdef MSGPACK_DEBUG_PARSER + hist[i++ % 256] = state; +#endif + switch (state) { + case read_type: + obj_parser = ucl_msgpack_get_parser_from_type (*p); + + if (obj_parser == NULL) { + ucl_create_err (&parser->err, "unknown msgpack format: %x", + (unsigned int)*p); + + return false; + } + /* Now check length sanity */ + if (obj_parser->flags & MSGPACK_FLAG_FIXED) { + if (obj_parser->len == 0) { + /* We have an embedded size */ + len = *p & ~obj_parser->prefix; + } + else { + if (remain < obj_parser->len) { + ucl_create_err (&parser->err, "not enough data remain to " + "read object's length: %u remain, %u needed", + (unsigned)remain, obj_parser->len); + + return false; + } + + len = obj_parser->len; + } + + if (!(obj_parser->flags & MSGPACK_FLAG_TYPEVALUE)) { + /* We must pass value as the second byte */ + if (remain > 0) { + p ++; + remain --; + } + } + else { + /* Len is irrelevant now */ + len = 0; + } + } + else { + /* Length is not embedded */ + if (remain < obj_parser->len) { + ucl_create_err (&parser->err, "not enough data remain to " + "read object's length: %u remain, %u needed", + (unsigned)remain, obj_parser->len); + + return false; + } + + p ++; + remain --; + + switch (obj_parser->len) { + case 1: + len = *p; + break; + case 2: + len = FROM_BE16 (*(uint16_t *)p); + break; + case 4: + len = FROM_BE32 (*(uint32_t *)p); + break; + case 8: + len = FROM_BE64 (*(uint64_t *)p); + break; + default: + assert (0); + break; + } + + p += obj_parser->len; + remain -= obj_parser->len; + } + + if (obj_parser->flags & MSGPACK_FLAG_ASSOC) { + /* We have just read the new associative map */ + state = start_assoc; + } + else if (obj_parser->flags & MSGPACK_FLAG_CONTAINER){ + state = start_array; + } + else { + state = next_state; + } + + break; + case start_assoc: + parser->cur_obj = ucl_object_new_full (UCL_OBJECT, + parser->chunks->priority); + /* Insert to the previous level container */ + if (parser->stack && !ucl_msgpack_insert_object (parser, + key, keylen, parser->cur_obj)) { + return false; + } + /* Get new container */ + container = ucl_msgpack_get_container (parser, obj_parser, len); + + if (container == NULL) { + return false; + } + + ret = obj_parser->func (parser, container, len, obj_parser->fmt, + p, remain); + CONSUME_RET; + key = NULL; + keylen = 0; + + if (len > 0) { + state = read_type; + next_state = read_assoc_key; + } + else { + /* Empty object */ + state = finish_assoc_value; + } + break; + + case start_array: + parser->cur_obj = ucl_object_new_full (UCL_ARRAY, + parser->chunks->priority); + /* Insert to the previous level container */ + if (parser->stack && !ucl_msgpack_insert_object (parser, + key, keylen, parser->cur_obj)) { + return false; + } + /* Get new container */ + container = ucl_msgpack_get_container (parser, obj_parser, len); + + if (container == NULL) { + return false; + } + + ret = obj_parser->func (parser, container, len, obj_parser->fmt, + p, remain); + CONSUME_RET; + + if (len > 0) { + state = read_type; + next_state = read_array_value; + } + else { + /* Empty array */ + state = finish_array_value; + } + break; + + case read_array_value: + /* + * p is now at the value start, len now contains length read and + * obj_parser contains the corresponding specific parser + */ + container = parser->stack; + + if (container == NULL) { + return false; + } + + ret = obj_parser->func (parser, container, len, obj_parser->fmt, + p, remain); + CONSUME_RET; + + + /* Insert value to the container and check if we have finished array */ + if (!ucl_msgpack_insert_object (parser, NULL, 0, + parser->cur_obj)) { + return false; + } + + if (ucl_msgpack_is_container_finished (container)) { + state = finish_array_value; + } + else { + /* Read more elements */ + state = read_type; + next_state = read_array_value; + } + + break; + + case read_assoc_key: + /* + * Keys must have string type for ucl msgpack + */ + if (!(obj_parser->flags & MSGPACK_FLAG_KEY)) { + ucl_create_err (&parser->err, "bad type for key: %u, expected " + "string", (unsigned)obj_parser->fmt); + + return false; + } + + key = p; + keylen = len; + + if (keylen > remain || keylen == 0) { + ucl_create_err (&parser->err, "too long or empty key"); + return false; + } + + p += len; + remain -= len; + + state = read_type; + next_state = read_assoc_value; + break; + + case read_assoc_value: + /* + * p is now at the value start, len now contains length read and + * obj_parser contains the corresponding specific parser + */ + container = parser->stack; + + if (container == NULL) { + return false; + } + + ret = obj_parser->func (parser, container, len, obj_parser->fmt, + p, remain); + CONSUME_RET; + + assert (key != NULL && keylen > 0); + + if (!ucl_msgpack_insert_object (parser, key, keylen, + parser->cur_obj)) { + return false; + } + + key = NULL; + keylen = 0; + + if (ucl_msgpack_is_container_finished (container)) { + state = finish_assoc_value; + } + else { + /* Read more elements */ + state = read_type; + next_state = read_assoc_key; + } + break; + + case finish_array_value: + case finish_assoc_value: + GET_NEXT_STATE; + state = read_type; + break; + + case error_state: + ucl_create_err (&parser->err, "invalid state machine state"); + + return false; + } + } + + /* Check the finishing state */ + switch (state) { + case start_array: + case start_assoc: + /* Empty container at the end */ + if (len != 0) { + ucl_create_err (&parser->err, "invalid non-empty container at the end"); + + return false; + } + + parser->cur_obj = ucl_object_new_full ( + state == start_array ? UCL_ARRAY : UCL_OBJECT, + parser->chunks->priority); + /* Insert to the previous level container */ + if (!ucl_msgpack_insert_object (parser, + key, keylen, parser->cur_obj)) { + return false; + } + /* Get new container */ + container = ucl_msgpack_get_container (parser, obj_parser, len); + + if (container == NULL) { + return false; + } + + ret = obj_parser->func (parser, container, len, obj_parser->fmt, + p, remain); + break; + + case read_array_value: + case read_assoc_value: + if (len != 0) { + ucl_create_err (&parser->err, "unfinished value at the end"); + + return false; + } + + container = parser->stack; + + if (container == NULL) { + return false; + } + + ret = obj_parser->func (parser, container, len, obj_parser->fmt, + p, remain); + CONSUME_RET; + + + /* Insert value to the container and check if we have finished array */ + if (!ucl_msgpack_insert_object (parser, NULL, 0, + parser->cur_obj)) { + return false; + } + break; + case finish_array_value: + case finish_assoc_value: + case read_type: + /* Valid finishing state */ + break; + default: + /* Invalid finishing state */ + ucl_create_err (&parser->err, "invalid state machine finishing state: %d", + state); + + return false; + } + + /* Rewind to the top level container */ + ucl_msgpack_get_next_container (parser); + assert (parser->stack == NULL || + (parser->stack->level & MSGPACK_CONTAINER_BIT) == 0); + + return true; +} + +bool +ucl_parse_msgpack (struct ucl_parser *parser) +{ + ucl_object_t *container = NULL; + const unsigned char *p; + bool ret; + + assert (parser != NULL); + assert (parser->chunks != NULL); + assert (parser->chunks->begin != NULL); + assert (parser->chunks->remain != 0); + + p = parser->chunks->begin; + + if (parser->stack) { + container = parser->stack->obj; + } + + /* + * When we start parsing message pack chunk, we must ensure that we + * have either a valid container or the top object inside message pack is + * of container type + */ + if (container == NULL) { + if ((*p & 0x80) != 0x80 && !(*p >= 0xdc && *p <= 0xdf)) { + ucl_create_err (&parser->err, "bad top level object for msgpack"); + return false; + } + } + + ret = ucl_msgpack_consume (parser); + + if (ret && parser->top_obj == NULL) { + parser->top_obj = parser->cur_obj; + } + + return ret; +} + +static ssize_t +ucl_msgpack_parse_map (struct ucl_parser *parser, + struct ucl_stack *container, size_t len, enum ucl_msgpack_format fmt, + const unsigned char *pos, size_t remain) +{ + container->obj = parser->cur_obj; + + return 0; +} + +static ssize_t +ucl_msgpack_parse_array (struct ucl_parser *parser, + struct ucl_stack *container, size_t len, enum ucl_msgpack_format fmt, + const unsigned char *pos, size_t remain) +{ + container->obj = parser->cur_obj; + + return 0; +} + +static ssize_t +ucl_msgpack_parse_string (struct ucl_parser *parser, + struct ucl_stack *container, size_t len, enum ucl_msgpack_format fmt, + const unsigned char *pos, size_t remain) +{ + ucl_object_t *obj; + + if (len > remain) { + return -1; + } + + obj = ucl_object_new_full (UCL_STRING, parser->chunks->priority); + obj->value.sv = pos; + obj->len = len; + + if (fmt >= msgpack_bin8 && fmt <= msgpack_bin32) { + obj->flags |= UCL_OBJECT_BINARY; + } + + if (!(parser->flags & UCL_PARSER_ZEROCOPY)) { + if (obj->flags & UCL_OBJECT_BINARY) { + obj->trash_stack[UCL_TRASH_VALUE] = malloc (len); + + if (obj->trash_stack[UCL_TRASH_VALUE] != NULL) { + memcpy (obj->trash_stack[UCL_TRASH_VALUE], pos, len); + } + } + else { + ucl_copy_value_trash (obj); + } + } + + parser->cur_obj = obj; + + return len; +} + +static ssize_t +ucl_msgpack_parse_int (struct ucl_parser *parser, + struct ucl_stack *container, size_t len, enum ucl_msgpack_format fmt, + const unsigned char *pos, size_t remain) +{ + ucl_object_t *obj; + + if (len > remain) { + return -1; + } + + obj = ucl_object_new_full (UCL_INT, parser->chunks->priority); + + switch (fmt) { + case msgpack_positive_fixint: + obj->value.iv = (*pos & 0x7f); + len = 1; + break; + case msgpack_negative_fixint: + obj->value.iv = - (*pos & 0x1f); + len = 1; + break; + case msgpack_uint8: + obj->value.iv = (unsigned char)*pos; + len = 1; + break; + case msgpack_int8: + obj->value.iv = (signed char)*pos; + len = 1; + break; + case msgpack_int16: + obj->value.iv = FROM_BE16 (*(int16_t *)pos); + len = 2; + break; + case msgpack_uint16: + obj->value.iv = FROM_BE16 (*(uint16_t *)pos); + len = 2; + break; + case msgpack_int32: + obj->value.iv = FROM_BE32 (*(int32_t *)pos); + len = 4; + break; + case msgpack_uint32: + obj->value.iv = FROM_BE32 (*(uint32_t *)pos); + len = 4; + break; + case msgpack_int64: + obj->value.iv = FROM_BE64 (*(int64_t *)pos); + len = 8; + break; + case msgpack_uint64: + obj->value.iv = FROM_BE64 (*(uint64_t *)pos); + len = 8; + break; + default: + assert (0); + break; + } + + parser->cur_obj = obj; + + return len; +} + +static ssize_t +ucl_msgpack_parse_float (struct ucl_parser *parser, + struct ucl_stack *container, size_t len, enum ucl_msgpack_format fmt, + const unsigned char *pos, size_t remain) +{ + ucl_object_t *obj; + union { + uint32_t i; + float f; + } d; + + if (len > remain) { + return -1; + } + + obj = ucl_object_new_full (UCL_FLOAT, parser->chunks->priority); + + switch (fmt) { + case msgpack_float32: + d.i = FROM_BE32 (*(uint32_t *)pos); + /* XXX: can be slow */ + obj->value.dv = d.f; + len = 4; + break; + case msgpack_float64: + obj->value.iv = FROM_BE64 (*(uint64_t *)pos); + len = 8; + break; + default: + assert (0); + break; + } + + parser->cur_obj = obj; + + return len; +} + +static ssize_t +ucl_msgpack_parse_bool (struct ucl_parser *parser, + struct ucl_stack *container, size_t len, enum ucl_msgpack_format fmt, + const unsigned char *pos, size_t remain) +{ + ucl_object_t *obj; + + if (len > remain) { + return -1; + } + + obj = ucl_object_new_full (UCL_BOOLEAN, parser->chunks->priority); + + switch (fmt) { + case msgpack_true: + obj->value.iv = true; + break; + case msgpack_false: + obj->value.iv = false; + break; + default: + assert (0); + break; + } + + parser->cur_obj = obj; + + return 1; +} + +static ssize_t +ucl_msgpack_parse_null (struct ucl_parser *parser, + struct ucl_stack *container, size_t len, enum ucl_msgpack_format fmt, + const unsigned char *pos, size_t remain) +{ + ucl_object_t *obj; + + if (len > remain) { + return -1; + } + + obj = ucl_object_new_full (UCL_NULL, parser->chunks->priority); + parser->cur_obj = obj; + + return 1; +} + +static ssize_t +ucl_msgpack_parse_ignore (struct ucl_parser *parser, + struct ucl_stack *container, size_t len, enum ucl_msgpack_format fmt, + const unsigned char *pos, size_t remain) +{ + if (len > remain) { + return -1; + } + + switch (fmt) { + case msgpack_fixext1: + len = 2; + break; + case msgpack_fixext2: + len = 3; + break; + case msgpack_fixext4: + len = 5; + break; + case msgpack_fixext8: + len = 9; + break; + case msgpack_fixext16: + len = 17; + break; + case msgpack_ext8: + case msgpack_ext16: + case msgpack_ext32: + len = len + 1; + break; + default: + ucl_create_err (&parser->err, "bad type: %x", (unsigned)fmt); + return -1; + } + + return len; +} diff --git a/contrib/libucl/ucl_parser.c b/contrib/libucl/ucl_parser.c index 03935d9d0..ae8b3c259 100644 --- a/contrib/libucl/ucl_parser.c +++ b/contrib/libucl/ucl_parser.c @@ -562,7 +562,8 @@ ucl_copy_or_store_ptr (struct ucl_parser *parser, * @return */ static inline ucl_object_t * -ucl_add_parser_stack (ucl_object_t *obj, struct ucl_parser *parser, bool is_array, int level) +ucl_parser_add_container (ucl_object_t *obj, struct ucl_parser *parser, + bool is_array, int level) { struct ucl_stack *st; @@ -573,7 +574,9 @@ ucl_add_parser_stack (ucl_object_t *obj, struct ucl_parser *parser, bool is_arra else { obj->type = UCL_OBJECT; } - obj->value.ov = ucl_hash_create (parser->flags & UCL_PARSER_KEY_LOWERCASE); + if (obj->value.ov == NULL) { + obj->value.ov = ucl_hash_create (parser->flags & UCL_PARSER_KEY_LOWERCASE); + } parser->state = UCL_STATE_KEY; } else { @@ -1001,6 +1004,98 @@ ucl_parser_append_elt (struct ucl_parser *parser, ucl_hash_t *cont, } } +bool +ucl_parser_process_object_element (struct ucl_parser *parser, ucl_object_t *nobj) +{ + ucl_hash_t *container; + ucl_object_t *tobj; + + container = parser->stack->obj->value.ov; + + tobj = __DECONST (ucl_object_t *, ucl_hash_search_obj (container, nobj)); + if (tobj == NULL) { + container = ucl_hash_insert_object (container, nobj, + parser->flags & UCL_PARSER_KEY_LOWERCASE); + nobj->prev = nobj; + nobj->next = NULL; + parser->stack->obj->len ++; + } + else { + unsigned priold = ucl_object_get_priority (tobj), + prinew = ucl_object_get_priority (nobj); + switch (parser->chunks->strategy) { + + case UCL_DUPLICATE_APPEND: + /* + * The logic here is the following: + * + * - if we have two objects with the same priority, then we form an + * implicit or explicit array + * - if a new object has bigger priority, then we overwrite an old one + * - if a new object has lower priority, then we ignore it + */ + + + /* Special case for inherited objects */ + if (tobj->flags & UCL_OBJECT_INHERITED) { + prinew = priold + 1; + } + + if (priold == prinew) { + ucl_parser_append_elt (parser, container, tobj, nobj); + } + else if (priold > prinew) { + /* + * We add this new object to a list of trash objects just to ensure + * that it won't come to any real object + * XXX: rather inefficient approach + */ + DL_APPEND (parser->trash_objs, nobj); + } + else { + ucl_hash_replace (container, tobj, nobj); + ucl_object_unref (tobj); + } + + break; + + case UCL_DUPLICATE_REWRITE: + /* We just rewrite old values regardless of priority */ + ucl_hash_replace (container, tobj, nobj); + ucl_object_unref (tobj); + + break; + + case UCL_DUPLICATE_ERROR: + ucl_create_err (&parser->err, "error while parsing %s: " + "line: %d, column: %d: duplicate element for key '%s' " + "has been found", + parser->cur_file ? parser->cur_file : "", + parser->chunks->line, parser->chunks->column, nobj->key); + return false; + + case UCL_DUPLICATE_MERGE: + /* + * Here we do have some old object so we just push it on top of objects stack + */ + if (tobj->type == UCL_OBJECT || tobj->type == UCL_ARRAY) { + ucl_object_unref (nobj); + nobj = tobj; + } + else { + /* For other types we create implicit array as usual */ + ucl_parser_append_elt (parser, container, tobj, nobj); + } + break; + } + } + + parser->stack->obj->value.ov = container; + parser->cur_obj = nobj; + + return true; +} + /** * Parse a key in an object * @param parser @@ -1018,8 +1113,7 @@ ucl_parse_key (struct ucl_parser *parser, struct ucl_chunk *chunk, bool got_quote = false, got_eq = false, got_semicolon = false, need_unescape = false, ucl_escape = false, var_expand = false, got_content = false, got_sep = false; - ucl_object_t *nobj, *tobj; - ucl_hash_t *container; + ucl_object_t *nobj; ssize_t keylen; p = chunk->pos; @@ -1204,57 +1298,17 @@ ucl_parse_key (struct ucl_parser *parser, struct ucl_chunk *chunk, return false; } - container = parser->stack->obj->value.ov; nobj->key = key; nobj->keylen = keylen; - tobj = __DECONST (ucl_object_t *, ucl_hash_search_obj (container, nobj)); - if (tobj == NULL) { - container = ucl_hash_insert_object (container, nobj, - parser->flags & UCL_PARSER_KEY_LOWERCASE); - nobj->prev = nobj; - nobj->next = NULL; - parser->stack->obj->len ++; - } - else { - /* - * The logic here is the following: - * - * - if we have two objects with the same priority, then we form an - * implicit or explicit array - * - if a new object has bigger priority, then we overwrite an old one - * - if a new object has lower priority, then we ignore it - */ - unsigned priold = ucl_object_get_priority (tobj), - prinew = ucl_object_get_priority (nobj); - - /* Special case for inherited objects */ - if (tobj->flags & UCL_OBJECT_INHERITED) { - prinew = priold + 1; - } - if (priold == prinew) { - ucl_parser_append_elt (parser, container, tobj, nobj); - } - else if (priold > prinew) { - /* - * We add this new object to a list of trash objects just to ensure - * that it won't come to any real object - * XXX: rather inefficient approach - */ - DL_APPEND (parser->trash_objs, nobj); - } - else { - ucl_hash_replace (container, tobj, nobj); - ucl_object_unref (tobj); - } + if (!ucl_parser_process_object_element (parser, nobj)) { + return false; } if (ucl_escape) { nobj->flags |= UCL_OBJECT_NEED_KEY_ESCAPE; } - parser->stack->obj->value.ov = container; - parser->cur_obj = nobj; return true; } @@ -1387,8 +1441,8 @@ ucl_parse_multiline_string (struct ucl_parser *parser, return len; } -static ucl_object_t* -ucl_get_value_object (struct ucl_parser *parser) +static inline ucl_object_t* +ucl_parser_get_container (struct ucl_parser *parser) { ucl_object_t *t, *obj = NULL; @@ -1400,7 +1454,12 @@ ucl_get_value_object (struct ucl_parser *parser) /* Object must be allocated */ obj = ucl_object_new_full (UCL_NULL, parser->chunks->priority); t = parser->stack->obj; - ucl_array_append (t, obj); + + if (!ucl_array_append (t, obj)) { + ucl_object_unref (obj); + return NULL; + } + parser->cur_obj = obj; } else { @@ -1451,7 +1510,7 @@ ucl_parse_value (struct ucl_parser *parser, struct ucl_chunk *chunk) return false; } - obj = ucl_get_value_object (parser); + obj = ucl_parser_get_container (parser); str_len = chunk->pos - c - 2; obj->type = UCL_STRING; if ((str_len = ucl_copy_or_store_ptr (parser, c + 1, @@ -1468,9 +1527,9 @@ ucl_parse_value (struct ucl_parser *parser, struct ucl_chunk *chunk) return true; break; case '{': - obj = ucl_get_value_object (parser); + obj = ucl_parser_get_container (parser); /* We have a new object */ - obj = ucl_add_parser_stack (obj, parser, false, parser->stack->level); + obj = ucl_parser_add_container (obj, parser, false, parser->stack->level); if (obj == NULL) { return false; } @@ -1480,9 +1539,9 @@ ucl_parse_value (struct ucl_parser *parser, struct ucl_chunk *chunk) return true; break; case '[': - obj = ucl_get_value_object (parser); + obj = ucl_parser_get_container (parser); /* We have a new array */ - obj = ucl_add_parser_stack (obj, parser, true, parser->stack->level); + obj = ucl_parser_add_container (obj, parser, true, parser->stack->level); if (obj == NULL) { return false; } @@ -1502,7 +1561,7 @@ ucl_parse_value (struct ucl_parser *parser, struct ucl_chunk *chunk) } break; case '<': - obj = ucl_get_value_object (parser); + obj = ucl_parser_get_container (parser); /* We have something like multiline value, which must be <<[A-Z]+\n */ if (chunk->end - p > 3) { if (memcmp (p, "<<", 2) == 0) { @@ -1545,7 +1604,7 @@ ucl_parse_value (struct ucl_parser *parser, struct ucl_chunk *chunk) default: parse_string: if (obj == NULL) { - obj = ucl_get_value_object (parser); + obj = ucl_parser_get_container (parser); } /* Parse atom */ @@ -1905,17 +1964,6 @@ ucl_state_machine (struct ucl_parser *parser) bool next_key = false, end_of_object = false, ret; if (parser->top_obj == NULL) { - if (*chunk->pos == '[') { - obj = ucl_add_parser_stack (NULL, parser, true, 0); - } - 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; } @@ -1939,7 +1987,9 @@ ucl_state_machine (struct ucl_parser *parser) UCL_CHARACTER_WHITESPACE_UNSAFE)) { ucl_chunk_skipc (chunk, p); } + p = chunk->pos; + if (*p == '[') { parser->state = UCL_STATE_VALUE; ucl_chunk_skipc (chunk, p); @@ -1950,6 +2000,23 @@ ucl_state_machine (struct ucl_parser *parser) ucl_chunk_skipc (chunk, p); } } + + if (parser->top_obj == NULL) { + if (parser->state == UCL_STATE_VALUE) { + obj = ucl_parser_add_container (NULL, parser, true, 0); + } + else { + obj = ucl_parser_add_container (NULL, parser, false, 0); + } + + if (obj == NULL) { + return false; + } + + parser->top_obj = obj; + parser->cur_obj = obj; + } + } break; case UCL_STATE_KEY: @@ -1983,7 +2050,7 @@ 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, + obj = ucl_parser_add_container (parser->cur_obj, parser, false, parser->stack->level + 1); if (obj == NULL) { return false; @@ -2270,8 +2337,9 @@ ucl_parser_set_variables_handler (struct ucl_parser *parser, } bool -ucl_parser_add_chunk_priority (struct ucl_parser *parser, const unsigned char *data, - size_t len, unsigned priority) +ucl_parser_add_chunk_full (struct ucl_parser *parser, const unsigned char *data, + size_t len, unsigned priority, enum ucl_duplicate_strategy strat, + enum ucl_parse_type parse_type) { struct ucl_chunk *chunk; @@ -2300,14 +2368,24 @@ ucl_parser_add_chunk_priority (struct ucl_parser *parser, const unsigned char *d chunk->line = 1; chunk->column = 0; chunk->priority = priority; + chunk->strategy = strat; + chunk->parse_type = parse_type; LL_PREPEND (parser->chunks, chunk); parser->recursion ++; + if (parser->recursion > UCL_MAX_RECURSION) { ucl_create_err (&parser->err, "maximum include nesting limit is reached: %d", parser->recursion); return false; } - return ucl_state_machine (parser); + + switch (parse_type) { + default: + case UCL_PARSE_UCL: + return ucl_state_machine (parser); + case UCL_PARSE_MSGPACK: + return ucl_parse_msgpack (parser); + } } ucl_create_err (&parser->err, "a parser is in an invalid state"); @@ -2315,6 +2393,19 @@ ucl_parser_add_chunk_priority (struct ucl_parser *parser, const unsigned char *d return false; } +bool +ucl_parser_add_chunk_priority (struct ucl_parser *parser, + const unsigned char *data, size_t len, unsigned priority) +{ + /* We dereference parser, so this check is essential */ + if (parser == NULL) { + return false; + } + + return ucl_parser_add_chunk_full (parser, data, len, + priority, UCL_DUPLICATE_APPEND, UCL_PARSE_UCL); +} + bool ucl_parser_add_chunk (struct ucl_parser *parser, const unsigned char *data, size_t len) @@ -2323,8 +2414,8 @@ ucl_parser_add_chunk (struct ucl_parser *parser, const unsigned char *data, return false; } - return ucl_parser_add_chunk_priority (parser, data, len, - parser->default_priority); + return ucl_parser_add_chunk_full (parser, data, len, + parser->default_priority, UCL_DUPLICATE_APPEND, UCL_PARSE_UCL); } bool diff --git a/contrib/libucl/ucl_util.c b/contrib/libucl/ucl_util.c index 7833e1f1c..9ea151e37 100644 --- a/contrib/libucl/ucl_util.c +++ b/contrib/libucl/ucl_util.c @@ -410,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 { @@ -424,6 +437,7 @@ ucl_copy_value_trash (const ucl_object_t *obj) } deconst->flags |= UCL_OBJECT_ALLOCATED_VALUE; } + return obj->trash_stack[UCL_TRASH_VALUE]; } @@ -796,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 @@ -806,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, - bool use_prefix, const char *prefix, const char *target, unsigned priority) + struct ucl_parser *parser, + struct ucl_include_params *params) { bool res; @@ -819,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; @@ -850,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; @@ -879,9 +908,7 @@ 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, - bool use_prefix, const char *prefix, const char *target, - bool soft_fail, unsigned priority) + struct ucl_parser *parser, struct ucl_include_params *params) { bool res; struct ucl_chunk *chunk; @@ -898,10 +925,10 @@ ucl_include_file_single (const unsigned char *data, size_t len, snprintf (filebuf, sizeof (filebuf), "%.*s", (int)len, data); if (ucl_realpath (filebuf, realbuf) == NULL) { - if (soft_fail) { + if (params->soft_fail) { return false; } - if (!must_exist) { + if (!params->must_exist) { return true; } ucl_create_err (&parser->err, "cannot open file %s: %s", @@ -912,22 +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 (soft_fail) { + 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)) { - if (soft_fail) { + if (!ucl_fetch_file (realbuf, &buf, &buflen, &parser->err, params->must_exist)) { + if (params->soft_fail) { return false; } - return (!must_exist || 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; @@ -971,26 +999,27 @@ ucl_include_file_single (const unsigned char *data, size_t len, prev_state = parser->state; parser->state = UCL_STATE_INIT; - if (use_prefix && prefix == NULL) { + if (params->use_prefix && params->prefix == NULL) { /* Auto generate a key name based on the included filename */ - prefix = basename (realbuf); - ext = strrchr(prefix, '.'); + 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 (prefix != NULL) { + 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, prefix, strlen (prefix))); + old_obj = __DECONST (ucl_object_t *, ucl_hash_search (container, + params->prefix, strlen (params->prefix))); - if (strcasecmp (target, "array") == 0 && old_obj == NULL) { + if (strcasecmp (params->target, "array") == 0 && old_obj == NULL) { /* Create an array with key: prefix */ - old_obj = ucl_object_new_full (UCL_ARRAY, priority); - old_obj->key = prefix; - old_obj->keylen = strlen (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; @@ -999,7 +1028,7 @@ ucl_include_file_single (const unsigned char *data, size_t len, parser->flags & UCL_PARSER_KEY_LOWERCASE); parser->stack->obj->len ++; - nest_obj = ucl_object_new_full (UCL_OBJECT, priority); + nest_obj = ucl_object_new_full (UCL_OBJECT, params->priority); nest_obj->prev = nest_obj; nest_obj->next = NULL; @@ -1007,9 +1036,9 @@ ucl_include_file_single (const unsigned char *data, size_t len, } else if (old_obj == NULL) { /* Create an object with key: prefix */ - nest_obj = ucl_object_new_full (UCL_OBJECT, priority); - nest_obj->key = prefix; - nest_obj->keylen = strlen (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; @@ -1018,10 +1047,11 @@ ucl_include_file_single (const unsigned char *data, size_t len, parser->flags & UCL_PARSER_KEY_LOWERCASE); parser->stack->obj->len ++; } - else if (strcasecmp (target, "array") == 0 || ucl_object_type(old_obj) == UCL_ARRAY) { + 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, priority); + nest_obj = ucl_object_new_full (UCL_OBJECT, params->priority); nest_obj->prev = nest_obj; nest_obj->next = NULL; @@ -1036,7 +1066,7 @@ ucl_include_file_single (const unsigned char *data, size_t len, new_obj->prev = new_obj; new_obj->next = NULL; - nest_obj = ucl_object_new_full (UCL_OBJECT, priority); + nest_obj = ucl_object_new_full (UCL_OBJECT, params->priority); nest_obj->prev = nest_obj; nest_obj->next = NULL; @@ -1054,7 +1084,7 @@ ucl_include_file_single (const unsigned char *data, size_t len, /* The key is not an object */ ucl_create_err (&parser->err, "Conflicting type for key: %s", - prefix); + params->prefix); return false; } } @@ -1076,8 +1106,9 @@ ucl_include_file_single (const unsigned char *data, size_t len, } } - res = ucl_parser_add_chunk_priority (parser, buf, buflen, priority); - if (!res && !must_exist) { + 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; @@ -1085,7 +1116,7 @@ ucl_include_file_single (const unsigned char *data, size_t len, } /* Stop nesting the include, take 1 level off the stack */ - if (prefix != NULL && nest_obj != NULL) { + if (params->prefix != NULL && nest_obj != NULL) { parser->stack = st->next; UCL_FREE (sizeof (struct ucl_stack), st); } @@ -1144,9 +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, bool use_prefix, const char *prefix, - const char *target, bool soft_fail, unsigned priority) + struct ucl_parser *parser, struct ucl_include_params *params) { const unsigned char *p = data, *end = data + len; bool need_glob = false; @@ -1155,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, use_prefix, prefix, target, soft_fail, priority); + if (!params->allow_glob) { + return ucl_include_file_single (data, len, parser, params); } else { /* Check for special symbols in a filename */ @@ -1174,13 +1202,12 @@ ucl_include_file (const unsigned char *data, size_t len, 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, use_prefix, prefix, target, soft_fail, priority)) { - if (soft_fail) { + strlen (globbuf.gl_pathv[i]), parser, params)) { + if (params->soft_fail) { continue; } globfree (&globbuf); @@ -1190,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, use_prefix, prefix, target, soft_fail, 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, use_prefix, prefix, target, soft_fail, priority); + return ucl_include_file_single (data, len, parser, params); #endif return true; @@ -1227,22 +1252,25 @@ 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, use_prefix, search; - const char *prefix, *target; - unsigned priority; + bool allow_url, search; + const char *duplicate; const ucl_object_t *param; 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; - use_prefix = false; - prefix = NULL; - target = "object"; - 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 */ @@ -1250,27 +1278,43 @@ ucl_include_common (const unsigned char *data, size_t len, 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); + params.must_exist = !ucl_object_toboolean (param); } else if (strncmp (param->key, "sign", param->keylen) == 0) { - need_sign = ucl_object_toboolean (param); + params.check_signature = ucl_object_toboolean (param); } else if (strncmp (param->key, "glob", param->keylen) == 0) { - allow_glob = ucl_object_toboolean (param); + 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) { - use_prefix = ucl_object_toboolean (param); + params.use_prefix = ucl_object_toboolean (param); } } else if (param->type == UCL_STRING) { if (strncmp (param->key, "key", param->keylen) == 0) { - prefix = ucl_object_tostring (param); + params.prefix = ucl_object_tostring (param); } else if (strncmp (param->key, "target", param->keylen) == 0) { - target = ucl_object_tostring (param); + params.target = ucl_object_tostring (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 (param->type == UCL_ARRAY) { @@ -1280,7 +1324,7 @@ ucl_include_common (const unsigned char *data, size_t len, } else if (param->type == UCL_INT) { if (strncmp (param->key, "priority", param->keylen) == 0) { - priority = ucl_object_toint (param); + params.priority = ucl_object_toint (param); } } } @@ -1289,20 +1333,17 @@ ucl_include_common (const unsigned char *data, size_t len, 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, need_sign, - !try_load, use_prefix, prefix, target, priority); + return ucl_include_url (data, len, parser, ¶ms); } else if (data != NULL) { /* Try to load a file */ - return ucl_include_file (data, len, parser, need_sign, !try_load, - allow_glob, use_prefix, prefix, target, false, priority); + return ucl_include_file (data, len, parser, ¶ms); } } else { if (allow_url && ucl_strnstr (data, "://", len) != NULL) { /* Globbing is not used for URL's */ - return ucl_include_url (data, len, parser, need_sign, - !try_load, use_prefix, prefix, target, priority); + return ucl_include_url (data, len, parser, ¶ms); } ip = ucl_object_iterate_new (parser->includepaths); @@ -1310,9 +1351,9 @@ ucl_include_common (const unsigned char *data, size_t len, 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, need_sign, - !try_load, allow_glob, use_prefix, prefix, target, true, priority))) { - if (!allow_glob) { + if ((search = ucl_include_file (ipath, strlen (ipath), + parser, ¶ms))) { + if (!params.allow_glob) { break; } } @@ -2903,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; @@ -2924,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 -- 2.39.5