]> git.ipfire.org Git - thirdparty/rspamd.git/commitdiff
Update libucl
authorVsevolod Stakhov <vsevolod@highsecure.ru>
Mon, 8 Feb 2016 18:26:58 +0000 (18:26 +0000)
committerVsevolod Stakhov <vsevolod@highsecure.ru>
Mon, 8 Feb 2016 18:26:58 +0000 (18:26 +0000)
contrib/libucl/CMakeLists.txt
contrib/libucl/ucl.h
contrib/libucl/ucl_emitter.c
contrib/libucl/ucl_emitter_streamline.c
contrib/libucl/ucl_hash.c
contrib/libucl/ucl_hash.h
contrib/libucl/ucl_internal.h
contrib/libucl/ucl_parser.c
contrib/libucl/ucl_schema.c
contrib/libucl/ucl_sexp.c [new file with mode: 0644]
contrib/libucl/ucl_util.c

index a1e91948487b84d701deafa9bf0790d2d729720d..cda8091161b3dc2924963aaa1cf266bfd9b2e5cc 100644 (file)
@@ -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)
index cfe62c081ecb970f11817d9804b742a00127302e..14e72d1193b1d9ab043bde14ecfe9870bb07e45d 100644 (file)
@@ -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
index 8bfbf09b8bd783d1b717b6ef2d7c4d2af45935cc..84108e21db0d405ded3bc522d839d198c9ec6591 100644 (file)
@@ -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;
index ff27c88244a5643758fbbdbb1010b78b7367e71a..a7178c5d74b03218b8e7be259afdd79d80f37f6c 100644 (file)
@@ -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;
index 7de8196fa01497760528cbc765deb98a4f554db2..174ebb6857146c647250d01e0b8923cc4b20d7bc 100644 (file)
@@ -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;
 
index 64c83eac8bc19294dec10d8062a26def673278f6..92021e34075ec06ca9b572f2edb184a21776d3ed 100644 (file)
@@ -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.
index 64938592aa9f8862a56066398775354cfd99830f..db8a12c408b1f0c347437f738c68c0c4e1a24cd6 100644 (file)
@@ -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;
 };
 
index 9bd41391ba36eb7e61ffc4c32bf49c534586f0e1..fa906b7fe88eb379d667ced6748286b870f5a5c0 100644 (file)
@@ -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, &macro_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
index 9b96da5efc6f809f6c5b4ed1bfd42f78f5e878d4..531cf848cee450d09caf3dd6852d576846a8641c 100644 (file)
@@ -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;
                }
diff --git a/contrib/libucl/ucl_sexp.c b/contrib/libucl/ucl_sexp.c
new file mode 100644 (file)
index 0000000..1ad93d2
--- /dev/null
@@ -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;
+}
index 2bd0c2a3fe1197fc245313b5798cc770a9e7dcea..15439feea3feea919804a0b165d2bc3eed61ee61 100644 (file)
@@ -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;
+}