@@ -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) \ |
@@ -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 |
@@ -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: |
@@ -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_ */ |
@@ -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 : "<unknown>", | |||
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 |
@@ -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 |