@@ -6,7 +6,8 @@ SET(UCLSRC ucl_util.c | |||
ucl_hash.c | |||
ucl_schema.c | |||
lua_ucl.c | |||
ucl_msgpack.c) | |||
ucl_msgpack.c | |||
ucl_sexp.c) | |||
SET (LIB_TYPE STATIC) |
@@ -147,11 +147,13 @@ typedef enum ucl_emitter { | |||
* UCL still has to perform copying implicitly. | |||
*/ | |||
typedef enum ucl_parser_flags { | |||
UCL_PARSER_DEFAULT = 0x0, /**< No special flags */ | |||
UCL_PARSER_KEY_LOWERCASE = 0x1, /**< Convert all keys to lower case */ | |||
UCL_PARSER_ZEROCOPY = 0x2, /**< Parse input in zero-copy mode if possible */ | |||
UCL_PARSER_NO_TIME = 0x4, /**< Do not parse time and treat time values as strings */ | |||
UCL_PARSER_NO_IMPLICIT_ARRAYS = 0x8 /** Create explicit arrays instead of implicit ones */ | |||
UCL_PARSER_DEFAULT = 0, /**< No special flags */ | |||
UCL_PARSER_KEY_LOWERCASE = (1 << 0), /**< Convert all keys to lower case */ | |||
UCL_PARSER_ZEROCOPY = (1 << 1), /**< Parse input in zero-copy mode if possible */ | |||
UCL_PARSER_NO_TIME = (1 << 2), /**< Do not parse time and treat time values as strings */ | |||
UCL_PARSER_NO_IMPLICIT_ARRAYS = (1 << 3), /** Create explicit arrays instead of implicit ones */ | |||
UCL_PARSER_SAVE_COMMENTS = (1 << 4), /** Save comments in the parser context */ | |||
UCL_PARSER_DISABLE_MACRO = (1 << 5) /** Treat macros as comments */ | |||
} ucl_parser_flags_t; | |||
/** | |||
@@ -159,17 +161,17 @@ typedef enum ucl_parser_flags { | |||
*/ | |||
typedef enum ucl_string_flags { | |||
UCL_STRING_RAW = 0x0, /**< Treat string as is */ | |||
UCL_STRING_ESCAPE = 0x1, /**< Perform JSON escape */ | |||
UCL_STRING_TRIM = 0x2, /**< Trim leading and trailing whitespaces */ | |||
UCL_STRING_PARSE_BOOLEAN = 0x4, /**< Parse passed string and detect boolean */ | |||
UCL_STRING_PARSE_INT = 0x8, /**< Parse passed string and detect integer number */ | |||
UCL_STRING_PARSE_DOUBLE = 0x10, /**< Parse passed string and detect integer or float number */ | |||
UCL_STRING_PARSE_TIME = 0x20, /**< Parse time strings */ | |||
UCL_STRING_ESCAPE = (1 << 0), /**< Perform JSON escape */ | |||
UCL_STRING_TRIM = (1 << 1), /**< Trim leading and trailing whitespaces */ | |||
UCL_STRING_PARSE_BOOLEAN = (1 << 2), /**< Parse passed string and detect boolean */ | |||
UCL_STRING_PARSE_INT = (1 << 3), /**< Parse passed string and detect integer number */ | |||
UCL_STRING_PARSE_DOUBLE = (1 << 4), /**< Parse passed string and detect integer or float number */ | |||
UCL_STRING_PARSE_TIME = (1 << 5), /**< Parse time strings */ | |||
UCL_STRING_PARSE_NUMBER = UCL_STRING_PARSE_INT|UCL_STRING_PARSE_DOUBLE|UCL_STRING_PARSE_TIME, /**< | |||
Parse passed string and detect number */ | |||
UCL_STRING_PARSE = UCL_STRING_PARSE_BOOLEAN|UCL_STRING_PARSE_NUMBER, /**< | |||
Parse passed string (and detect booleans and numbers) */ | |||
UCL_STRING_PARSE_BYTES = 0x40 /**< Treat numbers as bytes */ | |||
UCL_STRING_PARSE_BYTES = (1 << 6) /**< Treat numbers as bytes */ | |||
} ucl_string_flags_t; | |||
/** | |||
@@ -1090,6 +1092,23 @@ UCL_EXTERN void ucl_parser_clear_error(struct ucl_parser *parser); | |||
*/ | |||
UCL_EXTERN void ucl_parser_free (struct ucl_parser *parser); | |||
/** | |||
* Get constant opaque pointer to comments structure for this parser. Increase | |||
* refcount to prevent this object to be destroyed on parser's destruction | |||
* @param parser parser structure | |||
* @return ucl comments pointer or NULL | |||
*/ | |||
UCL_EXTERN const ucl_object_t * ucl_parser_get_comments (struct ucl_parser *parser); | |||
/** | |||
* Utility function to find a comment object for the specified object in the input | |||
* @param comments comments object | |||
* @param srch search object | |||
* @return string comment enclosed in ucl_object_t | |||
*/ | |||
UCL_EXTERN const ucl_object_t * ucl_comments_find (const ucl_object_t *comments, | |||
const ucl_object_t *srch); | |||
/** | |||
* Add new public key to parser for signatures check | |||
* @param parser parser object | |||
@@ -1171,8 +1190,8 @@ struct ucl_emitter_context { | |||
unsigned int indent; | |||
/** Top level object */ | |||
const ucl_object_t *top; | |||
/** The rest of context */ | |||
unsigned char data[1]; | |||
/** Optional comments */ | |||
const ucl_object_t *comments; | |||
}; | |||
/** | |||
@@ -1202,11 +1221,13 @@ UCL_EXTERN unsigned char *ucl_object_emit_len (const ucl_object_t *obj, | |||
* @param emit_type if type is #UCL_EMIT_JSON then emit json, if type is | |||
* #UCL_EMIT_CONFIG then emit config like object | |||
* @param emitter a set of emitter functions | |||
* @param comments optional comments for the parser | |||
* @return dump of an object (must be freed after using) or NULL in case of error | |||
*/ | |||
UCL_EXTERN bool ucl_object_emit_full (const ucl_object_t *obj, | |||
enum ucl_emitter emit_type, | |||
struct ucl_emitter_functions *emitter); | |||
struct ucl_emitter_functions *emitter, | |||
const ucl_object_t *comments); | |||
/** | |||
* Start streamlined UCL object emitter |
@@ -362,6 +362,7 @@ ucl_emitter_common_elt (struct ucl_emitter_context *ctx, | |||
const struct ucl_emitter_functions *func = ctx->func; | |||
bool flag; | |||
struct ucl_object_userdata *ud; | |||
const ucl_object_t *comment = NULL, *cur_comment; | |||
const char *ud_out = ""; | |||
if (ctx->id != UCL_EMIT_CONFIG && !first) { | |||
@@ -379,6 +380,25 @@ ucl_emitter_common_elt (struct ucl_emitter_context *ctx, | |||
ucl_add_tabs (func, ctx->indent, compact); | |||
if (ctx->comments && ctx->id == UCL_EMIT_CONFIG) { | |||
comment = ucl_object_find_keyl (ctx->comments, (const char *)&obj, | |||
sizeof (void *)); | |||
if (comment) { | |||
if (!(comment->flags & UCL_OBJECT_INHERITED)) { | |||
DL_FOREACH (comment, cur_comment) { | |||
func->ucl_emitter_append_len (cur_comment->value.sv, | |||
cur_comment->len, | |||
func->ud); | |||
func->ucl_emitter_append_character ('\n', 1, func->ud); | |||
ucl_add_tabs (func, ctx->indent, compact); | |||
} | |||
comment = NULL; | |||
} | |||
} | |||
} | |||
switch (obj->type) { | |||
case UCL_INT: | |||
ucl_emitter_print_key (print_key, ctx, obj, compact); | |||
@@ -438,6 +458,19 @@ ucl_emitter_common_elt (struct ucl_emitter_context *ctx, | |||
ucl_emitter_finish_object (ctx, obj, compact, !print_key); | |||
break; | |||
} | |||
if (comment) { | |||
DL_FOREACH (comment, cur_comment) { | |||
func->ucl_emitter_append_len (cur_comment->value.sv, | |||
cur_comment->len, | |||
func->ud); | |||
func->ucl_emitter_append_character ('\n', 1, func->ud); | |||
if (cur_comment->next) { | |||
ucl_add_tabs (func, ctx->indent, compact); | |||
} | |||
} | |||
} | |||
} | |||
/* | |||
@@ -605,10 +638,10 @@ ucl_object_emit_len (const ucl_object_t *obj, enum ucl_emitter emit_type, | |||
} | |||
func = ucl_object_emit_memory_funcs ((void **)&res); | |||
s = func->ud; | |||
if (func != NULL) { | |||
ucl_object_emit_full (obj, emit_type, func); | |||
s = func->ud; | |||
ucl_object_emit_full (obj, emit_type, func, NULL); | |||
if (outlen != NULL) { | |||
*outlen = s->i; | |||
@@ -622,7 +655,8 @@ ucl_object_emit_len (const ucl_object_t *obj, enum ucl_emitter emit_type, | |||
bool | |||
ucl_object_emit_full (const ucl_object_t *obj, enum ucl_emitter emit_type, | |||
struct ucl_emitter_functions *emitter) | |||
struct ucl_emitter_functions *emitter, | |||
const ucl_object_t *comments) | |||
{ | |||
const struct ucl_emitter_context *ctx; | |||
struct ucl_emitter_context my_ctx; | |||
@@ -634,6 +668,7 @@ ucl_object_emit_full (const ucl_object_t *obj, enum ucl_emitter emit_type, | |||
my_ctx.func = emitter; | |||
my_ctx.indent = 0; | |||
my_ctx.top = obj; | |||
my_ctx.comments = comments; | |||
my_ctx.ops->ucl_emitter_write_elt (&my_ctx, obj, true, false); | |||
res = true; |
@@ -38,12 +38,20 @@ struct ucl_emitter_streamline_stack { | |||
struct ucl_emitter_context_streamline { | |||
/* Inherited from the main context */ | |||
/** Name of emitter (e.g. json, compact_json) */ | |||
const char *name; | |||
/** Unique id (e.g. UCL_EMIT_JSON for standard emitters */ | |||
int id; | |||
/** A set of output functions */ | |||
const struct ucl_emitter_functions *func; | |||
/** A set of output operations */ | |||
const struct ucl_emitter_operations *ops; | |||
unsigned int ident; | |||
/** Current amount of indent tabs */ | |||
unsigned int indent; | |||
/** Top level object */ | |||
const ucl_object_t *top; | |||
/** Optional comments */ | |||
const ucl_object_t *comments; | |||
/* Streamline specific fields */ | |||
struct ucl_emitter_streamline_stack *containers; |
@@ -247,7 +247,7 @@ ucl_hash_create (bool ignore_case) | |||
return new; | |||
} | |||
void ucl_hash_destroy (ucl_hash_t* hashlin, ucl_hash_free_func *func) | |||
void ucl_hash_destroy (ucl_hash_t* hashlin, ucl_hash_free_func func) | |||
{ | |||
const ucl_object_t *cur, *tmp; | |||
@@ -31,8 +31,8 @@ | |||
struct ucl_hash_node_s; | |||
typedef struct ucl_hash_node_s ucl_hash_node_t; | |||
typedef int ucl_hash_cmp_func (const void* void_a, const void* void_b); | |||
typedef void ucl_hash_free_func (void *ptr); | |||
typedef int (*ucl_hash_cmp_func) (const void* void_a, const void* void_b); | |||
typedef void (*ucl_hash_free_func) (void *ptr); | |||
typedef void* ucl_hash_iter_t; | |||
@@ -51,7 +51,7 @@ ucl_hash_t* ucl_hash_create (bool ignore_case); | |||
/** | |||
* Deinitializes the hashtable. | |||
*/ | |||
void ucl_hash_destroy (ucl_hash_t* hashlin, ucl_hash_free_func *func); | |||
void ucl_hash_destroy (ucl_hash_t* hashlin, ucl_hash_free_func func); | |||
/** | |||
* Inserts an element in the the hashtable. |
@@ -211,6 +211,8 @@ struct ucl_parser { | |||
struct ucl_variable *variables; | |||
ucl_variable_handler var_handler; | |||
void *var_data; | |||
ucl_object_t *comments; | |||
ucl_object_t *last_comment; | |||
UT_string *err; | |||
}; | |||
@@ -89,6 +89,39 @@ ucl_set_err (struct ucl_parser *parser, int code, const char *str, UT_string **e | |||
parser->err_code = code; | |||
} | |||
static void | |||
ucl_save_comment (struct ucl_parser *parser, const char *begin, size_t len) | |||
{ | |||
ucl_object_t *nobj; | |||
if (len > 0 && begin != NULL) { | |||
nobj = ucl_object_fromstring_common (begin, len, 0); | |||
if (parser->last_comment) { | |||
/* We need to append data to an existing object */ | |||
DL_APPEND (parser->last_comment, nobj); | |||
} | |||
else { | |||
parser->last_comment = nobj; | |||
} | |||
} | |||
} | |||
static void | |||
ucl_attach_comment (struct ucl_parser *parser, ucl_object_t *obj, bool before) | |||
{ | |||
if (parser->last_comment) { | |||
ucl_object_insert_key (parser->comments, parser->last_comment, | |||
(const char *)&obj, sizeof (void *), true); | |||
if (before) { | |||
parser->last_comment->flags |= UCL_OBJECT_INHERITED; | |||
} | |||
parser->last_comment = NULL; | |||
} | |||
} | |||
/** | |||
* Skip all comments from the current pos resolving nested and multiline comments | |||
* @param parser | |||
@@ -98,7 +131,7 @@ static bool | |||
ucl_skip_comments (struct ucl_parser *parser) | |||
{ | |||
struct ucl_chunk *chunk = parser->chunks; | |||
const unsigned char *p; | |||
const unsigned char *p, *beg = NULL; | |||
int comments_nested = 0; | |||
bool quoted = false; | |||
@@ -108,9 +141,17 @@ start: | |||
if (chunk->remain > 0 && *p == '#') { | |||
if (parser->state != UCL_STATE_SCOMMENT && | |||
parser->state != UCL_STATE_MCOMMENT) { | |||
beg = p; | |||
while (p < chunk->end) { | |||
if (*p == '\n') { | |||
if (parser->flags & UCL_PARSER_SAVE_COMMENTS) { | |||
ucl_save_comment (parser, beg, p - beg); | |||
beg = NULL; | |||
} | |||
ucl_chunk_skipc (chunk, p); | |||
goto start; | |||
} | |||
ucl_chunk_skipc (chunk, p); | |||
@@ -119,6 +160,7 @@ start: | |||
} | |||
else if (chunk->remain >= 2 && *p == '/') { | |||
if (p[1] == '*') { | |||
beg = p; | |||
ucl_chunk_skipc (chunk, p); | |||
comments_nested ++; | |||
ucl_chunk_skipc (chunk, p); | |||
@@ -134,6 +176,11 @@ start: | |||
if (*p == '/') { | |||
comments_nested --; | |||
if (comments_nested == 0) { | |||
if (parser->flags & UCL_PARSER_SAVE_COMMENTS) { | |||
ucl_save_comment (parser, beg, p - beg + 1); | |||
beg = NULL; | |||
} | |||
ucl_chunk_skipc (chunk, p); | |||
goto start; | |||
} | |||
@@ -147,6 +194,7 @@ start: | |||
continue; | |||
} | |||
} | |||
ucl_chunk_skipc (chunk, p); | |||
} | |||
if (comments_nested != 0) { | |||
@@ -157,6 +205,10 @@ start: | |||
} | |||
} | |||
if (beg && p > beg && (parser->flags & UCL_PARSER_SAVE_COMMENTS)) { | |||
ucl_save_comment (parser, beg, p - beg); | |||
} | |||
return true; | |||
} | |||
@@ -451,6 +503,11 @@ ucl_expand_variable (struct ucl_parser *parser, unsigned char **dst, | |||
size_t out_len = 0; | |||
bool vars_found = false; | |||
if (parser->flags & UCL_PARSER_DISABLE_MACRO) { | |||
*dst = NULL; | |||
return in_len; | |||
} | |||
p = src; | |||
while (p != end) { | |||
if (*p == '$') { | |||
@@ -590,12 +647,14 @@ ucl_parser_add_container (ucl_object_t *obj, struct ucl_parser *parser, | |||
} | |||
st = UCL_ALLOC (sizeof (struct ucl_stack)); | |||
if (st == NULL) { | |||
ucl_set_err (parser, UCL_EINTERNAL, "cannot allocate memory for an object", | |||
&parser->err); | |||
ucl_object_unref (obj); | |||
return NULL; | |||
} | |||
st->obj = obj; | |||
st->level = level; | |||
LL_PREPEND (parser->stack, st); | |||
@@ -1092,6 +1151,7 @@ ucl_parser_process_object_element (struct ucl_parser *parser, ucl_object_t *nobj | |||
parser->stack->obj->value.ov = container; | |||
parser->cur_obj = nobj; | |||
ucl_attach_comment (parser, nobj, false); | |||
return true; | |||
} | |||
@@ -1120,7 +1180,10 @@ ucl_parse_key (struct ucl_parser *parser, struct ucl_chunk *chunk, | |||
if (*p == '.') { | |||
/* It is macro actually */ | |||
ucl_chunk_skipc (chunk, p); | |||
if (!(parser->flags & UCL_PARSER_DISABLE_MACRO)) { | |||
ucl_chunk_skipc (chunk, p); | |||
} | |||
parser->prev_state = parser->state; | |||
parser->state = UCL_STATE_MACRO_NAME; | |||
*end_of_object = false; | |||
@@ -1461,6 +1524,7 @@ ucl_parser_get_container (struct ucl_parser *parser) | |||
} | |||
parser->cur_obj = obj; | |||
ucl_attach_comment (parser, obj, false); | |||
} | |||
else { | |||
/* Object has been already allocated */ | |||
@@ -1707,12 +1771,19 @@ ucl_parse_after_value (struct ucl_parser *parser, struct ucl_chunk *chunk) | |||
parser->stack = st->next; | |||
UCL_FREE (sizeof (struct ucl_stack), st); | |||
if (parser->cur_obj) { | |||
ucl_attach_comment (parser, parser->cur_obj, true); | |||
} | |||
while (parser->stack != NULL) { | |||
st = parser->stack; | |||
if (st->next == NULL || st->next->level == st->level) { | |||
break; | |||
} | |||
parser->stack = st->next; | |||
parser->cur_obj = st->obj; | |||
UCL_FREE (sizeof (struct ucl_stack), st); | |||
} | |||
} | |||
@@ -1752,6 +1823,109 @@ ucl_parse_after_value (struct ucl_parser *parser, struct ucl_chunk *chunk) | |||
return true; | |||
} | |||
static bool | |||
ucl_skip_macro_as_comment (struct ucl_parser *parser, | |||
struct ucl_chunk *chunk) | |||
{ | |||
const unsigned char *p, *c; | |||
enum { | |||
macro_skip_start = 0, | |||
macro_has_symbols, | |||
macro_has_obrace, | |||
macro_has_quote, | |||
macro_has_backslash, | |||
macro_has_sqbrace, | |||
macro_save | |||
} state = macro_skip_start, prev_state = macro_skip_start; | |||
p = chunk->pos; | |||
c = chunk->pos; | |||
while (p < chunk->end) { | |||
switch (state) { | |||
case macro_skip_start: | |||
if (!ucl_test_character (*p, UCL_CHARACTER_WHITESPACE)) { | |||
state = macro_has_symbols; | |||
} | |||
else if (ucl_test_character (*p, UCL_CHARACTER_WHITESPACE_UNSAFE)) { | |||
state = macro_save; | |||
continue; | |||
} | |||
ucl_chunk_skipc (chunk, p); | |||
break; | |||
case macro_has_symbols: | |||
if (*p == '{') { | |||
state = macro_has_sqbrace; | |||
} | |||
else if (*p == '(') { | |||
state = macro_has_obrace; | |||
} | |||
else if (*p == '"') { | |||
state = macro_has_quote; | |||
} | |||
else if (*p == '\n') { | |||
state = macro_save; | |||
continue; | |||
} | |||
ucl_chunk_skipc (chunk, p); | |||
break; | |||
case macro_has_obrace: | |||
if (*p == '\\') { | |||
prev_state = state; | |||
state = macro_has_backslash; | |||
} | |||
else if (*p == ')') { | |||
state = macro_has_symbols; | |||
} | |||
ucl_chunk_skipc (chunk, p); | |||
break; | |||
case macro_has_sqbrace: | |||
if (*p == '\\') { | |||
prev_state = state; | |||
state = macro_has_backslash; | |||
} | |||
else if (*p == '}') { | |||
state = macro_save; | |||
} | |||
ucl_chunk_skipc (chunk, p); | |||
break; | |||
case macro_has_quote: | |||
if (*p == '\\') { | |||
prev_state = state; | |||
state = macro_has_backslash; | |||
} | |||
else if (*p == '"') { | |||
state = macro_save; | |||
} | |||
ucl_chunk_skipc (chunk, p); | |||
break; | |||
case macro_has_backslash: | |||
state = prev_state; | |||
ucl_chunk_skipc (chunk, p); | |||
break; | |||
case macro_save: | |||
if (parser->flags & UCL_PARSER_SAVE_COMMENTS) { | |||
ucl_save_comment (parser, c, p - c); | |||
} | |||
return true; | |||
} | |||
} | |||
return false; | |||
} | |||
/** | |||
* Handle macro data | |||
* @param parser | |||
@@ -2067,7 +2241,7 @@ ucl_state_machine (struct ucl_parser *parser) | |||
break; | |||
case UCL_STATE_VALUE: | |||
/* We need to check what we do have */ | |||
if (!ucl_parse_value (parser, chunk)) { | |||
if (!parser->cur_obj || !ucl_parse_value (parser, chunk)) { | |||
parser->prev_state = parser->state; | |||
parser->state = UCL_STATE_ERROR; | |||
return false; | |||
@@ -2095,42 +2269,60 @@ ucl_state_machine (struct ucl_parser *parser) | |||
/* Skip everything at the end */ | |||
return true; | |||
} | |||
p = chunk->pos; | |||
break; | |||
case UCL_STATE_MACRO_NAME: | |||
if (!ucl_test_character (*p, UCL_CHARACTER_WHITESPACE_UNSAFE) && | |||
*p != '(') { | |||
ucl_chunk_skipc (chunk, p); | |||
if (parser->flags & UCL_PARSER_DISABLE_MACRO) { | |||
if (!ucl_skip_macro_as_comment (parser, chunk)) { | |||
/* We have invalid macro */ | |||
ucl_create_err (&parser->err, | |||
"error on line %d at column %d: invalid macro", | |||
chunk->line, | |||
chunk->column); | |||
parser->state = UCL_STATE_ERROR; | |||
return false; | |||
} | |||
else { | |||
p = chunk->pos; | |||
parser->state = parser->prev_state; | |||
} | |||
} | |||
else { | |||
if (p - c > 0) { | |||
/* We got macro name */ | |||
macro_len = (size_t) (p - c); | |||
HASH_FIND (hh, parser->macroes, c, macro_len, macro); | |||
if (macro == NULL) { | |||
if (!ucl_test_character (*p, UCL_CHARACTER_WHITESPACE_UNSAFE) && | |||
*p != '(') { | |||
ucl_chunk_skipc (chunk, p); | |||
} | |||
else { | |||
if (p - c > 0) { | |||
/* We got macro name */ | |||
macro_len = (size_t) (p - c); | |||
HASH_FIND (hh, parser->macroes, c, macro_len, macro); | |||
if (macro == NULL) { | |||
ucl_create_err (&parser->err, | |||
"error on line %d at column %d: " | |||
"unknown macro: '%.*s', character: '%c'", | |||
chunk->line, | |||
chunk->column, | |||
(int) (p - c), | |||
c, | |||
*chunk->pos); | |||
parser->state = UCL_STATE_ERROR; | |||
return false; | |||
} | |||
/* Now we need to skip all spaces */ | |||
SKIP_SPACES_COMMENTS(parser, chunk, p); | |||
parser->state = UCL_STATE_MACRO; | |||
} | |||
else { | |||
/* We have invalid macro name */ | |||
ucl_create_err (&parser->err, | |||
"error on line %d at column %d: " | |||
"unknown macro: '%.*s', character: '%c'", | |||
"error on line %d at column %d: invalid macro name", | |||
chunk->line, | |||
chunk->column, | |||
(int) (p - c), | |||
c, | |||
*chunk->pos); | |||
chunk->column); | |||
parser->state = UCL_STATE_ERROR; | |||
return false; | |||
} | |||
/* Now we need to skip all spaces */ | |||
SKIP_SPACES_COMMENTS(parser, chunk, p); | |||
parser->state = UCL_STATE_MACRO; | |||
} | |||
else { | |||
/* We have invalid macro name */ | |||
ucl_create_err (&parser->err, | |||
"error on line %d at column %d: invalid macro name", | |||
chunk->line, | |||
chunk->column); | |||
parser->state = UCL_STATE_ERROR; | |||
return false; | |||
} | |||
} | |||
break; | |||
@@ -2154,6 +2346,7 @@ ucl_state_machine (struct ucl_parser *parser) | |||
macro_len = ucl_expand_variable (parser, ¯o_escaped, | |||
macro_start, macro_len); | |||
parser->state = parser->prev_state; | |||
if (macro_escaped == NULL) { | |||
if (macro->is_context) { | |||
ret = macro->h.context_handler (macro_start, macro_len, | |||
@@ -2194,7 +2387,6 @@ ucl_state_machine (struct ucl_parser *parser) | |||
} | |||
break; | |||
default: | |||
/* TODO: add all states */ | |||
ucl_set_err (parser, UCL_EINTERNAL, | |||
"internal error: parser is in an unknown state", &parser->err); | |||
parser->state = UCL_STATE_ERROR; | |||
@@ -2202,35 +2394,54 @@ ucl_state_machine (struct ucl_parser *parser) | |||
} | |||
} | |||
if (parser->last_comment) { | |||
if (parser->cur_obj) { | |||
ucl_attach_comment (parser, parser->cur_obj, true); | |||
} | |||
else if (parser->stack && parser->stack->obj) { | |||
ucl_attach_comment (parser, parser->stack->obj, true); | |||
} | |||
else if (parser->top_obj) { | |||
ucl_attach_comment (parser, parser->top_obj, true); | |||
} | |||
else { | |||
ucl_object_unref (parser->last_comment); | |||
} | |||
} | |||
return true; | |||
} | |||
struct ucl_parser* | |||
ucl_parser_new (int flags) | |||
{ | |||
struct ucl_parser *new; | |||
struct ucl_parser *parser; | |||
new = UCL_ALLOC (sizeof (struct ucl_parser)); | |||
if (new == NULL) { | |||
parser = UCL_ALLOC (sizeof (struct ucl_parser)); | |||
if (parser == NULL) { | |||
return NULL; | |||
} | |||
memset (new, 0, sizeof (struct ucl_parser)); | |||
memset (parser, 0, sizeof (struct ucl_parser)); | |||
ucl_parser_register_macro (new, "include", ucl_include_handler, new); | |||
ucl_parser_register_macro (new, "try_include", ucl_try_include_handler, new); | |||
ucl_parser_register_macro (new, "includes", ucl_includes_handler, new); | |||
ucl_parser_register_macro (new, "priority", ucl_priority_handler, new); | |||
ucl_parser_register_macro (new, "load", ucl_load_handler, new); | |||
ucl_parser_register_context_macro (new, "inherit", ucl_inherit_handler, new); | |||
ucl_parser_register_macro (parser, "include", ucl_include_handler, parser); | |||
ucl_parser_register_macro (parser, "try_include", ucl_try_include_handler, parser); | |||
ucl_parser_register_macro (parser, "includes", ucl_includes_handler, parser); | |||
ucl_parser_register_macro (parser, "priority", ucl_priority_handler, parser); | |||
ucl_parser_register_macro (parser, "load", ucl_load_handler, parser); | |||
ucl_parser_register_context_macro (parser, "inherit", ucl_inherit_handler, parser); | |||
new->flags = flags; | |||
new->includepaths = NULL; | |||
parser->flags = flags; | |||
parser->includepaths = NULL; | |||
if (flags & UCL_PARSER_SAVE_COMMENTS) { | |||
parser->comments = ucl_object_typed_new (UCL_OBJECT); | |||
} | |||
/* Initial assumption about filevars */ | |||
ucl_parser_set_filevars (new, NULL, false); | |||
ucl_parser_set_filevars (parser, NULL, false); | |||
return new; | |||
return parser; | |||
} | |||
bool |
@@ -753,7 +753,7 @@ ucl_schema_resolve_ref (const ucl_object_t *root, const char *ref, | |||
if (ext_obj == NULL) { | |||
if (ucl_strnstr (p, "://", strlen (p)) != NULL) { | |||
if (!ucl_fetch_url (p, &url_buf, &url_buflen, &url_err, true)) { | |||
free (url_copy); | |||
ucl_schema_create_error (err, | |||
UCL_SCHEMA_INVALID_SCHEMA, | |||
root, | |||
@@ -761,13 +761,14 @@ ucl_schema_resolve_ref (const ucl_object_t *root, const char *ref, | |||
p, | |||
url_err != NULL ? utstring_body (url_err) | |||
: "unknown"); | |||
free (url_copy); | |||
return NULL; | |||
} | |||
} | |||
else { | |||
if (!ucl_fetch_file (p, &url_buf, &url_buflen, &url_err, | |||
true)) { | |||
free (url_copy); | |||
ucl_schema_create_error (err, | |||
UCL_SCHEMA_INVALID_SCHEMA, | |||
root, | |||
@@ -775,6 +776,8 @@ ucl_schema_resolve_ref (const ucl_object_t *root, const char *ref, | |||
p, | |||
url_err != NULL ? utstring_body (url_err) | |||
: "unknown"); | |||
free (url_copy); | |||
return NULL; | |||
} | |||
} | |||
@@ -782,11 +785,12 @@ ucl_schema_resolve_ref (const ucl_object_t *root, const char *ref, | |||
parser = ucl_parser_new (0); | |||
if (!ucl_parser_add_chunk (parser, url_buf, url_buflen)) { | |||
free (url_copy); | |||
ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, root, | |||
"cannot fetch reference %s: %s", p, | |||
ucl_parser_get_error (parser)); | |||
ucl_parser_free (parser); | |||
free (url_copy); | |||
return NULL; | |||
} | |||
@@ -794,9 +798,10 @@ ucl_schema_resolve_ref (const ucl_object_t *root, const char *ref, | |||
ext_obj = url_obj; | |||
ucl_object_insert_key (ext_ref, url_obj, p, 0, true); | |||
free (url_buf); | |||
free (url_copy); | |||
} | |||
free (url_copy); | |||
if (hash_ptr) { | |||
p = hash_ptr + 1; | |||
} |
@@ -0,0 +1,226 @@ | |||
/* | |||
* Copyright (c) 2015, Vsevolod Stakhov | |||
* All rights reserved. | |||
* | |||
* Redistribution and use in source and binary forms, with or without | |||
* modification, are permitted provided that the following conditions are met: | |||
* * Redistributions of source code must retain the above copyright | |||
* notice, this list of conditions and the following disclaimer. | |||
* * Redistributions in binary form must reproduce the above copyright | |||
* notice, this list of conditions and the following disclaimer in the | |||
* documentation and/or other materials provided with the distribution. | |||
* | |||
* THIS SOFTWARE IS PROVIDED BY AUTHOR ''AS IS'' AND ANY | |||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |||
* DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY | |||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||
*/ | |||
#ifdef HAVE_CONFIG_H | |||
#include "config.h" | |||
#endif | |||
#include <ucl.h> | |||
#include "ucl.h" | |||
#include "ucl_internal.h" | |||
#include "utlist.h" | |||
#define NEXT_STATE do { \ | |||
if (p >= end) { \ | |||
if (state != read_ebrace) { \ | |||
ucl_create_err (&parser->err,\ | |||
"extra data");\ | |||
state = parse_err; \ | |||
} \ | |||
} \ | |||
else { \ | |||
switch (*p) { \ | |||
case '(': \ | |||
state = read_obrace; \ | |||
break; \ | |||
case ')': \ | |||
state = read_ebrace; \ | |||
break; \ | |||
default: \ | |||
len = 0; \ | |||
mult = 1; \ | |||
state = read_length; \ | |||
break; \ | |||
} \ | |||
} \ | |||
} while(0) | |||
bool | |||
ucl_parse_csexp (struct ucl_parser *parser) | |||
{ | |||
const unsigned char *p, *end; | |||
ucl_object_t *obj; | |||
struct ucl_stack *st; | |||
uint64_t len = 0, mult = 1; | |||
enum { | |||
start_parse, | |||
read_obrace, | |||
read_length, | |||
read_value, | |||
read_ebrace, | |||
parse_err | |||
} state = start_parse; | |||
assert (parser != NULL); | |||
assert (parser->chunks != NULL); | |||
assert (parser->chunks->begin != NULL); | |||
assert (parser->chunks->remain != 0); | |||
p = parser->chunks->begin; | |||
end = p + parser->chunks->remain; | |||
while (p < end) { | |||
switch (state) { | |||
case start_parse: | |||
/* At this point we expect open brace */ | |||
if (*p == '(') { | |||
state = read_obrace; | |||
} | |||
else { | |||
ucl_create_err (&parser->err, "bad starting character for " | |||
"sexp block: %x", (int)*p); | |||
state = parse_err; | |||
} | |||
break; | |||
case read_obrace: | |||
st = calloc (1, sizeof (*st)); | |||
if (st == NULL) { | |||
ucl_create_err (&parser->err, "no memory"); | |||
state = parse_err; | |||
continue; | |||
} | |||
st->obj = ucl_object_typed_new (UCL_ARRAY); | |||
if (st->obj == NULL) { | |||
ucl_create_err (&parser->err, "no memory"); | |||
state = parse_err; | |||
free (st); | |||
continue; | |||
} | |||
if (parser->stack == NULL) { | |||
/* We have no stack */ | |||
parser->stack = st; | |||
if (parser->top_obj == NULL) { | |||
parser->top_obj = st->obj; | |||
} | |||
} | |||
else { | |||
/* Prepend new element to the stack */ | |||
LL_PREPEND (parser->stack, st); | |||
} | |||
p ++; | |||
NEXT_STATE; | |||
break; | |||
case read_length: | |||
if (*p == ':') { | |||
if (len == 0) { | |||
ucl_create_err (&parser->err, "zero length element"); | |||
state = parse_err; | |||
continue; | |||
} | |||
state = read_value; | |||
} | |||
else if (*p >= '0' && *p <= '9') { | |||
len += (*p - '0') * mult; | |||
mult *= 10; | |||
if (len > UINT32_MAX) { | |||
ucl_create_err (&parser->err, "too big length of an " | |||
"element"); | |||
state = parse_err; | |||
continue; | |||
} | |||
} | |||
else { | |||
ucl_create_err (&parser->err, "bad length character: %x", | |||
(int)*p); | |||
state = parse_err; | |||
continue; | |||
} | |||
p ++; | |||
break; | |||
case read_value: | |||
if ((uint64_t)(end - p) > len || len == 0) { | |||
ucl_create_err (&parser->err, "invalid length: %llu, %ld " | |||
"remain", (long long unsigned)len, (long)(end - p)); | |||
state = parse_err; | |||
continue; | |||
} | |||
obj = ucl_object_typed_new (UCL_STRING); | |||
obj->value.sv = (const char*)p; | |||
obj->len = len; | |||
obj->flags |= UCL_OBJECT_BINARY; | |||
if (!(parser->flags & UCL_PARSER_ZEROCOPY)) { | |||
ucl_copy_value_trash (obj); | |||
} | |||
ucl_array_append (parser->stack->obj, obj); | |||
p += len; | |||
NEXT_STATE; | |||
break; | |||
case read_ebrace: | |||
if (parser->stack == NULL) { | |||
/* We have an extra end brace */ | |||
ucl_create_err (&parser->err, "invalid length: %llu, %ld " | |||
"remain", (long long unsigned)len, (long)(end - p)); | |||
state = parse_err; | |||
continue; | |||
} | |||
/* Pop the container */ | |||
st = parser->stack; | |||
parser->stack = st->next; | |||
if (parser->stack->obj->type == UCL_ARRAY) { | |||
ucl_array_append (parser->stack->obj, st->obj); | |||
} | |||
else { | |||
ucl_create_err (&parser->err, "bad container object, array " | |||
"expected"); | |||
state = parse_err; | |||
continue; | |||
} | |||
free (st); | |||
st = NULL; | |||
p++; | |||
NEXT_STATE; | |||
break; | |||
case parse_err: | |||
default: | |||
return false; | |||
} | |||
} | |||
if (state != read_ebrace) { | |||
ucl_create_err (&parser->err, "invalid finishing state: %d", state); | |||
return false; | |||
} | |||
return true; | |||
} |
@@ -236,7 +236,7 @@ ucl_object_free_internal (ucl_object_t *obj, bool allow_rec, ucl_object_dtor dto | |||
} | |||
else if (obj->type == UCL_OBJECT) { | |||
if (obj->value.ov != NULL) { | |||
ucl_hash_destroy (obj->value.ov, (ucl_hash_free_func *)dtor); | |||
ucl_hash_destroy (obj->value.ov, (ucl_hash_free_func)dtor); | |||
} | |||
obj->value.ov = NULL; | |||
} | |||
@@ -504,6 +504,10 @@ ucl_parser_free (struct ucl_parser *parser) | |||
free (parser->cur_file); | |||
} | |||
if (parser->comments) { | |||
ucl_object_unref (parser->comments); | |||
} | |||
UCL_FREE (sizeof (struct ucl_parser), parser); | |||
} | |||
@@ -1037,6 +1041,16 @@ 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, params->priority); | |||
if (nest_obj == NULL) { | |||
ucl_create_err (&parser->err, "cannot allocate memory for an object"); | |||
if (buflen > 0) { | |||
ucl_munmap (buf, buflen); | |||
} | |||
return false; | |||
} | |||
nest_obj->key = params->prefix; | |||
nest_obj->keylen = strlen (params->prefix); | |||
ucl_copy_key_trash(nest_obj); | |||
@@ -1052,6 +1066,14 @@ ucl_include_file_single (const unsigned char *data, size_t len, | |||
if (ucl_object_type(old_obj) == UCL_ARRAY) { | |||
/* Append to the existing array */ | |||
nest_obj = ucl_object_new_full (UCL_OBJECT, params->priority); | |||
if (nest_obj == NULL) { | |||
ucl_create_err (&parser->err, "cannot allocate memory for an object"); | |||
if (buflen > 0) { | |||
ucl_munmap (buf, buflen); | |||
} | |||
return false; | |||
} | |||
nest_obj->prev = nest_obj; | |||
nest_obj->next = NULL; | |||
@@ -1060,6 +1082,14 @@ ucl_include_file_single (const unsigned char *data, size_t len, | |||
else { | |||
/* Convert the object to an array */ | |||
new_obj = ucl_object_typed_new (UCL_ARRAY); | |||
if (new_obj == NULL) { | |||
ucl_create_err (&parser->err, "cannot allocate memory for an object"); | |||
if (buflen > 0) { | |||
ucl_munmap (buf, buflen); | |||
} | |||
return false; | |||
} | |||
new_obj->key = old_obj->key; | |||
new_obj->keylen = old_obj->keylen; | |||
new_obj->flags |= UCL_OBJECT_MULTIVALUE; | |||
@@ -1067,6 +1097,14 @@ ucl_include_file_single (const unsigned char *data, size_t len, | |||
new_obj->next = NULL; | |||
nest_obj = ucl_object_new_full (UCL_OBJECT, params->priority); | |||
if (nest_obj == NULL) { | |||
ucl_create_err (&parser->err, "cannot allocate memory for an object"); | |||
if (buflen > 0) { | |||
ucl_munmap (buf, buflen); | |||
} | |||
return false; | |||
} | |||
nest_obj->prev = nest_obj; | |||
nest_obj->next = NULL; | |||
@@ -1085,6 +1123,10 @@ ucl_include_file_single (const unsigned char *data, size_t len, | |||
ucl_create_err (&parser->err, | |||
"Conflicting type for key: %s", | |||
params->prefix); | |||
if (buflen > 0) { | |||
ucl_munmap (buf, buflen); | |||
} | |||
return false; | |||
} | |||
} | |||
@@ -1097,7 +1139,11 @@ ucl_include_file_single (const unsigned char *data, size_t len, | |||
if (st == NULL) { | |||
ucl_create_err (&parser->err, "cannot allocate memory for an object"); | |||
ucl_object_unref (nest_obj); | |||
return NULL; | |||
if (buflen > 0) { | |||
ucl_munmap (buf, buflen); | |||
} | |||
return false; | |||
} | |||
st->obj = nest_obj; | |||
st->level = parser->stack->level; | |||
@@ -1579,6 +1625,10 @@ ucl_load_handler (const unsigned char *data, size_t len, | |||
old_obj = __DECONST (ucl_object_t *, ucl_hash_search (container, prefix, strlen (prefix))); | |||
if (old_obj != NULL) { | |||
ucl_create_err (&parser->err, "Key %s already exists", prefix); | |||
if (buflen > 0) { | |||
ucl_munmap (buf, buflen); | |||
} | |||
return false; | |||
} | |||
@@ -1610,6 +1660,7 @@ ucl_load_handler (const unsigned char *data, size_t len, | |||
parser->flags & UCL_PARSER_KEY_LOWERCASE); | |||
parser->stack->obj->value.ov = container; | |||
} | |||
return true; | |||
} | |||
@@ -3323,3 +3374,25 @@ ucl_object_type_to_string (ucl_type_t type) | |||
return res; | |||
} | |||
const ucl_object_t * | |||
ucl_parser_get_comments (struct ucl_parser *parser) | |||
{ | |||
if (parser && parser->comments) { | |||
return parser->comments; | |||
} | |||
return NULL; | |||
} | |||
const ucl_object_t * | |||
ucl_comments_find (const ucl_object_t *comments, | |||
const ucl_object_t *srch) | |||
{ | |||
if (comments && srch) { | |||
return ucl_object_find_keyl (comments, (const char *)&srch, | |||
sizeof (void *)); | |||
} | |||
return NULL; | |||
} |