]> source.dussan.org Git - rspamd.git/commitdiff
Replace RCL to UCL from libucl to avoid duplicity in the code.
authorVsevolod Stakhov <vsevolod@highsecure.ru>
Tue, 22 Oct 2013 16:04:20 +0000 (17:04 +0100)
committerVsevolod Stakhov <vsevolod@highsecure.ru>
Tue, 22 Oct 2013 16:04:20 +0000 (17:04 +0100)
48 files changed:
CMakeLists.txt
lib/CMakeLists.txt
src/cfg_file.h
src/cfg_rcl.c
src/cfg_rcl.h
src/cfg_utils.c
src/lua/lua_cfg_file.c
src/lua/lua_common.h
src/lua/lua_config.c
src/lua/lua_rcl.c
src/plugins/chartable.c
src/plugins/dkim_check.c
src/plugins/fuzzy_check.c
src/plugins/regexp.c
src/plugins/spf.c
src/plugins/surbl.c
src/rcl/CMakeLists.txt [deleted file]
src/rcl/rcl.h [deleted file]
src/rcl/rcl_chartable.h [deleted file]
src/rcl/rcl_emitter.c [deleted file]
src/rcl/rcl_internal.h [deleted file]
src/rcl/rcl_parser.c [deleted file]
src/rcl/rcl_util.c [deleted file]
src/ucl/.gitignore [new file with mode: 0644]
src/ucl/CMakeLists.txt [new file with mode: 0644]
src/ucl/README.md [new file with mode: 0644]
src/ucl/include/ucl.h [new file with mode: 0644]
src/ucl/src/ucl_chartable.h [new file with mode: 0644]
src/ucl/src/ucl_emitter.c [new file with mode: 0644]
src/ucl/src/ucl_internal.h [new file with mode: 0644]
src/ucl/src/ucl_parser.c [new file with mode: 0644]
src/ucl/src/ucl_util.c [new file with mode: 0644]
src/ucl/tests/1.in [new file with mode: 0644]
src/ucl/tests/1.res [new file with mode: 0644]
src/ucl/tests/2.in [new file with mode: 0644]
src/ucl/tests/2.res [new file with mode: 0644]
src/ucl/tests/3.in [new file with mode: 0644]
src/ucl/tests/3.res [new file with mode: 0644]
src/ucl/tests/4.in [new file with mode: 0644]
src/ucl/tests/4.res [new file with mode: 0644]
src/ucl/tests/generate.res [new file with mode: 0644]
src/ucl/tests/rcl_test.json.xz [new file with mode: 0644]
src/ucl/tests/run_tests.sh [new file with mode: 0755]
src/ucl/tests/test_basic.c [new file with mode: 0644]
src/ucl/tests/test_generate.c [new file with mode: 0644]
src/ucl/tests/test_speed.c [new file with mode: 0644]
src/ucl/utils/chargen.c [new file with mode: 0644]
src/ucl/utils/objdump.c [new file with mode: 0644]

index 52f42203d5a9765431abf957543efe54f002e7f1..7bac51678ec199ee5afade472e4e94e7e973b07f 100644 (file)
@@ -1032,7 +1032,10 @@ ENDIF(HG)
 
 ################################ SOURCES SECTION ###########################
 
-INCLUDE_DIRECTORIES("${CMAKE_SOURCE_DIR}/src" "${CMAKE_BINARY_DIR}/src" "${CMAKE_SOURCE_DIR}/contrib/uthash")
+INCLUDE_DIRECTORIES("${CMAKE_SOURCE_DIR}/src" 
+                                       "${CMAKE_BINARY_DIR}/src" 
+                                       "${CMAKE_SOURCE_DIR}/src/ucl/include"
+                                       "${CMAKE_SOURCE_DIR}/contrib/uthash")
 
 SET(RSPAMDSRC  src/modules.c
                                src/controller.c
@@ -1073,7 +1076,7 @@ ENDIF(NOT HIREDIS_FOUND)
 ADD_SUBDIRECTORY(src/lua)
 ADD_SUBDIRECTORY(src/json)
 ADD_SUBDIRECTORY(src/cdb)
-ADD_SUBDIRECTORY(src/rcl)
+ADD_SUBDIRECTORY(src/ucl)
 ADD_SUBDIRECTORY(lib)
 
 ADD_SUBDIRECTORY(src/client)
index 561058e1ad8331d826b20b3dc20355f2282bb6ca..9f4636dd73fe6b20a4aeea29f5ff85ca74ad30b6 100644 (file)
@@ -76,7 +76,7 @@ ENDIF(CMAKE_COMPILER_IS_GNUCC)
 
 TARGET_LINK_LIBRARIES(rspamd-util ${CMAKE_REQUIRED_LIBRARIES})
 TARGET_LINK_LIBRARIES(rspamd-util pcre)
-TARGET_LINK_LIBRARIES(rspamd-util rspamd-rcl)
+TARGET_LINK_LIBRARIES(rspamd-util rspamd-ucl)
 TARGET_LINK_LIBRARIES(rspamd-util ${GLIB2_LIBRARIES})
 TARGET_LINK_LIBRARIES(rspamd-util event)
 
index e7e12297752b95dad4324c1e4407211275d9160e..875ce337367133b0f59f583eb168042ca588719a 100644 (file)
@@ -13,7 +13,7 @@
 #include "symbols_cache.h"
 #include "cfg_rcl.h"
 #include "utlist.h"
-#include "rcl/rcl.h"
+#include "ucl.h"
 
 #define DEFAULT_BIND_PORT 768
 #define DEFAULT_CONTROL_PORT 7608
@@ -283,7 +283,7 @@ struct worker_conf {
        GQueue *active_workers;                                                 /**< linked list of spawned workers                                             */
        gboolean has_socket;                                                    /**< whether we should make listening socket in main process */
        gpointer *ctx;                                                                  /**< worker's context                                                                   */
-       rspamd_cl_object_t *options;                                    /**< other worker's options                                                             */
+       ucl_object_t *options;                                  /**< other worker's options                                                             */
 };
 
 /**
@@ -348,7 +348,7 @@ struct config_file {
        GList *workers;                                                                 /**< linked list of all workers params                                  */
        struct rspamd_worker_cfg_parser *wrk_parsers;   /**< hash for worker config parsers, indexed by worker quarks */
        gchar *filters_str;                                                             /**< string of filters                                                                  */
-       rspamd_cl_object_t *rcl_obj;                                    /**< rcl object                                                                                 */
+       ucl_object_t *rcl_obj;                                  /**< rcl object                                                                                 */
        GHashTable* variables;                                                  /**< hash of $variables defined in config, indexed by variable name */
        GHashTable* metrics;                                                    /**< hash of metrics indexed by metric name                             */
        GList* symbols_groups;                                                  /**< groups of symbols                                                                  */
@@ -448,7 +448,7 @@ void free_config (struct config_file *cfg);
  * @param opt_name name of option to get
  * @return module value or NULL if option does not defined
  */
-rspamd_cl_object_t* get_module_opt (struct config_file *cfg, const gchar *module_name,
+ucl_object_t* get_module_opt (struct config_file *cfg, const gchar *module_name,
                const gchar *opt_name);
 
 /**
index 2c4bb28456f683d9bd43503b5a9d6a575f64088b..5320e17ba2f4bb309fdcbcf7e34fdaea78357b77 100644 (file)
  * Common section handlers
  */
 static gboolean
-rspamd_rcl_logging_handler (struct config_file *cfg, rspamd_cl_object_t *obj,
+rspamd_rcl_logging_handler (struct config_file *cfg, ucl_object_t *obj,
                gpointer ud, struct rspamd_rcl_section *section, GError **err)
 {
-       rspamd_cl_object_t *val;
+       ucl_object_t *val;
        gchar *filepath;
        const gchar *facility, *log_type, *log_level;
 
-       val = rspamd_cl_obj_get_key (obj, "type");
-       if (val != NULL && rspamd_cl_obj_tostring_safe (val, &log_type)) {
+       val = ucl_obj_get_key (obj, "type");
+       if (val != NULL && ucl_obj_tostring_safe (val, &log_type)) {
                if (g_ascii_strcasecmp (log_type, "file") == 0) {
                        /* Need to get filename */
-                       val = rspamd_cl_obj_get_key (obj, "filename");
-                       if (val == NULL || val->type != RSPAMD_CL_STRING) {
+                       val = ucl_obj_get_key (obj, "filename");
+                       if (val == NULL || val->type != UCL_STRING) {
                                g_set_error (err, CFG_RCL_ERROR, ENOENT, "filename attribute must be specified for file logging type");
                                return FALSE;
                        }
-                       if ((filepath = realpath (rspamd_cl_obj_tostring (val), NULL)) == NULL ||
+                       if ((filepath = realpath (ucl_obj_tostring (val), NULL)) == NULL ||
                                        access (filepath, W_OK) == -1) {
                                g_set_error (err, CFG_RCL_ERROR, errno, "log file is inaccessible");
                                return FALSE;
@@ -57,8 +57,8 @@ rspamd_rcl_logging_handler (struct config_file *cfg, rspamd_cl_object_t *obj,
                        /* Need to get facility */
                        cfg->log_facility = LOG_DAEMON;
                        cfg->log_type = RSPAMD_LOG_SYSLOG;
-                       val = rspamd_cl_obj_get_key (obj, "facility");
-                       if (val != NULL && rspamd_cl_obj_tostring_safe (val, &facility)) {
+                       val = ucl_obj_get_key (obj, "facility");
+                       if (val != NULL && ucl_obj_tostring_safe (val, &facility)) {
                                if (g_ascii_strcasecmp (facility, "LOG_AUTH") == 0 ||
                                                g_ascii_strcasecmp (facility, "auth") == 0 ) {
                                        cfg->log_facility = LOG_AUTH;
@@ -131,8 +131,8 @@ rspamd_rcl_logging_handler (struct config_file *cfg, rspamd_cl_object_t *obj,
        }
 
        /* Handle log level */
-       val = rspamd_cl_obj_get_key (obj, "level");
-       if (val != NULL && rspamd_cl_obj_tostring_safe (val, &log_level)) {
+       val = ucl_obj_get_key (obj, "level");
+       if (val != NULL && ucl_obj_tostring_safe (val, &log_level)) {
                if (g_ascii_strcasecmp (log_level, "error") == 0) {
                        cfg->log_level = G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL;
                }
@@ -155,15 +155,15 @@ rspamd_rcl_logging_handler (struct config_file *cfg, rspamd_cl_object_t *obj,
 }
 
 static gboolean
-rspamd_rcl_options_handler (struct config_file *cfg, rspamd_cl_object_t *obj,
+rspamd_rcl_options_handler (struct config_file *cfg, ucl_object_t *obj,
                gpointer ud, struct rspamd_rcl_section *section, GError **err)
 {
-       rspamd_cl_object_t *val;
+       ucl_object_t *val;
        const gchar *user_settings, *domain_settings;
 
        /* Handle user and domain settings */
-       val = rspamd_cl_obj_get_key (obj, "user_settings");
-       if (val != NULL && rspamd_cl_obj_tostring_safe (val, &user_settings)) {
+       val = ucl_obj_get_key (obj, "user_settings");
+       if (val != NULL && ucl_obj_tostring_safe (val, &user_settings)) {
                if (!read_settings (user_settings, "Users' settings", cfg, cfg->user_settings)) {
                        g_set_error (err, CFG_RCL_ERROR, EINVAL, "cannot read settings: %s", user_settings);
                        return FALSE;
@@ -171,8 +171,8 @@ rspamd_rcl_options_handler (struct config_file *cfg, rspamd_cl_object_t *obj,
                cfg->user_settings_str = memory_pool_strdup (cfg->cfg_pool, user_settings);
        }
 
-       val = rspamd_cl_obj_get_key (obj, "domain_settings");
-       if (val != NULL && rspamd_cl_obj_tostring_safe (val, &domain_settings)) {
+       val = ucl_obj_get_key (obj, "domain_settings");
+       if (val != NULL && ucl_obj_tostring_safe (val, &domain_settings)) {
                if (!read_settings (domain_settings, "Domains settings", cfg, cfg->domain_settings)) {
                        g_set_error (err, CFG_RCL_ERROR, EINVAL, "cannot read settings: %s", domain_settings);
                        return FALSE;
@@ -202,11 +202,11 @@ rspamd_symbols_group_find_func (gconstpointer a, gconstpointer b)
  */
 static gboolean
 rspamd_rcl_insert_symbol (struct config_file *cfg, struct metric *metric,
-               rspamd_cl_object_t *obj, GError **err)
+               ucl_object_t *obj, GError **err)
 {
        const gchar *group = "ungrouped", *description = NULL;
        gdouble symbol_score, *score_ptr;
-       rspamd_cl_object_t *val;
+       ucl_object_t *val;
        struct symbols_group *sym_group;
        struct symbol_def *sym_def;
        GList *metric_list, *group_list;
@@ -221,26 +221,26 @@ rspamd_rcl_insert_symbol (struct config_file *cfg, struct metric *metric,
         *      group = ...;
         * }
         */
-       if (rspamd_cl_obj_todouble_safe (obj, &symbol_score)) {
+       if (ucl_obj_todouble_safe (obj, &symbol_score)) {
                description = NULL;
        }
-       else if (obj->type == RSPAMD_CL_OBJECT) {
-               val = rspamd_cl_obj_get_key (obj, "score");
-               if (val == NULL || !rspamd_cl_obj_todouble_safe (val, &symbol_score)) {
-                       g_set_error (err, CFG_RCL_ERROR, EINVAL, "invalid symbol score: %s", obj->key);
+       else if (obj->type == UCL_OBJECT) {
+               val = ucl_obj_get_key (obj, "score");
+               if (val == NULL || !ucl_obj_todouble_safe (val, &symbol_score)) {
+                       g_set_error (err, CFG_RCL_ERROR, EINVAL, "invalid symbol score: %s", ucl_object_key (obj));
                        return FALSE;
                }
-               val = rspamd_cl_obj_get_key (obj, "description");
+               val = ucl_obj_get_key (obj, "description");
                if (val != NULL) {
-                       description = rspamd_cl_obj_tostring (val);
+                       description = ucl_obj_tostring (val);
                }
-               val = rspamd_cl_obj_get_key (obj, "group");
+               val = ucl_obj_get_key (obj, "group");
                if (val != NULL) {
-                       rspamd_cl_obj_tostring_safe (val, &group);
+                       ucl_obj_tostring_safe (val, &group);
                }
        }
        else {
-               g_set_error (err, CFG_RCL_ERROR, EINVAL, "invalid symbol type: %s", obj->key);
+               g_set_error (err, CFG_RCL_ERROR, EINVAL, "invalid symbol type: %s", ucl_object_key (obj));
                return FALSE;
        }
 
@@ -249,7 +249,7 @@ rspamd_rcl_insert_symbol (struct config_file *cfg, struct metric *metric,
 
        *score_ptr = symbol_score;
        sym_def->weight_ptr = score_ptr;
-       sym_def->name = memory_pool_strdup (cfg->cfg_pool, obj->key);
+       sym_def->name = memory_pool_strdup (cfg->cfg_pool, ucl_object_key (obj));
        sym_def->description = (gchar *)description;
 
        g_hash_table_insert (metric->symbols, sym_def->name, score_ptr);
@@ -285,10 +285,10 @@ rspamd_rcl_insert_symbol (struct config_file *cfg, struct metric *metric,
 }
 
 static gboolean
-rspamd_rcl_metric_handler (struct config_file *cfg, rspamd_cl_object_t *obj,
+rspamd_rcl_metric_handler (struct config_file *cfg, ucl_object_t *obj,
                gpointer ud, struct rspamd_rcl_section *section, GError **err)
 {
-       rspamd_cl_object_t *val, *cur, *tmp;
+       ucl_object_t *val, *cur, *tmp;
        const gchar *metric_name, *subject_name;
        struct metric *metric;
        struct metric_action *action;
@@ -296,8 +296,8 @@ rspamd_rcl_metric_handler (struct config_file *cfg, rspamd_cl_object_t *obj,
        gint action_value;
        gboolean new = TRUE;
 
-       val = rspamd_cl_obj_get_key (obj, "name");
-       if (val == NULL || !rspamd_cl_obj_tostring_safe (val, &metric_name)) {
+       val = ucl_obj_get_key (obj, "name");
+       if (val == NULL || !ucl_obj_tostring_safe (val, &metric_name)) {
                metric_name = DEFAULT_METRIC;
        }
 
@@ -310,16 +310,16 @@ rspamd_rcl_metric_handler (struct config_file *cfg, rspamd_cl_object_t *obj,
        }
 
        /* Handle actions */
-       val = rspamd_cl_obj_get_key (obj, "actions");
+       val = ucl_obj_get_key (obj, "actions");
        if (val != NULL) {
-               if (val->type != RSPAMD_CL_OBJECT) {
+               if (val->type != UCL_OBJECT) {
                        g_set_error (err, CFG_RCL_ERROR, EINVAL, "actions must be an object");
                        return FALSE;
                }
                HASH_ITER (hh, val, cur, tmp) {
-                       if (!check_action_str (cur->key, &action_value) ||
-                                       !rspamd_cl_obj_todouble_safe (cur, &action_score)) {
-                               g_set_error (err, CFG_RCL_ERROR, EINVAL, "invalid action definition: %s", cur->key);
+                       if (!check_action_str (ucl_object_key (cur), &action_value) ||
+                                       !ucl_obj_todouble_safe (cur, &action_score)) {
+                               g_set_error (err, CFG_RCL_ERROR, EINVAL, "invalid action definition: %s", ucl_object_key (cur));
                                return FALSE;
                        }
                        action = &metric->actions[action_value];
@@ -333,12 +333,12 @@ rspamd_rcl_metric_handler (struct config_file *cfg, rspamd_cl_object_t *obj,
        }
 
        /* Handle symbols */
-       val = rspamd_cl_obj_get_key (obj, "symbols");
+       val = ucl_obj_get_key (obj, "symbols");
        if (val != NULL) {
-               if (val->type == RSPAMD_CL_ARRAY) {
+               if (val->type == UCL_ARRAY) {
                        val = val->value.ov;
                }
-               if (val->type != RSPAMD_CL_OBJECT) {
+               if (val->type != UCL_OBJECT) {
                        g_set_error (err, CFG_RCL_ERROR, EINVAL, "symbols must be an object");
                        return FALSE;
                }
@@ -349,13 +349,13 @@ rspamd_rcl_metric_handler (struct config_file *cfg, rspamd_cl_object_t *obj,
                }
        }
 
-       val = rspamd_cl_obj_get_key (obj, "grow_factor");
-       if (val && rspamd_cl_obj_todouble_safe (val, &grow_factor)) {
+       val = ucl_obj_get_key (obj, "grow_factor");
+       if (val && ucl_obj_todouble_safe (val, &grow_factor)) {
                metric->grow_factor = grow_factor;
        }
 
-       val = rspamd_cl_obj_get_key (obj, "subject");
-       if (val && rspamd_cl_obj_tostring_safe (val, &subject_name)) {
+       val = ucl_obj_get_key (obj, "subject");
+       if (val && ucl_obj_tostring_safe (val, &subject_name)) {
                metric->subject = (gchar *)subject_name;
        }
 
@@ -367,10 +367,10 @@ rspamd_rcl_metric_handler (struct config_file *cfg, rspamd_cl_object_t *obj,
 }
 
 static gboolean
-rspamd_rcl_worker_handler (struct config_file *cfg, rspamd_cl_object_t *obj,
+rspamd_rcl_worker_handler (struct config_file *cfg, ucl_object_t *obj,
                gpointer ud, struct rspamd_rcl_section *section, GError **err)
 {
-       rspamd_cl_object_t *val, *cur, *tmp;
+       ucl_object_t *val, *cur, *tmp;
        const gchar *worker_type, *worker_bind;
        GQuark qtype;
        struct worker_conf *wrk;
@@ -378,8 +378,8 @@ rspamd_rcl_worker_handler (struct config_file *cfg, rspamd_cl_object_t *obj,
        struct rspamd_worker_cfg_parser *wparser;
        struct rspamd_worker_param_parser *whandler;
 
-       val = rspamd_cl_obj_get_key (obj, "type");
-       if (val != NULL && rspamd_cl_obj_tostring_safe (val, &worker_type)) {
+       val = ucl_obj_get_key (obj, "type");
+       if (val != NULL && ucl_obj_tostring_safe (val, &worker_type)) {
                qtype = g_quark_try_string (worker_type);
                if (qtype != 0) {
                        wrk = memory_pool_alloc0 (cfg->cfg_pool, sizeof (struct worker_conf));
@@ -403,13 +403,13 @@ rspamd_rcl_worker_handler (struct config_file *cfg, rspamd_cl_object_t *obj,
                return FALSE;
        }
 
-       val = rspamd_cl_obj_get_key (obj, "bind_socket");
+       val = ucl_obj_get_key (obj, "bind_socket");
        if (val != NULL) {
-               if (val->type == RSPAMD_CL_ARRAY) {
+               if (val->type == UCL_ARRAY) {
                        val = val->value.ov;
                }
                LL_FOREACH (val, cur) {
-                       if (!rspamd_cl_obj_tostring_safe (cur, &worker_bind)) {
+                       if (!ucl_obj_tostring_safe (cur, &worker_bind)) {
                                continue;
                        }
                        bcf = memory_pool_alloc0 (cfg->cfg_pool, sizeof (struct rspamd_worker_bind_conf));
@@ -430,9 +430,9 @@ rspamd_rcl_worker_handler (struct config_file *cfg, rspamd_cl_object_t *obj,
 
        /* Parse other attributes */
        HASH_FIND_INT (cfg->wrk_parsers, (gint *)&qtype, wparser);
-       if (wparser != NULL && obj->type == RSPAMD_CL_OBJECT) {
+       if (wparser != NULL && obj->type == UCL_OBJECT) {
                HASH_ITER (hh, obj->value.ov, cur, tmp) {
-                       HASH_FIND_STR (wparser->parsers, cur->key, whandler);
+                       HASH_FIND_STR (wparser->parsers, ucl_object_key (cur), whandler);
                        if (whandler != NULL) {
                                if (!whandler->handler (cfg, cur, &whandler->parser, section, err)) {
                                        return FALSE;
@@ -451,7 +451,7 @@ rspamd_rcl_worker_handler (struct config_file *cfg, rspamd_cl_object_t *obj,
  * for default handlers
  */
 static gboolean
-rspamd_rcl_empty_handler (struct config_file *cfg, rspamd_cl_object_t *obj,
+rspamd_rcl_empty_handler (struct config_file *cfg, ucl_object_t *obj,
                gpointer ud, struct rspamd_rcl_section *section, GError **err)
 {
        return rspamd_rcl_section_parse_defaults (section, cfg, obj, cfg, err);
@@ -470,7 +470,7 @@ rspamd_rcl_empty_handler (struct config_file *cfg, rspamd_cl_object_t *obj,
 static inline struct rspamd_rcl_section*
 rspamd_rcl_add_section (struct rspamd_rcl_section *top,
                const gchar *name, rspamd_rcl_handler_t handler,
-               enum rspamd_cl_type type, gboolean required, gboolean strict_type)
+               enum ucl_type type, gboolean required, gboolean strict_type)
 {
        struct rspamd_rcl_section *new;
 
@@ -520,7 +520,7 @@ rspamd_rcl_config_init (void)
        /**
         * Logging section
         */
-       sub = rspamd_rcl_add_section (new, "logging", rspamd_rcl_logging_handler, RSPAMD_CL_OBJECT,
+       sub = rspamd_rcl_add_section (new, "logging", rspamd_rcl_logging_handler, UCL_OBJECT,
                        FALSE, TRUE);
        /* Default handlers */
        rspamd_rcl_add_default_handler (sub, "log_buffer", rspamd_rcl_parse_struct_integer,
@@ -536,7 +536,7 @@ rspamd_rcl_config_init (void)
        /**
         * Options section
         */
-       sub = rspamd_rcl_add_section (new, "options", rspamd_rcl_options_handler, RSPAMD_CL_OBJECT,
+       sub = rspamd_rcl_add_section (new, "options", rspamd_rcl_options_handler, UCL_OBJECT,
                        FALSE, TRUE);
        rspamd_rcl_add_default_handler (sub, "statfile_pool_size", rspamd_rcl_parse_struct_integer,
                        G_STRUCT_OFFSET (struct config_file, max_statfile_size), RSPAMD_CL_FLAG_INT_SIZE);
@@ -580,13 +580,13 @@ rspamd_rcl_config_init (void)
        /**
         * Metric section
         */
-       sub = rspamd_rcl_add_section (new, "metric", rspamd_rcl_metric_handler, RSPAMD_CL_OBJECT,
+       sub = rspamd_rcl_add_section (new, "metric", rspamd_rcl_metric_handler, UCL_OBJECT,
                        FALSE, TRUE);
 
        /**
         * Worker section
         */
-       sub = rspamd_rcl_add_section (new, "worker", rspamd_rcl_worker_handler, RSPAMD_CL_OBJECT,
+       sub = rspamd_rcl_add_section (new, "worker", rspamd_rcl_worker_handler, UCL_OBJECT,
                        FALSE, TRUE);
        rspamd_rcl_add_default_handler (sub, "count", rspamd_rcl_parse_struct_integer,
                        G_STRUCT_OFFSET (struct worker_conf, count), RSPAMD_CL_FLAG_INT_16);
@@ -634,19 +634,19 @@ rspamd_rcl_config_get_section (struct rspamd_rcl_section *top,
 
 gboolean
 rspamd_read_rcl_config (struct rspamd_rcl_section *top,
-               struct config_file *cfg, rspamd_cl_object_t *obj, GError **err)
+               struct config_file *cfg, ucl_object_t *obj, GError **err)
 {
-       rspamd_cl_object_t *found;
+       ucl_object_t *found;
        struct rspamd_rcl_section *cur, *tmp;
 
-       if (obj->type != RSPAMD_CL_OBJECT) {
+       if (obj->type != UCL_OBJECT) {
                g_set_error (err, CFG_RCL_ERROR, EINVAL, "top configuration must be an object");
                return FALSE;
        }
 
        /* Iterate over known sections and ignore unknown ones */
        HASH_ITER (hh, top, cur, tmp) {
-               found = rspamd_cl_obj_get_key (obj, cur->name);
+               found = ucl_obj_get_key (obj, cur->name);
                if (found == NULL) {
                        if (cur->required) {
                                g_set_error (err, CFG_RCL_ERROR, ENOENT, "required section %s is missing", cur->name);
@@ -673,19 +673,19 @@ rspamd_read_rcl_config (struct rspamd_rcl_section *top,
 }
 
 gboolean rspamd_rcl_section_parse_defaults (struct rspamd_rcl_section *section,
-               struct config_file *cfg, rspamd_cl_object_t *obj, gpointer ptr,
+               struct config_file *cfg, ucl_object_t *obj, gpointer ptr,
                GError **err)
 {
-       rspamd_cl_object_t *found;
+       ucl_object_t *found;
        struct rspamd_rcl_default_handler_data *cur, *tmp;
 
-       if (obj->type != RSPAMD_CL_OBJECT) {
+       if (obj->type != UCL_OBJECT) {
                g_set_error (err, CFG_RCL_ERROR, EINVAL, "default configuration must be an object");
                return FALSE;
        }
 
        HASH_ITER (hh, section->default_parser, cur, tmp) {
-               found = rspamd_cl_obj_get_key (obj, cur->key);
+               found = ucl_obj_get_key (obj, cur->key);
                if (found != NULL) {
                        cur->pd.user_struct = ptr;
                        if (!cur->handler (cfg, found, &cur->pd, section, err)) {
@@ -698,7 +698,7 @@ gboolean rspamd_rcl_section_parse_defaults (struct rspamd_rcl_section *section,
 }
 
 gboolean
-rspamd_rcl_parse_struct_string (struct config_file *cfg, rspamd_cl_object_t *obj,
+rspamd_rcl_parse_struct_string (struct config_file *cfg, ucl_object_t *obj,
                gpointer ud, struct rspamd_rcl_section *section, GError **err)
 {
        struct rspamd_rcl_struct_parser *pd = ud;
@@ -707,19 +707,19 @@ rspamd_rcl_parse_struct_string (struct config_file *cfg, rspamd_cl_object_t *obj
 
        target = (gchar **)(((gchar *)pd->user_struct) + pd->offset);
        switch (obj->type) {
-       case RSPAMD_CL_STRING:
+       case UCL_STRING:
                /* Direct assigning is safe, as object is likely linked to the cfg mem_pool */
-               *target = obj->value.sv;
+               *target = ucl_copy_value_trash (obj);
                break;
-       case RSPAMD_CL_INT:
+       case UCL_INT:
                *target = memory_pool_alloc (cfg->cfg_pool, num_str_len);
                rspamd_snprintf (*target, num_str_len, "%L", obj->value.iv);
                break;
-       case RSPAMD_CL_FLOAT:
+       case UCL_FLOAT:
                *target = memory_pool_alloc (cfg->cfg_pool, num_str_len);
                rspamd_snprintf (*target, num_str_len, "%f", obj->value.dv);
                break;
-       case RSPAMD_CL_BOOLEAN:
+       case UCL_BOOLEAN:
                *target = memory_pool_alloc (cfg->cfg_pool, num_str_len);
                rspamd_snprintf (*target, num_str_len, "%b", (gboolean)obj->value.iv);
                break;
@@ -732,7 +732,7 @@ rspamd_rcl_parse_struct_string (struct config_file *cfg, rspamd_cl_object_t *obj
 }
 
 gboolean
-rspamd_rcl_parse_struct_integer (struct config_file *cfg, rspamd_cl_object_t *obj,
+rspamd_rcl_parse_struct_integer (struct config_file *cfg, ucl_object_t *obj,
                gpointer ud, struct rspamd_rcl_section *section, GError **err)
 {
        struct rspamd_rcl_struct_parser *pd = ud;
@@ -747,7 +747,7 @@ rspamd_rcl_parse_struct_integer (struct config_file *cfg, rspamd_cl_object_t *ob
 
        if (pd->flags == RSPAMD_CL_FLAG_INT_32) {
                target.i32p = (gint32 *)(((gchar *)pd->user_struct) + pd->offset);
-               if (!rspamd_cl_obj_toint_safe (obj, &val)) {
+               if (!ucl_obj_toint_safe (obj, &val)) {
                        g_set_error (err, CFG_RCL_ERROR, EINVAL, "cannot convert param to integer");
                        return FALSE;
                }
@@ -755,7 +755,7 @@ rspamd_rcl_parse_struct_integer (struct config_file *cfg, rspamd_cl_object_t *ob
        }
        else if (pd->flags == RSPAMD_CL_FLAG_INT_64) {
                target.i64p = (gint64 *)(((gchar *)pd->user_struct) + pd->offset);
-               if (!rspamd_cl_obj_toint_safe (obj, &val)) {
+               if (!ucl_obj_toint_safe (obj, &val)) {
                        g_set_error (err, CFG_RCL_ERROR, EINVAL, "cannot convert param to integer");
                        return FALSE;
                }
@@ -763,7 +763,7 @@ rspamd_rcl_parse_struct_integer (struct config_file *cfg, rspamd_cl_object_t *ob
        }
        else if (pd->flags == RSPAMD_CL_FLAG_INT_SIZE) {
                target.sp = (gsize *)(((gchar *)pd->user_struct) + pd->offset);
-               if (!rspamd_cl_obj_toint_safe (obj, &val)) {
+               if (!ucl_obj_toint_safe (obj, &val)) {
                        g_set_error (err, CFG_RCL_ERROR, EINVAL, "cannot convert param to integer");
                        return FALSE;
                }
@@ -771,7 +771,7 @@ rspamd_rcl_parse_struct_integer (struct config_file *cfg, rspamd_cl_object_t *ob
        }
        else if (pd->flags == RSPAMD_CL_FLAG_INT_16) {
                target.i16p = (gint16 *)(((gchar *)pd->user_struct) + pd->offset);
-               if (!rspamd_cl_obj_toint_safe (obj, &val)) {
+               if (!ucl_obj_toint_safe (obj, &val)) {
                        g_set_error (err, CFG_RCL_ERROR, EINVAL, "cannot convert param to integer");
                        return FALSE;
                }
@@ -779,7 +779,7 @@ rspamd_rcl_parse_struct_integer (struct config_file *cfg, rspamd_cl_object_t *ob
        }
        else {
                target.ip = (gint *)(((gchar *)pd->user_struct) + pd->offset);
-               if (!rspamd_cl_obj_toint_safe (obj, &val)) {
+               if (!ucl_obj_toint_safe (obj, &val)) {
                        g_set_error (err, CFG_RCL_ERROR, EINVAL, "cannot convert param to integer");
                        return FALSE;
                }
@@ -790,7 +790,7 @@ rspamd_rcl_parse_struct_integer (struct config_file *cfg, rspamd_cl_object_t *ob
 }
 
 gboolean
-rspamd_rcl_parse_struct_double (struct config_file *cfg, rspamd_cl_object_t *obj,
+rspamd_rcl_parse_struct_double (struct config_file *cfg, ucl_object_t *obj,
                gpointer ud, struct rspamd_rcl_section *section, GError **err)
 {
        struct rspamd_rcl_struct_parser *pd = ud;
@@ -798,7 +798,7 @@ rspamd_rcl_parse_struct_double (struct config_file *cfg, rspamd_cl_object_t *obj
 
        target = (gdouble *)(((gchar *)pd->user_struct) + pd->offset);
 
-       if (!rspamd_cl_obj_todouble_safe (obj, target)) {
+       if (!ucl_obj_todouble_safe (obj, target)) {
                g_set_error (err, CFG_RCL_ERROR, EINVAL, "cannot convert param to double");
                return FALSE;
        }
@@ -807,7 +807,7 @@ rspamd_rcl_parse_struct_double (struct config_file *cfg, rspamd_cl_object_t *obj
 }
 
 gboolean
-rspamd_rcl_parse_struct_time (struct config_file *cfg, rspamd_cl_object_t *obj,
+rspamd_rcl_parse_struct_time (struct config_file *cfg, ucl_object_t *obj,
                gpointer ud, struct rspamd_rcl_section *section, GError **err)
 {
        struct rspamd_rcl_struct_parser *pd = ud;
@@ -820,7 +820,7 @@ rspamd_rcl_parse_struct_time (struct config_file *cfg, rspamd_cl_object_t *obj,
        } target;
        gdouble val;
 
-       if (!rspamd_cl_obj_todouble_safe (obj, &val)) {
+       if (!ucl_obj_todouble_safe (obj, &val)) {
                g_set_error (err, CFG_RCL_ERROR, EINVAL, "cannot convert param to double");
                return FALSE;
        }
@@ -856,37 +856,37 @@ rspamd_rcl_parse_struct_time (struct config_file *cfg, rspamd_cl_object_t *obj,
 }
 
 gboolean
-rspamd_rcl_parse_struct_string_list (struct config_file *cfg, rspamd_cl_object_t *obj,
+rspamd_rcl_parse_struct_string_list (struct config_file *cfg, ucl_object_t *obj,
                gpointer ud, struct rspamd_rcl_section *section, GError **err)
 {
        struct rspamd_rcl_struct_parser *pd = ud;
        GList **target;
        gchar *val;
-       rspamd_cl_object_t *cur;
+       ucl_object_t *cur;
        const gsize num_str_len = 32;
 
        target = (GList **)(((gchar *)pd->user_struct) + pd->offset);
 
-       if (obj->type != RSPAMD_CL_ARRAY) {
+       if (obj->type != UCL_ARRAY) {
                g_set_error (err, CFG_RCL_ERROR, EINVAL, "an array of strings is expected");
                return FALSE;
        }
 
        for (cur = obj; cur != NULL; cur = cur->next) {
                switch (cur->type) {
-               case RSPAMD_CL_STRING:
+               case UCL_STRING:
                        /* Direct assigning is safe, as curect is likely linked to the cfg mem_pool */
-                       val = cur->value.sv;
+                       val = ucl_copy_value_trash (obj);
                        break;
-               case RSPAMD_CL_INT:
+               case UCL_INT:
                        val = memory_pool_alloc (cfg->cfg_pool, num_str_len);
                        rspamd_snprintf (val, num_str_len, "%L", cur->value.iv);
                        break;
-               case RSPAMD_CL_FLOAT:
+               case UCL_FLOAT:
                        val = memory_pool_alloc (cfg->cfg_pool, num_str_len);
                        rspamd_snprintf (val, num_str_len, "%f", cur->value.dv);
                        break;
-               case RSPAMD_CL_BOOLEAN:
+               case UCL_BOOLEAN:
                        val = memory_pool_alloc (cfg->cfg_pool, num_str_len);
                        rspamd_snprintf (val, num_str_len, "%b", (gboolean)cur->value.iv);
                        break;
@@ -904,7 +904,7 @@ rspamd_rcl_parse_struct_string_list (struct config_file *cfg, rspamd_cl_object_t
 }
 
 gboolean
-rspamd_rcl_parse_struct_boolean (struct config_file *cfg, rspamd_cl_object_t *obj,
+rspamd_rcl_parse_struct_boolean (struct config_file *cfg, ucl_object_t *obj,
                gpointer ud, struct rspamd_rcl_section *section, GError **err)
 {
        struct rspamd_rcl_struct_parser *pd = ud;
@@ -912,10 +912,10 @@ rspamd_rcl_parse_struct_boolean (struct config_file *cfg, rspamd_cl_object_t *ob
 
        target = (gboolean *)(((gchar *)pd->user_struct) + pd->offset);
 
-       if (obj->type == RSPAMD_CL_BOOLEAN) {
+       if (obj->type == UCL_BOOLEAN) {
                *target = obj->value.iv;
        }
-       else if (obj->type == RSPAMD_CL_INT) {
+       else if (obj->type == UCL_INT) {
                *target = obj->value.iv;
        }
        else {
index fb0fea7e3a9c8c358d5fe1d8f5cd9a36cd864ddc..2c1441fbbe4824710fcc9b096ced2f7f464fa82e 100644 (file)
@@ -25,7 +25,7 @@
 #define CFG_RCL_H_
 
 #include "config.h"
-#include "rcl/rcl.h"
+#include "ucl/include/ucl.h"
 
 #define CFG_RCL_ERROR cfg_rcl_error_quark ()
 static inline GQuark
@@ -61,7 +61,7 @@ struct rspamd_rcl_struct_parser {
  * @param err error object
  * @return TRUE if a section has been parsed
  */
-typedef gboolean (*rspamd_rcl_handler_t) (struct config_file *cfg, rspamd_cl_object_t *obj,
+typedef gboolean (*rspamd_rcl_handler_t) (struct config_file *cfg, ucl_object_t *obj,
                gpointer ud, struct rspamd_rcl_section *section, GError **err);
 
 struct rspamd_rcl_default_handler_data {
@@ -74,7 +74,7 @@ struct rspamd_rcl_default_handler_data {
 struct rspamd_rcl_section {
        const gchar *name;                                      /**< name of section */
        rspamd_rcl_handler_t handler;           /**< handler of section attributes */
-       enum rspamd_cl_type type;                       /**< type of attribute */
+       enum ucl_type type;                     /**< type of attribute */
        gboolean required;                                      /**< whether this param is required */
        gboolean strict_type;                           /**< whether we need strict type */
        UT_hash_handle hh;                                      /** hash handle */
@@ -105,7 +105,7 @@ struct rspamd_rcl_section *rspamd_rcl_config_get_section (struct rspamd_rcl_sect
  * @return TRUE if an object can be parsed
  */
 gboolean rspamd_read_rcl_config (struct rspamd_rcl_section *top,
-               struct config_file *cfg, rspamd_cl_object_t *obj, GError **err);
+               struct config_file *cfg, ucl_object_t *obj, GError **err);
 
 
 /**
@@ -118,7 +118,7 @@ gboolean rspamd_read_rcl_config (struct rspamd_rcl_section *top,
  * @return TRUE if the object has been parsed
  */
 gboolean rspamd_rcl_section_parse_defaults (struct rspamd_rcl_section *section,
-               struct config_file *cfg, rspamd_cl_object_t *obj, gpointer ptr,
+               struct config_file *cfg, ucl_object_t *obj, gpointer ptr,
                GError **err);
 /**
  * Here is a section of common handlers that accepts rcl_struct_parser
@@ -135,7 +135,7 @@ gboolean rspamd_rcl_section_parse_defaults (struct rspamd_rcl_section *section,
  * @param err error pointer
  * @return TRUE if a string value has been successfully parsed
  */
-gboolean rspamd_rcl_parse_struct_string (struct config_file *cfg, rspamd_cl_object_t *obj,
+gboolean rspamd_rcl_parse_struct_string (struct config_file *cfg, ucl_object_t *obj,
                gpointer ud, struct rspamd_rcl_section *section, GError **err);
 
 /**
@@ -147,7 +147,7 @@ gboolean rspamd_rcl_parse_struct_string (struct config_file *cfg, rspamd_cl_obje
  * @param err error pointer
  * @return TRUE if a value has been successfully parsed
  */
-gboolean rspamd_rcl_parse_struct_integer (struct config_file *cfg, rspamd_cl_object_t *obj,
+gboolean rspamd_rcl_parse_struct_integer (struct config_file *cfg, ucl_object_t *obj,
                gpointer ud, struct rspamd_rcl_section *section, GError **err);
 
 
@@ -160,7 +160,7 @@ gboolean rspamd_rcl_parse_struct_integer (struct config_file *cfg, rspamd_cl_obj
  * @param err error pointer
  * @return TRUE if a value has been successfully parsed
  */
-gboolean rspamd_rcl_parse_struct_double (struct config_file *cfg, rspamd_cl_object_t *obj,
+gboolean rspamd_rcl_parse_struct_double (struct config_file *cfg, ucl_object_t *obj,
                gpointer ud, struct rspamd_rcl_section *section, GError **err);
 
 /**
@@ -172,7 +172,7 @@ gboolean rspamd_rcl_parse_struct_double (struct config_file *cfg, rspamd_cl_obje
  * @param err error pointer
  * @return TRUE if a value has been successfully parsed
  */
-gboolean rspamd_rcl_parse_struct_time (struct config_file *cfg, rspamd_cl_object_t *obj,
+gboolean rspamd_rcl_parse_struct_time (struct config_file *cfg, ucl_object_t *obj,
                gpointer ud, struct rspamd_rcl_section *section, GError **err);
 
 /**
@@ -184,7 +184,7 @@ gboolean rspamd_rcl_parse_struct_time (struct config_file *cfg, rspamd_cl_object
  * @param err error pointer
  * @return TRUE if a value has been successfully parsed
  */
-gboolean rspamd_rcl_parse_struct_string_list (struct config_file *cfg, rspamd_cl_object_t *obj,
+gboolean rspamd_rcl_parse_struct_string_list (struct config_file *cfg, ucl_object_t *obj,
                gpointer ud, struct rspamd_rcl_section *section, GError **err);
 
 /**
@@ -196,7 +196,7 @@ gboolean rspamd_rcl_parse_struct_string_list (struct config_file *cfg, rspamd_cl
  * @param err error pointer
  * @return TRUE if a value has been successfully parsed
  */
-gboolean rspamd_rcl_parse_struct_boolean (struct config_file *cfg, rspamd_cl_object_t *obj,
+gboolean rspamd_rcl_parse_struct_boolean (struct config_file *cfg, ucl_object_t *obj,
                gpointer ud, struct rspamd_rcl_section *section, GError **err);
 
 /**
index 906f4406fc4ef8b56a926c2d895e3575a0af6e94..8a49237ac70ae4dc65230b0587ee5000079e69dc 100644 (file)
@@ -265,7 +265,7 @@ free_config (struct config_file *cfg)
        struct symbols_group                    *gr;
 
        remove_all_maps (cfg);
-       rspamd_cl_obj_unref (cfg->rcl_obj);
+       ucl_obj_unref (cfg->rcl_obj);
        g_hash_table_remove_all (cfg->variables);
        g_hash_table_unref (cfg->variables);
        g_hash_table_remove_all (cfg->metrics);
@@ -299,14 +299,14 @@ free_config (struct config_file *cfg)
        memory_pool_delete (cfg->cfg_pool);
 }
 
-rspamd_cl_object_t        *
+ucl_object_t        *
 get_module_opt (struct config_file *cfg, const gchar *module_name, const gchar *opt_name)
 {
-       rspamd_cl_object_t *res = NULL, *sec;
+       ucl_object_t *res = NULL, *sec;
 
-       sec = rspamd_cl_obj_get_key (cfg->rcl_obj, module_name);
+       sec = ucl_obj_get_key (cfg->rcl_obj, module_name);
        if (sec != NULL) {
-               res = rspamd_cl_obj_get_key (cfg->rcl_obj, opt_name);
+               res = ucl_obj_get_key (cfg->rcl_obj, opt_name);
        }
 
        return res;
index 4f7a3bca5b83a4af1fa22cb86458a8e488639321..0e22ec8f2e9ae46ec923a4ce1059852fd6b25a5f 100644 (file)
@@ -125,7 +125,7 @@ lua_post_load_config (struct config_file *cfg)
        const gchar                          *name, *val;
        gchar                                *sym;
        struct expression                    *expr, *old_expr;
-       rspamd_cl_object_t                   *obj;
+       ucl_object_t                   *obj;
        gsize                                 keylen;
 
        /* First check all module options that may be overriden in 'config' global */
@@ -140,10 +140,7 @@ lua_post_load_config (struct config_file *cfg)
                        if (name != NULL && lua_istable (L, -1)) {
                                obj = lua_rcl_obj_get (L, -1);
                                if (obj != NULL) {
-                                       obj->key = memory_pool_alloc (cfg->cfg_pool, keylen + 1);
-                                       memcpy (obj->key, name, keylen);
-                                       obj->key[keylen] = '\0';
-                                       HASH_ADD_KEYPTR (hh, cfg->rcl_obj->value.ov, obj->key, keylen, obj);
+                                       cfg->rcl_obj = ucl_object_insert_key (cfg->rcl_obj, obj, name, keylen, true);
                                }
                        }
                }
index c6ba06160cf6b5033cf0ef00049cdeafb6311255..c878a50f360d6bc02a3d710a099a75027034410a 100644 (file)
@@ -6,7 +6,7 @@
 
 #include "main.h"
 #include "cfg_file.h"
-#include "rcl/rcl.h"
+#include "ucl.h"
 #include <lua.h>
 #include <lauxlib.h>
 #include <lualib.h>
@@ -107,14 +107,14 @@ void free_lua_locked (struct lua_locked_state *st);
  * @param L lua state
  * @param obj object to push
  */
-gint lua_rcl_obj_push (lua_State *L, rspamd_cl_object_t *obj);
+gint lua_rcl_obj_push (lua_State *L, ucl_object_t *obj);
 
 /**
  * Extract rcl object from lua object
  * @param L
  * @return
  */
-rspamd_cl_object_t * lua_rcl_obj_get (lua_State *L, gint idx);
+ucl_object_t * lua_rcl_obj_get (lua_State *L, gint idx);
 
 /**
  * Open libraries functions
index d3b7c6af948fb70c4c011e5d0f97f7592505590b..df8526d0f6d00a5f8a8bd1398bdf5f426d07ac05 100644 (file)
@@ -154,7 +154,7 @@ lua_config_get_module_opt (lua_State * L)
 {
        struct config_file             *cfg = lua_check_config (L);
        const gchar                     *mname, *optname;
-       rspamd_cl_object_t              *obj;
+       ucl_object_t              *obj;
 
        if (cfg) {
                mname = luaL_checkstring (L, 2);
@@ -190,13 +190,13 @@ lua_config_get_all_opt (lua_State * L)
 {
        struct config_file             *cfg = lua_check_config (L);
        const gchar                     *mname;
-       rspamd_cl_object_t              *obj;
+       ucl_object_t              *obj;
 
        if (cfg) {
                mname = luaL_checkstring (L, 2);
 
                if (mname) {
-                       obj = rspamd_cl_obj_get_key (cfg->rcl_obj, mname);
+                       obj = ucl_obj_get_key (cfg->rcl_obj, mname);
                        if (obj != NULL) {
                                return lua_rcl_obj_push (L, obj);
                        }
index 91f1fff797e098a2e22e04fc3106579723505f0a..b695f4f11f1dd3aad31a45f587eb562e5ec34cf6 100644 (file)
  */
 
 #include "lua_common.h"
-#include "rcl/rcl.h"
 
 /**
  * @file lua rcl bindings
  */
 
-static gint lua_rcl_obj_push_array (lua_State *L, rspamd_cl_object_t *obj);
-static gint lua_rcl_obj_push_simple (lua_State *L, rspamd_cl_object_t *obj);
-static void lua_rcl_table_get (lua_State *L, rspamd_cl_object_t *top, gint idx);
-static void lua_rcl_elt_get (lua_State *L, rspamd_cl_object_t *top, gint idx);
+static gint lua_rcl_obj_push_array (lua_State *L, ucl_object_t *obj);
+static gint lua_rcl_obj_push_simple (lua_State *L, ucl_object_t *obj);
+static void lua_rcl_table_get (lua_State *L, ucl_object_t *top, gint idx);
+static void lua_rcl_elt_get (lua_State *L, ucl_object_t *top, gint idx);
 
 /**
  * Push a single element of an object to lua
@@ -40,7 +39,7 @@ static void lua_rcl_elt_get (lua_State *L, rspamd_cl_object_t *top, gint idx);
  * @param obj
  */
 static void
-lua_rcl_obj_push_elt (lua_State *L, const char *key, rspamd_cl_object_t *obj)
+lua_rcl_obj_push_elt (lua_State *L, const char *key, ucl_object_t *obj)
 {
        lua_pushstring (L, key);
        lua_rcl_obj_push (L, obj);
@@ -54,9 +53,9 @@ lua_rcl_obj_push_elt (lua_State *L, const char *key, rspamd_cl_object_t *obj)
  * @return
  */
 static gint
-lua_rcl_obj_push_obj (lua_State *L, rspamd_cl_object_t *obj)
+lua_rcl_obj_push_obj (lua_State *L, ucl_object_t *obj)
 {
-       rspamd_cl_object_t *cur, *tmp;
+       ucl_object_t *cur, *tmp;
 
        if (obj->next != NULL) {
                /* Actually we need to push this as an array */
@@ -65,7 +64,7 @@ lua_rcl_obj_push_obj (lua_State *L, rspamd_cl_object_t *obj)
 
        lua_newtable (L);
        HASH_ITER (hh, obj, cur, tmp) {
-               lua_rcl_obj_push_elt (L, obj->key, obj);
+               lua_rcl_obj_push_elt (L, ucl_object_key (obj), obj);
        }
 
        return 1;
@@ -78,9 +77,9 @@ lua_rcl_obj_push_obj (lua_State *L, rspamd_cl_object_t *obj)
  * @return
  */
 static gint
-lua_rcl_obj_push_array (lua_State *L, rspamd_cl_object_t *obj)
+lua_rcl_obj_push_array (lua_State *L, ucl_object_t *obj)
 {
-       rspamd_cl_object_t *cur;
+       ucl_object_t *cur;
        gint i = 1;
 
        lua_newtable (L);
@@ -98,7 +97,7 @@ lua_rcl_obj_push_array (lua_State *L, rspamd_cl_object_t *obj)
  * Push a simple object to lua depending on its actual type
  */
 static gint
-lua_rcl_obj_push_simple (lua_State *L, rspamd_cl_object_t *obj)
+lua_rcl_obj_push_simple (lua_State *L, ucl_object_t *obj)
 {
        if (obj->next != NULL) {
                /* Actually we need to push this as an array */
@@ -106,22 +105,22 @@ lua_rcl_obj_push_simple (lua_State *L, rspamd_cl_object_t *obj)
        }
 
        switch (obj->type) {
-       case RSPAMD_CL_BOOLEAN:
-               lua_pushboolean (L, rspamd_cl_obj_toboolean (obj));
+       case UCL_BOOLEAN:
+               lua_pushboolean (L, ucl_obj_toboolean (obj));
                break;
-       case RSPAMD_CL_STRING:
-               lua_pushstring (L, rspamd_cl_obj_tostring (obj));
+       case UCL_STRING:
+               lua_pushstring (L, ucl_obj_tostring (obj));
                break;
-       case RSPAMD_CL_INT:
+       case UCL_INT:
 #if LUA_VERSION_NUM >= 501
-               lua_pushinteger (L, rspamd_cl_obj_toint (obj));
+               lua_pushinteger (L, ucl_obj_toint (obj));
 #else
-               lua_pushnumber (L, rspamd_cl_obj_toint (obj));
+               lua_pushnumber (L, ucl_obj_toint (obj));
 #endif
                break;
-       case RSPAMD_CL_FLOAT:
-       case RSPAMD_CL_TIME:
-               lua_pushnumber (L, rspamd_cl_obj_todouble (obj));
+       case UCL_FLOAT:
+       case UCL_TIME:
+               lua_pushnumber (L, ucl_obj_todouble (obj));
                break;
        default:
                lua_pushnil (L);
@@ -137,12 +136,12 @@ lua_rcl_obj_push_simple (lua_State *L, rspamd_cl_object_t *obj)
  * @param obj object to push
  */
 gint
-lua_rcl_obj_push (lua_State *L, rspamd_cl_object_t *obj)
+lua_rcl_obj_push (lua_State *L, ucl_object_t *obj)
 {
        switch (obj->type) {
-       case RSPAMD_CL_OBJECT:
+       case UCL_OBJECT:
                return lua_rcl_obj_push_obj (L, obj->value.ov);
-       case RSPAMD_CL_ARRAY:
+       case UCL_ARRAY:
                return lua_rcl_obj_push_array (L, obj->value.ov);
        default:
                return lua_rcl_obj_push_simple (L, obj);
@@ -156,9 +155,9 @@ lua_rcl_obj_push (lua_State *L, rspamd_cl_object_t *obj)
  * @param idx
  */
 static void
-lua_rcl_table_get (lua_State *L, rspamd_cl_object_t *top, gint idx)
+lua_rcl_table_get (lua_State *L, ucl_object_t *top, gint idx)
 {
-       rspamd_cl_object_t *obj;
+       ucl_object_t *obj;
        gsize keylen;
        const gchar *k;
 
@@ -168,13 +167,10 @@ lua_rcl_table_get (lua_State *L, rspamd_cl_object_t *top, gint idx)
        while (lua_next (L, -2) != 0) {
                /* copy key to avoid modifications */
                lua_pushvalue (L, -2);
-               obj = rspamd_cl_object_new ();
+               obj = ucl_object_new ();
                if (obj != NULL) {
                        k = lua_tolstring (L, -1, &keylen);
-                       obj->key = g_malloc (keylen + 1);
-                       memcpy (obj->key, k, keylen);
-                       obj->key[keylen] = '\0';
-                       HASH_ADD_KEYPTR (hh, top->value.ov, obj->key, keylen, obj);
+                       ucl_object_insert_key (top, obj, k, keylen, true);
                        lua_rcl_elt_get (L, obj, -2);
                }
 
@@ -190,7 +186,7 @@ lua_rcl_table_get (lua_State *L, rspamd_cl_object_t *top, gint idx)
  * @param idx
  */
 static void
-lua_rcl_elt_get (lua_State *L, rspamd_cl_object_t *obj, gint idx)
+lua_rcl_elt_get (lua_State *L, ucl_object_t *obj, gint idx)
 {
        gint type;
 
@@ -199,23 +195,23 @@ lua_rcl_elt_get (lua_State *L, rspamd_cl_object_t *obj, gint idx)
        switch (type) {
        case LUA_TFUNCTION:
                lua_pushvalue (L, idx);
-               obj->type = RSPAMD_CL_USERDATA;
+               obj->type = UCL_USERDATA;
                obj->value.ud = GINT_TO_POINTER (luaL_ref (L, LUA_REGISTRYINDEX));
                break;
        case LUA_TSTRING:
-               obj->type = RSPAMD_CL_STRING;
+               obj->type = UCL_STRING;
                obj->value.sv = g_strdup (lua_tostring (L, idx));
                break;
        case LUA_TNUMBER:
-               obj->type = RSPAMD_CL_FLOAT;
+               obj->type = UCL_FLOAT;
                obj->value.dv = lua_tonumber (L, idx);
                break;
        case LUA_TBOOLEAN:
-               obj->type = RSPAMD_CL_BOOLEAN;
+               obj->type = UCL_BOOLEAN;
                obj->value.iv = lua_toboolean (L, idx);
                break;
        case LUA_TTABLE:
-               obj->type = RSPAMD_CL_OBJECT;
+               obj->type = UCL_OBJECT;
                lua_rcl_table_get (L, obj, idx);
                break;
        }
@@ -226,13 +222,13 @@ lua_rcl_elt_get (lua_State *L, rspamd_cl_object_t *obj, gint idx)
  * @param L
  * @return
  */
-rspamd_cl_object_t *
+ucl_object_t *
 lua_rcl_obj_get (lua_State *L, gint idx)
 {
-       rspamd_cl_object_t *obj;
+       ucl_object_t *obj;
        gint t;
 
-       obj = rspamd_cl_object_new ();
+       obj = ucl_object_new ();
 
        if (obj != NULL) {
                t = lua_type (L, idx);
index a140c58937ec541530e704bace978e475af69e54..69197dfd6ad672c57dd133b19852d8a253d1d4ce 100644 (file)
@@ -86,17 +86,17 @@ chartable_module_init (struct config_file *cfg, struct module_ctx **ctx)
 gint
 chartable_module_config (struct config_file *cfg)
 {
-       rspamd_cl_object_t             *value;
+       ucl_object_t             *value;
        gint                            res = TRUE;
 
        if ((value = get_module_opt (cfg, "chartable", "symbol")) != NULL) {
-               chartable_module_ctx->symbol = rspamd_cl_obj_tostring (value);
+               chartable_module_ctx->symbol = ucl_obj_tostring (value);
        }
        else {
                chartable_module_ctx->symbol = DEFAULT_SYMBOL;
        } 
        if ((value = get_module_opt (cfg, "chartable", "threshold")) != NULL) {
-               if (!rspamd_cl_obj_todouble_safe (value, &chartable_module_ctx->threshold)) {
+               if (!ucl_obj_todouble_safe (value, &chartable_module_ctx->threshold)) {
                        msg_warn ("invalid numeric value");
                        chartable_module_ctx->threshold = DEFAULT_THRESHOLD;
                }
index 7ae11631ca3fec0e6172bbdbf220e76ea25ec52d..8e3fa4c23facbe054fb62f4bf43cdf2ec0b791ad 100644 (file)
@@ -114,7 +114,7 @@ dkim_module_init (struct config_file *cfg, struct module_ctx **ctx)
 gint
 dkim_module_config (struct config_file *cfg)
 {
-       rspamd_cl_object_t             *value;
+       ucl_object_t             *value;
        gint                            res = TRUE;
        guint                           cache_size, cache_expire;
        gboolean                                                got_trusted = FALSE;
@@ -122,72 +122,72 @@ dkim_module_config (struct config_file *cfg)
        dkim_module_ctx->whitelist_ip = radix_tree_create ();
 
        if ((value = get_module_opt (cfg, "dkim", "symbol_reject")) != NULL) {
-               dkim_module_ctx->symbol_reject = rspamd_cl_obj_tostring (value);
+               dkim_module_ctx->symbol_reject = ucl_obj_tostring (value);
        }
        else {
                dkim_module_ctx->symbol_reject = DEFAULT_SYMBOL_REJECT;
        }
        if ((value = get_module_opt (cfg, "dkim", "symbol_tempfail")) != NULL) {
-               dkim_module_ctx->symbol_tempfail = rspamd_cl_obj_tostring (value);
+               dkim_module_ctx->symbol_tempfail = ucl_obj_tostring (value);
        }
        else {
                dkim_module_ctx->symbol_tempfail = DEFAULT_SYMBOL_TEMPFAIL;
        }
        if ((value = get_module_opt (cfg, "dkim", "symbol_allow")) != NULL) {
-               dkim_module_ctx->symbol_allow = rspamd_cl_obj_tostring (value);
+               dkim_module_ctx->symbol_allow = ucl_obj_tostring (value);
        }
        else {
                dkim_module_ctx->symbol_allow = DEFAULT_SYMBOL_ALLOW;
        }
        if ((value = get_module_opt (cfg, "dkim", "dkim_cache_size")) != NULL) {
-               cache_size = rspamd_cl_obj_toint (value);
+               cache_size = ucl_obj_toint (value);
        }
        else {
                cache_size = DEFAULT_CACHE_SIZE;
        }
        if ((value = get_module_opt (cfg, "dkim", "dkim_cache_expire")) != NULL) {
-               cache_expire = rspamd_cl_obj_todouble (value);
+               cache_expire = ucl_obj_todouble (value);
        }
        else {
                cache_expire = DEFAULT_CACHE_MAXAGE;
        }
        if ((value = get_module_opt (cfg, "dkim", "time_jitter")) != NULL) {
-               dkim_module_ctx->time_jitter = rspamd_cl_obj_todouble (value);
+               dkim_module_ctx->time_jitter = ucl_obj_todouble (value);
        }
        else {
                dkim_module_ctx->time_jitter = DEFAULT_TIME_JITTER;
        }
        if ((value = get_module_opt (cfg, "dkim", "whitelist")) != NULL) {
-               if (! add_map (cfg, rspamd_cl_obj_tostring (value),
+               if (! add_map (cfg, ucl_obj_tostring (value),
                                "DKIM whitelist", read_radix_list, fin_radix_list,
                                (void **)&dkim_module_ctx->whitelist_ip)) {
-                       msg_warn ("cannot load whitelist from %s", rspamd_cl_obj_tostring (value));
+                       msg_warn ("cannot load whitelist from %s", ucl_obj_tostring (value));
                }
        }
        if ((value = get_module_opt (cfg, "dkim", "domains")) != NULL) {
-               if (! add_map (cfg, rspamd_cl_obj_tostring (value),
+               if (! add_map (cfg, ucl_obj_tostring (value),
                                "DKIM domains", read_kv_list, fin_kv_list,
                                (void **)&dkim_module_ctx->dkim_domains)) {
-                       msg_warn ("cannot load dkim domains list from %s", rspamd_cl_obj_tostring (value));
+                       msg_warn ("cannot load dkim domains list from %s", ucl_obj_tostring (value));
                }
                else {
                        got_trusted = TRUE;
                }
        }
        if ((value = get_module_opt (cfg, "dkim", "strict_multiplier")) != NULL) {
-               dkim_module_ctx->strict_multiplier = rspamd_cl_obj_toint (value);
+               dkim_module_ctx->strict_multiplier = ucl_obj_toint (value);
        }
        else {
                dkim_module_ctx->strict_multiplier = 1;
        }
        if ((value = get_module_opt (cfg, "dkim", "trusted_only")) != NULL) {
-               dkim_module_ctx->trusted_only = rspamd_cl_obj_toboolean (value);
+               dkim_module_ctx->trusted_only = ucl_obj_toboolean (value);
        }
        else {
                dkim_module_ctx->trusted_only = FALSE;
        }
        if ((value = get_module_opt (cfg, "dkim", "skip_multi")) != NULL) {
-               dkim_module_ctx->skip_multi = rspamd_cl_obj_toboolean (value);
+               dkim_module_ctx->skip_multi = ucl_obj_toboolean (value);
        }
        else {
                dkim_module_ctx->skip_multi = FALSE;
index 4a900d4db54a077181e80e36c12c75771eb4d228..60549b84937fb2e883905f0b3b6ea30e5ff30871 100644 (file)
@@ -186,24 +186,24 @@ parse_flags_string_old (struct config_file *cfg, const gchar *str)
 }
 
 static void
-parse_flags_string (struct config_file *cfg, rspamd_cl_object_t *val)
+parse_flags_string (struct config_file *cfg, ucl_object_t *val)
 {
-       rspamd_cl_object_t *elt;
+       ucl_object_t *elt;
        struct fuzzy_mapping *map;
 
-       if (val->type == RSPAMD_CL_STRING) {
-               parse_flags_string_old (cfg, rspamd_cl_obj_tostring (val));
+       if (val->type == UCL_STRING) {
+               parse_flags_string_old (cfg, ucl_obj_tostring (val));
        }
-       else if (val->type == RSPAMD_CL_OBJECT) {
-               elt = rspamd_cl_obj_get_key (val, "symbol");
+       else if (val->type == UCL_OBJECT) {
+               elt = ucl_obj_get_key (val, "symbol");
                if (elt != NULL) {
                        map =  memory_pool_alloc (fuzzy_module_ctx->fuzzy_pool, sizeof (struct fuzzy_mapping));
-                       map->symbol = rspamd_cl_obj_tostring (elt);
-                       elt = rspamd_cl_obj_get_key (val, "flag");
-                       if (elt != NULL && rspamd_cl_obj_toint_safe (elt, &map->fuzzy_flag)) {
-                               elt = rspamd_cl_obj_get_key (val, "weight");
+                       map->symbol = ucl_obj_tostring (elt);
+                       elt = ucl_obj_get_key (val, "flag");
+                       if (elt != NULL && ucl_obj_toint_safe (elt, &map->fuzzy_flag)) {
+                               elt = ucl_obj_get_key (val, "weight");
                                if (elt != NULL) {
-                                       map->weight = rspamd_cl_obj_todouble (elt);
+                                       map->weight = ucl_obj_todouble (elt);
                                }
                                else {
                                        map->weight = fuzzy_module_ctx->max_score;
@@ -377,64 +377,64 @@ fuzzy_check_module_init (struct config_file *cfg, struct module_ctx **ctx)
 gint
 fuzzy_check_module_config (struct config_file *cfg)
 {
-       rspamd_cl_object_t             *value, *cur;
+       ucl_object_t             *value, *cur;
        gint                            res = TRUE;
 
        if ((value = get_module_opt (cfg, "fuzzy_check", "symbol")) != NULL) {
-               fuzzy_module_ctx->symbol = rspamd_cl_obj_tostring (value);
+               fuzzy_module_ctx->symbol = ucl_obj_tostring (value);
        }
        else {
                fuzzy_module_ctx->symbol = DEFAULT_SYMBOL;
        }
        if ((value = get_module_opt (cfg, "fuzzy_check", "max_score")) != NULL) {
-               fuzzy_module_ctx->max_score = rspamd_cl_obj_todouble (value);
+               fuzzy_module_ctx->max_score = ucl_obj_todouble (value);
        }
        else {
                fuzzy_module_ctx->max_score = 0.;
        }
 
        if ((value = get_module_opt (cfg, "fuzzy_check", "min_length")) != NULL) {
-               fuzzy_module_ctx->min_hash_len = rspamd_cl_obj_toint (value);
+               fuzzy_module_ctx->min_hash_len = ucl_obj_toint (value);
        }
        else {
                fuzzy_module_ctx->min_hash_len = 0;
        }
        if ((value = get_module_opt (cfg, "fuzzy_check", "min_bytes")) != NULL) {
-               fuzzy_module_ctx->min_bytes = rspamd_cl_obj_toint (value);
+               fuzzy_module_ctx->min_bytes = ucl_obj_toint (value);
        }
        else {
                fuzzy_module_ctx->min_bytes = 0;
        }
        if ((value = get_module_opt (cfg, "fuzzy_check", "min_height")) != NULL) {
-               fuzzy_module_ctx->min_height = rspamd_cl_obj_toint (value);
+               fuzzy_module_ctx->min_height = ucl_obj_toint (value);
        }
        else {
                fuzzy_module_ctx->min_height = 0;
        }
        if ((value = get_module_opt (cfg, "fuzzy_check", "min_width")) != NULL) {
-               fuzzy_module_ctx->min_width = rspamd_cl_obj_toint (value);
+               fuzzy_module_ctx->min_width = ucl_obj_toint (value);
        }
        else {
                fuzzy_module_ctx->min_width = 0;
        }
        if ((value = get_module_opt (cfg, "fuzzy_check", "timeout")) != NULL) {
-               fuzzy_module_ctx->io_timeout = rspamd_cl_obj_todouble (value) * 1000;
+               fuzzy_module_ctx->io_timeout = ucl_obj_todouble (value) * 1000;
        }
        else {
                fuzzy_module_ctx->io_timeout = DEFAULT_IO_TIMEOUT;
        }
        if ((value = get_module_opt (cfg, "fuzzy_check", "mime_types")) != NULL) {
                LL_FOREACH (value, cur) {
-                       fuzzy_module_ctx->mime_types = parse_mime_types (rspamd_cl_obj_tostring (cur));
+                       fuzzy_module_ctx->mime_types = parse_mime_types (ucl_obj_tostring (cur));
                }
        }
 
        if ((value = get_module_opt (cfg, "fuzzy_check", "whitelist")) != NULL) {
                fuzzy_module_ctx->whitelist = radix_tree_create ();
-               if (!add_map (cfg, rspamd_cl_obj_tostring (value),
+               if (!add_map (cfg, ucl_obj_tostring (value),
                                "Fuzzy whitelist", read_radix_list, fin_radix_list,
                                (void **)&fuzzy_module_ctx->whitelist)) {
-                       msg_err ("cannot add whitelist '%s'", rspamd_cl_obj_tostring (value));
+                       msg_err ("cannot add whitelist '%s'", ucl_obj_tostring (value));
                }
        }
        else {
@@ -443,7 +443,7 @@ fuzzy_check_module_config (struct config_file *cfg)
 
        if ((value = get_module_opt (cfg, "fuzzy_check", "servers")) != NULL) {
                LL_FOREACH (value, cur) {
-                       parse_servers_string (rspamd_cl_obj_tostring (cur));
+                       parse_servers_string (ucl_obj_tostring (cur));
                }
        }
        if ((value = get_module_opt (cfg, "fuzzy_check", "fuzzy_map")) != NULL) {
index f3eadfa4543dc0088f706a6c6ba5b53ea6a9702c..a1c4fa04965c1ba6684069cdb33609d1a112e216 100644 (file)
@@ -44,7 +44,7 @@
 
 struct regexp_module_item {
        struct expression               *expr;
-       gchar                           *symbol;
+       const gchar                           *symbol;
        guint32                         avg_time;
        gpointer                        lua_function;
 };
@@ -574,12 +574,12 @@ gint
 regexp_module_config (struct config_file *cfg)
 {
        struct regexp_module_item      *cur_item;
-       rspamd_cl_object_t              *sec, *value, *tmp;
+       ucl_object_t              *sec, *value, *tmp;
        gint                            res = TRUE;
        struct regexp_json_buf         *jb, **pjb;
 
 
-       HASH_FIND_STR (cfg->rcl_obj, "regexp", sec);
+       sec = ucl_object_find_key (cfg->rcl_obj, "regexp");
        if (sec == NULL) {
                msg_err ("regexp module enabled, but no rules are defined");
                return TRUE;
@@ -590,44 +590,44 @@ regexp_module_config (struct config_file *cfg)
        regexp_module_ctx->workers = NULL;
 
        HASH_ITER (hh, sec, value, tmp) {
-               if (g_ascii_strncasecmp (value->key, "autolearn", sizeof ("autolearn") - 1) == 0) {
-                       parse_autolearn_param (value->key, rspamd_cl_obj_tostring (value), cfg);
+               if (g_ascii_strncasecmp (ucl_object_key (value), "autolearn", sizeof ("autolearn") - 1) == 0) {
+                       parse_autolearn_param (ucl_object_key (value), ucl_obj_tostring (value), cfg);
                }
-               else if (g_ascii_strncasecmp (value->key, "dynamic_rules", sizeof ("dynamic_rules") - 1) == 0) {
+               else if (g_ascii_strncasecmp (ucl_object_key (value), "dynamic_rules", sizeof ("dynamic_rules") - 1) == 0) {
                        jb = g_malloc (sizeof (struct regexp_json_buf));
                        pjb = g_malloc (sizeof (struct regexp_json_buf *));
                        jb->buf = NULL;
                        jb->cfg = cfg;
                        *pjb = jb;
-                       if (!add_map (cfg, rspamd_cl_obj_tostring (value),
+                       if (!add_map (cfg, ucl_obj_tostring (value),
                                        "Dynamic regexp rules", json_regexp_read_cb, json_regexp_fin_cb,
                                        (void **)pjb)) {
-                               msg_err ("cannot add map %s", rspamd_cl_obj_tostring (value));
+                               msg_err ("cannot add map %s", ucl_obj_tostring (value));
                        }
                }
-               else if (g_ascii_strncasecmp (value->key, "max_size", sizeof ("max_size") - 1) == 0) {
-                       regexp_module_ctx->max_size = rspamd_cl_obj_toint (value);
+               else if (g_ascii_strncasecmp (ucl_object_key (value), "max_size", sizeof ("max_size") - 1) == 0) {
+                       regexp_module_ctx->max_size = ucl_obj_toint (value);
                }
-               else if (g_ascii_strncasecmp (value->key, "max_threads", sizeof ("max_threads") - 1) == 0) {
-                       regexp_module_ctx->max_threads = rspamd_cl_obj_toint (value);
+               else if (g_ascii_strncasecmp (ucl_object_key (value), "max_threads", sizeof ("max_threads") - 1) == 0) {
+                       regexp_module_ctx->max_threads = ucl_obj_toint (value);
                }
-               else if (value->type == RSPAMD_CL_STRING) {
+               else if (value->type == UCL_STRING) {
                        cur_item = memory_pool_alloc0 (regexp_module_ctx->regexp_pool, sizeof (struct regexp_module_item));
-                       cur_item->symbol = value->key;
-                       if (!read_regexp_expression (regexp_module_ctx->regexp_pool, cur_item, value->key,
-                                       rspamd_cl_obj_tostring (value), cfg->raw_mode)) {
+                       cur_item->symbol = ucl_object_key (value);
+                       if (!read_regexp_expression (regexp_module_ctx->regexp_pool, cur_item, ucl_object_key (value),
+                                       ucl_obj_tostring (value), cfg->raw_mode)) {
                                res = FALSE;
                        }
                        register_symbol (&cfg->cache, cur_item->symbol, 1, process_regexp_item, cur_item);
                }
-               else if (value->type == RSPAMD_CL_USERDATA) {
+               else if (value->type == UCL_USERDATA) {
                        cur_item = memory_pool_alloc0 (regexp_module_ctx->regexp_pool, sizeof (struct regexp_module_item));
-                       cur_item->symbol = value->key;
+                       cur_item->symbol = ucl_object_key (value);
                        cur_item->lua_function = value->value.ud;
                        register_symbol (&cfg->cache, cur_item->symbol, 1, process_regexp_item, cur_item);
                }
                else {
-                       msg_warn ("unknown type of attribute %s for regexp module", value->key);
+                       msg_warn ("unknown type of attribute %s for regexp module", ucl_object_key (value));
                }
        }
 
@@ -1112,7 +1112,7 @@ optimize_regexp_expression (struct expression **e, GQueue * stack, gboolean res)
 }
 
 static                          gboolean
-process_regexp_expression (struct expression *expr, gchar *symbol, struct worker_task *task,
+process_regexp_expression (struct expression *expr, const gchar *symbol, struct worker_task *task,
                const gchar *additional, struct lua_locked_state *nL)
 {
        GQueue                         *stack;
index 25419096163a7836f65dbe2f3cf5132c9e1fa3c8..cf1fbb1807381ba3ffd2a614416f9e31295b4be9 100644 (file)
@@ -101,44 +101,44 @@ spf_module_init (struct config_file *cfg, struct module_ctx **ctx)
 gint
 spf_module_config (struct config_file *cfg)
 {
-       rspamd_cl_object_t             *value;
+       ucl_object_t             *value;
        gint                            res = TRUE;
        guint                           cache_size, cache_expire;
 
        spf_module_ctx->whitelist_ip = radix_tree_create ();
        
        if ((value = get_module_opt (cfg, "spf", "symbol_fail")) != NULL) {
-               spf_module_ctx->symbol_fail = rspamd_cl_obj_tostring (value);
+               spf_module_ctx->symbol_fail = ucl_obj_tostring (value);
        }
        else {
                spf_module_ctx->symbol_fail = DEFAULT_SYMBOL_FAIL;
        }
        if ((value = get_module_opt (cfg, "spf", "symbol_softfail")) != NULL) {
-               spf_module_ctx->symbol_softfail = rspamd_cl_obj_tostring (value);
+               spf_module_ctx->symbol_softfail = ucl_obj_tostring (value);
        }
        else {
                spf_module_ctx->symbol_softfail = DEFAULT_SYMBOL_SOFTFAIL;
        }
        if ((value = get_module_opt (cfg, "spf", "symbol_allow")) != NULL) {
-               spf_module_ctx->symbol_allow = rspamd_cl_obj_tostring (value);
+               spf_module_ctx->symbol_allow = ucl_obj_tostring (value);
        }
        else {
                spf_module_ctx->symbol_allow = DEFAULT_SYMBOL_ALLOW;
        }
        if ((value = get_module_opt (cfg, "spf", "spf_cache_size")) != NULL) {
-               cache_size = rspamd_cl_obj_toint (value);
+               cache_size = ucl_obj_toint (value);
        }
        else {
                cache_size = DEFAULT_CACHE_SIZE;
        }
        if ((value = get_module_opt (cfg, "spf", "spf_cache_expire")) != NULL) {
-               cache_expire = rspamd_cl_obj_toint (value);
+               cache_expire = ucl_obj_toint (value);
        }
        else {
                cache_expire = DEFAULT_CACHE_MAXAGE;
        }
        if ((value = get_module_opt (cfg, "spf", "whitelist")) != NULL) {
-               if (! add_map (cfg, rspamd_cl_obj_tostring (value),
+               if (! add_map (cfg, ucl_obj_tostring (value),
                                "SPF whitelist", read_radix_list, fin_radix_list,
                                (void **)&spf_module_ctx->whitelist_ip)) {
                        msg_warn ("cannot load whitelist from %s", value);
index 8b5121aa885870679848be7361042444b72117bd..5d49eccec3411fdecd6cb142d173c2a58b0ea2ee 100644 (file)
@@ -303,7 +303,7 @@ surbl_module_config (struct config_file *cfg)
        struct suffix_item             *new_suffix, *cur_suffix = NULL;
        struct surbl_bit_item          *new_bit;
 
-       rspamd_cl_object_t              *value, *cur, *cur_rule, *tmp, *cur_bit;
+       ucl_object_t              *value, *cur, *cur_rule, *tmp, *cur_bit;
        const gchar                    *redir_val;
        guint32                         bit;
        gint                            i, idx;
@@ -318,7 +318,7 @@ surbl_module_config (struct config_file *cfg)
                                                                i * sizeof (struct redirector_upstream));
                idx = 0;
                LL_FOREACH (value, cur) {
-                       redir_val = rspamd_cl_obj_tostring (cur);
+                       redir_val = ucl_obj_tostring (cur);
                        surbl_module_ctx->redirectors[idx].up.priority = 100;
                        if (! parse_host_port_priority (surbl_module_ctx->surbl_pool,
                                        redir_val, &surbl_module_ctx->redirectors[idx].addr,
@@ -340,100 +340,100 @@ surbl_module_config (struct config_file *cfg)
                surbl_module_ctx->use_redirector = (surbl_module_ctx->redirectors_number != 0);
        }
        if ((value = get_module_opt (cfg, "surbl", "redirector_symbol")) != NULL) {
-               surbl_module_ctx->redirector_symbol = rspamd_cl_obj_tostring (value);
+               surbl_module_ctx->redirector_symbol = ucl_obj_tostring (value);
                register_virtual_symbol (&cfg->cache, surbl_module_ctx->redirector_symbol, 1.0);
        }
        else {
                surbl_module_ctx->redirector_symbol = NULL;
        }
        if ((value = get_module_opt (cfg, "surbl", "weight")) != NULL) {
-               surbl_module_ctx->weight = rspamd_cl_obj_toint (value);
+               surbl_module_ctx->weight = ucl_obj_toint (value);
        }
        else {
                surbl_module_ctx->weight = DEFAULT_SURBL_WEIGHT;
        }
        if ((value = get_module_opt (cfg, "surbl", "url_expire")) != NULL) {
-               surbl_module_ctx->url_expire = rspamd_cl_obj_todouble (value);
+               surbl_module_ctx->url_expire = ucl_obj_todouble (value);
        }
        else {
                surbl_module_ctx->url_expire = DEFAULT_SURBL_URL_EXPIRE;
        }
        if ((value = get_module_opt (cfg, "surbl", "redirector_connect_timeout")) != NULL) {
-               surbl_module_ctx->connect_timeout = rspamd_cl_obj_todouble (value);
+               surbl_module_ctx->connect_timeout = ucl_obj_todouble (value);
        }
        else {
                surbl_module_ctx->connect_timeout = DEFAULT_REDIRECTOR_CONNECT_TIMEOUT;
        }
        if ((value = get_module_opt (cfg, "surbl", "redirector_read_timeout")) != NULL) {
-               surbl_module_ctx->read_timeout = rspamd_cl_obj_todouble (value);
+               surbl_module_ctx->read_timeout = ucl_obj_todouble (value);
        }
        else {
                surbl_module_ctx->read_timeout = DEFAULT_REDIRECTOR_READ_TIMEOUT;
        }
        if ((value = get_module_opt (cfg, "surbl", "redirector_hosts_map")) != NULL) {
-               add_map (cfg, rspamd_cl_obj_tostring (value),
+               add_map (cfg, ucl_obj_tostring (value),
                                "SURBL redirectors list", read_redirectors_list, fin_redirectors_list,
                                (void **)&surbl_module_ctx->redirector_hosts);
        }
 
        if ((value = get_module_opt (cfg, "surbl", "max_urls")) != NULL) {
-               surbl_module_ctx->max_urls = rspamd_cl_obj_toint (value);
+               surbl_module_ctx->max_urls = ucl_obj_toint (value);
        }
        else {
                surbl_module_ctx->max_urls = DEFAULT_SURBL_MAX_URLS;
        }
        if ((value = get_module_opt (cfg, "surbl", "exceptions")) != NULL) {
-               if (add_map (cfg, rspamd_cl_obj_tostring (value),
+               if (add_map (cfg, ucl_obj_tostring (value),
                                "SURBL exceptions list", read_exceptions_list, fin_exceptions_list,
                                (void **)&surbl_module_ctx->exceptions)) {
                        surbl_module_ctx->tld2_file = memory_pool_strdup (surbl_module_ctx->surbl_pool,
-                                       rspamd_cl_obj_tostring (value) + sizeof ("file://") - 1);
+                                       ucl_obj_tostring (value) + sizeof ("file://") - 1);
                }
        }
        if ((value = get_module_opt (cfg, "surbl", "whitelist")) != NULL) {
-               if (add_map (cfg, rspamd_cl_obj_tostring (value),
+               if (add_map (cfg, ucl_obj_tostring (value),
                                "SURBL whitelist", read_host_list, fin_host_list,
                                (void **)&surbl_module_ctx->whitelist)) {
                        surbl_module_ctx->whitelist_file = memory_pool_strdup (surbl_module_ctx->surbl_pool,
-                                       rspamd_cl_obj_tostring (value) + sizeof ("file://") - 1);
+                                       ucl_obj_tostring (value) + sizeof ("file://") - 1);
                }
        }
 
        value = get_module_opt (cfg, "surbl", "rule");
-       if (value != NULL && value->type == RSPAMD_CL_OBJECT) {
+       if (value != NULL && value->type == UCL_OBJECT) {
                LL_FOREACH (value, cur_rule) {
-                       cur = rspamd_cl_obj_get_key (cur_rule, "suffix");
+                       cur = ucl_obj_get_key (cur_rule, "suffix");
                        if (cur == NULL) {
                                msg_err ("surbl rule must have explicit symbol definition");
                                continue;
                        }
                        new_suffix = memory_pool_alloc (surbl_module_ctx->surbl_pool, sizeof (struct suffix_item));
                        new_suffix->suffix = memory_pool_strdup (surbl_module_ctx->surbl_pool,
-                                       rspamd_cl_obj_tostring (cur));
+                                       ucl_obj_tostring (cur));
                        new_suffix->options = 0;
                        new_suffix->bits = NULL;
 
-                       cur = rspamd_cl_obj_get_key (cur_rule, "symbol");
+                       cur = ucl_obj_get_key (cur_rule, "symbol");
                        if (cur == NULL) {
                                msg_warn ("surbl rule for suffix %s lacks symbol, using %s as symbol", new_suffix->suffix,
                                                DEFAULT_SURBL_SYMBOL);
                                new_suffix->suffix = memory_pool_strdup (surbl_module_ctx->surbl_pool,
                                                DEFAULT_SURBL_SYMBOL);
                        }
-                       cur = rspamd_cl_obj_get_key (cur_rule, "options");
-                       if (cur != NULL && cur->type == RSPAMD_CL_STRING) {
-                               if (strstr (rspamd_cl_obj_tostring (cur), "noip") != NULL) {
+                       cur = ucl_obj_get_key (cur_rule, "options");
+                       if (cur != NULL && cur->type == UCL_STRING) {
+                               if (strstr (ucl_obj_tostring (cur), "noip") != NULL) {
                                        new_suffix->options |= SURBL_OPTION_NOIP;
                                }
                        }
-                       cur = rspamd_cl_obj_get_key (cur_rule, "bits");
-                       if (cur != NULL && cur->type == RSPAMD_CL_OBJECT) {
+                       cur = ucl_obj_get_key (cur_rule, "bits");
+                       if (cur != NULL && cur->type == UCL_OBJECT) {
                                HASH_ITER (hh, cur->value.ov, cur_bit, tmp) {
-                                       if (cur_bit->key != NULL && cur_bit->type == RSPAMD_CL_INT) {
-                                               bit = rspamd_cl_obj_toint (cur_bit);
+                                       if (ucl_object_key (cur_bit) != NULL && cur_bit->type == UCL_INT) {
+                                               bit = ucl_obj_toint (cur_bit);
                                                new_bit = memory_pool_alloc (surbl_module_ctx->surbl_pool, sizeof (struct surbl_bit_item));
                                                new_bit->bit = bit;
-                                               new_bit->symbol = memory_pool_strdup (surbl_module_ctx->surbl_pool, cur->key);
+                                               new_bit->symbol = memory_pool_strdup (surbl_module_ctx->surbl_pool, ucl_object_key (cur_bit));
                                                msg_debug ("add new bit suffix: %d with symbol: %s", (gint)new_bit->bit, new_bit->symbol);
                                                new_suffix->bits = g_list_prepend (new_suffix->bits, new_bit);
                                        }
diff --git a/src/rcl/CMakeLists.txt b/src/rcl/CMakeLists.txt
deleted file mode 100644 (file)
index 134db72..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-# Lua support makefile
-SET(RCLSRC                       rcl_util.c
-                                         rcl_parser.c
-                                         rcl_emitter.c)
-
-ADD_LIBRARY(rspamd-rcl ${LINK_TYPE} ${RCLSRC})
-SET_TARGET_PROPERTIES(rspamd-rcl PROPERTIES VERSION ${RSPAMD_VERSION})
-SET_TARGET_PROPERTIES(rspamd-rcl PROPERTIES COMPILE_FLAGS "-DRSPAMD_LIB")
-IF(HAVE_FETCH_H)
-       TARGET_LINK_LIBRARIES(rspamd-rcl fetch)
-ELSE(HAVE_FETCH_H)
-       IF(CURL_FOUND)
-               TARGET_LINK_LIBRARIES(rspamd-rcl ${CURL_LIBRARIES})
-       ENDIF(CURL_FOUND)
-ENDIF(HAVE_FETCH_H)
-IF(NO_SHARED MATCHES "OFF")
-       INSTALL(TARGETS rspamd-rcl 
-       LIBRARY DESTINATION ${LIBDIR} 
-       PUBLIC_HEADER DESTINATION ${INCLUDEDIR})
-ENDIF(NO_SHARED MATCHES "OFF")
\ No newline at end of file
diff --git a/src/rcl/rcl.h b/src/rcl/rcl.h
deleted file mode 100644 (file)
index 48f0554..0000000
+++ /dev/null
@@ -1,391 +0,0 @@
-/* Copyright (c) 2013, 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 ''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.
- */
-
-#ifndef RCL_H_
-#define RCL_H_
-
-#include "config.h"
-#include "uthash.h"
-
-/**
- * @file rcl.h
- * RCL is an rspamd configuration language, which is a form of
- * JSON with less strict rules that make it more comfortable for
- * using as a configuration language
- */
-
-enum rspamd_cl_error {
-       RSPAMD_CL_EOK = 0,
-       RSPAMD_CL_ESYNTAX,
-       RSPAMD_CL_EIO,
-       RSPAMD_CL_ESTATE,
-       RSPAMD_CL_ENESTED,
-       RSPAMD_CL_EMACRO,
-       RSPAMD_CL_ERECURSION,
-       RSPAMD_CL_EINTERNAL,
-       RSPAMD_CL_ESSL
-};
-
-enum rspamd_cl_type {
-       RSPAMD_CL_OBJECT = 0,
-       RSPAMD_CL_ARRAY,
-       RSPAMD_CL_INT,
-       RSPAMD_CL_FLOAT,
-       RSPAMD_CL_STRING,
-       RSPAMD_CL_BOOLEAN,
-       RSPAMD_CL_TIME,
-       RSPAMD_CL_USERDATA
-};
-
-enum rspamd_cl_emitter {
-       RSPAMD_CL_EMIT_JSON = 0,
-       RSPAMD_CL_EMIT_JSON_COMPACT,
-       RSPAMD_CL_EMIT_CONFIG
-};
-
-enum rspamd_cl_flags {
-       RSPAMD_CL_FLAG_KEY_LOWERCASE = 0x1
-};
-
-typedef struct rspamd_cl_object_s {
-       gchar *key;                                                             /**< the key of an object */
-       union {
-               gint64 iv;                                                      /**< int value of an object */
-               gchar *sv;                                                      /**< string value of an object */
-               gdouble dv;                                                     /**< double value of an object */
-               struct rspamd_cl_object_s *ov;          /**< array or hash                      */
-               gpointer ud;                                            /**< opaque user data           */
-       } value;
-       enum rspamd_cl_type type;                               /**< real type                          */
-       gint ref;                                                               /**< reference count            */
-       struct rspamd_cl_object_s *next;                /**< array handle                       */
-       UT_hash_handle hh;                                              /**< hash handle                        */
-} rspamd_cl_object_t;
-
-
-/**
- * Creates a new object
- * @return new object
- */
-static inline rspamd_cl_object_t *
-rspamd_cl_object_new (void)
-{
-       rspamd_cl_object_t *new;
-       new = g_slice_alloc0 (sizeof (rspamd_cl_object_t));
-       if (new != NULL) {
-               new->ref = 1;
-       }
-       return new;
-}
-
-
-/**
- * Converts an object to double value
- * @param obj CL object
- * @param target target double variable
- * @return TRUE if conversion was successful
- */
-static inline gboolean
-rspamd_cl_obj_todouble_safe (rspamd_cl_object_t *obj, gdouble *target)
-{
-       if (obj == NULL) {
-               return FALSE;
-       }
-       switch (obj->type) {
-       case RSPAMD_CL_INT:
-               *target = obj->value.iv; /* Probaly could cause overflow */
-               break;
-       case RSPAMD_CL_FLOAT:
-       case RSPAMD_CL_TIME:
-               *target = obj->value.dv;
-               break;
-       default:
-               return FALSE;
-       }
-
-       return TRUE;
-}
-
-/**
- * Unsafe version of \ref rspamd_cl_obj_todouble_safe
- * @param obj CL object
- * @return double value
- */
-static inline gdouble
-rspamd_cl_obj_todouble (rspamd_cl_object_t *obj)
-{
-       gdouble result = 0.;
-
-       rspamd_cl_obj_todouble_safe (obj, &result);
-       return result;
-}
-
-/**
- * Converts an object to integer value
- * @param obj CL object
- * @param target target integer variable
- * @return TRUE if conversion was successful
- */
-static inline gboolean
-rspamd_cl_obj_toint_safe (rspamd_cl_object_t *obj, gint64 *target)
-{
-       if (obj == NULL) {
-               return FALSE;
-       }
-       switch (obj->type) {
-       case RSPAMD_CL_INT:
-               *target = obj->value.iv;
-               break;
-       case RSPAMD_CL_FLOAT:
-       case RSPAMD_CL_TIME:
-               *target = obj->value.dv; /* Loosing of decimal points */
-               break;
-       default:
-               return FALSE;
-       }
-
-       return TRUE;
-}
-
-/**
- * Unsafe version of \ref rspamd_cl_obj_toint_safe
- * @param obj CL object
- * @return int value
- */
-static inline gint64
-rspamd_cl_obj_toint (rspamd_cl_object_t *obj)
-{
-       gint64 result = 0;
-
-       rspamd_cl_obj_toint_safe (obj, &result);
-       return result;
-}
-
-/**
- * Converts an object to boolean value
- * @param obj CL object
- * @param target target boolean variable
- * @return TRUE if conversion was successful
- */
-static inline gboolean
-rspamd_cl_obj_toboolean_safe (rspamd_cl_object_t *obj, gboolean *target)
-{
-       if (obj == NULL) {
-               return FALSE;
-       }
-       switch (obj->type) {
-       case RSPAMD_CL_BOOLEAN:
-               *target = (obj->value.iv == TRUE);
-               break;
-       default:
-               return FALSE;
-       }
-
-       return TRUE;
-}
-
-/**
- * Unsafe version of \ref rspamd_cl_obj_toboolean_safe
- * @param obj CL object
- * @return boolean value
- */
-static inline gboolean
-rspamd_cl_obj_toboolean (rspamd_cl_object_t *obj)
-{
-       gboolean result = FALSE;
-
-       rspamd_cl_obj_toboolean_safe (obj, &result);
-       return result;
-}
-
-/**
- * Converts an object to string value
- * @param obj CL object
- * @param target target string variable, no need to free value
- * @return TRUE if conversion was successful
- */
-static inline gboolean
-rspamd_cl_obj_tostring_safe (rspamd_cl_object_t *obj, const gchar **target)
-{
-       if (obj == NULL) {
-               return FALSE;
-       }
-       switch (obj->type) {
-       case RSPAMD_CL_STRING:
-               *target = obj->value.sv;
-               break;
-       default:
-               return FALSE;
-       }
-
-       return TRUE;
-}
-
-/**
- * Unsafe version of \ref rspamd_cl_obj_tostring_safe
- * @param obj CL object
- * @return string value
- */
-static inline const gchar *
-rspamd_cl_obj_tostring (rspamd_cl_object_t *obj)
-{
-       const gchar *result = NULL;
-
-       rspamd_cl_obj_tostring_safe (obj, &result);
-       return result;
-}
-
-/**
- * Return object identified by a key in the specified object
- * @param obj object to get a key from (must be of type RSPAMD_CL_OBJECT)
- * @param key key to search
- * @return object matched the specified key or NULL if key is not found
- */
-static inline rspamd_cl_object_t *
-rspamd_cl_obj_get_key (rspamd_cl_object_t *obj, const gchar *key)
-{
-       gsize keylen;
-       rspamd_cl_object_t *ret;
-
-       if (obj == NULL || obj->type != RSPAMD_CL_OBJECT || key == NULL) {
-               return NULL;
-       }
-
-       keylen = strlen (key);
-       HASH_FIND(hh, obj->value.ov, key, keylen, ret);
-
-       return ret;
-}
-
-/**
- * Macro handler for a parser
- * @param data the content of macro
- * @param len the length of content
- * @param ud opaque user data
- * @param err error pointer
- * @return TRUE if macro has been parsed
- */
-typedef gboolean (*rspamd_cl_macro_handler) (const guchar *data, gsize len, gpointer ud, GError **err);
-
-/* Opaque parser */
-struct rspamd_cl_parser;
-
-/**
- * Creates new parser object
- * @param pool pool to allocate memory from
- * @return new parser object
- */
-struct rspamd_cl_parser* rspamd_cl_parser_new (gint flags);
-
-/**
- * Register new handler for a macro
- * @param parser parser object
- * @param macro macro name (without leading dot)
- * @param handler handler (it is called immediately after macro is parsed)
- * @param ud opaque user data for a handler
- */
-void rspamd_cl_parser_register_macro (struct rspamd_cl_parser *parser, const gchar *macro,
-               rspamd_cl_macro_handler handler, gpointer ud);
-
-/**
- * Load new chunk to a parser
- * @param parser parser structure
- * @param data the pointer to the beginning of a chunk
- * @param len the length of a chunk
- * @param err if *err is NULL it is set to parser error
- * @return TRUE if chunk has been added and FALSE in case of error
- */
-gboolean rspamd_cl_parser_add_chunk (struct rspamd_cl_parser *parser, const guchar *data,
-               gsize len, GError **err);
-
-/**
- * Load and add data from a file
- * @param parser parser structure
- * @param filename the name of file
- * @param err if *err is NULL it is set to parser error
- * @return TRUE if chunk has been added and FALSE in case of error
- */
-gboolean rspamd_cl_parser_add_file (struct rspamd_cl_parser *parser, const gchar *filename,
-               GError **err);
-
-/**
- * Get a top object for a parser
- * @param parser parser structure
- * @param err if *err is NULL it is set to parser error
- * @return top parser object or NULL
- */
-rspamd_cl_object_t* rspamd_cl_parser_get_object (struct rspamd_cl_parser *parser, GError **err);
-
-/**
- * Free cl parser object
- * @param parser parser object
- */
-void rspamd_cl_parser_free (struct rspamd_cl_parser *parser);
-
-/**
- * Free cl object
- * @param obj cl object to free
- */
-void rspamd_cl_obj_free (rspamd_cl_object_t *obj);
-
-/**
- * Icrease reference count for an object
- * @param obj object to ref
- */
-static inline rspamd_cl_object_t *
-rspamd_cl_obj_ref (rspamd_cl_object_t *obj) {
-       obj->ref ++;
-       return obj;
-}
-
-/**
- * Decrease reference count for an object
- * @param obj object to unref
- */
-static inline void
-rspamd_cl_obj_unref (rspamd_cl_object_t *obj) {
-       if (--obj->ref <= 0) {
-               rspamd_cl_obj_free (obj);
-       }
-}
-
-/**
- * Emit object to a string
- * @param obj object
- * @param emit_type if type is RSPAMD_CL_EMIT_JSON then emit json, if type is
- * RSPAMD_CL_EMIT_CONFIG then emit config like object
- * @return dump of an object (must be freed after using) or NULL in case of error
- */
-guchar *rspamd_cl_object_emit (rspamd_cl_object_t *obj, enum rspamd_cl_emitter emit_type);
-
-/**
- * Add new public key to parser for signatures check
- * @param parser parser object
- * @param key PEM representation of a key
- * @param len length of the key
- * @param err if *err is NULL it is set to parser error
- * @return TRUE if a key has been successfully added
- */
-gboolean rspamd_cl_pubkey_add (struct rspamd_cl_parser *parser, const guchar *key, gsize len, GError **err);
-
-#endif /* RCL_H_ */
diff --git a/src/rcl/rcl_chartable.h b/src/rcl/rcl_chartable.h
deleted file mode 100644 (file)
index 2c02c95..0000000
+++ /dev/null
@@ -1,256 +0,0 @@
-/* Copyright (c) 2013, 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 ''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.
- */
-
-#ifndef RCL_CHARTABLE_H_
-#define RCL_CHARTABLE_H_
-
-#include "rcl_internal.h"
-
-static const unsigned char rcl_chartable[255] = {
-RCL_CHARACTER_VALUE_END, RCL_CHARACTER_DENIED, RCL_CHARACTER_DENIED,
-RCL_CHARACTER_DENIED, RCL_CHARACTER_DENIED, RCL_CHARACTER_DENIED,
-RCL_CHARACTER_DENIED, RCL_CHARACTER_DENIED, RCL_CHARACTER_DENIED,
-RCL_CHARACTER_WHITESPACE|RCL_CHARACTER_WHITESPACE_UNSAFE,
-RCL_CHARACTER_WHITESPACE_UNSAFE|RCL_CHARACTER_VALUE_END,
-RCL_CHARACTER_WHITESPACE_UNSAFE, RCL_CHARACTER_WHITESPACE_UNSAFE,
-RCL_CHARACTER_WHITESPACE_UNSAFE|RCL_CHARACTER_VALUE_END, RCL_CHARACTER_DENIED,
-RCL_CHARACTER_DENIED, RCL_CHARACTER_DENIED, RCL_CHARACTER_DENIED,
-RCL_CHARACTER_DENIED, RCL_CHARACTER_DENIED, RCL_CHARACTER_DENIED,
-RCL_CHARACTER_DENIED, RCL_CHARACTER_DENIED, RCL_CHARACTER_DENIED,
-RCL_CHARACTER_DENIED, RCL_CHARACTER_DENIED, RCL_CHARACTER_DENIED,
-RCL_CHARACTER_DENIED, RCL_CHARACTER_DENIED, RCL_CHARACTER_DENIED,
-RCL_CHARACTER_DENIED, RCL_CHARACTER_DENIED,
-RCL_CHARACTER_WHITESPACE|RCL_CHARACTER_WHITESPACE_UNSAFE|RCL_CHARACTER_VALUE_STR /*   */,
-RCL_CHARACTER_VALUE_STR /* ! */, RCL_CHARACTER_VALUE_STR /* " */,
-RCL_CHARACTER_VALUE_END /* # */, RCL_CHARACTER_VALUE_STR /* $ */,
-RCL_CHARACTER_VALUE_STR /* % */, RCL_CHARACTER_VALUE_STR /* & */,
-RCL_CHARACTER_VALUE_STR /* ' */, RCL_CHARACTER_VALUE_STR /* ( */,
-RCL_CHARACTER_VALUE_STR /* ) */, RCL_CHARACTER_VALUE_STR /* * */,
-RCL_CHARACTER_VALUE_STR|RCL_CHARACTER_VALUE_DIGIT /* + */,
-RCL_CHARACTER_VALUE_END /* , */,
-RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR|RCL_CHARACTER_VALUE_DIGIT_START|RCL_CHARACTER_VALUE_DIGIT /* - */,
-RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR|RCL_CHARACTER_VALUE_DIGIT /* . */,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR /* / */,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR|RCL_CHARACTER_VALUE_DIGIT_START|RCL_CHARACTER_VALUE_DIGIT /* 0 */,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR|RCL_CHARACTER_VALUE_DIGIT_START|RCL_CHARACTER_VALUE_DIGIT /* 1 */,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR|RCL_CHARACTER_VALUE_DIGIT_START|RCL_CHARACTER_VALUE_DIGIT /* 2 */,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR|RCL_CHARACTER_VALUE_DIGIT_START|RCL_CHARACTER_VALUE_DIGIT /* 3 */,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR|RCL_CHARACTER_VALUE_DIGIT_START|RCL_CHARACTER_VALUE_DIGIT /* 4 */,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR|RCL_CHARACTER_VALUE_DIGIT_START|RCL_CHARACTER_VALUE_DIGIT /* 5 */,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR|RCL_CHARACTER_VALUE_DIGIT_START|RCL_CHARACTER_VALUE_DIGIT /* 6 */,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR|RCL_CHARACTER_VALUE_DIGIT_START|RCL_CHARACTER_VALUE_DIGIT /* 7 */,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR|RCL_CHARACTER_VALUE_DIGIT_START|RCL_CHARACTER_VALUE_DIGIT /* 8 */,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR|RCL_CHARACTER_VALUE_DIGIT_START|RCL_CHARACTER_VALUE_DIGIT /* 9 */,
-RCL_CHARACTER_VALUE_STR /* : */, RCL_CHARACTER_VALUE_END /* ; */,
-RCL_CHARACTER_VALUE_STR /* < */, RCL_CHARACTER_VALUE_STR /* = */,
-RCL_CHARACTER_VALUE_STR /* > */, RCL_CHARACTER_VALUE_STR /* ? */,
-RCL_CHARACTER_VALUE_STR /* @ */,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR|RCL_CHARACTER_VALUE_DIGIT /* A */,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR|RCL_CHARACTER_VALUE_DIGIT /* B */,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR|RCL_CHARACTER_VALUE_DIGIT /* C */,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR|RCL_CHARACTER_VALUE_DIGIT /* D */,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR|RCL_CHARACTER_VALUE_DIGIT /* E */,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR|RCL_CHARACTER_VALUE_DIGIT /* F */,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR|RCL_CHARACTER_VALUE_DIGIT /* G */,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR|RCL_CHARACTER_VALUE_DIGIT /* H */,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR|RCL_CHARACTER_VALUE_DIGIT /* I */,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR|RCL_CHARACTER_VALUE_DIGIT /* J */,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR|RCL_CHARACTER_VALUE_DIGIT /* K */,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR|RCL_CHARACTER_VALUE_DIGIT /* L */,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR|RCL_CHARACTER_VALUE_DIGIT /* M */,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR|RCL_CHARACTER_VALUE_DIGIT /* N */,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR|RCL_CHARACTER_VALUE_DIGIT /* O */,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR|RCL_CHARACTER_VALUE_DIGIT /* P */,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR|RCL_CHARACTER_VALUE_DIGIT /* Q */,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR|RCL_CHARACTER_VALUE_DIGIT /* R */,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR|RCL_CHARACTER_VALUE_DIGIT /* S */,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR|RCL_CHARACTER_VALUE_DIGIT /* T */,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR|RCL_CHARACTER_VALUE_DIGIT /* U */,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR|RCL_CHARACTER_VALUE_DIGIT /* V */,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR|RCL_CHARACTER_VALUE_DIGIT /* W */,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR|RCL_CHARACTER_VALUE_DIGIT /* X */,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR|RCL_CHARACTER_VALUE_DIGIT /* Y */,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR|RCL_CHARACTER_VALUE_DIGIT /* Z */,
-RCL_CHARACTER_VALUE_STR /* [ */, RCL_CHARACTER_VALUE_STR /* \ */,
-RCL_CHARACTER_VALUE_END /* ] */, RCL_CHARACTER_VALUE_STR /* ^ */,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR /* _ */,
-RCL_CHARACTER_VALUE_STR /* ` */,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR|RCL_CHARACTER_VALUE_DIGIT /* a */,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR|RCL_CHARACTER_VALUE_DIGIT /* b */,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR|RCL_CHARACTER_VALUE_DIGIT /* c */,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR|RCL_CHARACTER_VALUE_DIGIT /* d */,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR|RCL_CHARACTER_VALUE_DIGIT /* e */,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR|RCL_CHARACTER_VALUE_DIGIT /* f */,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR|RCL_CHARACTER_VALUE_DIGIT /* g */,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR|RCL_CHARACTER_VALUE_DIGIT /* h */,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR|RCL_CHARACTER_VALUE_DIGIT /* i */,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR|RCL_CHARACTER_VALUE_DIGIT /* j */,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR|RCL_CHARACTER_VALUE_DIGIT /* k */,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR|RCL_CHARACTER_VALUE_DIGIT /* l */,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR|RCL_CHARACTER_VALUE_DIGIT /* m */,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR|RCL_CHARACTER_VALUE_DIGIT /* n */,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR|RCL_CHARACTER_VALUE_DIGIT /* o */,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR|RCL_CHARACTER_VALUE_DIGIT /* p */,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR|RCL_CHARACTER_VALUE_DIGIT /* q */,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR|RCL_CHARACTER_VALUE_DIGIT /* r */,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR|RCL_CHARACTER_VALUE_DIGIT /* s */,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR|RCL_CHARACTER_VALUE_DIGIT /* t */,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR|RCL_CHARACTER_VALUE_DIGIT /* u */,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR|RCL_CHARACTER_VALUE_DIGIT /* v */,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR|RCL_CHARACTER_VALUE_DIGIT /* w */,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR|RCL_CHARACTER_VALUE_DIGIT /* x */,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR|RCL_CHARACTER_VALUE_DIGIT /* y */,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR|RCL_CHARACTER_VALUE_DIGIT /* z */,
-RCL_CHARACTER_VALUE_STR /* { */, RCL_CHARACTER_VALUE_STR /* | */,
-RCL_CHARACTER_VALUE_END /* } */, RCL_CHARACTER_VALUE_STR /* ~ */,
-RCL_CHARACTER_DENIED,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR,
-RCL_CHARACTER_KEY_START|RCL_CHARACTER_KEY|RCL_CHARACTER_VALUE_STR
-};
-
-#endif /* RCL_CHARTABLE_H_ */
diff --git a/src/rcl/rcl_emitter.c b/src/rcl/rcl_emitter.c
deleted file mode 100644 (file)
index bf394ee..0000000
+++ /dev/null
@@ -1,451 +0,0 @@
-/* Copyright (c) 2013, 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 ''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.
- */
-
-#include "config.h"
-#include "rcl.h"
-#include "rcl_internal.h"
-
-/**
- * @file rcl_emitter.c
- * Serialise RCL object to the RCL format
- */
-
-
-static void rspamd_cl_elt_write_json (rspamd_cl_object_t *obj, GString *buf, guint tabs,
-               gboolean start_tabs, gboolean compact);
-static void rspamd_cl_obj_write_json (rspamd_cl_object_t *obj, GString *buf, guint tabs,
-               gboolean start_tabs, gboolean compact);
-static void rspamd_cl_elt_write_rcl (rspamd_cl_object_t *obj, GString *buf, guint tabs,
-               gboolean start_tabs, gboolean is_top, gboolean expand_array);
-
-/**
- * Add tabulation to the output buffer
- * @param buf target buffer
- * @param tabs number of tabs to add
- */
-static inline void
-rspamd_cl_add_tabs (GString *buf, guint tabs, gboolean compact)
-{
-       while (!compact && tabs--) {
-               g_string_append_len (buf, "    ", 4);
-       }
-}
-
-/**
- * Serialise string
- * @param str string to emit
- * @param buf target buffer
- */
-static void
-rspamd_cl_elt_string_write_json (const gchar *str, GString *buf)
-{
-       const gchar *p = str;
-
-       g_string_append_c (buf, '"');
-       while (*p != '\0') {
-               switch (*p) {
-               case '\n':
-                       g_string_append_len (buf, "\\n", 2);
-                       break;
-               case '\r':
-                       g_string_append_len (buf, "\\r", 2);
-                       break;
-               case '\b':
-                       g_string_append_len (buf, "\\b", 2);
-                       break;
-               case '\t':
-                       g_string_append_len (buf, "\\t", 2);
-                       break;
-               case '\f':
-                       g_string_append_len (buf, "\\f", 2);
-                       break;
-               case '\\':
-                       g_string_append_len (buf, "\\\\", 2);
-                       break;
-               case '"':
-                       g_string_append_len (buf, "\\\"", 2);
-                       break;
-               default:
-                       g_string_append_c (buf, *p);
-                       break;
-               }
-               p ++;
-       }
-       g_string_append_c (buf, '"');
-}
-
-/**
- * Write a single object to the buffer
- * @param obj object to write
- * @param buf target buffer
- */
-static void
-rspamd_cl_elt_obj_write_json (rspamd_cl_object_t *obj, GString *buf, guint tabs, gboolean start_tabs, gboolean compact)
-{
-       rspamd_cl_object_t *cur, *tmp;
-
-       if (start_tabs) {
-               rspamd_cl_add_tabs (buf, tabs, compact);
-       }
-       if (compact) {
-               g_string_append_c (buf, '{');
-       }
-       else {
-               g_string_append_len (buf, "{\n", 2);
-       }
-       HASH_ITER (hh, obj, cur, tmp) {
-               rspamd_cl_add_tabs (buf, tabs + 1, compact);
-               rspamd_cl_elt_string_write_json (cur->key, buf);
-               if (compact) {
-                       g_string_append_c (buf, ':');
-               }
-               else {
-                       g_string_append_len (buf, ": ", 2);
-               }
-               rspamd_cl_obj_write_json (cur, buf, tabs + 1, FALSE, compact);
-               if (cur->hh.next != NULL) {
-                       if (compact) {
-                               g_string_append_c (buf, ',');
-                       }
-                       else {
-                               g_string_append_len (buf, ",\n", 2);
-                       }
-               }
-               else if (!compact) {
-                       g_string_append_c (buf, '\n');
-               }
-       }
-       rspamd_cl_add_tabs (buf, tabs, compact);
-       g_string_append_c (buf, '}');
-}
-
-/**
- * Write a single array to the buffer
- * @param obj array to write
- * @param buf target buffer
- */
-static void
-rspamd_cl_elt_array_write_json (rspamd_cl_object_t *obj, GString *buf, guint tabs, gboolean start_tabs, gboolean compact)
-{
-       rspamd_cl_object_t *cur = obj;
-
-       if (start_tabs) {
-               rspamd_cl_add_tabs (buf, tabs, compact);
-       }
-       if (compact) {
-               g_string_append_c (buf, '[');
-       }
-       else {
-               g_string_append_len (buf, "[\n", 2);
-       }
-       while (cur) {
-               rspamd_cl_elt_write_json (cur, buf, tabs + 1, TRUE, compact);
-               if (cur->next != NULL) {
-                       if (compact) {
-                               g_string_append_c (buf, ',');
-                       }
-                       else {
-                               g_string_append_len (buf, ",\n", 2);
-                       }
-               }
-               else if (!compact) {
-                       g_string_append_c (buf, '\n');
-               }
-               cur = cur->next;
-       }
-       rspamd_cl_add_tabs (buf, tabs, compact);
-       g_string_append_c (buf, ']');
-}
-
-/**
- * Emit a single element
- * @param obj object
- * @param buf buffer
- */
-static void
-rspamd_cl_elt_write_json (rspamd_cl_object_t *obj, GString *buf, guint tabs, gboolean start_tabs, gboolean compact)
-{
-       switch (obj->type) {
-       case RSPAMD_CL_INT:
-               if (start_tabs) {
-                       rspamd_cl_add_tabs (buf, tabs, compact);
-               }
-               g_string_append_printf (buf, "%ld", (long int)rspamd_cl_obj_toint (obj));
-               break;
-       case RSPAMD_CL_FLOAT:
-               if (start_tabs) {
-                       rspamd_cl_add_tabs (buf, tabs, compact);
-               }
-               g_string_append_printf (buf, "%lf", rspamd_cl_obj_todouble (obj));
-               break;
-       case RSPAMD_CL_TIME:
-               if (start_tabs) {
-                       rspamd_cl_add_tabs (buf, tabs, compact);
-               }
-               g_string_append_printf (buf, "%lf", rspamd_cl_obj_todouble (obj));
-               break;
-       case RSPAMD_CL_BOOLEAN:
-               if (start_tabs) {
-                       rspamd_cl_add_tabs (buf, tabs, compact);
-               }
-               g_string_append_printf (buf, "%s", rspamd_cl_obj_toboolean (obj) ? "true" : "false");
-               break;
-       case RSPAMD_CL_STRING:
-               if (start_tabs) {
-                       rspamd_cl_add_tabs (buf, tabs, compact);
-               }
-               rspamd_cl_elt_string_write_json (rspamd_cl_obj_tostring (obj), buf);
-               break;
-       case RSPAMD_CL_OBJECT:
-               rspamd_cl_elt_obj_write_json (obj->value.ov, buf, tabs, start_tabs, compact);
-               break;
-       case RSPAMD_CL_ARRAY:
-               rspamd_cl_elt_array_write_json (obj->value.ov, buf, tabs, start_tabs, compact);
-               break;
-       case RSPAMD_CL_USERDATA:
-               break;
-       }
-}
-
-/**
- * Write a single object to the buffer
- * @param obj object
- * @param buf target buffer
- */
-static void
-rspamd_cl_obj_write_json (rspamd_cl_object_t *obj, GString *buf, guint tabs, gboolean start_tabs, gboolean compact)
-{
-       rspamd_cl_object_t *cur;
-       gboolean is_array = (obj->next != NULL);
-
-       if (is_array) {
-               /* This is an array actually */
-               if (start_tabs) {
-                       rspamd_cl_add_tabs (buf, tabs, compact);
-               }
-
-               if (compact) {
-                       g_string_append_c (buf, '[');
-               }
-               else {
-                       g_string_append_len (buf, "[\n", 2);
-               }
-               cur = obj;
-               while (cur != NULL) {
-                       rspamd_cl_elt_write_json (cur, buf, tabs + 1, TRUE, compact);
-                       if (cur->next) {
-                               g_string_append_c (buf, ',');
-                       }
-                       if (!compact) {
-                               g_string_append_c (buf, '\n');
-                       }
-                       cur = cur->next;
-               }
-               rspamd_cl_add_tabs (buf, tabs, compact);
-               g_string_append_c (buf, ']');
-       }
-       else {
-               rspamd_cl_elt_write_json (obj, buf, tabs, start_tabs, compact);
-       }
-
-}
-
-/**
- * Emit an object to json
- * @param obj object
- * @return json output (should be freed after using)
- */
-static guchar *
-rspamd_cl_object_emit_json (rspamd_cl_object_t *obj, gboolean compact)
-{
-       GString *buf;
-       guchar *res;
-
-       /* Allocate large enough buffer */
-       buf = g_string_sized_new (BUFSIZ);
-
-       rspamd_cl_obj_write_json (obj, buf, 0, FALSE, compact);
-
-       res = buf->str;
-       g_string_free (buf, FALSE);
-
-       return res;
-}
-
-/**
- * Write a single object to the buffer
- * @param obj object to write
- * @param buf target buffer
- */
-static void
-rspamd_cl_elt_obj_write_rcl (rspamd_cl_object_t *obj, GString *buf, guint tabs, gboolean start_tabs, gboolean is_top)
-{
-       rspamd_cl_object_t *cur, *tmp;
-
-       if (start_tabs) {
-               rspamd_cl_add_tabs (buf, tabs, is_top);
-       }
-       if (!is_top) {
-               g_string_append_len (buf, "{\n", 2);
-       }
-
-       HASH_ITER (hh, obj, cur, tmp) {
-               rspamd_cl_add_tabs (buf, tabs + 1, is_top);
-               g_string_append (buf, cur->key);
-               if (cur->type != RSPAMD_CL_OBJECT && cur->type != RSPAMD_CL_ARRAY) {
-                       g_string_append_len (buf, " = ", 3);
-               }
-               else {
-                       g_string_append_c (buf, ' ');
-               }
-               rspamd_cl_elt_write_rcl (cur, buf, is_top ? tabs : tabs + 1, FALSE, FALSE, TRUE);
-               if (cur->type != RSPAMD_CL_OBJECT && cur->type != RSPAMD_CL_ARRAY) {
-                       g_string_append_len (buf, ";\n", 2);
-               }
-               else {
-                       g_string_append_c (buf, '\n');
-               }
-       }
-       rspamd_cl_add_tabs (buf, tabs, is_top);
-       if (!is_top) {
-               g_string_append_c (buf, '}');
-       }
-}
-
-/**
- * Write a single array to the buffer
- * @param obj array to write
- * @param buf target buffer
- */
-static void
-rspamd_cl_elt_array_write_rcl (rspamd_cl_object_t *obj, GString *buf, guint tabs, gboolean start_tabs, gboolean is_top)
-{
-       rspamd_cl_object_t *cur = obj;
-
-       if (start_tabs) {
-               rspamd_cl_add_tabs (buf, tabs, FALSE);
-       }
-
-       g_string_append_len (buf, "[\n", 2);
-       while (cur) {
-               rspamd_cl_elt_write_rcl (cur, buf, tabs + 1, TRUE, FALSE, FALSE);
-               g_string_append_len (buf, ",\n", 2);
-               cur = cur->next;
-       }
-       rspamd_cl_add_tabs (buf, tabs, FALSE);
-       g_string_append_c (buf, ']');
-}
-
-/**
- * Emit a single element
- * @param obj object
- * @param buf buffer
- */
-static void
-rspamd_cl_elt_write_rcl (rspamd_cl_object_t *obj, GString *buf, guint tabs,
-               gboolean start_tabs, gboolean is_top, gboolean expand_array)
-{
-       if (expand_array && obj->next) {
-               rspamd_cl_elt_array_write_rcl (obj, buf, tabs, start_tabs, is_top);
-       }
-       else {
-               switch (obj->type) {
-               case RSPAMD_CL_INT:
-                       if (start_tabs) {
-                               rspamd_cl_add_tabs (buf, tabs, FALSE);
-                       }
-                       g_string_append_printf (buf, "%ld", (long int)rspamd_cl_obj_toint (obj));
-                       break;
-               case RSPAMD_CL_FLOAT:
-                       if (start_tabs) {
-                               rspamd_cl_add_tabs (buf, tabs, FALSE);
-                       }
-                       g_string_append_printf (buf, "%.4lf", rspamd_cl_obj_todouble (obj));
-                       break;
-               case RSPAMD_CL_TIME:
-                       if (start_tabs) {
-                               rspamd_cl_add_tabs (buf, tabs, FALSE);
-                       }
-                       g_string_append_printf (buf, "%.4lf", rspamd_cl_obj_todouble (obj));
-                       break;
-               case RSPAMD_CL_BOOLEAN:
-                       if (start_tabs) {
-                               rspamd_cl_add_tabs (buf, tabs, FALSE);
-                       }
-                       g_string_append_printf (buf, "%s", rspamd_cl_obj_toboolean (obj) ? "true" : "false");
-                       break;
-               case RSPAMD_CL_STRING:
-                       if (start_tabs) {
-                               rspamd_cl_add_tabs (buf, tabs, FALSE);
-                       }
-                       rspamd_cl_elt_string_write_json (rspamd_cl_obj_tostring (obj), buf);
-                       break;
-               case RSPAMD_CL_OBJECT:
-                       rspamd_cl_elt_obj_write_rcl (obj->value.ov, buf, tabs, start_tabs, is_top);
-                       break;
-               case RSPAMD_CL_ARRAY:
-                       rspamd_cl_elt_array_write_rcl (obj->value.ov, buf, tabs, start_tabs, is_top);
-                       break;
-               case RSPAMD_CL_USERDATA:
-                       break;
-               }
-       }
-}
-
-/**
- * Emit an object to rcl
- * @param obj object
- * @return rcl output (should be freed after using)
- */
-static guchar *
-rspamd_cl_object_emit_rcl (rspamd_cl_object_t *obj)
-{
-       GString *buf;
-       guchar *res;
-
-       /* Allocate large enough buffer */
-       buf = g_string_sized_new (BUFSIZ);
-
-       rspamd_cl_elt_write_rcl (obj, buf, 0, FALSE, TRUE, TRUE);
-
-       res = buf->str;
-       g_string_free (buf, FALSE);
-
-       return res;
-}
-
-guchar *
-rspamd_cl_object_emit (rspamd_cl_object_t *obj, enum rspamd_cl_emitter emit_type)
-{
-       if (emit_type == RSPAMD_CL_EMIT_JSON) {
-               return rspamd_cl_object_emit_json (obj, FALSE);
-       }
-       else if (emit_type == RSPAMD_CL_EMIT_JSON_COMPACT) {
-               return rspamd_cl_object_emit_json (obj, TRUE);
-       }
-       else {
-               return rspamd_cl_object_emit_rcl (obj);
-       }
-
-       return NULL;
-}
diff --git a/src/rcl/rcl_internal.h b/src/rcl/rcl_internal.h
deleted file mode 100644 (file)
index c2928da..0000000
+++ /dev/null
@@ -1,146 +0,0 @@
-/* Copyright (c) 2013, 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 ''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.
- */
-
-#ifndef RCL_INTERNAL_H_
-#define RCL_INTERNAL_H_
-
-#include "rcl.h"
-#include "utlist.h"
-#ifdef HAVE_OPENSSL
-#include <openssl/evp.h>
-#endif
-
-/**
- * @file rcl_internal.h
- * Internal structures and functions of RCL library
- */
-
-#define RCL_ERROR rcl_error_quark ()
-static inline GQuark
-rcl_error_quark (void)
-{
-       return g_quark_from_static_string ("rcl-error-quark");
-}
-
-#define RCL_MAX_RECURSION 16
-
-enum rcl_character_type {
-       RCL_CHARACTER_DENIED = 0,
-       RCL_CHARACTER_KEY = 1,
-       RCL_CHARACTER_KEY_START = 1 << 1,
-       RCL_CHARACTER_WHITESPACE = 1 << 2,
-       RCL_CHARACTER_WHITESPACE_UNSAFE = 1 << 3,
-       RCL_CHARACTER_VALUE_END = 1 << 4,
-       RCL_CHARACTER_VALUE_STR = 1 << 5,
-       RCL_CHARACTER_VALUE_DIGIT = 1 << 6,
-       RCL_CHARACTER_VALUE_DIGIT_START = 1 << 7
-};
-
-enum rspamd_cl_parser_state {
-       RSPAMD_RCL_STATE_INIT = 0,
-       RSPAMD_RCL_STATE_OBJECT,
-       RSPAMD_RCL_STATE_ARRAY,
-       RSPAMD_RCL_STATE_KEY,
-       RSPAMD_RCL_STATE_VALUE,
-       RSPAMD_RCL_STATE_AFTER_VALUE,
-       RSPAMD_RCL_STATE_ARRAY_VALUE,
-       RSPAMD_RCL_STATE_SCOMMENT,
-       RSPAMD_RCL_STATE_MCOMMENT,
-       RSPAMD_RCL_STATE_MACRO_NAME,
-       RSPAMD_RCL_STATE_MACRO,
-       RSPAMD_RCL_STATE_ERROR
-};
-
-struct rspamd_cl_macro {
-       gchar *name;
-       rspamd_cl_macro_handler handler;
-       gpointer ud;
-       UT_hash_handle hh;
-};
-
-struct rspamd_cl_stack {
-       rspamd_cl_object_t *obj;
-       struct rspamd_cl_stack *next;
-};
-
-struct rspamd_cl_chunk {
-       const guchar *begin;
-       const guchar *end;
-       const guchar *pos;
-       gsize remain;
-       guint line;
-       guint column;
-       struct rspamd_cl_chunk *next;
-};
-
-#ifdef HAVE_OPENSSL
-struct rspamd_cl_pubkey {
-       EVP_PKEY *key;
-       struct rspamd_cl_pubkey *next;
-};
-#else
-struct rspamd_cl_pubkey {
-       struct rspamd_cl_pubkey *next;
-};
-#endif
-
-struct rspamd_cl_parser {
-       enum rspamd_cl_parser_state state;
-       enum rspamd_cl_parser_state prev_state;
-       guint recursion;
-       gint flags;
-       rspamd_cl_object_t *top_obj;
-       rspamd_cl_object_t *cur_obj;
-       struct rspamd_cl_macro *macroes;
-       struct rspamd_cl_stack *stack;
-       struct rspamd_cl_chunk *chunks;
-       struct rspamd_cl_pubkey *keys;
-};
-
-/**
- * Unescape json string inplace
- * @param str
- */
-void rspamd_cl_unescape_json_string (gchar *str);
-
-/**
- * Handle include macro
- * @param data include data
- * @param len length of data
- * @param ud user data
- * @param err error ptr
- * @return
- */
-gboolean rspamd_cl_include_handler (const guchar *data, gsize len, gpointer ud, GError **err);
-
-/**
- * Handle includes macro
- * @param data include data
- * @param len length of data
- * @param ud user data
- * @param err error ptr
- * @return
- */
-gboolean rspamd_cl_includes_handler (const guchar *data, gsize len, gpointer ud, GError **err);
-
-#endif /* RCL_INTERNAL_H_ */
diff --git a/src/rcl/rcl_parser.c b/src/rcl/rcl_parser.c
deleted file mode 100644 (file)
index b601df3..0000000
+++ /dev/null
@@ -1,1380 +0,0 @@
-/* Copyright (c) 2013, 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 ''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.
- */
-
-#include "config.h"
-#include "rcl.h"
-#include "rcl_internal.h"
-#include "rcl_chartable.h"
-#include "util.h"
-
-/**
- * @file rcl_parser.c
- * The implementation of rcl parser
- */
-
-struct rspamd_cl_parser_saved_state {
-       guint line;
-       guint column;
-       gsize remain;
-       const guchar *pos;
-};
-
-/**
- * Move up to len characters
- * @param parser
- * @param begin
- * @param len
- * @return new position in chunk
- */
-static inline void
-rspamd_cl_chunk_skipc (struct rspamd_cl_chunk *chunk, guchar c)
-{
-       if (c == '\n') {
-               chunk->line ++;
-               chunk->column = 0;
-       }
-       else {
-               chunk->column ++;
-       }
-
-       chunk->pos ++;
-       chunk->remain --;
-}
-
-/**
- * Save parser state
- * @param chunk
- * @param s
- */
-static inline void
-rspamd_cl_chunk_save_state (struct rspamd_cl_chunk *chunk, struct rspamd_cl_parser_saved_state *s)
-{
-       s->column = chunk->column;
-       s->pos = chunk->pos;
-       s->line = chunk->line;
-       s->remain = chunk->remain;
-}
-
-/**
- * Restore parser state
- * @param chunk
- * @param s
- */
-static inline void
-rspamd_cl_chunk_restore_state (struct rspamd_cl_chunk *chunk, struct rspamd_cl_parser_saved_state *s)
-{
-       chunk->column = s->column;
-       chunk->pos = s->pos;
-       chunk->line = s->line;
-       chunk->remain = s->remain;
-}
-
-static inline gboolean
-rcl_test_character (guchar c, gint type_flags)
-{
-       return (rcl_chartable[c] & type_flags) != 0;
-}
-
-static inline void
-rspamd_cl_set_err (struct rspamd_cl_chunk *chunk, gint code, const char *str, GError **err)
-{
-       g_set_error (err, RCL_ERROR, code, "error on line %d at column %d: '%s', character: '%c'",
-                       chunk->line, chunk->column, str, *chunk->pos);
-}
-
-static gboolean
-rspamd_cl_skip_comments (struct rspamd_cl_parser *parser, GError **err)
-{
-       struct rspamd_cl_chunk *chunk = parser->chunks;
-       const guchar *p;
-       gint comments_nested = 0;
-
-       p = chunk->pos;
-
-start:
-       if (*p == '#') {
-               if (parser->state != RSPAMD_RCL_STATE_SCOMMENT &&
-                               parser->state != RSPAMD_RCL_STATE_MCOMMENT) {
-                       while (p < chunk->end) {
-                               if (*p == '\n') {
-                                       rspamd_cl_chunk_skipc (chunk, *++p);
-                                       /* Check comments again */
-                                       goto start;
-                               }
-                               rspamd_cl_chunk_skipc (chunk, *++p);
-                       }
-               }
-       }
-       else if (*p == '/' && chunk->remain >= 2) {
-               if (p[1] == '*') {
-                       rspamd_cl_chunk_skipc (chunk, *++p);
-                       comments_nested ++;
-                       rspamd_cl_chunk_skipc (chunk, *++p);
-
-                       while (p < chunk->end) {
-                               if (*p == '*') {
-                                       rspamd_cl_chunk_skipc (chunk, *++p);
-                                       if (*p == '/') {
-                                               comments_nested --;
-                                               if (comments_nested == 0) {
-                                                       rspamd_cl_chunk_skipc (chunk, *++p);
-                                                       goto start;
-                                               }
-                                       }
-                                       rspamd_cl_chunk_skipc (chunk, *++p);
-                               }
-                               else if (p[0] == '/' && chunk->remain >= 2 && p[1] == '*') {
-                                       comments_nested ++;
-                                       rspamd_cl_chunk_skipc (chunk, *++p);
-                                       rspamd_cl_chunk_skipc (chunk, *++p);
-                                       continue;
-                               }
-                               rspamd_cl_chunk_skipc (chunk, *++p);
-                       }
-                       if (comments_nested != 0) {
-                               rspamd_cl_set_err (chunk, RSPAMD_CL_ENESTED, "comments nesting is invalid", err);
-                               return FALSE;
-                       }
-               }
-       }
-
-       return TRUE;
-}
-
-/**
- * Return multiplier for a character
- * @param c multiplier character
- * @param is_bytes if TRUE use 1024 multiplier
- * @return multiplier
- */
-static inline gulong
-rspamd_cl_lex_num_multiplier (const guchar c, gboolean is_bytes) {
-       const struct {
-               char c;
-               glong mult_normal;
-               glong mult_bytes;
-       } multipliers[] = {
-                       {'m', 1000 * 1000, 1024 * 1024},
-                       {'k', 1000, 1024},
-                       {'g', 1000 * 1000 * 1000, 1024 * 1024 * 1024}
-       };
-       gint i;
-
-       for (i = 0; i < 3; i ++) {
-               if (g_ascii_tolower (c) == multipliers[i].c) {
-                       if (is_bytes) {
-                               return multipliers[i].mult_bytes;
-                       }
-                       return multipliers[i].mult_normal;
-               }
-       }
-
-       return 1;
-}
-
-
-/**
- * Return multiplier for time scaling
- * @param c
- * @return
- */
-static inline gdouble
-rspamd_cl_lex_time_multiplier (const guchar c) {
-       const struct {
-               char c;
-               gdouble mult;
-       } multipliers[] = {
-                       {'m', 60},
-                       {'h', 60 * 60},
-                       {'d', 60 * 60 * 24},
-                       {'w', 60 * 60 * 24 * 7},
-                       {'y', 60 * 60 * 24 * 7 * 365}
-       };
-       gint i;
-
-       for (i = 0; i < 5; i ++) {
-               if (g_ascii_tolower (c) == multipliers[i].c) {
-                       return multipliers[i].mult;
-               }
-       }
-
-       return 1;
-}
-
-/**
- * Return TRUE if a character is a end of an atom
- * @param c
- * @return
- */
-static inline gboolean
-rspamd_cl_lex_is_atom_end (const guchar c)
-{
-       return rcl_test_character (c, RCL_CHARACTER_VALUE_END);
-}
-
-static inline gboolean
-rspamd_cl_lex_is_comment (const guchar c1, const guchar c2)
-{
-       if (c1 == '/') {
-               if (c2 == '*') {
-                       return TRUE;
-               }
-       }
-       else if (c1 == '#') {
-               return TRUE;
-       }
-       return FALSE;
-}
-
-/**
- * Parse possible number
- * @param parser
- * @param chunk
- * @param err
- * @return TRUE if a number has been parsed
- */
-static gboolean
-rspamd_cl_lex_number (struct rspamd_cl_parser *parser,
-               struct rspamd_cl_chunk *chunk, rspamd_cl_object_t *obj, GError **err)
-{
-       const guchar *p = chunk->pos, *c = chunk->pos;
-       gchar *endptr;
-       gboolean got_dot = FALSE, got_exp = FALSE, need_double = FALSE, is_date = FALSE;
-       gdouble dv;
-       gint64 lv;
-       struct rspamd_cl_parser_saved_state s;
-
-       rspamd_cl_chunk_save_state (chunk, &s);
-
-       if (*p == '-') {
-               rspamd_cl_chunk_skipc (chunk, *p);
-               p ++;
-       }
-       while (p < chunk->end) {
-               if (g_ascii_isdigit (*p)) {
-                       rspamd_cl_chunk_skipc (chunk, *p);
-                       p ++;
-               }
-               else {
-                       if (p == c) {
-                               /* Empty digits sequence, not a number */
-                               rspamd_cl_chunk_restore_state (chunk, &s);
-                               return FALSE;
-                       }
-                       else if (*p == '.') {
-                               if (got_dot) {
-                                       /* Double dots, not a number */
-                                       rspamd_cl_chunk_restore_state (chunk, &s);
-                                       return FALSE;
-                               }
-                               else {
-                                       got_dot = TRUE;
-                                       need_double = TRUE;
-                                       rspamd_cl_chunk_skipc (chunk, *p);
-                                       p ++;
-                               }
-                       }
-                       else if (*p == 'e' || *p == 'E') {
-                               if (got_exp) {
-                                       /* Double exp, not a number */
-                                       rspamd_cl_chunk_restore_state (chunk, &s);
-                                       return FALSE;
-                               }
-                               else {
-                                       got_exp = TRUE;
-                                       need_double = TRUE;
-                                       rspamd_cl_chunk_skipc (chunk, *p);
-                                       p ++;
-                                       if (p >= chunk->end) {
-                                               rspamd_cl_chunk_restore_state (chunk, &s);
-                                               return FALSE;
-                                       }
-                                       if (!g_ascii_isdigit (*p) && *p != '+' && *p != '-') {
-                                               /* Wrong exponent sign */
-                                               rspamd_cl_chunk_restore_state (chunk, &s);
-                                               return FALSE;
-                                       }
-                                       else {
-                                               rspamd_cl_chunk_skipc (chunk, *p);
-                                               p ++;
-                                       }
-                               }
-                       }
-                       else {
-                               /* Got the end of the number, need to check */
-                               break;
-                       }
-               }
-       }
-
-       errno = 0;
-       if (need_double) {
-               dv = strtod (c, &endptr);
-       }
-       else {
-               lv = strtoimax (c, &endptr, 10);
-       }
-       if (errno == ERANGE) {
-               rspamd_cl_set_err (chunk, RSPAMD_CL_ESYNTAX, "numeric value is out of range", err);
-               parser->prev_state = parser->state;
-               parser->state = RSPAMD_RCL_STATE_ERROR;
-               rspamd_cl_chunk_restore_state (chunk, &s);
-               return FALSE;
-       }
-
-       /* Now check endptr */
-       if (endptr == NULL || rspamd_cl_lex_is_atom_end (*endptr) || *endptr == '\0') {
-               chunk->pos = endptr;
-               goto set_obj;
-       }
-
-       if ((guchar *)endptr < chunk->end) {
-               p = endptr;
-               chunk->pos = p;
-               switch (*p) {
-               case 'm':
-               case 'M':
-               case 'g':
-               case 'G':
-               case 'k':
-               case 'K':
-                       if (chunk->end - p > 2) {
-                               if (p[1] == 's' || p[1] == 'S') {
-                                       /* Milliseconds */
-                                       if (!need_double) {
-                                               need_double = TRUE;
-                                               dv = lv;
-                                       }
-                                       is_date = TRUE;
-                                       if (p[0] == 'm' || p[0] == 'M') {
-                                               dv /= 1000.;
-                                       }
-                                       else {
-                                               dv *= rspamd_cl_lex_num_multiplier (*p, FALSE);
-                                       }
-                                       rspamd_cl_chunk_skipc (chunk, *p);
-                                       rspamd_cl_chunk_skipc (chunk, *p);
-                                       p += 2;
-                                       goto set_obj;
-                               }
-                               else if (p[1] == 'b' || p[1] == 'B') {
-                                       /* Megabytes */
-                                       if (need_double) {
-                                               need_double = FALSE;
-                                               lv = dv;
-                                       }
-                                       lv *= rspamd_cl_lex_num_multiplier (*p, TRUE);
-                                       rspamd_cl_chunk_skipc (chunk, *p);
-                                       rspamd_cl_chunk_skipc (chunk, *p);
-                                       p += 2;
-                                       goto set_obj;
-                               }
-                               else if (rspamd_cl_lex_is_atom_end (p[1])) {
-                                       if (need_double) {
-                                               dv *= rspamd_cl_lex_num_multiplier (*p, FALSE);
-                                       }
-                                       else {
-                                               lv *= rspamd_cl_lex_num_multiplier (*p, FALSE);
-                                       }
-                                       rspamd_cl_chunk_skipc (chunk, *p);
-                                       p ++;
-                                       goto set_obj;
-                               }
-                               else if (chunk->end - p >= 3) {
-                                       if (g_ascii_tolower (p[0]) == 'm' &&
-                                                       g_ascii_tolower (p[1]) == 'i' &&
-                                                       g_ascii_tolower (p[2]) == 'n') {
-                                               /* Minutes */
-                                               if (!need_double) {
-                                                       need_double = TRUE;
-                                                       dv = lv;
-                                               }
-                                               is_date = TRUE;
-                                               dv *= 60.;
-                                               rspamd_cl_chunk_skipc (chunk, *p);
-                                               rspamd_cl_chunk_skipc (chunk, *p);
-                                               rspamd_cl_chunk_skipc (chunk, *p);
-                                               p += 3;
-                                               goto set_obj;
-                                       }
-                               }
-                       }
-                       else {
-                               if (need_double) {
-                                       dv *= rspamd_cl_lex_num_multiplier (*p, FALSE);
-                               }
-                               else {
-                                       lv *= rspamd_cl_lex_num_multiplier (*p, FALSE);
-                               }
-                               rspamd_cl_chunk_skipc (chunk, *p);
-                               p ++;
-                               goto set_obj;
-                       }
-                       break;
-               case 'S':
-               case 's':
-                       if (p == chunk->end - 1 || rspamd_cl_lex_is_atom_end (*++p)) {
-                               if (!need_double) {
-                                       need_double = TRUE;
-                                       dv = lv;
-                               }
-                               rspamd_cl_chunk_skipc (chunk, *p);
-                               is_date = TRUE;
-                               goto set_obj;
-                       }
-                       break;
-               case 'h':
-               case 'H':
-               case 'd':
-               case 'D':
-               case 'w':
-               case 'W':
-               case 'Y':
-               case 'y':
-                       if (p == chunk->end - 1 || rspamd_cl_lex_is_atom_end (p[1])) {
-                               if (!need_double) {
-                                       need_double = TRUE;
-                                       dv = lv;
-                               }
-                               is_date = TRUE;
-                               dv *= rspamd_cl_lex_time_multiplier (*p);
-                               rspamd_cl_chunk_skipc (chunk, *p);
-                               p ++;
-                               goto set_obj;
-                       }
-                       break;
-               }
-       }
-
-       chunk->pos = c;
-       return FALSE;
-
-set_obj:
-       if (need_double || is_date) {
-               if (!is_date) {
-                       obj->type = RSPAMD_CL_FLOAT;
-               }
-               else {
-                       obj->type = RSPAMD_CL_TIME;
-               }
-               obj->value.dv = dv;
-       }
-       else {
-               obj->type = RSPAMD_CL_INT;
-               obj->value.iv = lv;
-       }
-       chunk->pos = p;
-       return TRUE;
-}
-
-/**
- * Parse quoted string with possible escapes
- * @param parser
- * @param chunk
- * @param err
- * @return TRUE if a string has been parsed
- */
-static gboolean
-rspamd_cl_lex_json_string (struct rspamd_cl_parser *parser,
-               struct rspamd_cl_chunk *chunk, gboolean *need_unescape, GError **err)
-{
-       const guchar *p = chunk->pos;
-       guchar c;
-       gint i;
-
-       while (p < chunk->end) {
-               c = *p;
-               if (c < 0x1F) {
-                       /* Unmasked control character */
-                       if (c == '\n') {
-                               rspamd_cl_set_err (chunk, RSPAMD_CL_ESYNTAX, "unexpected newline", err);
-                       }
-                       else {
-                               rspamd_cl_set_err (chunk, RSPAMD_CL_ESYNTAX, "unexpected control character", err);
-                       }
-                       return FALSE;
-               }
-               if (c == '\\') {
-                       rspamd_cl_chunk_skipc (chunk, *p);
-                       p ++;
-                       c = *p;
-                       if (p >= chunk->end) {
-                               rspamd_cl_set_err (chunk, RSPAMD_CL_ESYNTAX, "unfinished escape character", err);
-                               return FALSE;
-                       }
-                       if (*p == 'u') {
-                               rspamd_cl_chunk_skipc (chunk, *p);
-                               p ++;
-                               for (i = 0; i < 4 && p < chunk->end; i ++) {
-                                       if (!g_ascii_isxdigit (*p)) {
-                                               rspamd_cl_set_err (chunk, RSPAMD_CL_ESYNTAX, "invalid utf escape", err);
-                                               return FALSE;
-                                       }
-                                       rspamd_cl_chunk_skipc (chunk, *p);
-                                       p ++;
-                               }
-                               if (p >= chunk->end) {
-                                       rspamd_cl_set_err (chunk, RSPAMD_CL_ESYNTAX, "unfinished escape character", err);
-                                       return FALSE;
-                               }
-                       }
-                       else if (c == '"' || c == '\\' || c == '/' || c == 'b' ||
-                                       c == 'f' || c == 'n' || c == 'r' || c == 't') {
-                               rspamd_cl_chunk_skipc (chunk, *p);
-                               p ++;
-                       }
-                       else {
-                               rspamd_cl_set_err (chunk, RSPAMD_CL_ESYNTAX, "invalid escape character", err);
-                               return FALSE;
-                       }
-                       *need_unescape = TRUE;
-                       continue;
-               }
-               else if (c == '"') {
-                       rspamd_cl_chunk_skipc (chunk, *p);
-                       p ++;
-                       return TRUE;
-               }
-               rspamd_cl_chunk_skipc (chunk, *p);
-               p ++;
-       }
-
-       return FALSE;
-}
-
-/**
- * Parse a key in an object
- * @param parser
- * @param chunk
- * @param err
- * @return TRUE if a key has been parsed
- */
-static gboolean
-rspamd_cl_parse_key (struct rspamd_cl_parser *parser,
-               struct rspamd_cl_chunk *chunk, GError **err)
-{
-       const guchar *p, *c = NULL, *end;
-       gboolean got_quote = FALSE, got_eq = FALSE, got_semicolon = FALSE, need_unescape = FALSE;
-       rspamd_cl_object_t *nobj, *tobj, *container;
-
-       p = chunk->pos;
-
-       if (*p == '.') {
-               /* It is macro actually */
-               rspamd_cl_chunk_skipc (chunk, *p);
-               parser->prev_state = parser->state;
-               parser->state = RSPAMD_RCL_STATE_MACRO_NAME;
-               return TRUE;
-       }
-       while (p < chunk->end) {
-               /*
-                * A key must start with alpha, number, '/' or '_' and end with space character
-                */
-               if (c == NULL) {
-                       if (rspamd_cl_lex_is_comment (p[0], p[1])) {
-                               if (!rspamd_cl_skip_comments (parser, err)) {
-                                       return FALSE;
-                               }
-                               p = chunk->pos;
-                       }
-                       else if (rcl_test_character (*p, RCL_CHARACTER_KEY_START)) {
-                               /* The first symbol */
-                               c = p;
-                               rspamd_cl_chunk_skipc (chunk, *p);
-                               p ++;
-                       }
-                       else if (*p == '"') {
-                               /* JSON style key */
-                               c = p + 1;
-                               got_quote = TRUE;
-                               rspamd_cl_chunk_skipc (chunk, *p);
-                               p ++;
-                       }
-                       else {
-                               /* Invalid identifier */
-                               rspamd_cl_set_err (chunk, RSPAMD_CL_ESYNTAX, "key must begin with a letter", err);
-                               return FALSE;
-                       }
-               }
-               else {
-                       /* Parse the body of a key */
-                       if (!got_quote) {
-                               if (rcl_test_character (*p, RCL_CHARACTER_KEY)) {
-                                       rspamd_cl_chunk_skipc (chunk, *p);
-                                       p ++;
-                               }
-                               else if (*p == ' ' || *p == '\t' || *p == ':' || *p == '=') {
-                                       end = p;
-                                       break;
-                               }
-                               else {
-                                       rspamd_cl_set_err (chunk, RSPAMD_CL_ESYNTAX, "invalid character in a key", err);
-                                       return FALSE;
-                               }
-                       }
-                       else {
-                               /* We need to parse json like quoted string */
-                               if (!rspamd_cl_lex_json_string (parser, chunk, &need_unescape, err)) {
-                                       return FALSE;
-                               }
-                               end = chunk->pos - 1;
-                               p = chunk->pos;
-                               break;
-                       }
-               }
-       }
-
-       if (p >= chunk->end) {
-               rspamd_cl_set_err (chunk, RSPAMD_CL_ESYNTAX, "unfinished key", err);
-               return FALSE;
-       }
-
-       /* We are now at the end of the key, need to parse the rest */
-       while (p < chunk->end) {
-               if (rcl_test_character (*p, RCL_CHARACTER_WHITESPACE)) {
-                       rspamd_cl_chunk_skipc (chunk, *p);
-                       p ++;
-               }
-               else if (*p == '=') {
-                       if (!got_eq && !got_semicolon) {
-                               rspamd_cl_chunk_skipc (chunk, *p);
-                               p ++;
-                               got_eq = TRUE;
-                       }
-                       else {
-                               rspamd_cl_set_err (chunk, RSPAMD_CL_ESYNTAX, "unexpected '=' character", err);
-                               return FALSE;
-                       }
-               }
-               else if (*p == ':') {
-                       if (!got_eq && !got_semicolon) {
-                               rspamd_cl_chunk_skipc (chunk, *p);
-                               p ++;
-                               got_semicolon = TRUE;
-                       }
-                       else {
-                               rspamd_cl_set_err (chunk, RSPAMD_CL_ESYNTAX, "unexpected ':' character", err);
-                               return FALSE;
-                       }
-               }
-               else if (rspamd_cl_lex_is_comment (p[0], p[1])) {
-                       /* Check for comment */
-                       if (!rspamd_cl_skip_comments (parser, err)) {
-                               return FALSE;
-                       }
-                       p = chunk->pos;
-               }
-               else {
-                       /* Start value */
-                       break;
-               }
-       }
-
-       if (p >= chunk->end) {
-               rspamd_cl_set_err (chunk, RSPAMD_CL_ESYNTAX, "unfinished key", err);
-               return FALSE;
-       }
-
-       /* Create a new object */
-       nobj = rspamd_cl_object_new ();
-       nobj->key = g_malloc (end - c + 1);
-       if (nobj->key == NULL) {
-               rspamd_cl_set_err (chunk, RSPAMD_CL_EINTERNAL, "cannot allocate memory for a key", err);
-               return FALSE;
-       }
-       if (parser->flags & RSPAMD_CL_FLAG_KEY_LOWERCASE) {
-               rspamd_strlcpy_tolower (nobj->key, c, end - c + 1);
-       }
-       else {
-               rspamd_strlcpy (nobj->key, c, end - c + 1);
-       }
-
-       if (need_unescape) {
-               rspamd_cl_unescape_json_string (nobj->key);
-       }
-
-       container = parser->stack->obj->value.ov;
-       HASH_FIND_STR (container, nobj->key, tobj);
-       if (tobj != NULL) {
-               /* Just insert a new object as the next element */
-               HASH_DELETE (hh, container, tobj);
-               LL_PREPEND (tobj, nobj);
-       }
-
-       HASH_ADD_KEYPTR (hh, container, nobj->key, strlen (nobj->key), nobj);
-       parser->stack->obj->value.ov = container;
-
-       parser->cur_obj = nobj;
-
-       return TRUE;
-}
-
-/**
- * Parse a cl string
- * @param parser
- * @param chunk
- * @param err
- * @return TRUE if a key has been parsed
- */
-static gboolean
-rspamd_cl_parse_string_value (struct rspamd_cl_parser *parser,
-               struct rspamd_cl_chunk *chunk, GError **err)
-{
-       const guchar *p;
-
-       p = chunk->pos;
-
-       while (p < chunk->end) {
-               if (rspamd_cl_lex_is_atom_end (*p) || rspamd_cl_lex_is_comment (p[0], p[1])) {
-                       break;
-               }
-               rspamd_cl_chunk_skipc (chunk, *p);
-               p ++;
-       }
-
-       if (p >= chunk->end) {
-               rspamd_cl_set_err (chunk, RSPAMD_CL_ESYNTAX, "unfinished value", err);
-               return FALSE;
-       }
-
-       return TRUE;
-}
-
-/**
- * Check whether a given string contains a boolean value
- * @param obj object to set
- * @param start start of a string
- * @param len length of a string
- * @return TRUE if a string is a boolean value
- */
-static inline gboolean
-rspamd_cl_maybe_parse_boolean (rspamd_cl_object_t *obj, const guchar *start, gsize len)
-{
-       const guchar *p = start;
-       gboolean ret = FALSE, val = FALSE;
-
-       if (len == 5) {
-               if (g_ascii_tolower (p[0]) == 'f' && g_ascii_strncasecmp (p, "false", 5) == 0) {
-                       ret = TRUE;
-                       val = FALSE;
-               }
-       }
-       else if (len == 4) {
-               if (g_ascii_tolower (p[0]) == 't' && g_ascii_strncasecmp (p, "true", 4) == 0) {
-                       ret = TRUE;
-                       val = TRUE;
-               }
-       }
-       else if (len == 3) {
-               if (g_ascii_tolower (p[0]) == 'y' && g_ascii_strncasecmp (p, "yes", 3) == 0) {
-                       ret = TRUE;
-                       val = TRUE;
-               }
-               if (g_ascii_tolower (p[0]) == 'o' && g_ascii_strncasecmp (p, "off", 3) == 0) {
-                       ret = TRUE;
-                       val = FALSE;
-               }
-       }
-       else if (len == 2) {
-               if (g_ascii_tolower (p[0]) == 'n' && g_ascii_strncasecmp (p, "no", 2) == 0) {
-                       ret = TRUE;
-                       val = FALSE;
-               }
-               else if (g_ascii_tolower (p[0]) == 'o' && g_ascii_strncasecmp (p, "on", 2) == 0) {
-                       ret = TRUE;
-                       val = TRUE;
-               }
-       }
-
-       if (ret) {
-               obj->type = RSPAMD_CL_BOOLEAN;
-               obj->value.iv = val;
-       }
-
-       return ret;
-}
-
-/**
- * Handle value data
- * @param parser
- * @param chunk
- * @param err
- * @return
- */
-static gboolean
-rspamd_cl_parse_value (struct rspamd_cl_parser *parser, struct rspamd_cl_chunk *chunk, GError **err)
-{
-       const guchar *p, *c;
-       struct rspamd_cl_stack *st;
-       rspamd_cl_object_t *obj = NULL;
-       guint stripped_spaces;
-       gint str_len;
-       gboolean need_unescape = FALSE;
-
-       p = chunk->pos;
-
-       while (p < chunk->end) {
-               if (obj == NULL) {
-                       if (parser->stack->obj->type == RSPAMD_CL_ARRAY) {
-                               /* Object must be allocated */
-                               obj = rspamd_cl_object_new ();
-                               parser->cur_obj = obj;
-                               LL_PREPEND (parser->stack->obj->value.ov, parser->cur_obj);
-                       }
-                       else {
-                               /* Object has been already allocated */
-                               obj = parser->cur_obj;
-                       }
-               }
-               c = p;
-               switch (*p) {
-               case '"':
-                       rspamd_cl_chunk_skipc (chunk, *p);
-                       p ++;
-                       if (!rspamd_cl_lex_json_string (parser, chunk, &need_unescape, err)) {
-                               return FALSE;
-                       }
-                       obj->value.sv = g_malloc (chunk->pos - c - 1);
-                       if (obj->value.sv == NULL) {
-                               rspamd_cl_set_err (chunk, RSPAMD_CL_EINTERNAL, "cannot allocate memory for a string", err);
-                               return FALSE;
-                       }
-                       rspamd_strlcpy (obj->value.sv, c + 1, chunk->pos - c - 1);
-                       if (need_unescape) {
-                               rspamd_cl_unescape_json_string (obj->value.sv);
-                       }
-                       obj->type = RSPAMD_CL_STRING;
-                       parser->state = RSPAMD_RCL_STATE_AFTER_VALUE;
-                       p = chunk->pos;
-                       return TRUE;
-                       break;
-               case '{':
-                       /* We have a new object */
-                       obj->type = RSPAMD_CL_OBJECT;
-
-                       parser->state = RSPAMD_RCL_STATE_KEY;
-                       st = g_slice_alloc0 (sizeof (struct rspamd_cl_stack));
-                       st->obj = obj;
-                       LL_PREPEND (parser->stack, st);
-                       parser->cur_obj = obj;
-
-                       rspamd_cl_chunk_skipc (chunk, *p);
-                       p ++;
-                       return TRUE;
-                       break;
-               case '[':
-                       /* We have a new array */
-                       obj = parser->cur_obj;
-                       obj->type = RSPAMD_CL_ARRAY;
-
-                       parser->state = RSPAMD_RCL_STATE_VALUE;
-                       st = g_slice_alloc0 (sizeof (struct rspamd_cl_stack));
-                       st->obj = obj;
-                       LL_PREPEND (parser->stack, st);
-                       parser->cur_obj = obj;
-
-                       rspamd_cl_chunk_skipc (chunk, *p);
-                       p ++;
-                       return TRUE;
-                       break;
-               default:
-                       /* Skip any spaces and comments */
-                       if (rcl_test_character (*p, RCL_CHARACTER_WHITESPACE_UNSAFE) ||
-                                       rspamd_cl_lex_is_comment (p[0], p[1])) {
-                               while (p < chunk->end && rcl_test_character (*p, RCL_CHARACTER_WHITESPACE_UNSAFE)) {
-                                       rspamd_cl_chunk_skipc (chunk, *p);
-                                       p ++;
-                               }
-                               if (!rspamd_cl_skip_comments (parser, err)) {
-                                       return FALSE;
-                               }
-                               p = chunk->pos;
-                               continue;
-                       }
-                       /* Parse atom */
-                       if (rcl_test_character (*p, RCL_CHARACTER_VALUE_DIGIT_START)) {
-                               if (!rspamd_cl_lex_number (parser, chunk, obj, err)) {
-                                       if (parser->state == RSPAMD_RCL_STATE_ERROR) {
-                                               return FALSE;
-                                       }
-                                       if (!rspamd_cl_parse_string_value (parser, chunk, err)) {
-                                               return FALSE;
-                                       }
-                                       if (!rspamd_cl_maybe_parse_boolean (obj, c, chunk->pos - c)) {
-                                               /* Cut trailing spaces */
-                                               stripped_spaces = 0;
-                                               while (rcl_test_character (*(chunk->pos - 1 - stripped_spaces),
-                                                               RCL_CHARACTER_WHITESPACE)) {
-                                                       stripped_spaces ++;
-                                               }
-                                               str_len = chunk->pos - c + 1 - stripped_spaces;
-                                               if (str_len <= 0) {
-                                                       rspamd_cl_set_err (chunk, RSPAMD_CL_ESYNTAX, "string value must not be empty", err);
-                                                       return FALSE;
-                                               }
-                                               obj->value.sv = g_malloc (str_len);
-                                               if (obj->value.sv == NULL) {
-                                                       rspamd_cl_set_err (chunk, RSPAMD_CL_EINTERNAL, "cannot allocate memory for a string", err);
-                                                       return FALSE;
-                                               }
-                                               rspamd_strlcpy (obj->value.sv, c, str_len);
-                                               obj->type = RSPAMD_CL_STRING;
-                                       }
-                                       parser->state = RSPAMD_RCL_STATE_AFTER_VALUE;
-                                       return TRUE;
-                               }
-                               else {
-                                       parser->state = RSPAMD_RCL_STATE_AFTER_VALUE;
-                                       return TRUE;
-                               }
-                       }
-                       else {
-                               if (!rspamd_cl_parse_string_value (parser, chunk, err)) {
-                                       return FALSE;
-                               }
-                               if (!rspamd_cl_maybe_parse_boolean (obj, c, chunk->pos - c)) {
-                                       /* Cut trailing spaces */
-                                       stripped_spaces = 0;
-                                       while (rcl_test_character (*(chunk->pos - 1 - stripped_spaces),
-                                                       RCL_CHARACTER_WHITESPACE)) {
-                                               stripped_spaces ++;
-                                       }
-                                       str_len = chunk->pos - c + 1 - stripped_spaces;
-                                       if (str_len <= 0) {
-                                               rspamd_cl_set_err (chunk, RSPAMD_CL_ESYNTAX, "string value must not be empty", err);
-                                               return FALSE;
-                                       }
-                                       obj->value.sv = g_malloc (str_len);
-                                       if (obj->value.sv == NULL) {
-                                               rspamd_cl_set_err (chunk, RSPAMD_CL_EINTERNAL, "cannot allocate memory for a string", err);
-                                               return FALSE;
-                                       }
-                                       rspamd_strlcpy (obj->value.sv, c, str_len);
-                                       obj->type = RSPAMD_CL_STRING;
-                               }
-                               parser->state = RSPAMD_RCL_STATE_AFTER_VALUE;
-                               return TRUE;
-                       }
-                       p = chunk->pos;
-                       break;
-               }
-       }
-
-       return TRUE;
-}
-
-/**
- * Handle after value data
- * @param parser
- * @param chunk
- * @param err
- * @return
- */
-static gboolean
-rspamd_cl_parse_after_value (struct rspamd_cl_parser *parser, struct rspamd_cl_chunk *chunk, GError **err)
-{
-       const guchar *p;
-       gboolean got_sep = FALSE;
-       struct rspamd_cl_stack *st;
-
-       p = chunk->pos;
-
-       while (p < chunk->end) {
-               if (rcl_test_character (*p, RCL_CHARACTER_WHITESPACE)) {
-                       /* Skip whitespaces */
-                       rspamd_cl_chunk_skipc (chunk, *p);
-                       p ++;
-               }
-               else if (rspamd_cl_lex_is_comment (p[0], p[1])) {
-                       /* Skip comment */
-                       if (!rspamd_cl_skip_comments (parser, err)) {
-                               return FALSE;
-                       }
-                       /* Treat comment as a separator */
-                       got_sep = TRUE;
-                       p = chunk->pos;
-               }
-               else if (*p == ',') {
-                       /* Got a separator */
-                       got_sep = TRUE;
-                       rspamd_cl_chunk_skipc (chunk, *p);
-                       p ++;
-               }
-               else if (*p == ';') {
-                       /* Got a separator */
-                       got_sep = TRUE;
-                       rspamd_cl_chunk_skipc (chunk, *p);
-                       p ++;
-               }
-               else if (*p == '\n') {
-                       got_sep = TRUE;
-                       rspamd_cl_chunk_skipc (chunk, *p);
-                       p ++;
-               }
-               else if (*p == '}' || *p == ']') {
-                       if (parser->stack == NULL) {
-                               rspamd_cl_set_err (chunk, RSPAMD_CL_ESYNTAX, "unexpected } detected", err);
-                               return FALSE;
-                       }
-                       if ((*p == '}' && parser->stack->obj->type == RSPAMD_CL_OBJECT) ||
-                                       (*p == ']' && parser->stack->obj->type == RSPAMD_CL_ARRAY)) {
-                               /* Pop object from a stack */
-
-                               st = parser->stack;
-                               parser->stack = st->next;
-                               g_slice_free1 (sizeof (struct rspamd_cl_stack), st);
-                       }
-                       else {
-                               rspamd_cl_set_err (chunk, RSPAMD_CL_ESYNTAX, "unexpected terminating symbol detected", err);
-                               return FALSE;
-                       }
-
-                       if (parser->stack == NULL) {
-                               /* Ignore everything after a top object */
-                               return TRUE;
-                       }
-                       else {
-                               rspamd_cl_chunk_skipc (chunk, *p);
-                               p ++;
-                       }
-                       got_sep = TRUE;
-               }
-               else {
-                       /* Anything else */
-                       if (!got_sep) {
-                               rspamd_cl_set_err (chunk, RSPAMD_CL_ESYNTAX, "delimiter is missing", err);
-                               return FALSE;
-                       }
-                       return TRUE;
-               }
-       }
-
-       return TRUE;
-}
-
-/**
- * Handle macro data
- * @param parser
- * @param chunk
- * @param err
- * @return
- */
-static gboolean
-rspamd_cl_parse_macro_value (struct rspamd_cl_parser *parser,
-               struct rspamd_cl_chunk *chunk, struct rspamd_cl_macro *macro,
-               guchar const **macro_start, gsize *macro_len, GError **err)
-{
-       const guchar *p, *c;
-       gboolean need_unescape = FALSE;
-
-       p = chunk->pos;
-
-       switch (*p) {
-       case '"':
-               /* We have macro value encoded in quotes */
-               c = p;
-               rspamd_cl_chunk_skipc (chunk, *p);
-               p ++;
-               if (!rspamd_cl_lex_json_string (parser, chunk, &need_unescape, err)) {
-                       return FALSE;
-               }
-
-               *macro_start = c + 1;
-               *macro_len = chunk->pos - c - 2;
-               p = chunk->pos;
-               break;
-       case '{':
-               /* We got a multiline macro body */
-               rspamd_cl_chunk_skipc (chunk, *p);
-               p ++;
-               /* Skip spaces at the beginning */
-               while (p < chunk->end) {
-                       if (rcl_test_character (*p, RCL_CHARACTER_WHITESPACE_UNSAFE)) {
-                               rspamd_cl_chunk_skipc (chunk, *p);
-                               p ++;
-                       }
-                       else {
-                               break;
-                       }
-               }
-               c = p;
-               while (p < chunk->end) {
-                       if (*p == '}') {
-                               break;
-                       }
-                       rspamd_cl_chunk_skipc (chunk, *p);
-                       p ++;
-               }
-               *macro_start = c;
-               *macro_len = p - c;
-               rspamd_cl_chunk_skipc (chunk, *p);
-               p ++;
-               break;
-       default:
-               /* Macro is not enclosed in quotes or braces */
-               c = p;
-               while (p < chunk->end) {
-                       if (rspamd_cl_lex_is_atom_end (*p)) {
-                               break;
-                       }
-                       rspamd_cl_chunk_skipc (chunk, *p);
-                       p ++;
-               }
-               *macro_start = c;
-               *macro_len = p - c;
-               break;
-       }
-
-       /* We are at the end of a macro */
-       /* Skip ';' and space characters and return to previous state */
-       while (p < chunk->end) {
-               if (!rcl_test_character (*p, RCL_CHARACTER_WHITESPACE_UNSAFE) && *p != ';') {
-                       break;
-               }
-               rspamd_cl_chunk_skipc (chunk, *p);
-               p ++;
-       }
-       return TRUE;
-}
-
-/**
- * Handle the main states of rcl parser
- * @param parser parser structure
- * @param data the pointer to the beginning of a chunk
- * @param len the length of a chunk
- * @param err if *err is NULL it is set to parser error
- * @return TRUE if chunk has been parsed and FALSE in case of error
- */
-static gboolean
-rspamd_cl_state_machine (struct rspamd_cl_parser *parser, GError **err)
-{
-       rspamd_cl_object_t *obj;
-       struct rspamd_cl_chunk *chunk = parser->chunks;
-       struct rspamd_cl_stack *st;
-       const guchar *p, *c, *macro_start = NULL;
-       gsize macro_len = 0;
-       struct rspamd_cl_macro *macro = NULL;
-
-       p = chunk->pos;
-       while (chunk->pos < chunk->end) {
-               switch (parser->state) {
-               case RSPAMD_RCL_STATE_INIT:
-                       /*
-                        * At the init state we can either go to the parse array or object
-                        * if we got [ or { correspondingly or can just treat new data as
-                        * a key of newly created object
-                        */
-                       if (!rspamd_cl_skip_comments (parser, err)) {
-                               parser->prev_state = parser->state;
-                               parser->state = RSPAMD_RCL_STATE_ERROR;
-                               return FALSE;
-                       }
-                       else {
-                               p = chunk->pos;
-                               obj = rspamd_cl_object_new ();
-                               if (*p == '[') {
-                                       parser->state = RSPAMD_RCL_STATE_VALUE;
-                                       obj->type = RSPAMD_CL_ARRAY;
-                                       rspamd_cl_chunk_skipc (chunk, *p);
-                                       p ++;
-                               }
-                               else {
-                                       parser->state = RSPAMD_RCL_STATE_KEY;
-                                       obj->type = RSPAMD_CL_OBJECT;
-                                       if (*p == '{') {
-                                               rspamd_cl_chunk_skipc (chunk, *p);
-                                               p ++;
-                                       }
-                               };
-                               parser->cur_obj = obj;
-                               parser->top_obj = obj;
-                               st = g_slice_alloc0 (sizeof (struct rspamd_cl_stack));
-                               st->obj = obj;
-                               LL_PREPEND (parser->stack, st);
-                       }
-                       break;
-               case RSPAMD_RCL_STATE_KEY:
-                       /* Skip any spaces */
-                       while (p < chunk->end && rcl_test_character (*p, RCL_CHARACTER_WHITESPACE_UNSAFE)) {
-                               rspamd_cl_chunk_skipc (chunk, *p);
-                               p ++;
-                       }
-                       if (*p == '}') {
-                               /* We have the end of an object */
-                               parser->state = RSPAMD_RCL_STATE_AFTER_VALUE;
-                               continue;
-                       }
-                       if (!rspamd_cl_parse_key (parser, chunk, err)) {
-                               parser->prev_state = parser->state;
-                               parser->state = RSPAMD_RCL_STATE_ERROR;
-                               return FALSE;
-                       }
-                       if (parser->state != RSPAMD_RCL_STATE_MACRO_NAME) {
-                               parser->state = RSPAMD_RCL_STATE_VALUE;
-                       }
-                       else {
-                               c = chunk->pos;
-                       }
-                       p = chunk->pos;
-                       break;
-               case RSPAMD_RCL_STATE_VALUE:
-                       /* We need to check what we do have */
-                       if (!rspamd_cl_parse_value (parser, chunk, err)) {
-                               parser->prev_state = parser->state;
-                               parser->state = RSPAMD_RCL_STATE_ERROR;
-                               return FALSE;
-                       }
-                       /* State is set in rspamd_cl_parse_value call */
-                       p = chunk->pos;
-                       break;
-               case RSPAMD_RCL_STATE_AFTER_VALUE:
-                       if (!rspamd_cl_parse_after_value (parser, chunk, err)) {
-                               parser->prev_state = parser->state;
-                               parser->state = RSPAMD_RCL_STATE_ERROR;
-                               return FALSE;
-                       }
-                       if (parser->stack != NULL) {
-                               if (parser->stack->obj->type == RSPAMD_CL_OBJECT) {
-                                       parser->state = RSPAMD_RCL_STATE_KEY;
-                               }
-                               else {
-                                       /* Array */
-                                       parser->state = RSPAMD_RCL_STATE_VALUE;
-                               }
-                       }
-                       else {
-                               /* Skip everything at the end */
-                               return TRUE;
-                       }
-                       p = chunk->pos;
-                       break;
-               case RSPAMD_RCL_STATE_MACRO_NAME:
-                       if (!rcl_test_character (*p, RCL_CHARACTER_WHITESPACE_UNSAFE)) {
-                               rspamd_cl_chunk_skipc (chunk, *p);
-                               p ++;
-                       }
-                       else if (p - c > 0) {
-                               /* We got macro name */
-                               HASH_FIND (hh, parser->macroes, c, p - c, macro);
-                               if (macro == NULL) {
-                                       rspamd_cl_set_err (chunk, RSPAMD_CL_EMACRO, "unknown macro", err);
-                                       parser->state = RSPAMD_RCL_STATE_ERROR;
-                                       return FALSE;
-                               }
-                               /* Now we need to skip all spaces */
-                               while (p < chunk->end) {
-                                       if (!rcl_test_character (*p, RCL_CHARACTER_WHITESPACE_UNSAFE)) {
-                                               if (rspamd_cl_lex_is_comment (p[0], p[1])) {
-                                                       /* Skip comment */
-                                                       if (!rspamd_cl_skip_comments (parser, err)) {
-                                                               return FALSE;
-                                                       }
-                                                       p = chunk->pos;
-                                               }
-                                               break;
-                                       }
-                                       rspamd_cl_chunk_skipc (chunk, *p);
-                                       p ++;
-                               }
-                               parser->state = RSPAMD_RCL_STATE_MACRO;
-                       }
-                       break;
-               case RSPAMD_RCL_STATE_MACRO:
-                       if (!rspamd_cl_parse_macro_value (parser, chunk, macro,
-                                       &macro_start, &macro_len, err)) {
-                               parser->prev_state = parser->state;
-                               parser->state = RSPAMD_RCL_STATE_ERROR;
-                               return FALSE;
-                       }
-                       parser->state = parser->prev_state;
-                       if (!macro->handler (macro_start, macro_len, macro->ud, err)) {
-                               return FALSE;
-                       }
-                       p = chunk->pos;
-                       break;
-               default:
-                       /* TODO: add all states */
-                       return FALSE;
-               }
-       }
-
-       return TRUE;
-}
-
-struct rspamd_cl_parser*
-rspamd_cl_parser_new (gint flags)
-{
-       struct rspamd_cl_parser *new;
-
-       new = g_slice_alloc0 (sizeof (struct rspamd_cl_parser));
-
-       rspamd_cl_parser_register_macro (new, "include", rspamd_cl_include_handler, new);
-       rspamd_cl_parser_register_macro (new, "includes", rspamd_cl_includes_handler, new);
-
-       new->flags = flags;
-
-       return new;
-}
-
-
-void
-rspamd_cl_parser_register_macro (struct rspamd_cl_parser *parser, const gchar *macro,
-               rspamd_cl_macro_handler handler, gpointer ud)
-{
-       struct rspamd_cl_macro *new;
-
-       new = g_slice_alloc0 (sizeof (struct rspamd_cl_macro));
-       new->handler = handler;
-       new->name = g_strdup (macro);
-       new->ud = ud;
-       HASH_ADD_KEYPTR (hh, parser->macroes, new->name, strlen (new->name), new);
-}
-
-gboolean
-rspamd_cl_parser_add_chunk (struct rspamd_cl_parser *parser, const guchar *data,
-               gsize len, GError **err)
-{
-       struct rspamd_cl_chunk *chunk;
-
-       if (parser->state != RSPAMD_RCL_STATE_ERROR) {
-               chunk = g_slice_alloc (sizeof (struct rspamd_cl_chunk));
-               chunk->begin = data;
-               chunk->remain = len;
-               chunk->pos = chunk->begin;
-               chunk->end = chunk->begin + len;
-               chunk->line = 1;
-               chunk->column = 0;
-               LL_PREPEND (parser->chunks, chunk);
-               parser->recursion ++;
-               if (parser->recursion > RCL_MAX_RECURSION) {
-                       g_set_error (err, RCL_ERROR, RSPAMD_CL_ERECURSION, "maximum include nesting limit is reached: %d",
-                                       parser->recursion);
-                       return FALSE;
-               }
-               return rspamd_cl_state_machine (parser, err);
-       }
-
-       g_set_error (err, RCL_ERROR, RSPAMD_CL_ESTATE, "a parser is in an invalid state");
-
-       return FALSE;
-}
diff --git a/src/rcl/rcl_util.c b/src/rcl/rcl_util.c
deleted file mode 100644 (file)
index c351fa8..0000000
+++ /dev/null
@@ -1,623 +0,0 @@
-/* Copyright (c) 2013, 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 ''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.
- */
-
-#include "config.h"
-#include "rcl.h"
-#include "rcl_internal.h"
-#include "util.h"
-
-#ifdef HAVE_OPENSSL
-#include <openssl/err.h>
-#include <openssl/sha.h>
-#include <openssl/rsa.h>
-#include <openssl/ssl.h>
-#include <openssl/evp.h>
-#endif
-
-/**
- * @file rcl_util.c
- * Utilities for rcl parsing
- */
-
-
-static void
-rspamd_cl_obj_free_internal (rspamd_cl_object_t *obj, gboolean allow_rec)
-{
-       rspamd_cl_object_t *sub, *tmp;
-
-       while (obj != NULL) {
-               if (obj->key != NULL) {
-                       g_free (obj->key);
-               }
-
-               if (obj->type == RSPAMD_CL_STRING) {
-                       g_free (obj->value.sv);
-               }
-               else if (obj->type == RSPAMD_CL_ARRAY) {
-                       sub = obj->value.ov;
-                       while (sub != NULL) {
-                               tmp = sub->next;
-                               rspamd_cl_obj_free_internal (sub, FALSE);
-                               sub = tmp;
-                       }
-               }
-               else if (obj->type == RSPAMD_CL_OBJECT) {
-                       HASH_ITER (hh, obj->value.ov, sub, tmp) {
-                               HASH_DELETE (hh, obj->value.ov, sub);
-                               rspamd_cl_obj_free_internal (sub, TRUE);
-                       }
-               }
-               tmp = obj->next;
-               g_slice_free1 (sizeof (rspamd_cl_object_t), obj);
-               obj = tmp;
-
-               if (!allow_rec) {
-                       break;
-               }
-       }
-}
-
-void
-rspamd_cl_obj_free (rspamd_cl_object_t *obj)
-{
-       rspamd_cl_obj_free_internal (obj, TRUE);
-}
-
-void
-rspamd_cl_unescape_json_string (gchar *str)
-{
-       gchar *t = str, *h = str;
-       gint i, uval;
-
-       /* t is target (tortoise), h is source (hare) */
-
-       while (*h != '\0') {
-               if (*h == '\\') {
-                       h ++;
-                       switch (*h) {
-                       case 'n':
-                               *t++ = '\n';
-                               break;
-                       case 'r':
-                               *t++ = '\r';
-                               break;
-                       case 'b':
-                               *t++ = '\b';
-                               break;
-                       case 't':
-                               *t++ = '\t';
-                               break;
-                       case 'f':
-                               *t++ = '\f';
-                               break;
-                       case '\\':
-                               *t++ = '\\';
-                               break;
-                       case '"':
-                               *t++ = '"';
-                               break;
-                       case 'u':
-                               /* Unicode escape */
-                               uval = 0;
-                               for (i = 0; i < 4; i++) {
-                                       uval <<= 4;
-                                       if (g_ascii_isdigit (h[i])) {
-                                               uval += h[i] - '0';
-                                       }
-                                       else if (h[i] >= 'a' && h[i] <= 'f') {
-                                               uval += h[i] - 'a' + 10;
-                                       }
-                                       else if (h[i] >= 'A' && h[i] <= 'F') {
-                                               uval += h[i] - 'A' + 10;
-                                       }
-                               }
-                               h += 3;
-                               /* Encode */
-                               if(uval < 0x80) {
-                                       t[0] = (char)uval;
-                                       t ++;
-                               }
-                               else if(uval < 0x800) {
-                                       t[0] = 0xC0 + ((uval & 0x7C0) >> 6);
-                                       t[1] = 0x80 + ((uval & 0x03F));
-                                       t += 2;
-                               }
-                               else if(uval < 0x10000) {
-                                       t[0] = 0xE0 + ((uval & 0xF000) >> 12);
-                                       t[1] = 0x80 + ((uval & 0x0FC0) >> 6);
-                                       t[2] = 0x80 + ((uval & 0x003F));
-                                       t += 3;
-                               }
-                               else if(uval <= 0x10FFFF) {
-                                       t[0] = 0xF0 + ((uval & 0x1C0000) >> 18);
-                                       t[1] = 0x80 + ((uval & 0x03F000) >> 12);
-                                       t[2] = 0x80 + ((uval & 0x000FC0) >> 6);
-                                       t[3] = 0x80 + ((uval & 0x00003F));
-                                       t += 4;
-                               }
-                               else {
-                                       *t++ = '?';
-                               }
-                               break;
-                       default:
-                               *t++ = '?';
-                               break;
-                       }
-                       h ++;
-               }
-               else {
-                       *t++ = *h++;
-               }
-       }
-       *t = '\0';
-}
-
-rspamd_cl_object_t*
-rspamd_cl_parser_get_object (struct rspamd_cl_parser *parser, GError **err)
-{
-       if (parser->state != RSPAMD_RCL_STATE_INIT && parser->state != RSPAMD_RCL_STATE_ERROR) {
-               return rspamd_cl_obj_ref (parser->top_obj);
-       }
-
-       return NULL;
-}
-
-void
-rspamd_cl_parser_free (struct rspamd_cl_parser *parser)
-{
-       struct rspamd_cl_stack *stack, *stmp;
-       struct rspamd_cl_macro *macro, *mtmp;
-       struct rspamd_cl_chunk *chunk, *ctmp;
-       struct rspamd_cl_pubkey *key, *ktmp;
-
-       if (parser->top_obj != NULL) {
-               rspamd_cl_obj_unref (parser->top_obj);
-       }
-
-       LL_FOREACH_SAFE (parser->stack, stack, stmp) {
-               g_slice_free1 (sizeof (struct rspamd_cl_stack), stack);
-       }
-       HASH_ITER (hh, parser->macroes, macro, mtmp) {
-               g_free (macro->name);
-               HASH_DEL (parser->macroes, macro);
-               g_slice_free1 (sizeof (struct rspamd_cl_macro), macro);
-       }
-       LL_FOREACH_SAFE (parser->chunks, chunk, ctmp) {
-               g_slice_free1 (sizeof (struct rspamd_cl_chunk), chunk);
-       }
-       LL_FOREACH_SAFE (parser->keys, key, ktmp) {
-               g_slice_free1 (sizeof (struct rspamd_cl_pubkey), key);
-       }
-
-       g_slice_free1 (sizeof (struct rspamd_cl_parser), parser);
-}
-
-gboolean
-rspamd_cl_pubkey_add (struct rspamd_cl_parser *parser, const guchar *key, gsize len, GError **err)
-{
-       struct rspamd_cl_pubkey *nkey;
-#ifndef HAVE_OPENSSL
-       g_set_error (err, RCL_ERROR, RSPAMD_CL_EINTERNAL, "cannot check signatures without openssl");
-       return FALSE;
-#else
-# if (OPENSSL_VERSION_NUMBER < 0x10000000L)
-       g_set_error (err, RCL_ERROR, RSPAMD_CL_EINTERNAL, "cannot check signatures, openssl version is unsupported");
-       return EXIT_FAILURE;
-# else
-       BIO *mem;
-
-       mem = BIO_new_mem_buf ((void *)key, len);
-       nkey = g_slice_alloc0 (sizeof (struct rspamd_cl_pubkey));
-       nkey->key = PEM_read_bio_PUBKEY (mem, &nkey->key, NULL, NULL);
-       BIO_free (mem);
-       if (nkey->key == NULL) {
-               g_slice_free1 (sizeof (struct rspamd_cl_pubkey), nkey);
-               g_set_error (err, RCL_ERROR, RSPAMD_CL_ESSL, "%s",
-                               ERR_error_string (ERR_get_error (), NULL));
-               return FALSE;
-       }
-       LL_PREPEND (parser->keys, nkey);
-# endif
-#endif
-       return TRUE;
-}
-
-#ifdef CURL_FOUND
-struct rspamd_cl_curl_cbdata {
-       guchar *buf;
-       gsize buflen;
-};
-
-static gsize
-rspamd_cl_curl_write_callback (gpointer contents, gsize size, gsize nmemb, gpointer ud)
-{
-       struct rspamd_cl_curl_cbdata *cbdata = ud;
-       gsize realsize = size * nmemb;
-
-       cbdata->buf = g_realloc (cbdata->buf, cbdata->buflen + realsize + 1);
-       if (cbdata->buf == NULL) {
-               return 0;
-       }
-
-       memcpy (&(cbdata->buf[cbdata->buflen]), contents, realsize);
-       cbdata->buflen += realsize;
-       cbdata->buf[cbdata->buflen] = 0;
-
-       return realsize;
-}
-#endif
-
-/**
- * Fetch a url and save results to the memory buffer
- * @param url url to fetch
- * @param len length of url
- * @param buf target buffer
- * @param buflen target length
- * @return
- */
-static gboolean
-rspamd_cl_fetch_url (const guchar *url, guchar **buf, gsize *buflen, GError **err)
-{
-
-#ifdef HAVE_FETCH_H
-       struct url *fetch_url;
-       struct url_stat us;
-       FILE *in;
-
-       fetch_url = fetchParseURL (url);
-       if (fetch_url == NULL) {
-               g_set_error (err, RCL_ERROR, RSPAMD_CL_EIO, "invalid URL %s: %s",
-                               url, strerror (errno));
-               return FALSE;
-       }
-       if ((in = fetchXGet (fetch_url, &us, "")) == NULL) {
-               g_set_error (err, RCL_ERROR, RSPAMD_CL_EIO, "cannot fetch URL %s: %s",
-                               url, strerror (errno));
-               fetchFreeURL (fetch_url);
-               return FALSE;
-       }
-
-       *buflen = us.size;
-       *buf = g_malloc (*buflen);
-       if (*buf == NULL) {
-               g_set_error (err, RCL_ERROR, RSPAMD_CL_EIO, "cannot allocate buffer for URL %s: %s",
-                               url, strerror (errno));
-               fclose (in);
-               fetchFreeURL (fetch_url);
-               return FALSE;
-       }
-
-       if (fread (*buf, *buflen, 1, in) != 1) {
-               g_set_error (err, RCL_ERROR, RSPAMD_CL_EIO, "cannot read URL %s: %s",
-                               url, strerror (errno));
-               fclose (in);
-               fetchFreeURL (fetch_url);
-               return FALSE;
-       }
-
-       fetchFreeURL (fetch_url);
-       return TRUE;
-#elif defined(CURL_FOUND)
-       CURL *curl;
-       gint r;
-       struct rspamd_cl_curl_cbdata cbdata;
-
-       curl = curl_easy_init ();
-       if (curl == NULL) {
-               g_set_error (err, RCL_ERROR, RSPAMD_CL_EINTERNAL, "CURL interface is broken");
-               return FALSE;
-       }
-       if ((r = curl_easy_setopt (curl, CURLOPT_URL, url)) != CURLE_OK) {
-               g_set_error (err, RCL_ERROR, RSPAMD_CL_EIO, "invalid URL %s: %s",
-                               url, curl_easy_strerror (r));
-               curl_easy_cleanup (curl);
-               return FALSE;
-       }
-       curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, rspamd_cl_curl_write_callback);
-       cbdata.buf = *buf;
-       cbdata.buflen = *buflen;
-       curl_easy_setopt (curl, CURLOPT_WRITEDATA, &cbdata);
-
-       if ((r = curl_easy_perform (curl)) != CURLE_OK) {
-               g_set_error (err, RCL_ERROR, RSPAMD_CL_EIO, "error fetching URL %s: %s",
-                               url, curl_easy_strerror (r));
-               curl_easy_cleanup (curl);
-               if (buf != NULL) {
-                       g_free (buf);
-               }
-               return FALSE;
-       }
-       *buf = cbdata.buf;
-       *buflen = cbdata.buflen;
-
-       return TRUE;
-#else
-       g_set_error (err, RCL_ERROR, RSPAMD_CL_EINTERNAL, "URL support is disabled");
-       return FALSE;
-#endif
-}
-
-/**
- * Fetch a file and save results to the memory buffer
- * @param filename filename to fetch
- * @param len length of filename
- * @param buf target buffer
- * @param buflen target length
- * @return
- */
-static gboolean
-rspamd_cl_fetch_file (const guchar *filename, guchar **buf, gsize *buflen, GError **err)
-{
-       gint fd;
-       struct stat st;
-
-       if (stat (filename, &st) == -1) {
-               g_set_error (err, RCL_ERROR, RSPAMD_CL_EIO, "cannot stat file %s: %s",
-                               filename, strerror (errno));
-               return FALSE;
-       }
-       if ((fd = open (filename, O_RDONLY)) == -1) {
-               g_set_error (err, RCL_ERROR, RSPAMD_CL_EIO, "cannot open file %s: %s",
-                               filename, strerror (errno));
-               return FALSE;
-       }
-       if ((*buf = mmap (NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0)) == MAP_FAILED) {
-               close (fd);
-               g_set_error (err, RCL_ERROR, RSPAMD_CL_EIO, "cannot mmap file %s: %s",
-                               filename, strerror (errno));
-               return FALSE;
-       }
-       *buflen = st.st_size;
-       close (fd);
-
-       return TRUE;
-}
-
-
-#if (defined(HAVE_OPENSSL) && OPENSSL_VERSION_NUMBER >= 0x10000000L)
-static inline gboolean
-rspamd_cl_sig_check (const guchar *data, gsize datalen,
-               const guchar *sig, gsize siglen, struct rspamd_cl_parser *parser)
-{
-       struct rspamd_cl_pubkey *key;
-       gchar dig[EVP_MAX_MD_SIZE];
-       guint diglen;
-       EVP_PKEY_CTX *key_ctx;
-       EVP_MD_CTX *sign_ctx = NULL;
-
-       sign_ctx = EVP_MD_CTX_create ();
-
-       LL_FOREACH (parser->keys, key) {
-               key_ctx = EVP_PKEY_CTX_new (key->key, NULL);
-               if (key_ctx != NULL) {
-                       if (EVP_PKEY_verify_init (key_ctx) <= 0) {
-                               EVP_PKEY_CTX_free (key_ctx);
-                               continue;
-                       }
-                       if (EVP_PKEY_CTX_set_rsa_padding (key_ctx, RSA_PKCS1_PADDING) <= 0) {
-                               EVP_PKEY_CTX_free (key_ctx);
-                               continue;
-                       }
-                       if (EVP_PKEY_CTX_set_signature_md (key_ctx, EVP_sha256 ()) <= 0) {
-                               EVP_PKEY_CTX_free (key_ctx);
-                               continue;
-                       }
-                       EVP_DigestInit (sign_ctx, EVP_sha256 ());
-                       EVP_DigestUpdate (sign_ctx, data, datalen);
-                       EVP_DigestFinal (sign_ctx, dig, &diglen);
-
-                       if (EVP_PKEY_verify (key_ctx, sig, siglen, dig, diglen) == 1) {
-                               EVP_MD_CTX_destroy (sign_ctx);
-                               EVP_PKEY_CTX_free (key_ctx);
-                               return TRUE;
-                       }
-
-                       EVP_PKEY_CTX_free (key_ctx);
-               }
-       }
-
-       EVP_MD_CTX_destroy (sign_ctx);
-
-       return FALSE;
-}
-#endif
-
-/**
- * Include an url to configuration
- * @param data
- * @param len
- * @param parser
- * @param err
- * @return
- */
-static gboolean
-rspamd_cl_include_url (const guchar *data, gsize len,
-               struct rspamd_cl_parser *parser, gboolean check_signature, GError **err)
-{
-
-       gboolean res;
-       guchar *buf = NULL, *sigbuf = NULL;
-       gsize buflen = 0, siglen = 0;
-       struct rspamd_cl_chunk *chunk;
-       gchar urlbuf[PATH_MAX];
-
-       rspamd_snprintf (urlbuf, sizeof (urlbuf), "%*s", len, data);
-
-       if (!rspamd_cl_fetch_url (urlbuf, &buf, &buflen, err)) {
-               return FALSE;
-       }
-
-       if (check_signature) {
-#if (defined(HAVE_OPENSSL) && OPENSSL_VERSION_NUMBER >= 0x10000000L)
-               /* We need to check signature first */
-               rspamd_snprintf (urlbuf, sizeof (urlbuf), "%*s.sig", len, data);
-               if (!rspamd_cl_fetch_file (urlbuf, &sigbuf, &siglen, err)) {
-                       return FALSE;
-               }
-               if (!rspamd_cl_sig_check (buf, buflen, sigbuf, siglen, parser)) {
-                       g_set_error (err, RCL_ERROR, RSPAMD_CL_ESSL, "cannot verify url %s: %s",
-                                                       urlbuf,
-                                                       ERR_error_string (ERR_get_error (), NULL));
-                       munmap (sigbuf, siglen);
-                       return FALSE;
-               }
-               munmap (sigbuf, siglen);
-#endif
-       }
-
-       res = rspamd_cl_parser_add_chunk (parser, buf, buflen, err);
-       if (res == TRUE) {
-               /* Remove chunk from the stack */
-               chunk = parser->chunks;
-               if (chunk != NULL) {
-                       parser->chunks = chunk->next;
-                       g_slice_free1 (sizeof (struct rspamd_cl_chunk), chunk);
-               }
-       }
-       g_free (buf);
-
-       return res;
-}
-
-/**
- * Include a file to configuration
- * @param data
- * @param len
- * @param parser
- * @param err
- * @return
- */
-static gboolean
-rspamd_cl_include_file (const guchar *data, gsize len,
-               struct rspamd_cl_parser *parser, gboolean check_signature, GError **err)
-{
-       gboolean res;
-       struct rspamd_cl_chunk *chunk;
-       guchar *buf = NULL, *sigbuf = NULL;
-       gsize buflen, siglen;
-       gchar filebuf[PATH_MAX], realbuf[PATH_MAX];
-
-       rspamd_snprintf (filebuf, sizeof (filebuf), "%*s", len, data);
-       if (realpath (filebuf, realbuf) == NULL) {
-               g_set_error (err, RCL_ERROR, RSPAMD_CL_EIO, "cannot open file %s: %s",
-                                                                       filebuf,
-                                                                       strerror (errno));
-               return FALSE;
-       }
-
-       if (!rspamd_cl_fetch_file (realbuf, &buf, &buflen, err)) {
-               return FALSE;
-       }
-
-       if (check_signature) {
-#if (defined(HAVE_OPENSSL) && OPENSSL_VERSION_NUMBER >= 0x10000000L)
-               /* We need to check signature first */
-               rspamd_snprintf (filebuf, sizeof (filebuf), "%s.sig", realbuf);
-               if (!rspamd_cl_fetch_file (filebuf, &sigbuf, &siglen, err)) {
-                       return FALSE;
-               }
-               if (!rspamd_cl_sig_check (buf, buflen, sigbuf, siglen, parser)) {
-                       g_set_error (err, RCL_ERROR, RSPAMD_CL_ESSL, "cannot verify file %s: %s",
-                                                       filebuf,
-                                                       ERR_error_string (ERR_get_error (), NULL));
-                       munmap (sigbuf, siglen);
-                       return FALSE;
-               }
-               munmap (sigbuf, siglen);
-#endif
-       }
-
-       res = rspamd_cl_parser_add_chunk (parser, buf, buflen, err);
-       if (res == TRUE) {
-               /* Remove chunk from the stack */
-               chunk = parser->chunks;
-               if (chunk != NULL) {
-                       parser->chunks = chunk->next;
-                       g_slice_free1 (sizeof (struct rspamd_cl_chunk), chunk);
-               }
-       }
-       munmap (buf, buflen);
-
-       return res;
-}
-
-/**
- * Handle include macro
- * @param data include data
- * @param len length of data
- * @param ud user data
- * @param err error ptr
- * @return
- */
-gboolean
-rspamd_cl_include_handler (const guchar *data, gsize len, gpointer ud, GError **err)
-{
-       struct rspamd_cl_parser *parser = ud;
-
-       if (*data == '/' || *data == '.') {
-               /* Try to load a file */
-               return rspamd_cl_include_file (data, len, parser, FALSE, err);
-       }
-
-       return rspamd_cl_include_url (data, len, parser, FALSE, err);
-}
-
-/**
- * Handle includes macro
- * @param data include data
- * @param len length of data
- * @param ud user data
- * @param err error ptr
- * @return
- */
-gboolean
-rspamd_cl_includes_handler (const guchar *data, gsize len, gpointer ud, GError **err)
-{
-       struct rspamd_cl_parser *parser = ud;
-
-       if (*data == '/' || *data == '.') {
-               /* Try to load a file */
-               return rspamd_cl_include_file (data, len, parser, TRUE, err);
-       }
-
-       return rspamd_cl_include_url (data, len, parser, TRUE, err);
-}
-
-gboolean
-rspamd_cl_parser_add_file (struct rspamd_cl_parser *parser, const gchar *filename,
-               GError **err)
-{
-       guchar *buf;
-       gsize len;
-       gboolean ret;
-
-       if (!rspamd_cl_fetch_file (filename, &buf, &len, err)) {
-               return FALSE;
-       }
-
-       ret = rspamd_cl_parser_add_chunk (parser, buf, len, err);
-
-       munmap (buf, len);
-
-       return ret;
-}
diff --git a/src/ucl/.gitignore b/src/ucl/.gitignore
new file mode 100644 (file)
index 0000000..ea72388
--- /dev/null
@@ -0,0 +1,3 @@
+.cproject
+.project
+.settings
diff --git a/src/ucl/CMakeLists.txt b/src/ucl/CMakeLists.txt
new file mode 100644 (file)
index 0000000..73937d1
--- /dev/null
@@ -0,0 +1,20 @@
+SET(UCLSRC            src/ucl_util.c
+                      src/ucl_parser.c
+                      src/ucl_emitter.c)
+
+ADD_LIBRARY(rspamd-ucl ${LINK_TYPE} ${UCLSRC})
+SET_TARGET_PROPERTIES(rspamd-ucl PROPERTIES VERSION ${RSPAMD_VERSION})
+SET_TARGET_PROPERTIES(rspamd-ucl PROPERTIES COMPILE_FLAGS "-DRSPAMD_LIB")
+
+IF(HAVE_FETCH_H)
+    TARGET_LINK_LIBRARIES(rspamd-ucl fetch)
+ELSE(HAVE_FETCH_H)
+    IF(CURL_FOUND)
+        TARGET_LINK_LIBRARIES(rspamd-ucl ${CURL_LIBRARIES})
+    ENDIF(CURL_FOUND)
+ENDIF(HAVE_FETCH_H)
+IF(NO_SHARED MATCHES "OFF")
+    INSTALL(TARGETS rspamd-ucl
+        LIBRARY DESTINATION ${LIBDIR}
+        PUBLIC_HEADER DESTINATION ${INCLUDEDIR})
+ENDIF(NO_SHARED MATCHES "OFF")        
\ No newline at end of file
diff --git a/src/ucl/README.md b/src/ucl/README.md
new file mode 100644 (file)
index 0000000..18779a8
--- /dev/null
@@ -0,0 +1,236 @@
+## Introduction
+
+This document describes the main features and principles of the configuration
+language called `UCL` - universal configuration language.
+
+## Basic structure
+
+UCL is heavily infused by `nginx` configuration as the example of a convenient configuration
+system. However, UCL is fully compatible with `JSON` format and is able to parse json files.
+For example, you can write the same configuration in the following ways:
+
+* in nginx like:
+
+```nginx
+param = value;
+section {
+    param = value;
+    param1 = value1;
+    flag = true;
+    number = 10k;
+    time = 0.2s;
+    string = "something";
+    subsection {
+        host = {
+            host = "hostname"; 
+            port = 900;
+        }
+        host = {
+            host = "hostname";
+            port = 901;
+        }
+    }
+}
+```
+
+* or in JSON:
+
+```json
+{
+    "param": "value",
+    "param1": "value1",
+    "flag": true,
+    "subsection": {
+        "host": [
+        {
+            "host": "hostname",
+            "port": 900
+        },
+        {
+            "host": "hostname",
+            "port": 901
+        }
+        ]
+    }
+}
+```
+
+## Improvements to the json notation.
+
+There are various things that make ucl configuration more convenient for editing than strict json:
+
+### General syntax sugar
+
+* Braces are not necessary to enclose a top object: it is automatically treated as an object:
+
+```json
+"key": "value"
+```
+is equal to:
+```json
+{"key": "value"}
+```
+
+* There is no requirement of quotes for strings and keys, moreover, `:` may be replaced `=` or even be skipped for objects:
+
+```nginx
+key = value;
+section {
+    key = value;
+}
+```
+is equal to:
+```json
+{
+    "key": "value",
+    "section": {
+        "key": "value"
+    }
+}
+```
+
+* No commas mess: you can safely place a comma or semicolon for the last element in an array or an object:
+
+```json
+{
+    "key1": "value",
+    "key2": "value",
+}
+```
+### Automatic arrays creation
+
+* Non-unique keys in an object are allowed and are automatically converted to the arrays internally:
+
+```json
+{
+    "key": "value1",
+    "key": "value2"
+}
+```
+is converted to:
+```json
+{
+    "key": ["value1", "value2"]
+}
+```
+
+### Convenient numbers and booleans
+
+* Numbers can have suffixes to specify standard multipliers:
+    + `[kKmMgG]` - standard 10 base multipliers (so `1k` is translated to 1000)
+    + `[kKmMgG]b` - 2 power multipliers (so `1kb` is translated to 1024)
+    + `[s|min|d|w|y]` - time multipliers, all time values are translated to float number of seconds, for example `10min` is translated to 600.0 and `10ms` is translated to 0.01
+* Booleans can be specified as `true` or `yes` or `on` and `false` or `no` or `off`.
+* It is still possible to treat numbers and booleans as strings by enclosing them in double quotes.
+
+## General improvements
+
+### Commments
+
+UCL supports different style of comments:
+
+* single line: `#` 
+* multiline: `/* ... */`
+
+Multiline comments may be nested:
+```c
+# Sample single line comment
+/* 
+ some comment
+ /* nested comment */
+ end of comment
+*/
+```
+
+### Macros support
+
+UCL supports external macros both multiline and single line ones:
+```nginx
+.macro "sometext";
+.macro {
+     Some long text
+     ....
+};
+```
+There are two internal macros provided by UCL:
+
+* `include` - read a file `/path/to/file` or an url `http://example.com/file` and include it to the current place of
+UCL configuration;
+* `includes` - read a file or an url like the previous macro, but fetch and check the signature file (which is obtained
+by `.sig` suffix appending).
+
+Public keys which are used for the last command are specified by the concrete UCL user.
+
+### Multiline strings
+
+UCL can handle multiline strings as well as single line ones. It uses shell/perl like notation for such objects:
+```
+key = <<EOD
+some text
+splitted to
+lines
+EOD
+```
+
+In this example `key` will be interpreted as the following string: `some text\nsplitted to\nlines`.
+Here are some rules for this syntax:
+
+* Multiline terminator must start just after `<<` symbols and it must consist of capital letters only (e.g. `<<eof` or `<< EOF` won't work);
+* Terminator must end with a single newline character (and no spaces are allowed between terminator and newline character);
+* To finish multiline string you need to include a terminator string just after newline and followed by a newline (no spaces or other characters are allowed as well);
+* The initial and the final newlines are not inserted to the resulting string, but you can still specify newlines at the begin and at the end of a value, for example:
+
+```
+key <<EOD
+
+some
+text
+
+EOD
+```
+
+## Emitter
+
+Each UCL object can be serialized to one of the three supported formats:
+
+* `JSON` - canonic json notation (with spaces indented structure);
+* `Compacted JSON` - compact json notation (without spaces or newlines);
+* `Configuration` - nginx like notation;
+* `YAML` - yaml inlined notation.
+
+## Performance
+
+Are UCL parser and emitter fast enough? Well, there are some numbers.
+I got a 19Mb file that consist of ~700 thousands lines of json (obtained via
+http://www.json-generator.com/). Then I checked jansson library that performs json
+parsing and emitting and compared it with UCL. Here are results:
+
+```
+jansson: parsed json in 1.3899 seconds
+jansson: emitted object in 0.2609 seconds
+
+ucl: parsed input in 0.6649 seconds
+ucl: emitted config in 0.2423 seconds
+ucl: emitted json in 0.2329 seconds
+ucl: emitted compact json in 0.1811 seconds
+ucl: emitted yaml in 0.2489 seconds
+```
+
+So far, UCL seems to be significantly faster than jansson on parsing and slightly faster on emitting. Moreover,
+UCL compiled with optimizations (-O3) performs significantly faster:
+```
+ucl: parsed input in 0.3002 seconds
+ucl: emitted config in 0.1174 seconds
+ucl: emitted json in 0.1174 seconds
+ucl: emitted compact json in 0.0991 seconds
+ucl: emitted yaml in 0.1354 seconds
+```
+
+You can do your own benchmarks by running `make test` in libucl top directory.
+
+## Conclusion
+
+UCL has clear design that should be very convenient for reading and writing. At the same time it is compatible with
+JSON language and therefore can be used as a simple JSON parser. Macroes logic provides an ability to extend configuration
+language (for example by including some lua code) and comments allows to disable or enable the parts of a configuration
+quickly.
diff --git a/src/ucl/include/ucl.h b/src/ucl/include/ucl.h
new file mode 100644 (file)
index 0000000..1b8fa86
--- /dev/null
@@ -0,0 +1,731 @@
+/* Copyright (c) 2013, 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 ''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.
+ */
+
+#ifndef RCL_H_
+#define RCL_H_
+
+#include <string.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include "config.h"
+
+#include "uthash.h"
+#include "utlist.h"
+#include "utstring.h"
+
+/**
+ * @file rcl.h
+ * RCL is an rspamd configuration language, which is a form of
+ * JSON with less strict rules that make it more comfortable for
+ * using as a configuration language
+ */
+
+/**
+ * XXX: Poorly named API functions, need to replace them with the appropriate
+ * named function. All API functions *must* use naming ucl_object_*. Usage of
+ * ucl_obj* should be avoided.
+ */
+#define ucl_object_todouble_safe ucl_obj_todouble_safe
+#define ucl_object_todouble ucl_obj_todouble
+#define ucl_object_tostring ucl_obj_tostring
+#define ucl_object_tostring_safe ucl_obj_tostring_safe
+#define ucl_object_tolstring ucl_obj_tolstring
+#define ucl_object_tolstring_safe ucl_obj_tolstring_safe
+#define ucl_object_toint ucl_obj_toint
+#define ucl_object_toint_safe ucl_obj_toint_safe
+#define ucl_object_toboolean ucl_obj_toboolean
+#define ucl_object_toboolean_safe ucl_obj_toboolean_safe
+#define ucl_object_find_key ucl_obj_get_key
+#define ucl_object_find_keyl ucl_obj_get_keyl
+#define ucl_object_unref ucl_obj_unref
+#define ucl_object_ref ucl_obj_ref
+#define ucl_object_free ucl_obj_free
+
+/**
+ * Memory allocation utilities
+ * UCL_ALLOC(size) - allocate memory for UCL
+ * UCL_FREE(size, ptr) - free memory of specified size at ptr
+ * Default: malloc and free
+ */
+#ifndef UCL_ALLOC
+#define UCL_ALLOC(size) malloc(size)
+#endif
+#ifndef UCL_FREE
+#define UCL_FREE(size, ptr) free(ptr)
+#endif
+
+enum ucl_error {
+       UCL_EOK = 0,   //!< UCL_EOK
+       UCL_ESYNTAX,   //!< UCL_ESYNTAX
+       UCL_EIO,       //!< UCL_EIO
+       UCL_ESTATE,    //!< UCL_ESTATE
+       UCL_ENESTED,   //!< UCL_ENESTED
+       UCL_EMACRO,    //!< UCL_EMACRO
+       UCL_ERECURSION,//!< UCL_ERECURSION
+       UCL_EINTERNAL, //!< UCL_EINTERNAL
+       UCL_ESSL       //!< UCL_ESSL
+};
+
+enum ucl_type {
+       UCL_OBJECT = 0,
+       UCL_ARRAY,
+       UCL_INT,
+       UCL_FLOAT,
+       UCL_STRING,
+       UCL_BOOLEAN,
+       UCL_TIME,
+       UCL_USERDATA
+};
+
+enum ucl_emitter {
+       UCL_EMIT_JSON = 0,
+       UCL_EMIT_JSON_COMPACT,
+       UCL_EMIT_CONFIG,
+       UCL_EMIT_YAML
+};
+
+enum ucl_flags {
+       UCL_FLAG_KEY_LOWERCASE = 0x1,
+       UCL_FLAG_ZEROCOPY = 0x2
+};
+
+typedef struct ucl_object_s {
+       union {
+               int64_t iv;                                                     /**< int value of an object */
+               const char *sv;                                 /**< string value of an object */
+               double dv;                                                      /**< double value of an object */
+               struct ucl_object_s *ov;                        /**< array or hash                      */
+               void* ud;                                                       /**< opaque user data           */
+       } value;
+       enum ucl_type type;                                             /**< real type                          */
+       int ref;                                                                /**< reference count            */
+       size_t len;                                                             /**< size of an object          */
+       struct ucl_object_s *next;                              /**< array handle                       */
+       struct ucl_object_s *prev;                              /**< array handle                       */
+       unsigned char* trash_stack[2];                  /**< pointer to allocated chunks */
+       UT_hash_handle hh;                                              /**< hash handle                        */
+} ucl_object_t;
+
+
+/**
+ * Copy and return a key of an object, returned key is zero-terminated
+ * @param obj CL object
+ * @return zero terminated key
+ */
+char* ucl_copy_key_trash (ucl_object_t *obj);
+
+/**
+ * Copy and return a string value of an object, returned key is zero-terminated
+ * @param obj CL object
+ * @return zero terminated string representation of object value
+ */
+char* ucl_copy_value_trash (ucl_object_t *obj);
+
+/**
+ * Creates a new object
+ * @return new object
+ */
+static inline ucl_object_t *
+ucl_object_new (void)
+{
+       ucl_object_t *new;
+       new = malloc (sizeof (ucl_object_t));
+       if (new != NULL) {
+               memset (new, 0, sizeof (ucl_object_t));
+               new->ref = 1;
+       }
+       return new;
+}
+
+/**
+ * String conversion flags
+ */
+enum ucl_string_flags {
+       UCL_STRING_ESCAPE = 0x1,  /**< UCL_STRING_ESCAPE perform JSON escape */
+       UCL_STRING_TRIM = 0x2,    /**< UCL_STRING_TRIM trim leading and trailing whitespaces */
+       UCL_STRING_PARSE_BOOLEAN = 0x4,    /**< UCL_STRING_PARSE_BOOLEAN parse passed string and detect boolean */
+       UCL_STRING_PARSE_INT = 0x8,    /**< UCL_STRING_PARSE_INT parse passed string and detect integer number */
+       UCL_STRING_PARSE_DOUBLE = 0x10,    /**< UCL_STRING_PARSE_DOUBLE parse passed string and detect integer or float number */
+       UCL_STRING_PARSE_NUMBER =  UCL_STRING_PARSE_INT|UCL_STRING_PARSE_DOUBLE ,  /**<
+                                                                       UCL_STRING_PARSE_NUMBER parse passed string and detect number */
+       UCL_STRING_PARSE =  UCL_STRING_PARSE_BOOLEAN|UCL_STRING_PARSE_NUMBER   /**<
+                                                                       UCL_STRING_PARSE parse passed string (and detect booleans and numbers) */
+};
+
+/**
+ * Convert any string to an ucl object making the specified transformations
+ * @param str fixed size or NULL terminated string
+ * @param len length (if len is zero, than str is treated as NULL terminated)
+ * @param flags conversion flags
+ * @return new object
+ */
+ucl_object_t * ucl_object_fromstring_common (const char *str, size_t len, enum ucl_string_flags flags);
+
+/**
+ * Create a UCL object from the specified string
+ * @param str NULL terminated string, will be json escaped
+ * @return new object
+ */
+static inline ucl_object_t *
+ucl_object_fromstring (const char *str)
+{
+       return ucl_object_fromstring_common (str, 0, UCL_STRING_ESCAPE);
+}
+
+/**
+ * Create a UCL object from the specified string
+ * @param str fixed size string, will be json escaped
+ * @param len length of a string
+ * @return new object
+ */
+static inline ucl_object_t *
+ucl_object_fromlstring (const char *str, size_t len)
+{
+       return ucl_object_fromstring_common (str, len, UCL_STRING_ESCAPE);
+}
+
+/**
+ * Create an object from an integer number
+ * @param iv number
+ * @return new object
+ */
+static inline ucl_object_t *
+ucl_object_fromint (int64_t iv)
+{
+       ucl_object_t *obj;
+
+       obj = ucl_object_new ();
+       if (obj != NULL) {
+               obj->type = UCL_INT;
+               obj->value.iv = iv;
+       }
+
+       return obj;
+}
+
+/**
+ * Create an object from a float number
+ * @param dv number
+ * @return new object
+ */
+static inline ucl_object_t *
+ucl_object_fromdouble (double dv)
+{
+       ucl_object_t *obj;
+
+       obj = ucl_object_new ();
+       if (obj != NULL) {
+               obj->type = UCL_FLOAT;
+               obj->value.dv = dv;
+       }
+
+       return obj;
+}
+
+/**
+ * Create an object from a boolean
+ * @param bv bool value
+ * @return new object
+ */
+static inline ucl_object_t *
+ucl_object_frombool (bool bv)
+{
+       ucl_object_t *obj;
+
+       obj = ucl_object_new ();
+       if (obj != NULL) {
+               obj->type = UCL_BOOLEAN;
+               obj->value.iv = bv;
+       }
+
+       return obj;
+}
+
+/**
+ * Insert a object 'elt' to the hash 'top' and associate it with key 'key'
+ * @param top destination object (will be created automatically if top is NULL)
+ * @param elt element to insert (must NOT be NULL)
+ * @param key key to associate with this object (either const or preallocated)
+ * @param keylen length of the key (or 0 for NULL terminated keys)
+ * @param copy_key make an internal copy of key
+ * @return new value of top object
+ */
+static inline ucl_object_t *
+ucl_object_insert_key (ucl_object_t *top, ucl_object_t *elt,
+               const char *key, size_t keylen, bool copy_key)
+{
+       ucl_object_t *found;
+
+       if (elt == NULL || key == NULL) {
+               return NULL;
+       }
+
+       if (top == NULL) {
+               top = ucl_object_new ();
+               top->type = UCL_OBJECT;
+       }
+       if (keylen == 0) {
+               keylen = strlen (key);
+       }
+
+       HASH_FIND (hh, top->value.ov, key, keylen, found);
+
+       if (!found) {
+               HASH_ADD_KEYPTR (hh, top->value.ov, key, keylen, elt);
+       }
+       DL_APPEND (found, elt);
+
+       if (copy_key) {
+               ucl_copy_key_trash (elt);
+       }
+
+       return top;
+}
+
+/**
+ * Append an element to the array object
+ * @param top destination object (will be created automatically if top is NULL)
+ * @param eltelement to append (must NOT be NULL)
+ * @return new value of top object
+ */
+static inline ucl_object_t *
+ucl_array_append (ucl_object_t *top, ucl_object_t *elt)
+{
+       if (elt == NULL) {
+               return NULL;
+       }
+
+       if (top == NULL) {
+               top = ucl_object_new ();
+               top->type = UCL_ARRAY;
+       }
+
+       DL_APPEND (top->value.ov, elt);
+
+       return top;
+}
+
+/**
+ * Append a element to another element forming an implicit array
+ * @param head head to append (may be NULL)
+ * @param elt new element
+ * @return new head if applicable
+ */
+static inline ucl_object_t *
+ucl_elt_append (ucl_object_t *head, ucl_object_t *elt)
+{
+       DL_APPEND (head, elt);
+       return head;
+}
+
+/**
+ * Converts an object to double value
+ * @param obj CL object
+ * @param target target double variable
+ * @return true if conversion was successful
+ */
+static inline bool
+ucl_obj_todouble_safe (ucl_object_t *obj, double *target)
+{
+       if (obj == NULL) {
+               return false;
+       }
+       switch (obj->type) {
+       case UCL_INT:
+               *target = obj->value.iv; /* Probaly could cause overflow */
+               break;
+       case UCL_FLOAT:
+       case UCL_TIME:
+               *target = obj->value.dv;
+               break;
+       default:
+               return false;
+       }
+
+       return true;
+}
+
+/**
+ * Unsafe version of \ref ucl_obj_todouble_safe
+ * @param obj CL object
+ * @return double value
+ */
+static inline double
+ucl_obj_todouble (ucl_object_t *obj)
+{
+       double result = 0.;
+
+       ucl_object_todouble_safe (obj, &result);
+       return result;
+}
+
+/**
+ * Converts an object to integer value
+ * @param obj CL object
+ * @param target target integer variable
+ * @return true if conversion was successful
+ */
+static inline bool
+ucl_obj_toint_safe (ucl_object_t *obj, int64_t *target)
+{
+       if (obj == NULL) {
+               return false;
+       }
+       switch (obj->type) {
+       case UCL_INT:
+               *target = obj->value.iv;
+               break;
+       case UCL_FLOAT:
+       case UCL_TIME:
+               *target = obj->value.dv; /* Loosing of decimal points */
+               break;
+       default:
+               return false;
+       }
+
+       return true;
+}
+
+/**
+ * Unsafe version of \ref ucl_obj_toint_safe
+ * @param obj CL object
+ * @return int value
+ */
+static inline int64_t
+ucl_obj_toint (ucl_object_t *obj)
+{
+       int64_t result = 0;
+
+       ucl_object_toint_safe (obj, &result);
+       return result;
+}
+
+/**
+ * Converts an object to boolean value
+ * @param obj CL object
+ * @param target target boolean variable
+ * @return true if conversion was successful
+ */
+static inline bool
+ucl_obj_toboolean_safe (ucl_object_t *obj, bool *target)
+{
+       if (obj == NULL) {
+               return false;
+       }
+       switch (obj->type) {
+       case UCL_BOOLEAN:
+               *target = (obj->value.iv == true);
+               break;
+       default:
+               return false;
+       }
+
+       return true;
+}
+
+/**
+ * Unsafe version of \ref ucl_obj_toboolean_safe
+ * @param obj CL object
+ * @return boolean value
+ */
+static inline bool
+ucl_obj_toboolean (ucl_object_t *obj)
+{
+       bool result = false;
+
+       ucl_object_toboolean_safe (obj, &result);
+       return result;
+}
+
+/**
+ * Converts an object to string value
+ * @param obj CL object
+ * @param target target string variable, no need to free value
+ * @return true if conversion was successful
+ */
+static inline bool
+ucl_obj_tostring_safe (ucl_object_t *obj, const char **target)
+{
+       if (obj == NULL) {
+               return false;
+       }
+
+       switch (obj->type) {
+       case UCL_STRING:
+               *target = ucl_copy_value_trash (obj);
+               break;
+       default:
+               return false;
+       }
+
+       return true;
+}
+
+/**
+ * Unsafe version of \ref ucl_obj_tostring_safe
+ * @param obj CL object
+ * @return string value
+ */
+static inline const char *
+ucl_obj_tostring (ucl_object_t *obj)
+{
+       const char *result = NULL;
+
+       ucl_object_tostring_safe (obj, &result);
+       return result;
+}
+
+/**
+ * Convert any object to a string in JSON notation if needed
+ * @param obj CL object
+ * @return string value
+ */
+static inline const char *
+ucl_obj_tostring_forced (ucl_object_t *obj)
+{
+       return ucl_copy_value_trash (obj);
+}
+
+/**
+ * Return string as char * and len, string may be not zero terminated, more efficient that tostring as it
+ * allows zero-copy
+ * @param obj CL object
+ * @param target target string variable, no need to free value
+ * @param tlen target length
+ * @return true if conversion was successful
+ */
+static inline bool
+ucl_obj_tolstring_safe (ucl_object_t *obj, const char **target, size_t *tlen)
+{
+       if (obj == NULL) {
+               return false;
+       }
+       switch (obj->type) {
+       case UCL_STRING:
+               *target = obj->value.sv;
+               *tlen = obj->len;
+               break;
+       default:
+               return false;
+       }
+
+       return true;
+}
+
+/**
+ * Unsafe version of \ref ucl_obj_tolstring_safe
+ * @param obj CL object
+ * @return string value
+ */
+static inline const char *
+ucl_obj_tolstring (ucl_object_t *obj, size_t *tlen)
+{
+       const char *result = NULL;
+
+       ucl_object_tolstring_safe (obj, &result, tlen);
+       return result;
+}
+
+/**
+ * Return object identified by a key in the specified object
+ * @param obj object to get a key from (must be of type UCL_OBJECT)
+ * @param key key to search
+ * @return object matched the specified key or NULL if key is not found
+ */
+static inline ucl_object_t *
+ucl_obj_get_key (ucl_object_t *obj, const char *key)
+{
+       size_t keylen;
+       ucl_object_t *ret;
+
+       if (obj == NULL || obj->type != UCL_OBJECT || key == NULL) {
+               return NULL;
+       }
+
+       keylen = strlen (key);
+       HASH_FIND (hh, obj->value.ov, key, keylen, ret);
+
+       return ret;
+}
+
+/**
+ * Return object identified by a fixed size key in the specified object
+ * @param obj object to get a key from (must be of type UCL_OBJECT)
+ * @param key key to search
+ * @param klen length of a key
+ * @return object matched the specified key or NULL if key is not found
+ */
+static inline ucl_object_t *
+ucl_obj_get_keyl (ucl_object_t *obj, const char *key, size_t klen)
+{
+       ucl_object_t *ret;
+
+       if (obj == NULL || obj->type != UCL_OBJECT || key == NULL) {
+               return NULL;
+       }
+
+       HASH_FIND (hh, obj->value.ov, key, klen, ret);
+
+       return ret;
+}
+
+/**
+ * Returns a key of an object as a NULL terminated string
+ * @param obj CL object
+ * @return key or NULL if there is no key
+ */
+static inline const char *
+ucl_object_key (ucl_object_t *obj)
+{
+       return ucl_copy_key_trash (obj);
+}
+
+/**
+ * Returns a key of an object as a fixed size string (may be more efficient)
+ * @param obj CL object
+ * @param len target key length
+ * @return key pointer
+ */
+static inline const char *
+ucl_object_keyl (ucl_object_t *obj, size_t *len)
+{
+       *len = obj->hh.keylen;
+       return obj->hh.key;
+}
+
+/**
+ * Macro handler for a parser
+ * @param data the content of macro
+ * @param len the length of content
+ * @param ud opaque user data
+ * @param err error pointer
+ * @return true if macro has been parsed
+ */
+typedef bool (*ucl_macro_handler) (const unsigned char *data, size_t len, void* ud, UT_string **err);
+
+/* Opaque parser */
+struct ucl_parser;
+
+/**
+ * Creates new parser object
+ * @param pool pool to allocate memory from
+ * @return new parser object
+ */
+struct ucl_parser* ucl_parser_new (int flags);
+
+/**
+ * Register new handler for a macro
+ * @param parser parser object
+ * @param macro macro name (without leading dot)
+ * @param handler handler (it is called immediately after macro is parsed)
+ * @param ud opaque user data for a handler
+ */
+void ucl_parser_register_macro (struct ucl_parser *parser, const char *macro,
+               ucl_macro_handler handler, void* ud);
+
+/**
+ * Load new chunk to a parser
+ * @param parser parser structure
+ * @param data the pointer to the beginning of a chunk
+ * @param len the length of a chunk
+ * @param err if *err is NULL it is set to parser error
+ * @return true if chunk has been added and false in case of error
+ */
+bool ucl_parser_add_chunk (struct ucl_parser *parser, const unsigned char *data,
+               size_t len, UT_string **err);
+
+/**
+ * Load and add data from a file
+ * @param parser parser structure
+ * @param filename the name of file
+ * @param err if *err is NULL it is set to parser error
+ * @return true if chunk has been added and false in case of error
+ */
+bool ucl_parser_add_file (struct ucl_parser *parser, const char *filename,
+               UT_string **err);
+
+/**
+ * Get a top object for a parser
+ * @param parser parser structure
+ * @param err if *err is NULL it is set to parser error
+ * @return top parser object or NULL
+ */
+ucl_object_t* ucl_parser_get_object (struct ucl_parser *parser, UT_string **err);
+
+/**
+ * Free cl parser object
+ * @param parser parser object
+ */
+void ucl_parser_free (struct ucl_parser *parser);
+
+/**
+ * Free cl object
+ * @param obj cl object to free
+ */
+void ucl_obj_free (ucl_object_t *obj);
+
+/**
+ * Icrease reference count for an object
+ * @param obj object to ref
+ */
+static inline ucl_object_t *
+ucl_obj_ref (ucl_object_t *obj) {
+       obj->ref ++;
+       return obj;
+}
+
+/**
+ * Decrease reference count for an object
+ * @param obj object to unref
+ */
+static inline void
+ucl_obj_unref (ucl_object_t *obj) {
+       if (--obj->ref <= 0) {
+               ucl_obj_free (obj);
+       }
+}
+
+/**
+ * Emit object to a string
+ * @param obj object
+ * @param emit_type if type is UCL_EMIT_JSON then emit json, if type is
+ * UCL_EMIT_CONFIG then emit config like object
+ * @return dump of an object (must be freed after using) or NULL in case of error
+ */
+unsigned char *ucl_object_emit (ucl_object_t *obj, enum ucl_emitter emit_type);
+
+/**
+ * Add new public key to parser for signatures check
+ * @param parser parser object
+ * @param key PEM representation of a key
+ * @param len length of the key
+ * @param err if *err is NULL it is set to parser error
+ * @return true if a key has been successfully added
+ */
+bool ucl_pubkey_add (struct ucl_parser *parser, const unsigned char *key, size_t len, UT_string **err);
+
+#endif /* RCL_H_ */
diff --git a/src/ucl/src/ucl_chartable.h b/src/ucl/src/ucl_chartable.h
new file mode 100644 (file)
index 0000000..232043c
--- /dev/null
@@ -0,0 +1,266 @@
+/* Copyright (c) 2013, 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 ''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.
+ */
+
+#ifndef UCL_CHARTABLE_H_
+#define UCL_CHARTABLE_H_
+
+#include "ucl_internal.h"
+
+static const unsigned int ucl_chartable[255] = {
+UCL_CHARACTER_VALUE_END, UCL_CHARACTER_DENIED, UCL_CHARACTER_DENIED,
+UCL_CHARACTER_DENIED, UCL_CHARACTER_DENIED, UCL_CHARACTER_DENIED,
+UCL_CHARACTER_DENIED, UCL_CHARACTER_DENIED, UCL_CHARACTER_JSON_UNSAFE,
+UCL_CHARACTER_WHITESPACE|UCL_CHARACTER_WHITESPACE_UNSAFE|UCL_CHARACTER_KEY_SEP|UCL_CHARACTER_JSON_UNSAFE,
+UCL_CHARACTER_WHITESPACE_UNSAFE|UCL_CHARACTER_VALUE_END|UCL_CHARACTER_JSON_UNSAFE,
+UCL_CHARACTER_WHITESPACE_UNSAFE,
+UCL_CHARACTER_WHITESPACE_UNSAFE|UCL_CHARACTER_JSON_UNSAFE,
+UCL_CHARACTER_WHITESPACE_UNSAFE|UCL_CHARACTER_VALUE_END|UCL_CHARACTER_JSON_UNSAFE,
+UCL_CHARACTER_DENIED, UCL_CHARACTER_DENIED, UCL_CHARACTER_DENIED,
+UCL_CHARACTER_DENIED, UCL_CHARACTER_DENIED, UCL_CHARACTER_DENIED,
+UCL_CHARACTER_DENIED, UCL_CHARACTER_DENIED, UCL_CHARACTER_DENIED,
+UCL_CHARACTER_DENIED, UCL_CHARACTER_DENIED, UCL_CHARACTER_DENIED,
+UCL_CHARACTER_DENIED, UCL_CHARACTER_DENIED, UCL_CHARACTER_DENIED,
+UCL_CHARACTER_DENIED, UCL_CHARACTER_DENIED, UCL_CHARACTER_DENIED,
+UCL_CHARACTER_WHITESPACE|UCL_CHARACTER_WHITESPACE_UNSAFE|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_KEY_SEP /*   */,
+UCL_CHARACTER_VALUE_STR /* ! */,
+UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_ESCAPE|UCL_CHARACTER_JSON_UNSAFE /* " */,
+UCL_CHARACTER_VALUE_END /* # */, UCL_CHARACTER_VALUE_STR /* $ */,
+UCL_CHARACTER_VALUE_STR /* % */, UCL_CHARACTER_VALUE_STR /* & */,
+UCL_CHARACTER_VALUE_STR /* ' */, UCL_CHARACTER_VALUE_STR /* ( */,
+UCL_CHARACTER_VALUE_STR /* ) */, UCL_CHARACTER_VALUE_STR /* * */,
+UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* + */,
+UCL_CHARACTER_VALUE_END /* , */,
+UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT_START|UCL_CHARACTER_VALUE_DIGIT /* - */,
+UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* . */,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_ESCAPE /* / */,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT_START|UCL_CHARACTER_VALUE_DIGIT /* 0 */,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT_START|UCL_CHARACTER_VALUE_DIGIT /* 1 */,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT_START|UCL_CHARACTER_VALUE_DIGIT /* 2 */,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT_START|UCL_CHARACTER_VALUE_DIGIT /* 3 */,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT_START|UCL_CHARACTER_VALUE_DIGIT /* 4 */,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT_START|UCL_CHARACTER_VALUE_DIGIT /* 5 */,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT_START|UCL_CHARACTER_VALUE_DIGIT /* 6 */,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT_START|UCL_CHARACTER_VALUE_DIGIT /* 7 */,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT_START|UCL_CHARACTER_VALUE_DIGIT /* 8 */,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT_START|UCL_CHARACTER_VALUE_DIGIT /* 9 */,
+UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_KEY_SEP /* : */,
+UCL_CHARACTER_VALUE_END /* ; */, UCL_CHARACTER_VALUE_STR /* < */,
+UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_KEY_SEP /* = */,
+UCL_CHARACTER_VALUE_STR /* > */, UCL_CHARACTER_VALUE_STR /* ? */,
+UCL_CHARACTER_VALUE_STR /* @ */,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* A */,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* B */,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* C */,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* D */,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* E */,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* F */,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* G */,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* H */,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* I */,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* J */,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* K */,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* L */,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* M */,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* N */,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* O */,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* P */,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* Q */,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* R */,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* S */,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* T */,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* U */,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* V */,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* W */,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* X */,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* Y */,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* Z */,
+UCL_CHARACTER_VALUE_STR /* [ */,
+UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_ESCAPE|UCL_CHARACTER_JSON_UNSAFE /* \ */,
+UCL_CHARACTER_VALUE_END /* ] */, UCL_CHARACTER_VALUE_STR /* ^ */,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR /* _ */,
+UCL_CHARACTER_VALUE_STR /* ` */,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* a */,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT|UCL_CHARACTER_ESCAPE /* b */,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* c */,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* d */,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* e */,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT|UCL_CHARACTER_ESCAPE /* f */,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* g */,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* h */,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* i */,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* j */,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* k */,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* l */,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* m */,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT|UCL_CHARACTER_ESCAPE /* n */,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* o */,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* p */,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* q */,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT|UCL_CHARACTER_ESCAPE /* r */,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* s */,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT|UCL_CHARACTER_ESCAPE /* t */,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT|UCL_CHARACTER_ESCAPE /* u */,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* v */,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* w */,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* x */,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* y */,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* z */,
+UCL_CHARACTER_VALUE_STR /* { */, UCL_CHARACTER_VALUE_STR /* | */,
+UCL_CHARACTER_VALUE_END /* } */, UCL_CHARACTER_VALUE_STR /* ~ */,
+UCL_CHARACTER_DENIED,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR,
+UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR
+};
+
+static inline bool
+ucl_test_character (unsigned char c, int type_flags)
+{
+       return (ucl_chartable[c] & type_flags) != 0;
+}
+
+#endif /* UCL_CHARTABLE_H_ */
diff --git a/src/ucl/src/ucl_emitter.c b/src/ucl/src/ucl_emitter.c
new file mode 100644 (file)
index 0000000..78e321c
--- /dev/null
@@ -0,0 +1,629 @@
+/* Copyright (c) 2013, 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 ''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.
+ */
+
+#include <float.h>
+#include <math.h>
+#include "ucl.h"
+#include "ucl_internal.h"
+#include "ucl_chartable.h"
+
+/**
+ * @file rcl_emitter.c
+ * Serialise RCL object to the RCL format
+ */
+
+
+static void ucl_obj_write_json (ucl_object_t *obj, UT_string *buf, unsigned int tabs, bool start_tabs, bool compact);
+static void ucl_elt_write_rcl (ucl_object_t *obj, UT_string *buf, unsigned int tabs,
+               bool start_tabs, bool is_top, bool expand_array);
+static void ucl_elt_write_yaml (ucl_object_t *obj, UT_string *buf, unsigned int tabs,
+               bool start_tabs, bool compact, bool expand_array);
+
+/**
+ * Add tabulation to the output buffer
+ * @param buf target buffer
+ * @param tabs number of tabs to add
+ */
+static inline void
+ucl_add_tabs (UT_string *buf, unsigned int tabs, bool compact)
+{
+       char *p;
+       unsigned int i;
+
+       if (!compact) {
+               while (buf->n - buf->i <= tabs * 4) {
+                       utstring_reserve (buf, buf->n * 2);
+               }
+               p = &buf->d[buf->i];
+               for (i = 0; i < tabs; i ++) {
+                       memset (&p[i * 4], ' ', 4);
+               }
+               buf->i += i * 4;
+               buf->d[buf->i] = '\0';
+       }
+}
+
+/**
+ * Serialise string
+ * @param str string to emit
+ * @param buf target buffer
+ */
+static void
+ucl_elt_string_write_json (const char *str, size_t size, UT_string *buf)
+{
+       const char *p = str, *c = str;
+       size_t len = 0;
+
+       utstring_append_c (buf, '"');
+       while (size) {
+               if (ucl_test_character (*p, UCL_CHARACTER_JSON_UNSAFE)) {
+                       if (len > 0) {
+                               utstring_append_len (buf, c, len);
+                       }
+                       switch (*p) {
+                       case '\n':
+                               utstring_append_len (buf, "\\n", 2);
+                               break;
+                       case '\r':
+                               utstring_append_len (buf, "\\r", 2);
+                               break;
+                       case '\b':
+                               utstring_append_len (buf, "\\b", 2);
+                               break;
+                       case '\t':
+                               utstring_append_len (buf, "\\t", 2);
+                               break;
+                       case '\f':
+                               utstring_append_len (buf, "\\f", 2);
+                               break;
+                       case '\\':
+                               utstring_append_len (buf, "\\\\", 2);
+                               break;
+                       case '"':
+                               utstring_append_len (buf, "\\\"", 2);
+                               break;
+                       }
+                       len = 0;
+                       c = ++p;
+               }
+               else {
+                       p ++;
+                       len ++;
+               }
+               size --;
+       }
+       if (len > 0) {
+               utstring_append_len (buf, c, len);
+       }
+       utstring_append_c (buf, '"');
+}
+
+static inline void
+ucl_print_float (UT_string *buf, double val)
+{
+       if (val == (double)(int)val) {
+               utstring_printf (buf, "%.1lf", val);
+       }
+       else if (fabs (val - (double)(int)val) < 0.0000001) {
+               /* Write at maximum precision */
+               utstring_printf (buf, "%.*lg", DBL_DIG, val);
+       }
+       else {
+               utstring_printf (buf, "%lf", val);
+       }
+}
+
+/**
+ * Write a single object to the buffer
+ * @param obj object to write
+ * @param buf target buffer
+ */
+static void
+ucl_elt_obj_write_json (ucl_object_t *obj, UT_string *buf, unsigned int tabs, bool start_tabs, bool compact)
+{
+       ucl_object_t *cur, *tmp;
+
+       if (start_tabs) {
+               ucl_add_tabs (buf, tabs, compact);
+       }
+       if (compact) {
+               utstring_append_c (buf, '{');
+       }
+       else {
+               utstring_append_len (buf, "{\n", 2);
+       }
+       HASH_ITER (hh, obj, cur, tmp) {
+               ucl_add_tabs (buf, tabs + 1, compact);
+               if (cur->hh.keylen > 0) {
+                       ucl_elt_string_write_json (cur->hh.key, cur->hh.keylen, buf);
+               }
+               else {
+                       utstring_append_len (buf, "null", 4);
+               }
+               if (compact) {
+                       utstring_append_c (buf, ':');
+               }
+               else {
+                       utstring_append_len (buf, ": ", 2);
+               }
+               ucl_obj_write_json (cur, buf, tabs + 1, false, compact);
+               if (cur->hh.next != NULL) {
+                       if (compact) {
+                               utstring_append_c (buf, ',');
+                       }
+                       else {
+                               utstring_append_len (buf, ",\n", 2);
+                       }
+               }
+               else if (!compact) {
+                       utstring_append_c (buf, '\n');
+               }
+       }
+       ucl_add_tabs (buf, tabs, compact);
+       utstring_append_c (buf, '}');
+}
+
+/**
+ * Write a single array to the buffer
+ * @param obj array to write
+ * @param buf target buffer
+ */
+static void
+ucl_elt_array_write_json (ucl_object_t *obj, UT_string *buf, unsigned int tabs, bool start_tabs, bool compact)
+{
+       ucl_object_t *cur = obj;
+
+       if (start_tabs) {
+               ucl_add_tabs (buf, tabs, compact);
+       }
+       if (compact) {
+               utstring_append_c (buf, '[');
+       }
+       else {
+               utstring_append_len (buf, "[\n", 2);
+       }
+       while (cur) {
+               ucl_elt_write_json (cur, buf, tabs + 1, true, compact);
+               if (cur->next != NULL) {
+                       if (compact) {
+                               utstring_append_c (buf, ',');
+                       }
+                       else {
+                               utstring_append_len (buf, ",\n", 2);
+                       }
+               }
+               else if (!compact) {
+                       utstring_append_c (buf, '\n');
+               }
+               cur = cur->next;
+       }
+       ucl_add_tabs (buf, tabs, compact);
+       utstring_append_c (buf, ']');
+}
+
+/**
+ * Emit a single element
+ * @param obj object
+ * @param buf buffer
+ */
+void
+ucl_elt_write_json (ucl_object_t *obj, UT_string *buf, unsigned int tabs, bool start_tabs, bool compact)
+{
+       switch (obj->type) {
+       case UCL_INT:
+               if (start_tabs) {
+                       ucl_add_tabs (buf, tabs, compact);
+               }
+               utstring_printf (buf, "%jd", (intmax_t)ucl_object_toint (obj));
+               break;
+       case UCL_FLOAT:
+       case UCL_TIME:
+               if (start_tabs) {
+                       ucl_add_tabs (buf, tabs, compact);
+               }
+               ucl_print_float (buf, ucl_object_todouble (obj));
+               break;
+       case UCL_BOOLEAN:
+               if (start_tabs) {
+                       ucl_add_tabs (buf, tabs, compact);
+               }
+               utstring_printf (buf, "%s", ucl_object_toboolean (obj) ? "true" : "false");
+               break;
+       case UCL_STRING:
+               if (start_tabs) {
+                       ucl_add_tabs (buf, tabs, compact);
+               }
+               ucl_elt_string_write_json (obj->value.sv, obj->len, buf);
+               break;
+       case UCL_OBJECT:
+               ucl_elt_obj_write_json (obj->value.ov, buf, tabs, start_tabs, compact);
+               break;
+       case UCL_ARRAY:
+               ucl_elt_array_write_json (obj->value.ov, buf, tabs, start_tabs, compact);
+               break;
+       case UCL_USERDATA:
+               break;
+       }
+}
+
+/**
+ * Write a single object to the buffer
+ * @param obj object
+ * @param buf target buffer
+ */
+static void
+ucl_obj_write_json (ucl_object_t *obj, UT_string *buf, unsigned int tabs, bool start_tabs, bool compact)
+{
+       ucl_object_t *cur;
+       bool is_array = (obj->next != NULL);
+
+       if (is_array) {
+               /* This is an array actually */
+               if (start_tabs) {
+                       ucl_add_tabs (buf, tabs, compact);
+               }
+
+               if (compact) {
+                       utstring_append_c (buf, '[');
+               }
+               else {
+                       utstring_append_len (buf, "[\n", 2);
+               }
+               cur = obj;
+               while (cur != NULL) {
+                       ucl_elt_write_json (cur, buf, tabs + 1, true, compact);
+                       if (cur->next) {
+                               utstring_append_c (buf, ',');
+                       }
+                       if (!compact) {
+                               utstring_append_c (buf, '\n');
+                       }
+                       cur = cur->next;
+               }
+               ucl_add_tabs (buf, tabs, compact);
+               utstring_append_c (buf, ']');
+       }
+       else {
+               ucl_elt_write_json (obj, buf, tabs, start_tabs, compact);
+       }
+
+}
+
+/**
+ * Emit an object to json
+ * @param obj object
+ * @return json output (should be freed after using)
+ */
+static UT_string *
+ucl_object_emit_json (ucl_object_t *obj, bool compact)
+{
+       UT_string *buf;
+
+       /* Allocate large enough buffer */
+       utstring_new (buf);
+
+       ucl_obj_write_json (obj, buf, 0, false, compact);
+
+       return buf;
+}
+
+/**
+ * Write a single object to the buffer
+ * @param obj object to write
+ * @param buf target buffer
+ */
+static void
+ucl_elt_obj_write_rcl (ucl_object_t *obj, UT_string *buf, unsigned int tabs, bool start_tabs, bool is_top)
+{
+       ucl_object_t *cur, *tmp;
+
+       if (start_tabs) {
+               ucl_add_tabs (buf, tabs, is_top);
+       }
+       if (!is_top) {
+               utstring_append_len (buf, "{\n", 2);
+       }
+
+       HASH_ITER (hh, obj, cur, tmp) {
+               ucl_add_tabs (buf, tabs + 1, is_top);
+               utstring_append_len (buf, cur->hh.key, cur->hh.keylen);
+               if (cur->type != UCL_OBJECT && cur->type != UCL_ARRAY) {
+                       utstring_append_len (buf, " = ", 3);
+               }
+               else {
+                       utstring_append_c (buf, ' ');
+               }
+               ucl_elt_write_rcl (cur, buf, is_top ? tabs : tabs + 1, false, false, true);
+               if (cur->type != UCL_OBJECT && cur->type != UCL_ARRAY) {
+                       utstring_append_len (buf, ";\n", 2);
+               }
+               else {
+                       utstring_append_c (buf, '\n');
+               }
+       }
+
+       ucl_add_tabs (buf, tabs, is_top);
+       if (!is_top) {
+               utstring_append_c (buf, '}');
+       }
+}
+
+/**
+ * Write a single array to the buffer
+ * @param obj array to write
+ * @param buf target buffer
+ */
+static void
+ucl_elt_array_write_rcl (ucl_object_t *obj, UT_string *buf, unsigned int tabs, bool start_tabs, bool is_top)
+{
+       ucl_object_t *cur = obj;
+
+       if (start_tabs) {
+               ucl_add_tabs (buf, tabs, false);
+       }
+
+       utstring_append_len (buf, "[\n", 2);
+       while (cur) {
+               ucl_elt_write_rcl (cur, buf, tabs + 1, true, false, false);
+               utstring_append_len (buf, ",\n", 2);
+               cur = cur->next;
+       }
+       ucl_add_tabs (buf, tabs, false);
+       utstring_append_c (buf, ']');
+}
+
+/**
+ * Emit a single element
+ * @param obj object
+ * @param buf buffer
+ */
+static void
+ucl_elt_write_rcl (ucl_object_t *obj, UT_string *buf, unsigned int tabs,
+               bool start_tabs, bool is_top, bool expand_array)
+{
+       if (expand_array && obj->next != NULL) {
+               ucl_elt_array_write_rcl (obj, buf, tabs, start_tabs, is_top);
+       }
+       else {
+               switch (obj->type) {
+               case UCL_INT:
+                       if (start_tabs) {
+                               ucl_add_tabs (buf, tabs, false);
+                       }
+                       utstring_printf (buf, "%jd", (intmax_t)ucl_object_toint (obj));
+                       break;
+               case UCL_FLOAT:
+               case UCL_TIME:
+                       if (start_tabs) {
+                               ucl_add_tabs (buf, tabs, false);
+                       }
+                       ucl_print_float (buf, ucl_object_todouble (obj));
+                       break;
+               case UCL_BOOLEAN:
+                       if (start_tabs) {
+                               ucl_add_tabs (buf, tabs, false);
+                       }
+                       utstring_printf (buf, "%s", ucl_object_toboolean (obj) ? "true" : "false");
+                       break;
+               case UCL_STRING:
+                       if (start_tabs) {
+                               ucl_add_tabs (buf, tabs, false);
+                       }
+                       ucl_elt_string_write_json (obj->value.sv, obj->len, buf);
+                       break;
+               case UCL_OBJECT:
+                       ucl_elt_obj_write_rcl (obj->value.ov, buf, tabs, start_tabs, is_top);
+                       break;
+               case UCL_ARRAY:
+                       ucl_elt_array_write_rcl (obj->value.ov, buf, tabs, start_tabs, is_top);
+                       break;
+               case UCL_USERDATA:
+                       break;
+               }
+       }
+}
+
+/**
+ * Emit an object to rcl
+ * @param obj object
+ * @return rcl output (should be freed after using)
+ */
+static UT_string *
+ucl_object_emit_rcl (ucl_object_t *obj)
+{
+       UT_string *buf;
+
+       /* Allocate large enough buffer */
+       utstring_new (buf);
+
+       ucl_elt_write_rcl (obj, buf, 0, false, true, true);
+
+       return buf;
+}
+
+
+/**
+ * Write a single object to the buffer
+ * @param obj object to write
+ * @param buf target buffer
+ */
+static void
+ucl_elt_obj_write_yaml (ucl_object_t *obj, UT_string *buf, unsigned int tabs, bool start_tabs, bool is_top)
+{
+       ucl_object_t *cur, *tmp;
+
+       if (start_tabs) {
+               ucl_add_tabs (buf, tabs, is_top);
+       }
+       if (!is_top) {
+               utstring_append_len (buf, ": {\n", 4);
+       }
+
+       HASH_ITER (hh, obj, cur, tmp) {
+               ucl_add_tabs (buf, tabs + 1, is_top);
+               utstring_append_len (buf, cur->hh.key, cur->hh.keylen);
+               if (cur->type != UCL_OBJECT && cur->type != UCL_ARRAY) {
+                       utstring_append_len (buf, " : ", 3);
+               }
+               else {
+                       utstring_append_c (buf, ' ');
+               }
+               ucl_elt_write_yaml (cur, buf, is_top ? tabs : tabs + 1, false, false, true);
+               if (cur->type != UCL_OBJECT && cur->type != UCL_ARRAY) {
+                       if (!is_top) {
+                               utstring_append_len (buf, ",\n", 2);
+                       }
+                       else {
+                               utstring_append_c (buf, '\n');
+                       }
+               }
+               else {
+                       utstring_append_c (buf, '\n');
+               }
+       }
+
+       ucl_add_tabs (buf, tabs, is_top);
+       if (!is_top) {
+               utstring_append_c (buf, '}');
+       }
+}
+
+/**
+ * Write a single array to the buffer
+ * @param obj array to write
+ * @param buf target buffer
+ */
+static void
+ucl_elt_array_write_yaml (ucl_object_t *obj, UT_string *buf, unsigned int tabs, bool start_tabs, bool is_top)
+{
+       ucl_object_t *cur = obj;
+
+       if (start_tabs) {
+               ucl_add_tabs (buf, tabs, false);
+       }
+
+       utstring_append_len (buf, ": [\n", 4);
+       while (cur) {
+               ucl_elt_write_yaml (cur, buf, tabs + 1, true, false, false);
+               utstring_append_len (buf, ",\n", 2);
+               cur = cur->next;
+       }
+       ucl_add_tabs (buf, tabs, false);
+       utstring_append_c (buf, ']');
+}
+
+/**
+ * Emit a single element
+ * @param obj object
+ * @param buf buffer
+ */
+static void
+ucl_elt_write_yaml (ucl_object_t *obj, UT_string *buf, unsigned int tabs,
+               bool start_tabs, bool is_top, bool expand_array)
+{
+       if (expand_array && obj->next != NULL) {
+               ucl_elt_array_write_yaml (obj, buf, tabs, start_tabs, is_top);
+               }
+       else {
+               switch (obj->type) {
+               case UCL_INT:
+                       if (start_tabs) {
+                               ucl_add_tabs (buf, tabs, false);
+                       }
+                       utstring_printf (buf, "%jd", (intmax_t)ucl_object_toint (obj));
+                       break;
+               case UCL_FLOAT:
+               case UCL_TIME:
+                       if (start_tabs) {
+                               ucl_add_tabs (buf, tabs, false);
+                       }
+                       ucl_print_float (buf, ucl_object_todouble (obj));
+                       break;
+               case UCL_BOOLEAN:
+                       if (start_tabs) {
+                               ucl_add_tabs (buf, tabs, false);
+                       }
+                       utstring_printf (buf, "%s", ucl_object_toboolean (obj) ? "true" : "false");
+                       break;
+               case UCL_STRING:
+                       if (start_tabs) {
+                               ucl_add_tabs (buf, tabs, false);
+                       }
+                       ucl_elt_string_write_json (obj->value.sv, obj->len, buf);
+                       break;
+               case UCL_OBJECT:
+                       ucl_elt_obj_write_yaml (obj->value.ov, buf, tabs, start_tabs, is_top);
+                       break;
+               case UCL_ARRAY:
+                       ucl_elt_array_write_yaml (obj->value.ov, buf, tabs, start_tabs, is_top);
+                       break;
+               case UCL_USERDATA:
+                       break;
+               }
+       }
+}
+
+/**
+ * Emit an object to rcl
+ * @param obj object
+ * @return rcl output (should be freed after using)
+ */
+static UT_string *
+ucl_object_emit_yaml (ucl_object_t *obj)
+{
+       UT_string *buf;
+
+       /* Allocate large enough buffer */
+       utstring_new (buf);
+
+       ucl_elt_write_yaml (obj, buf, 0, false, true, true);
+
+       return buf;
+}
+
+unsigned char *
+ucl_object_emit (ucl_object_t *obj, enum ucl_emitter emit_type)
+{
+       UT_string *buf = NULL;
+       unsigned char *res = NULL;
+
+       if (emit_type == UCL_EMIT_JSON) {
+               buf = ucl_object_emit_json (obj, false);
+       }
+       else if (emit_type == UCL_EMIT_JSON_COMPACT) {
+               buf = ucl_object_emit_json (obj, true);
+       }
+       else if (emit_type == UCL_EMIT_YAML) {
+               buf = ucl_object_emit_yaml (obj);
+       }
+       else {
+               buf = ucl_object_emit_rcl (obj);
+       }
+
+       if (buf != NULL) {
+               res = utstring_body (buf);
+               free (buf);
+       }
+
+       return res;
+}
diff --git a/src/ucl/src/ucl_internal.h b/src/ucl/src/ucl_internal.h
new file mode 100644 (file)
index 0000000..97613c8
--- /dev/null
@@ -0,0 +1,249 @@
+/* Copyright (c) 2013, 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 ''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.
+ */
+
+#ifndef UCL_INTERNAL_H_
+#define UCL_INTERNAL_H_
+
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+
+#include <limits.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <unistd.h>
+#include <ctype.h>
+
+#include "utlist.h"
+#include "ucl.h"
+
+#ifdef HAVE_OPENSSL
+#include <openssl/evp.h>
+#endif
+
+/**
+ * @file rcl_internal.h
+ * Internal structures and functions of UCL library
+ */
+
+#define UCL_MAX_RECURSION 16
+#define UCL_TRASH_KEY 0
+#define UCL_TRASH_VALUE 1
+
+enum ucl_parser_state {
+       UCL_STATE_INIT = 0,
+       UCL_STATE_OBJECT,
+       UCL_STATE_ARRAY,
+       UCL_STATE_KEY,
+       UCL_STATE_VALUE,
+       UCL_STATE_AFTER_VALUE,
+       UCL_STATE_ARRAY_VALUE,
+       UCL_STATE_SCOMMENT,
+       UCL_STATE_MCOMMENT,
+       UCL_STATE_MACRO_NAME,
+       UCL_STATE_MACRO,
+       UCL_STATE_ERROR
+};
+
+enum ucl_character_type {
+       UCL_CHARACTER_DENIED = 0,
+       UCL_CHARACTER_KEY = 1,
+       UCL_CHARACTER_KEY_START = 1 << 1,
+       UCL_CHARACTER_WHITESPACE = 1 << 2,
+       UCL_CHARACTER_WHITESPACE_UNSAFE = 1 << 3,
+       UCL_CHARACTER_VALUE_END = 1 << 4,
+       UCL_CHARACTER_VALUE_STR = 1 << 5,
+       UCL_CHARACTER_VALUE_DIGIT = 1 << 6,
+       UCL_CHARACTER_VALUE_DIGIT_START = 1 << 7,
+       UCL_CHARACTER_ESCAPE = 1 << 8,
+       UCL_CHARACTER_KEY_SEP = 1 << 9,
+       UCL_CHARACTER_JSON_UNSAFE = 1 << 10
+};
+
+struct ucl_macro {
+       char *name;
+       ucl_macro_handler handler;
+       void* ud;
+       UT_hash_handle hh;
+};
+
+struct ucl_stack {
+       ucl_object_t *obj;
+       struct ucl_stack *next;
+};
+
+struct ucl_chunk {
+       const unsigned char *begin;
+       const unsigned char *end;
+       const unsigned char *pos;
+       size_t remain;
+       unsigned int line;
+       unsigned int column;
+       struct ucl_chunk *next;
+};
+
+#ifdef HAVE_OPENSSL
+struct ucl_pubkey {
+       EVP_PKEY *key;
+       struct ucl_pubkey *next;
+};
+#else
+struct ucl_pubkey {
+       struct ucl_pubkey *next;
+};
+#endif
+
+struct ucl_parser {
+       enum ucl_parser_state state;
+       enum ucl_parser_state prev_state;
+       unsigned int recursion;
+       int flags;
+       ucl_object_t *top_obj;
+       ucl_object_t *cur_obj;
+       struct ucl_macro *macroes;
+       struct ucl_stack *stack;
+       struct ucl_chunk *chunks;
+       struct ucl_pubkey *keys;
+};
+
+/**
+ * Unescape json string inplace
+ * @param str
+ */
+size_t ucl_unescape_json_string (char *str, size_t len);
+
+/**
+ * Handle include macro
+ * @param data include data
+ * @param len length of data
+ * @param ud user data
+ * @param err error ptr
+ * @return
+ */
+bool ucl_include_handler (const unsigned char *data, size_t len, void* ud, UT_string **err);
+
+/**
+ * Handle includes macro
+ * @param data include data
+ * @param len length of data
+ * @param ud user data
+ * @param err error ptr
+ * @return
+ */
+bool ucl_includes_handler (const unsigned char *data, size_t len, void* ud, UT_string **err);
+
+size_t ucl_strlcpy (char *dst, const char *src, size_t siz);
+size_t ucl_strlcpy_unsafe (char *dst, const char *src, size_t siz);
+size_t ucl_strlcpy_tolower (char *dst, const char *src, size_t siz);
+
+
+void ucl_elt_write_json (ucl_object_t *obj, UT_string *buf, unsigned int tabs,
+               bool start_tabs, bool compact);
+
+#ifdef __GNUC__
+static inline void
+ucl_create_err (UT_string **err, const char *fmt, ...)
+__attribute__ (( format( printf, 2, 3) ));
+#endif
+
+static inline void
+ucl_create_err (UT_string **err, const char *fmt, ...)
+
+{
+       if (*err == NULL) {
+               utstring_new (*err);
+               va_list ap;
+               va_start (ap, fmt);
+               utstring_printf_va (*err, fmt, ap);
+               va_end (ap);
+       }
+}
+
+/**
+ * Check whether a given string contains a boolean value
+ * @param obj object to set
+ * @param start start of a string
+ * @param len length of a string
+ * @return true if a string is a boolean value
+ */
+static inline bool
+ucl_maybe_parse_boolean (ucl_object_t *obj, const unsigned char *start, size_t len)
+{
+       const unsigned char *p = start;
+       bool ret = false, val = false;
+
+       if (len == 5) {
+               if (tolower (p[0]) == 'f' && strncasecmp (p, "false", 5) == 0) {
+                       ret = true;
+                       val = false;
+               }
+       }
+       else if (len == 4) {
+               if (tolower (p[0]) == 't' && strncasecmp (p, "true", 4) == 0) {
+                       ret = true;
+                       val = true;
+               }
+       }
+       else if (len == 3) {
+               if (tolower (p[0]) == 'y' && strncasecmp (p, "yes", 3) == 0) {
+                       ret = true;
+                       val = true;
+               }
+               if (tolower (p[0]) == 'o' && strncasecmp (p, "off", 3) == 0) {
+                       ret = true;
+                       val = false;
+               }
+       }
+       else if (len == 2) {
+               if (tolower (p[0]) == 'n' && strncasecmp (p, "no", 2) == 0) {
+                       ret = true;
+                       val = false;
+               }
+               else if (tolower (p[0]) == 'o' && strncasecmp (p, "on", 2) == 0) {
+                       ret = true;
+                       val = true;
+               }
+       }
+
+       if (ret) {
+               obj->type = UCL_BOOLEAN;
+               obj->value.iv = val;
+       }
+
+       return ret;
+}
+
+/**
+ * Check numeric string
+ * @param obj object to set if a string is numeric
+ * @param start start of string
+ * @param end end of string
+ * @param pos position where parsing has stopped
+ * @param allow_double allow parsing of floating point values
+ * @return 0 if string is numeric and error code (EINVAL or ERANGE) in case of conversion error
+ */
+int ucl_maybe_parse_number (ucl_object_t *obj,
+               const char *start, const char *end, const char **pos, bool allow_double);
+
+#endif /* UCL_INTERNAL_H_ */
diff --git a/src/ucl/src/ucl_parser.c b/src/ucl/src/ucl_parser.c
new file mode 100644 (file)
index 0000000..30106eb
--- /dev/null
@@ -0,0 +1,1420 @@
+/* Copyright (c) 2013, 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 ''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.
+ */
+
+#include "ucl.h"
+#include "ucl_internal.h"
+#include "ucl_chartable.h"
+
+/**
+ * @file rcl_parser.c
+ * The implementation of rcl parser
+ */
+
+struct ucl_parser_saved_state {
+       unsigned int line;
+       unsigned int column;
+       size_t remain;
+       const unsigned char *pos;
+};
+
+/**
+ * Move up to len characters
+ * @param parser
+ * @param begin
+ * @param len
+ * @return new position in chunk
+ */
+#define ucl_chunk_skipc(chunk, p)    do{                                       \
+    if (*(p) == '\n') {                                                                                \
+        (chunk)->line ++;                                                                      \
+        (chunk)->column = 0;                                                           \
+    }                                                                                                          \
+    else (chunk)->column ++;                                                           \
+    (p++);                                                                                                     \
+    (chunk)->pos ++;                                                                           \
+    (chunk)->remain --;                                                                                \
+    } while (0)
+
+/**
+ * Save parser state
+ * @param chunk
+ * @param s
+ */
+static inline void
+ucl_chunk_save_state (struct ucl_chunk *chunk, struct ucl_parser_saved_state *s)
+{
+       s->column = chunk->column;
+       s->pos = chunk->pos;
+       s->line = chunk->line;
+       s->remain = chunk->remain;
+}
+
+/**
+ * Restore parser state
+ * @param chunk
+ * @param s
+ */
+static inline void
+ucl_chunk_restore_state (struct ucl_chunk *chunk, struct ucl_parser_saved_state *s)
+{
+       chunk->column = s->column;
+       chunk->pos = s->pos;
+       chunk->line = s->line;
+       chunk->remain = s->remain;
+}
+
+static inline void
+ucl_set_err (struct ucl_chunk *chunk, int code, const char *str, UT_string **err)
+{
+       ucl_create_err (err, "error on line %d at column %d: '%s', character: '%c'",
+                       chunk->line, chunk->column, str, *chunk->pos);
+}
+
+static bool
+ucl_skip_comments (struct ucl_parser *parser, UT_string **err)
+{
+       struct ucl_chunk *chunk = parser->chunks;
+       const unsigned char *p;
+       int comments_nested = 0;
+
+       p = chunk->pos;
+
+start:
+       if (*p == '#') {
+               if (parser->state != UCL_STATE_SCOMMENT &&
+                               parser->state != UCL_STATE_MCOMMENT) {
+                       while (p < chunk->end) {
+                               if (*p == '\n') {
+                                       ucl_chunk_skipc (chunk, p);
+                                       goto start;
+                               }
+                               ucl_chunk_skipc (chunk, p);
+                       }
+               }
+       }
+       else if (*p == '/' && chunk->remain >= 2) {
+               if (p[1] == '*') {
+                       ucl_chunk_skipc (chunk, p);
+                       comments_nested ++;
+                       ucl_chunk_skipc (chunk, p);
+
+                       while (p < chunk->end) {
+                               if (*p == '*') {
+                                       ucl_chunk_skipc (chunk, p);
+                                       if (*p == '/') {
+                                               comments_nested --;
+                                               if (comments_nested == 0) {
+                                                       ucl_chunk_skipc (chunk, p);
+                                                       goto start;
+                                               }
+                                       }
+                                       ucl_chunk_skipc (chunk, p);
+                               }
+                               else if (p[0] == '/' && chunk->remain >= 2 && p[1] == '*') {
+                                       comments_nested ++;
+                                       ucl_chunk_skipc (chunk, p);
+                                       ucl_chunk_skipc (chunk, p);
+                                       continue;
+                               }
+                               ucl_chunk_skipc (chunk, p);
+                       }
+                       if (comments_nested != 0) {
+                               ucl_set_err (chunk, UCL_ENESTED, "comments nesting is invalid", err);
+                               return false;
+                       }
+               }
+       }
+
+       return true;
+}
+
+/**
+ * Return multiplier for a character
+ * @param c multiplier character
+ * @param is_bytes if true use 1024 multiplier
+ * @return multiplier
+ */
+static inline unsigned long
+ucl_lex_num_multiplier (const unsigned char c, bool is_bytes) {
+       const struct {
+               char c;
+               long mult_normal;
+               long mult_bytes;
+       } multipliers[] = {
+                       {'m', 1000 * 1000, 1024 * 1024},
+                       {'k', 1000, 1024},
+                       {'g', 1000 * 1000 * 1000, 1024 * 1024 * 1024}
+       };
+       int i;
+
+       for (i = 0; i < 3; i ++) {
+               if (tolower (c) == multipliers[i].c) {
+                       if (is_bytes) {
+                               return multipliers[i].mult_bytes;
+                       }
+                       return multipliers[i].mult_normal;
+               }
+       }
+
+       return 1;
+}
+
+
+/**
+ * Return multiplier for time scaling
+ * @param c
+ * @return
+ */
+static inline double
+ucl_lex_time_multiplier (const unsigned char c) {
+       const struct {
+               char c;
+               double mult;
+       } multipliers[] = {
+                       {'m', 60},
+                       {'h', 60 * 60},
+                       {'d', 60 * 60 * 24},
+                       {'w', 60 * 60 * 24 * 7},
+                       {'y', 60 * 60 * 24 * 7 * 365}
+       };
+       int i;
+
+       for (i = 0; i < 5; i ++) {
+               if (tolower (c) == multipliers[i].c) {
+                       return multipliers[i].mult;
+               }
+       }
+
+       return 1;
+}
+
+/**
+ * Return true if a character is a end of an atom
+ * @param c
+ * @return
+ */
+static inline bool
+ucl_lex_is_atom_end (const unsigned char c)
+{
+       return ucl_test_character (c, UCL_CHARACTER_VALUE_END);
+}
+
+static inline bool
+ucl_lex_is_comment (const unsigned char c1, const unsigned char c2)
+{
+       if (c1 == '/') {
+               if (c2 == '*') {
+                       return true;
+               }
+       }
+       else if (c1 == '#') {
+               return true;
+       }
+       return false;
+}
+
+static inline size_t
+ucl_copy_or_store_ptr (struct ucl_parser *parser,
+               const unsigned char *src, unsigned char **dst,
+               const char **dst_const, size_t in_len,
+               bool need_unescape, bool need_lowercase, UT_string **err)
+{
+       size_t ret = 0;
+
+       if (need_unescape || need_lowercase || !(parser->flags & UCL_FLAG_ZEROCOPY)) {
+               /* Copy string */
+               *dst = UCL_ALLOC (in_len + 1);
+               if (*dst == NULL) {
+                       ucl_set_err (parser->chunks, 0, "cannot allocate memory for a string", err);
+                       return false;
+               }
+               if (need_lowercase) {
+                       ret = ucl_strlcpy_tolower (*dst, src, in_len + 1);
+               }
+               else {
+                       ret = ucl_strlcpy_unsafe (*dst, src, in_len + 1);
+               }
+
+               if (need_unescape) {
+                       ret = ucl_unescape_json_string (*dst, ret);
+               }
+               *dst_const = *dst;
+       }
+       else {
+               *dst_const = src;
+               ret = in_len;
+       }
+
+       return ret;
+}
+
+int
+ucl_maybe_parse_number (ucl_object_t *obj,
+               const char *start, const char *end, const char **pos, bool allow_double)
+{
+       const char *p = start, *c = start;
+       char *endptr;
+       bool got_dot = false, got_exp = false, need_double = false, is_date = false;
+       double dv;
+       int64_t lv;
+
+       if (*p == '-') {
+               p ++;
+       }
+       while (p < end) {
+               if (isdigit (*p)) {
+                       p ++;
+               }
+               else if (allow_double) {
+                       if (p == c) {
+                               /* Empty digits sequence, not a number */
+                               *pos = start;
+                               return EINVAL;
+                       }
+                       else if (*p == '.') {
+                               if (got_dot) {
+                                       /* Double dots, not a number */
+                                       *pos = start;
+                                       return EINVAL;
+                               }
+                               else {
+                                       got_dot = true;
+                                       need_double = true;
+                                       p ++;
+                               }
+                       }
+                       else if (*p == 'e' || *p == 'E') {
+                               if (got_exp) {
+                                       /* Double exp, not a number */
+                                       *pos = start;
+                                       return EINVAL;
+                               }
+                               else {
+                                       got_exp = true;
+                                       need_double = true;
+                                       p ++;
+                                       if (p >= end) {
+                                               *pos = start;
+                                               return EINVAL;
+                                       }
+                                       if (!isdigit (*p) && *p != '+' && *p != '-') {
+                                               /* Wrong exponent sign */
+                                               *pos = start;
+                                               return EINVAL;
+                                       }
+                                       else {
+                                               p ++;
+                                       }
+                               }
+                       }
+                       else {
+                               /* Got the end of the number, need to check */
+                               break;
+                       }
+               }
+               else {
+                       break;
+               }
+       }
+
+       errno = 0;
+       if (need_double) {
+               dv = strtod (c, &endptr);
+       }
+       else {
+               lv = strtoimax (c, &endptr, 10);
+       }
+       if (errno == ERANGE) {
+               *pos = start;
+               return ERANGE;
+       }
+
+       /* Now check endptr */
+       if (endptr == NULL || ucl_lex_is_atom_end (*endptr) || *endptr == '\0') {
+               p = endptr;
+               goto set_obj;
+       }
+
+       if (endptr < end) {
+               p = endptr;
+               switch (*p) {
+               case 'm':
+               case 'M':
+               case 'g':
+               case 'G':
+               case 'k':
+               case 'K':
+                       if (end - p >= 2) {
+                               if (p[1] == 's' || p[1] == 'S') {
+                                       /* Milliseconds */
+                                       if (!need_double) {
+                                               need_double = true;
+                                               dv = lv;
+                                       }
+                                       is_date = true;
+                                       if (p[0] == 'm' || p[0] == 'M') {
+                                               dv /= 1000.;
+                                       }
+                                       else {
+                                               dv *= ucl_lex_num_multiplier (*p, false);
+                                       }
+                                       p += 2;
+                                       goto set_obj;
+                               }
+                               else if (p[1] == 'b' || p[1] == 'B') {
+                                       /* Megabytes */
+                                       if (need_double) {
+                                               need_double = false;
+                                               lv = dv;
+                                       }
+                                       lv *= ucl_lex_num_multiplier (*p, true);
+                                       p += 2;
+                                       goto set_obj;
+                               }
+                               else if (ucl_lex_is_atom_end (p[1])) {
+                                       if (need_double) {
+                                               dv *= ucl_lex_num_multiplier (*p, false);
+                                       }
+                                       else {
+                                               lv *= ucl_lex_num_multiplier (*p, false);
+                                       }
+                                       p ++;
+                                       goto set_obj;
+                               }
+                               else if (end - p >= 3) {
+                                       if (tolower (p[0]) == 'm' &&
+                                                       tolower (p[1]) == 'i' &&
+                                                       tolower (p[2]) == 'n') {
+                                               /* Minutes */
+                                               if (!need_double) {
+                                                       need_double = true;
+                                                       dv = lv;
+                                               }
+                                               is_date = true;
+                                               dv *= 60.;
+                                               p += 3;
+                                               goto set_obj;
+                                       }
+                               }
+                       }
+                       else {
+                               if (need_double) {
+                                       dv *= ucl_lex_num_multiplier (*p, false);
+                               }
+                               else {
+                                       lv *= ucl_lex_num_multiplier (*p, false);
+                               }
+                               p ++;
+                               goto set_obj;
+                       }
+                       break;
+               case 'S':
+               case 's':
+                       if (p == end - 1 || ucl_lex_is_atom_end (p[1])) {
+                               if (!need_double) {
+                                       need_double = true;
+                                       dv = lv;
+                               }
+                               p ++;
+                               is_date = true;
+                               goto set_obj;
+                       }
+                       break;
+               case 'h':
+               case 'H':
+               case 'd':
+               case 'D':
+               case 'w':
+               case 'W':
+               case 'Y':
+               case 'y':
+                       if (p == end - 1 || ucl_lex_is_atom_end (p[1])) {
+                               if (!need_double) {
+                                       need_double = true;
+                                       dv = lv;
+                               }
+                               is_date = true;
+                               dv *= ucl_lex_time_multiplier (*p);
+                               p ++;
+                               goto set_obj;
+                       }
+                       break;
+               }
+       }
+
+       *pos = c;
+       return EINVAL;
+
+       set_obj:
+       if (allow_double && (need_double || is_date)) {
+               if (!is_date) {
+                       obj->type = UCL_FLOAT;
+               }
+               else {
+                       obj->type = UCL_TIME;
+               }
+               obj->value.dv = dv;
+       }
+       else {
+               obj->type = UCL_INT;
+               obj->value.iv = lv;
+       }
+       *pos = p;
+       return 0;
+}
+
+/**
+ * Parse possible number
+ * @param parser
+ * @param chunk
+ * @param err
+ * @return true if a number has been parsed
+ */
+static bool
+ucl_lex_number (struct ucl_parser *parser,
+               struct ucl_chunk *chunk, ucl_object_t *obj, UT_string **err)
+{
+       const unsigned char *pos;
+       int ret;
+
+       ret = ucl_maybe_parse_number (obj, chunk->pos, chunk->end, (const char **)&pos, true);
+
+       if (ret == 0) {
+               chunk->remain -= pos - chunk->pos;
+               chunk->column += pos - chunk->pos;
+               chunk->pos = pos;
+               return true;
+       }
+       else if (ret == ERANGE) {
+               ucl_set_err (chunk, ERANGE, "numeric value out of range", err);
+       }
+
+       return false;
+}
+
+/**
+ * Parse quoted string with possible escapes
+ * @param parser
+ * @param chunk
+ * @param err
+ * @return true if a string has been parsed
+ */
+static bool
+ucl_lex_json_string (struct ucl_parser *parser,
+               struct ucl_chunk *chunk, bool *need_unescape, UT_string **err)
+{
+       const unsigned char *p = chunk->pos;
+       unsigned char c;
+       int i;
+
+       while (p < chunk->end) {
+               c = *p;
+               if (c < 0x1F) {
+                       /* Unmasked control character */
+                       if (c == '\n') {
+                               ucl_set_err (chunk, UCL_ESYNTAX, "unexpected newline", err);
+                       }
+                       else {
+                               ucl_set_err (chunk, UCL_ESYNTAX, "unexpected control character", err);
+                       }
+                       return false;
+               }
+               else if (c == '\\') {
+                       ucl_chunk_skipc (chunk, p);
+                       c = *p;
+                       if (p >= chunk->end) {
+                               ucl_set_err (chunk, UCL_ESYNTAX, "unfinished escape character", err);
+                               return false;
+                       }
+                       else if (ucl_test_character (c, UCL_CHARACTER_ESCAPE)) {
+                               if (c == 'u') {
+                                       ucl_chunk_skipc (chunk, p);
+                                       for (i = 0; i < 4 && p < chunk->end; i ++) {
+                                               if (!isxdigit (*p)) {
+                                                       ucl_set_err (chunk, UCL_ESYNTAX, "invalid utf escape", err);
+                                                       return false;
+                                               }
+                                               ucl_chunk_skipc (chunk, p);
+                                       }
+                                       if (p >= chunk->end) {
+                                               ucl_set_err (chunk, UCL_ESYNTAX, "unfinished escape character", err);
+                                               return false;
+                                       }
+                               }
+                               else {
+                                       ucl_chunk_skipc (chunk, p);
+                               }
+                       }
+                       else {
+                               ucl_set_err (chunk, UCL_ESYNTAX, "invalid escape character", err);
+                               return false;
+                       }
+                       *need_unescape = true;
+                       continue;
+               }
+               else if (c == '"') {
+                       ucl_chunk_skipc (chunk, p);
+                       return true;
+               }
+               ucl_chunk_skipc (chunk, p);
+       }
+
+       ucl_set_err (chunk, UCL_ESYNTAX, "no quote at the end of json string", err);
+       return false;
+}
+
+/**
+ * Parse a key in an object
+ * @param parser
+ * @param chunk
+ * @param err
+ * @return true if a key has been parsed
+ */
+static bool
+ucl_parse_key (struct ucl_parser *parser,
+               struct ucl_chunk *chunk, UT_string **err)
+{
+       const unsigned char *p, *c = NULL, *end;
+       const char *key;
+       bool got_quote = false, got_eq = false, got_semicolon = false, need_unescape = false;
+       ucl_object_t *nobj, *tobj, *container;
+       size_t keylen;
+
+       p = chunk->pos;
+
+       if (*p == '.') {
+               /* It is macro actually */
+               ucl_chunk_skipc (chunk, p);
+               parser->prev_state = parser->state;
+               parser->state = UCL_STATE_MACRO_NAME;
+               return true;
+       }
+       while (p < chunk->end) {
+               /*
+                * A key must start with alpha, number, '/' or '_' and end with space character
+                */
+               if (c == NULL) {
+                       if (ucl_lex_is_comment (p[0], p[1])) {
+                               if (!ucl_skip_comments (parser, err)) {
+                                       return false;
+                               }
+                               p = chunk->pos;
+                       }
+                       else if (ucl_test_character (*p, UCL_CHARACTER_KEY_START)) {
+                               /* The first symbol */
+                               c = p;
+                               ucl_chunk_skipc (chunk, p);
+                       }
+                       else if (*p == '"') {
+                               /* JSON style key */
+                               c = p + 1;
+                               got_quote = true;
+                               ucl_chunk_skipc (chunk, p);
+                       }
+                       else {
+                               /* Invalid identifier */
+                               ucl_set_err (chunk, UCL_ESYNTAX, "key must begin with a letter", err);
+                               return false;
+                       }
+               }
+               else {
+                       /* Parse the body of a key */
+                       if (!got_quote) {
+                               if (ucl_test_character (*p, UCL_CHARACTER_KEY)) {
+                                       ucl_chunk_skipc (chunk, p);
+                               }
+                               else if (ucl_test_character (*p, UCL_CHARACTER_KEY_SEP)) {
+                                       end = p;
+                                       break;
+                               }
+                               else {
+                                       ucl_set_err (chunk, UCL_ESYNTAX, "invalid character in a key", err);
+                                       return false;
+                               }
+                       }
+                       else {
+                               /* We need to parse json like quoted string */
+                               if (!ucl_lex_json_string (parser, chunk, &need_unescape, err)) {
+                                       return false;
+                               }
+                               end = chunk->pos - 1;
+                               p = chunk->pos;
+                               break;
+                       }
+               }
+       }
+
+       if (p >= chunk->end) {
+               ucl_set_err (chunk, UCL_ESYNTAX, "unfinished key", err);
+               return false;
+       }
+
+       /* We are now at the end of the key, need to parse the rest */
+       while (p < chunk->end) {
+               if (ucl_test_character (*p, UCL_CHARACTER_WHITESPACE)) {
+                       ucl_chunk_skipc (chunk, p);
+               }
+               else if (*p == '=') {
+                       if (!got_eq && !got_semicolon) {
+                               ucl_chunk_skipc (chunk, p);
+                               got_eq = true;
+                       }
+                       else {
+                               ucl_set_err (chunk, UCL_ESYNTAX, "unexpected '=' character", err);
+                               return false;
+                       }
+               }
+               else if (*p == ':') {
+                       if (!got_eq && !got_semicolon) {
+                               ucl_chunk_skipc (chunk, p);
+                               got_semicolon = true;
+                       }
+                       else {
+                               ucl_set_err (chunk, UCL_ESYNTAX, "unexpected ':' character", err);
+                               return false;
+                       }
+               }
+               else if (ucl_lex_is_comment (p[0], p[1])) {
+                       /* Check for comment */
+                       if (!ucl_skip_comments (parser, err)) {
+                               return false;
+                       }
+                       p = chunk->pos;
+               }
+               else {
+                       /* Start value */
+                       break;
+               }
+       }
+
+       if (p >= chunk->end) {
+               ucl_set_err (chunk, UCL_ESYNTAX, "unfinished key", err);
+               return false;
+       }
+
+       /* Create a new object */
+       nobj = ucl_object_new ();
+       keylen = ucl_copy_or_store_ptr (parser, c, &nobj->trash_stack[UCL_TRASH_KEY],
+                       &key, end - c, need_unescape, parser->flags & UCL_FLAG_KEY_LOWERCASE, err);
+       if (keylen == 0) {
+               return false;
+       }
+
+       container = parser->stack->obj->value.ov;
+       HASH_FIND (hh, container, key, keylen, tobj);
+       if (tobj == NULL) {
+               DL_APPEND (tobj, nobj);
+               HASH_ADD_KEYPTR (hh, container, key, keylen, nobj);
+       }
+       else {
+               DL_APPEND (tobj, nobj);
+       }
+
+       parser->stack->obj->value.ov = container;
+
+       parser->cur_obj = nobj;
+
+       return true;
+}
+
+/**
+ * Parse a cl string
+ * @param parser
+ * @param chunk
+ * @param err
+ * @return true if a key has been parsed
+ */
+static bool
+ucl_parse_string_value (struct ucl_parser *parser,
+               struct ucl_chunk *chunk, UT_string **err)
+{
+       const unsigned char *p;
+       enum {
+               UCL_BRACE_ROUND = 0,
+               UCL_BRACE_SQUARE,
+               UCL_BRACE_FIGURE
+       };
+       int braces[3][2] = {{0, 0}, {0, 0}, {0, 0}};
+
+       p = chunk->pos;
+
+       while (p < chunk->end) {
+
+               /* Skip pairs of figure braces */
+               if (*p == '{') {
+                       braces[UCL_BRACE_FIGURE][0] ++;
+               }
+               else if (*p == '}') {
+                       braces[UCL_BRACE_FIGURE][1] ++;
+                       if (braces[UCL_BRACE_FIGURE][1] == braces[UCL_BRACE_FIGURE][0]) {
+                               /* This is not a termination symbol, continue */
+                               ucl_chunk_skipc (chunk, p);
+                               continue;
+                       }
+               }
+               /* Skip pairs of square braces */
+               else if (*p == '[') {
+                       braces[UCL_BRACE_SQUARE][0] ++;
+               }
+               else if (*p == ']') {
+                       braces[UCL_BRACE_SQUARE][1] ++;
+                       if (braces[UCL_BRACE_SQUARE][1] == braces[UCL_BRACE_SQUARE][0]) {
+                               /* This is not a termination symbol, continue */
+                               ucl_chunk_skipc (chunk, p);
+                               continue;
+                       }
+               }
+
+               if (ucl_lex_is_atom_end (*p) || ucl_lex_is_comment (p[0], p[1])) {
+                       break;
+               }
+               ucl_chunk_skipc (chunk, p);
+       }
+
+       if (p >= chunk->end) {
+               ucl_set_err (chunk, UCL_ESYNTAX, "unfinished value", err);
+               return false;
+       }
+
+       return true;
+}
+
+/**
+ * Parse multiline string ending with \n{term}\n
+ * @param parser
+ * @param chunk
+ * @param term
+ * @param term_len
+ * @param err
+ * @return size of multiline string or 0 in case of error
+ */
+static int
+ucl_parse_multiline_string (struct ucl_parser *parser,
+               struct ucl_chunk *chunk, const unsigned char *term,
+               int term_len, unsigned char const **beg, UT_string **err)
+{
+       const unsigned char *p, *c;
+       bool newline = false;
+       int len = 0;
+
+       p = chunk->pos;
+
+       c = p;
+
+       while (p < chunk->end) {
+               if (newline) {
+                       if (chunk->end - p < term_len) {
+                               return 0;
+                       }
+                       else if (memcmp (p, term, term_len) == 0 && (p[term_len] == '\n' || p[term_len] == '\r')) {
+                               len = p - c;
+                               chunk->remain -= term_len;
+                               chunk->pos = p + term_len;
+                               chunk->column = term_len;
+                               *beg = c;
+                               break;
+                       }
+               }
+               if (*p == '\n') {
+                       newline = true;
+               }
+               else {
+                       newline = false;
+               }
+               ucl_chunk_skipc (chunk, p);
+       }
+
+       return len;
+}
+
+/**
+ * Handle value data
+ * @param parser
+ * @param chunk
+ * @param err
+ * @return
+ */
+static bool
+ucl_parse_value (struct ucl_parser *parser, struct ucl_chunk *chunk, UT_string **err)
+{
+       const unsigned char *p, *c;
+       struct ucl_stack *st;
+       ucl_object_t *obj = NULL, *t;
+       unsigned int stripped_spaces;
+       int str_len;
+       bool need_unescape = false;
+
+       p = chunk->pos;
+
+       while (p < chunk->end) {
+               if (obj == NULL) {
+                       if (parser->stack->obj->type == UCL_ARRAY) {
+                               /* Object must be allocated */
+                               obj = ucl_object_new ();
+                               t = parser->stack->obj->value.ov;
+                               DL_APPEND (t, obj);
+                               parser->cur_obj = obj;
+                               parser->stack->obj->value.ov = t;
+                       }
+                       else {
+                               /* Object has been already allocated */
+                               obj = parser->cur_obj;
+                       }
+               }
+               c = p;
+               switch (*p) {
+               case '"':
+                       ucl_chunk_skipc (chunk, p);
+                       if (!ucl_lex_json_string (parser, chunk, &need_unescape, err)) {
+                               return false;
+                       }
+                       str_len = chunk->pos - c - 2;
+                       obj->type = UCL_STRING;
+                       if ((str_len = ucl_copy_or_store_ptr (parser, c + 1, &obj->trash_stack[UCL_TRASH_VALUE],
+                                       &obj->value.sv, str_len, need_unescape, false, err)) == 0) {
+                               return false;
+                       }
+                       obj->len = str_len;
+                       parser->state = UCL_STATE_AFTER_VALUE;
+                       p = chunk->pos;
+                       return true;
+                       break;
+               case '{':
+                       /* We have a new object */
+                       obj->type = UCL_OBJECT;
+
+                       parser->state = UCL_STATE_KEY;
+                       st = UCL_ALLOC (sizeof (struct ucl_stack));
+                       st->obj = obj;
+                       LL_PREPEND (parser->stack, st);
+                       parser->cur_obj = obj;
+
+                       ucl_chunk_skipc (chunk, p);
+                       return true;
+                       break;
+               case '[':
+                       /* We have a new array */
+                       obj = parser->cur_obj;
+                       obj->type = UCL_ARRAY;
+
+                       parser->state = UCL_STATE_VALUE;
+                       st = UCL_ALLOC (sizeof (struct ucl_stack));
+                       st->obj = obj;
+                       LL_PREPEND (parser->stack, st);
+                       parser->cur_obj = obj;
+
+                       ucl_chunk_skipc (chunk, p);
+                       return true;
+                       break;
+               case '<':
+                       /* We have something like multiline value, which must be <<[A-Z]+\n */
+                       if (chunk->end - p > 3) {
+                               if (memcmp (p, "<<", 2) == 0) {
+                                       p += 2;
+                                       /* We allow only uppercase characters in multiline definitions */
+                                       while (p < chunk->end && *p >= 'A' && *p <= 'Z') {
+                                               p ++;
+                                       }
+                                       if (*p =='\n') {
+                                               /* Set chunk positions and start multiline parsing */
+                                               c += 2;
+                                               chunk->remain -= p - c;
+                                               chunk->pos = p + 1;
+                                               chunk->column = 0;
+                                               chunk->line ++;
+                                               if ((str_len = ucl_parse_multiline_string (parser, chunk, c,
+                                                               p - c, &c, err)) == 0) {
+                                                       ucl_set_err (chunk, UCL_ESYNTAX, "unterminated multiline value", err);
+                                                       return false;
+                                               }
+                                               obj->type = UCL_STRING;
+                                               if ((str_len = ucl_copy_or_store_ptr (parser, c, &obj->trash_stack[UCL_TRASH_VALUE],
+                                                       &obj->value.sv, str_len - 1, false, false, err)) == 0) {
+                                                       return false;
+                                               }
+                                               obj->len = str_len;
+                                               parser->state = UCL_STATE_AFTER_VALUE;
+                                               return true;
+                                       }
+                               }
+                       }
+                       /* Fallback to ordinary strings */
+               default:
+                       /* Skip any spaces and comments */
+                       if (ucl_test_character (*p, UCL_CHARACTER_WHITESPACE_UNSAFE) ||
+                                       ucl_lex_is_comment (p[0], p[1])) {
+                               while (p < chunk->end && ucl_test_character (*p, UCL_CHARACTER_WHITESPACE_UNSAFE)) {
+                                       ucl_chunk_skipc (chunk, p);
+                               }
+                               if (!ucl_skip_comments (parser, err)) {
+                                       return false;
+                               }
+                               p = chunk->pos;
+                               continue;
+                       }
+                       /* Parse atom */
+                       if (ucl_test_character (*p, UCL_CHARACTER_VALUE_DIGIT_START)) {
+                               if (!ucl_lex_number (parser, chunk, obj, err)) {
+                                       if (parser->state == UCL_STATE_ERROR) {
+                                               return false;
+                                       }
+                                       if (!ucl_parse_string_value (parser, chunk, err)) {
+                                               return false;
+                                       }
+                                       if (!ucl_maybe_parse_boolean (obj, c, chunk->pos - c)) {
+                                               /* Cut trailing spaces */
+                                               stripped_spaces = 0;
+                                               while (ucl_test_character (*(chunk->pos - 1 - stripped_spaces),
+                                                               UCL_CHARACTER_WHITESPACE)) {
+                                                       stripped_spaces ++;
+                                               }
+                                               str_len = chunk->pos - c - stripped_spaces;
+                                               if (str_len <= 0) {
+                                                       ucl_set_err (chunk, 0, "string value must not be empty", err);
+                                                       return false;
+                                               }
+                                               obj->type = UCL_STRING;
+                                               if ((str_len = ucl_copy_or_store_ptr (parser, c, &obj->trash_stack[UCL_TRASH_VALUE],
+                                                               &obj->value.sv, str_len, false, false, err)) == 0) {
+                                                       return false;
+                                               }
+                                               obj->len = str_len;
+                                       }
+                                       parser->state = UCL_STATE_AFTER_VALUE;
+                                       return true;
+                               }
+                               else {
+                                       parser->state = UCL_STATE_AFTER_VALUE;
+                                       return true;
+                               }
+                       }
+                       else {
+                               if (!ucl_parse_string_value (parser, chunk, err)) {
+                                       return false;
+                               }
+                               if (!ucl_maybe_parse_boolean (obj, c, chunk->pos - c)) {
+                                       /* TODO: remove cut&paste */
+                                       /* Cut trailing spaces */
+                                       stripped_spaces = 0;
+                                       while (ucl_test_character (*(chunk->pos - 1 - stripped_spaces),
+                                                       UCL_CHARACTER_WHITESPACE)) {
+                                               stripped_spaces ++;
+                                       }
+                                       str_len = chunk->pos - c - stripped_spaces;
+                                       if (str_len <= 0) {
+                                               ucl_set_err (chunk, 0, "string value must not be empty", err);
+                                               return false;
+                                       }
+                                       obj->type = UCL_STRING;
+                                       if ((str_len = ucl_copy_or_store_ptr (parser, c, &obj->trash_stack[UCL_TRASH_VALUE],
+                                                       &obj->value.sv, str_len, false, false, err)) == 0) {
+                                               return false;
+                                       }
+                                       obj->len = str_len;
+                               }
+                               parser->state = UCL_STATE_AFTER_VALUE;
+                               return true;
+                       }
+                       p = chunk->pos;
+                       break;
+               }
+       }
+
+       return true;
+}
+
+/**
+ * Handle after value data
+ * @param parser
+ * @param chunk
+ * @param err
+ * @return
+ */
+static bool
+ucl_parse_after_value (struct ucl_parser *parser, struct ucl_chunk *chunk, UT_string **err)
+{
+       const unsigned char *p;
+       bool got_sep = false;
+       struct ucl_stack *st;
+
+       p = chunk->pos;
+
+       while (p < chunk->end) {
+               if (ucl_test_character (*p, UCL_CHARACTER_WHITESPACE)) {
+                       /* Skip whitespaces */
+                       ucl_chunk_skipc (chunk, p);
+               }
+               else if (ucl_lex_is_comment (p[0], p[1])) {
+                       /* Skip comment */
+                       if (!ucl_skip_comments (parser, err)) {
+                               return false;
+                       }
+                       /* Treat comment as a separator */
+                       got_sep = true;
+                       p = chunk->pos;
+               }
+               else if (ucl_test_character (*p, UCL_CHARACTER_VALUE_END)) {
+                       if (*p == '}' || *p == ']') {
+                               if (parser->stack == NULL) {
+                                       ucl_set_err (chunk, UCL_ESYNTAX, "unexpected } detected", err);
+                                       return false;
+                               }
+                               if ((*p == '}' && parser->stack->obj->type == UCL_OBJECT) ||
+                                               (*p == ']' && parser->stack->obj->type == UCL_ARRAY)) {
+                                       /* Pop object from a stack */
+
+                                       st = parser->stack;
+                                       parser->stack = st->next;
+                                       UCL_FREE (sizeof (struct ucl_stack), st);
+                               }
+                               else {
+                                       ucl_set_err (chunk, UCL_ESYNTAX, "unexpected terminating symbol detected", err);
+                                       return false;
+                               }
+
+                               if (parser->stack == NULL) {
+                                       /* Ignore everything after a top object */
+                                       return true;
+                               }
+                               else {
+                                       ucl_chunk_skipc (chunk, p);
+                               }
+                               got_sep = true;
+                       }
+                       else {
+                               /* Got a separator */
+                               got_sep = true;
+                               ucl_chunk_skipc (chunk, p);
+                       }
+               }
+               else {
+                       /* Anything else */
+                       if (!got_sep) {
+                               ucl_set_err (chunk, UCL_ESYNTAX, "delimiter is missing", err);
+                               return false;
+                       }
+                       return true;
+               }
+       }
+
+       return true;
+}
+
+/**
+ * Handle macro data
+ * @param parser
+ * @param chunk
+ * @param err
+ * @return
+ */
+static bool
+ucl_parse_macro_value (struct ucl_parser *parser,
+               struct ucl_chunk *chunk, struct ucl_macro *macro,
+               unsigned char const **macro_start, size_t *macro_len, UT_string **err)
+{
+       const unsigned char *p, *c;
+       bool need_unescape = false;
+
+       p = chunk->pos;
+
+       switch (*p) {
+       case '"':
+               /* We have macro value encoded in quotes */
+               c = p;
+               ucl_chunk_skipc (chunk, p);
+               if (!ucl_lex_json_string (parser, chunk, &need_unescape, err)) {
+                       return false;
+               }
+
+               *macro_start = c + 1;
+               *macro_len = chunk->pos - c - 2;
+               p = chunk->pos;
+               break;
+       case '{':
+               /* We got a multiline macro body */
+               ucl_chunk_skipc (chunk, p);
+               /* Skip spaces at the beginning */
+               while (p < chunk->end) {
+                       if (ucl_test_character (*p, UCL_CHARACTER_WHITESPACE_UNSAFE)) {
+                               ucl_chunk_skipc (chunk, p);
+                       }
+                       else {
+                               break;
+                       }
+               }
+               c = p;
+               while (p < chunk->end) {
+                       if (*p == '}') {
+                               break;
+                       }
+                       ucl_chunk_skipc (chunk, p);
+               }
+               *macro_start = c;
+               *macro_len = p - c;
+               ucl_chunk_skipc (chunk, p);
+               break;
+       default:
+               /* Macro is not enclosed in quotes or braces */
+               c = p;
+               while (p < chunk->end) {
+                       if (ucl_lex_is_atom_end (*p)) {
+                               break;
+                       }
+                       ucl_chunk_skipc (chunk, p);
+               }
+               *macro_start = c;
+               *macro_len = p - c;
+               break;
+       }
+
+       /* We are at the end of a macro */
+       /* Skip ';' and space characters and return to previous state */
+       while (p < chunk->end) {
+               if (!ucl_test_character (*p, UCL_CHARACTER_WHITESPACE_UNSAFE) && *p != ';') {
+                       break;
+               }
+               ucl_chunk_skipc (chunk, p);
+       }
+       return true;
+}
+
+/**
+ * Handle the main states of rcl parser
+ * @param parser parser structure
+ * @param data the pointer to the beginning of a chunk
+ * @param len the length of a chunk
+ * @param err if *err is NULL it is set to parser error
+ * @return true if chunk has been parsed and false in case of error
+ */
+static bool
+ucl_state_machine (struct ucl_parser *parser, UT_string **err)
+{
+       ucl_object_t *obj;
+       struct ucl_chunk *chunk = parser->chunks;
+       struct ucl_stack *st;
+       const unsigned char *p, *c, *macro_start = NULL;
+       size_t macro_len = 0;
+       struct ucl_macro *macro = NULL;
+
+       p = chunk->pos;
+       while (chunk->pos < chunk->end) {
+               switch (parser->state) {
+               case UCL_STATE_INIT:
+                       /*
+                        * At the init state we can either go to the parse array or object
+                        * if we got [ or { correspondingly or can just treat new data as
+                        * a key of newly created object
+                        */
+                       if (!ucl_skip_comments (parser, err)) {
+                               parser->prev_state = parser->state;
+                               parser->state = UCL_STATE_ERROR;
+                               return false;
+                       }
+                       else {
+                               p = chunk->pos;
+                               obj = ucl_object_new ();
+                               if (*p == '[') {
+                                       parser->state = UCL_STATE_VALUE;
+                                       obj->type = UCL_ARRAY;
+                                       ucl_chunk_skipc (chunk, p);
+                               }
+                               else {
+                                       parser->state = UCL_STATE_KEY;
+                                       obj->type = UCL_OBJECT;
+                                       if (*p == '{') {
+                                               ucl_chunk_skipc (chunk, p);
+                                       }
+                               };
+                               parser->cur_obj = obj;
+                               parser->top_obj = obj;
+                               st = UCL_ALLOC (sizeof (struct ucl_stack));
+                               st->obj = obj;
+                               LL_PREPEND (parser->stack, st);
+                       }
+                       break;
+               case UCL_STATE_KEY:
+                       /* Skip any spaces */
+                       while (p < chunk->end && ucl_test_character (*p, UCL_CHARACTER_WHITESPACE_UNSAFE)) {
+                               ucl_chunk_skipc (chunk, p);
+                       }
+                       if (*p == '}') {
+                               /* We have the end of an object */
+                               parser->state = UCL_STATE_AFTER_VALUE;
+                               continue;
+                       }
+                       if (!ucl_parse_key (parser, chunk, err)) {
+                               parser->prev_state = parser->state;
+                               parser->state = UCL_STATE_ERROR;
+                               return false;
+                       }
+                       if (parser->state != UCL_STATE_MACRO_NAME) {
+                               parser->state = UCL_STATE_VALUE;
+                       }
+                       else {
+                               c = chunk->pos;
+                       }
+                       p = chunk->pos;
+                       break;
+               case UCL_STATE_VALUE:
+                       /* We need to check what we do have */
+                       if (!ucl_parse_value (parser, chunk, err)) {
+                               parser->prev_state = parser->state;
+                               parser->state = UCL_STATE_ERROR;
+                               return false;
+                       }
+                       /* State is set in ucl_parse_value call */
+                       p = chunk->pos;
+                       break;
+               case UCL_STATE_AFTER_VALUE:
+                       if (!ucl_parse_after_value (parser, chunk, err)) {
+                               parser->prev_state = parser->state;
+                               parser->state = UCL_STATE_ERROR;
+                               return false;
+                       }
+                       if (parser->stack != NULL) {
+                               if (parser->stack->obj->type == UCL_OBJECT) {
+                                       parser->state = UCL_STATE_KEY;
+                               }
+                               else {
+                                       /* Array */
+                                       parser->state = UCL_STATE_VALUE;
+                               }
+                       }
+                       else {
+                               /* 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)) {
+                               ucl_chunk_skipc (chunk, p);
+                       }
+                       else if (p - c > 0) {
+                               /* We got macro name */
+                               HASH_FIND (hh, parser->macroes, c, (p - c), macro);
+                               if (macro == NULL) {
+                                       ucl_set_err (chunk, UCL_EMACRO, "unknown macro", err);
+                                       parser->state = UCL_STATE_ERROR;
+                                       return false;
+                               }
+                               /* Now we need to skip all spaces */
+                               while (p < chunk->end) {
+                                       if (!ucl_test_character (*p, UCL_CHARACTER_WHITESPACE_UNSAFE)) {
+                                               if (ucl_lex_is_comment (p[0], p[1])) {
+                                                       /* Skip comment */
+                                                       if (!ucl_skip_comments (parser, err)) {
+                                                               return false;
+                                                       }
+                                                       p = chunk->pos;
+                                               }
+                                               break;
+                                       }
+                                       ucl_chunk_skipc (chunk, p);
+                               }
+                               parser->state = UCL_STATE_MACRO;
+                       }
+                       break;
+               case UCL_STATE_MACRO:
+                       if (!ucl_parse_macro_value (parser, chunk, macro,
+                                       &macro_start, &macro_len, err)) {
+                               parser->prev_state = parser->state;
+                               parser->state = UCL_STATE_ERROR;
+                               return false;
+                       }
+                       parser->state = parser->prev_state;
+                       if (!macro->handler (macro_start, macro_len, macro->ud, err)) {
+                               return false;
+                       }
+                       p = chunk->pos;
+                       break;
+               default:
+                       /* TODO: add all states */
+                       ucl_set_err (chunk, UCL_EMACRO, "internal error: parser is in an unknown state", err);
+                       parser->state = UCL_STATE_ERROR;
+                       return false;
+               }
+       }
+
+       return true;
+}
+
+struct ucl_parser*
+ucl_parser_new (int flags)
+{
+       struct ucl_parser *new;
+
+       new = UCL_ALLOC (sizeof (struct ucl_parser));
+       memset (new, 0, sizeof (struct ucl_parser));
+
+       ucl_parser_register_macro (new, "include", ucl_include_handler, new);
+       ucl_parser_register_macro (new, "includes", ucl_includes_handler, new);
+
+       new->flags = flags;
+
+       return new;
+}
+
+
+void
+ucl_parser_register_macro (struct ucl_parser *parser, const char *macro,
+               ucl_macro_handler handler, void* ud)
+{
+       struct ucl_macro *new;
+
+       new = UCL_ALLOC (sizeof (struct ucl_macro));
+       memset (new, 0, sizeof (struct ucl_macro));
+       new->handler = handler;
+       new->name = strdup (macro);
+       new->ud = ud;
+       HASH_ADD_KEYPTR (hh, parser->macroes, new->name, strlen (new->name), new);
+}
+
+bool
+ucl_parser_add_chunk (struct ucl_parser *parser, const unsigned char *data,
+               size_t len, UT_string **err)
+{
+       struct ucl_chunk *chunk;
+
+       if (parser->state != UCL_STATE_ERROR) {
+               chunk = UCL_ALLOC (sizeof (struct ucl_chunk));
+               chunk->begin = data;
+               chunk->remain = len;
+               chunk->pos = chunk->begin;
+               chunk->end = chunk->begin + len;
+               chunk->line = 1;
+               chunk->column = 0;
+               LL_PREPEND (parser->chunks, chunk);
+               parser->recursion ++;
+               if (parser->recursion > UCL_MAX_RECURSION) {
+                       ucl_create_err (err, "maximum include nesting limit is reached: %d",
+                                       parser->recursion);
+                       return false;
+               }
+               return ucl_state_machine (parser, err);
+       }
+
+       ucl_create_err (err, "a parser is in an invalid state");
+
+       return false;
+}
diff --git a/src/ucl/src/ucl_util.c b/src/ucl/src/ucl_util.c
new file mode 100644 (file)
index 0000000..510035c
--- /dev/null
@@ -0,0 +1,841 @@
+/* Copyright (c) 2013, 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 ''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.
+ */
+
+#include "ucl.h"
+#include "ucl_internal.h"
+#include "ucl_chartable.h"
+
+#ifdef HAVE_OPENSSL
+#include <openssl/err.h>
+#include <openssl/sha.h>
+#include <openssl/rsa.h>
+#include <openssl/ssl.h>
+#include <openssl/evp.h>
+#endif
+
+/**
+ * @file rcl_util.c
+ * Utilities for rcl parsing
+ */
+
+
+static void
+ucl_object_free_internal (ucl_object_t *obj, bool allow_rec)
+{
+       ucl_object_t *sub, *tmp;
+
+       while (obj != NULL) {
+               if (obj->trash_stack[UCL_TRASH_KEY] != NULL) {
+                       UCL_FREE (obj->hh.keylen, obj->trash_stack[UCL_TRASH_KEY]);
+               }
+               if (obj->trash_stack[UCL_TRASH_VALUE] != NULL) {
+                       UCL_FREE (obj->len, obj->trash_stack[UCL_TRASH_VALUE]);
+               }
+
+               if (obj->type == UCL_ARRAY) {
+                       sub = obj->value.ov;
+                       while (sub != NULL) {
+                               tmp = sub->next;
+                               ucl_object_free_internal (sub, false);
+                               sub = tmp;
+                       }
+               }
+               else if (obj->type == UCL_OBJECT) {
+                       HASH_ITER (hh, obj->value.ov, sub, tmp) {
+                               HASH_DELETE (hh, obj->value.ov, sub);
+                               ucl_object_free_internal (sub, true);
+                       }
+               }
+               tmp = obj->next;
+               UCL_FREE (sizeof (ucl_object_t), obj);
+               obj = tmp;
+
+               if (!allow_rec) {
+                       break;
+               }
+       }
+}
+
+void
+ucl_obj_free (ucl_object_t *obj)
+{
+       ucl_object_free_internal (obj, true);
+}
+
+size_t
+ucl_unescape_json_string (char *str, size_t len)
+{
+       char *t = str, *h = str;
+       int i, uval;
+
+       /* t is target (tortoise), h is source (hare) */
+
+       while (len) {
+               if (*h == '\\') {
+                       h ++;
+                       switch (*h) {
+                       case 'n':
+                               *t++ = '\n';
+                               break;
+                       case 'r':
+                               *t++ = '\r';
+                               break;
+                       case 'b':
+                               *t++ = '\b';
+                               break;
+                       case 't':
+                               *t++ = '\t';
+                               break;
+                       case 'f':
+                               *t++ = '\f';
+                               break;
+                       case '\\':
+                               *t++ = '\\';
+                               break;
+                       case '"':
+                               *t++ = '"';
+                               break;
+                       case 'u':
+                               /* Unicode escape */
+                               uval = 0;
+                               for (i = 0; i < 4; i++) {
+                                       uval <<= 4;
+                                       if (isdigit (h[i])) {
+                                               uval += h[i] - '0';
+                                       }
+                                       else if (h[i] >= 'a' && h[i] <= 'f') {
+                                               uval += h[i] - 'a' + 10;
+                                       }
+                                       else if (h[i] >= 'A' && h[i] <= 'F') {
+                                               uval += h[i] - 'A' + 10;
+                                       }
+                               }
+                               h += 3;
+                               len -= 3;
+                               /* Encode */
+                               if(uval < 0x80) {
+                                       t[0] = (char)uval;
+                                       t ++;
+                               }
+                               else if(uval < 0x800) {
+                                       t[0] = 0xC0 + ((uval & 0x7C0) >> 6);
+                                       t[1] = 0x80 + ((uval & 0x03F));
+                                       t += 2;
+                               }
+                               else if(uval < 0x10000) {
+                                       t[0] = 0xE0 + ((uval & 0xF000) >> 12);
+                                       t[1] = 0x80 + ((uval & 0x0FC0) >> 6);
+                                       t[2] = 0x80 + ((uval & 0x003F));
+                                       t += 3;
+                               }
+                               else if(uval <= 0x10FFFF) {
+                                       t[0] = 0xF0 + ((uval & 0x1C0000) >> 18);
+                                       t[1] = 0x80 + ((uval & 0x03F000) >> 12);
+                                       t[2] = 0x80 + ((uval & 0x000FC0) >> 6);
+                                       t[3] = 0x80 + ((uval & 0x00003F));
+                                       t += 4;
+                               }
+                               else {
+                                       *t++ = '?';
+                               }
+                               break;
+                       default:
+                               *t++ = '?';
+                               break;
+                       }
+                       h ++;
+                       len --;
+               }
+               else {
+                       *t++ = *h++;
+               }
+               len --;
+       }
+       *t = '\0';
+
+       return (t - str);
+}
+
+char *
+ucl_copy_key_trash (ucl_object_t *obj)
+{
+       if (obj->trash_stack[UCL_TRASH_KEY] == NULL && obj->hh.key != NULL) {
+               obj->trash_stack[UCL_TRASH_KEY] = malloc (obj->hh.keylen + 1);
+               if (obj->trash_stack[UCL_TRASH_KEY] != NULL) {
+                       memcpy (obj->trash_stack[UCL_TRASH_KEY], obj->hh.key, obj->hh.keylen);
+                       obj->trash_stack[UCL_TRASH_KEY][obj->hh.keylen] = '\0';
+               }
+               obj->hh.key = obj->trash_stack[UCL_TRASH_KEY];
+       }
+
+       return obj->trash_stack[UCL_TRASH_KEY];
+}
+
+char *
+ucl_copy_value_trash (ucl_object_t *obj)
+{
+       UT_string *emitted;
+       if (obj->trash_stack[UCL_TRASH_VALUE] == NULL) {
+               if (obj->type == UCL_STRING) {
+                       /* Special case for strings */
+                       obj->trash_stack[UCL_TRASH_VALUE] = malloc (obj->len + 1);
+                       if (obj->trash_stack[UCL_TRASH_VALUE] != NULL) {
+                               memcpy (obj->trash_stack[UCL_TRASH_VALUE], obj->value.sv, obj->len);
+                               obj->trash_stack[UCL_TRASH_VALUE][obj->len] = '\0';
+                       }
+               }
+               else {
+                       /* Just emit value in json notation */
+                       utstring_new (emitted);
+
+                       if (emitted != NULL) {
+                               ucl_elt_write_json (obj, emitted, 0, 0, true);
+                               obj->trash_stack[UCL_TRASH_VALUE] = emitted->d;
+                               obj->len = emitted->i;
+                               free (emitted);
+                       }
+               }
+       }
+       return obj->trash_stack[UCL_TRASH_VALUE];
+}
+
+ucl_object_t*
+ucl_parser_get_object (struct ucl_parser *parser, UT_string **err)
+{
+       if (parser->state != UCL_STATE_INIT && parser->state != UCL_STATE_ERROR) {
+               return ucl_object_ref (parser->top_obj);
+       }
+
+       return NULL;
+}
+
+void
+ucl_parser_free (struct ucl_parser *parser)
+{
+       struct ucl_stack *stack, *stmp;
+       struct ucl_macro *macro, *mtmp;
+       struct ucl_chunk *chunk, *ctmp;
+       struct ucl_pubkey *key, *ktmp;
+
+       if (parser->top_obj != NULL) {
+               ucl_object_unref (parser->top_obj);
+       }
+
+       LL_FOREACH_SAFE (parser->stack, stack, stmp) {
+               free (stack);
+       }
+       HASH_ITER (hh, parser->macroes, macro, mtmp) {
+               free (macro->name);
+               HASH_DEL (parser->macroes, macro);
+               UCL_FREE (sizeof (struct ucl_macro), macro);
+       }
+       LL_FOREACH_SAFE (parser->chunks, chunk, ctmp) {
+               UCL_FREE (sizeof (struct ucl_chunk), chunk);
+       }
+       LL_FOREACH_SAFE (parser->keys, key, ktmp) {
+               UCL_FREE (sizeof (struct ucl_pubkey), key);
+       }
+
+       UCL_FREE (sizeof (struct ucl_parser), parser);
+}
+
+bool
+ucl_pubkey_add (struct ucl_parser *parser, const unsigned char *key, size_t len, UT_string **err)
+{
+       struct ucl_pubkey *nkey;
+#ifndef HAVE_OPENSSL
+       ucl_create_err (err, "cannot check signatures without openssl");
+       return false;
+#else
+# if (OPENSSL_VERSION_NUMBER < 0x10000000L)
+       ucl_create_err (err, "cannot check signatures, openssl version is unsupported");
+       return EXIT_FAILURE;
+# else
+       BIO *mem;
+
+       mem = BIO_new_mem_buf ((void *)key, len);
+       nkey = UCL_ALLOC (sizeof (struct ucl_pubkey));
+       nkey->key = PEM_read_bio_PUBKEY (mem, &nkey->key, NULL, NULL);
+       BIO_free (mem);
+       if (nkey->key == NULL) {
+               UCL_FREE (sizeof (struct ucl_pubkey), nkey);
+               ucl_create_err (err, "%s",
+                               ERR_error_string (ERR_get_error (), NULL));
+               return false;
+       }
+       LL_PREPEND (parser->keys, nkey);
+# endif
+#endif
+       return true;
+}
+
+#ifdef CURL_FOUND
+struct ucl_curl_cbdata {
+       unsigned char *buf;
+       size_t buflen;
+};
+
+static size_t
+ucl_curl_write_callback (void* contents, size_t size, size_t nmemb, void* ud)
+{
+       struct ucl_curl_cbdata *cbdata = ud;
+       size_t realsize = size * nmemb;
+
+       cbdata->buf = g_realloc (cbdata->buf, cbdata->buflen + realsize + 1);
+       if (cbdata->buf == NULL) {
+               return 0;
+       }
+
+       memcpy (&(cbdata->buf[cbdata->buflen]), contents, realsize);
+       cbdata->buflen += realsize;
+       cbdata->buf[cbdata->buflen] = 0;
+
+       return realsize;
+}
+#endif
+
+/**
+ * Fetch a url and save results to the memory buffer
+ * @param url url to fetch
+ * @param len length of url
+ * @param buf target buffer
+ * @param buflen target length
+ * @return
+ */
+static bool
+ucl_fetch_url (const unsigned char *url, unsigned char **buf, size_t *buflen, UT_string **err)
+{
+
+#ifdef HAVE_FETCH_H
+       struct url *fetch_url;
+       struct url_stat us;
+       FILE *in;
+
+       fetch_url = fetchParseURL (url);
+       if (fetch_url == NULL) {
+               ucl_create_err (err, "invalid URL %s: %s",
+                               url, strerror (errno));
+               return false;
+       }
+       if ((in = fetchXGet (fetch_url, &us, "")) == NULL) {
+               ucl_create_err (err, "cannot fetch URL %s: %s",
+                               url, strerror (errno));
+               fetchFreeURL (fetch_url);
+               return false;
+       }
+
+       *buflen = us.size;
+       *buf = g_malloc (*buflen);
+       if (*buf == NULL) {
+               ucl_create_err (err, "cannot allocate buffer for URL %s: %s",
+                               url, strerror (errno));
+               fclose (in);
+               fetchFreeURL (fetch_url);
+               return false;
+       }
+
+       if (fread (*buf, *buflen, 1, in) != 1) {
+               ucl_create_err (err, "cannot read URL %s: %s",
+                               url, strerror (errno));
+               fclose (in);
+               fetchFreeURL (fetch_url);
+               return false;
+       }
+
+       fetchFreeURL (fetch_url);
+       return true;
+#elif defined(CURL_FOUND)
+       CURL *curl;
+       int r;
+       struct ucl_curl_cbdata cbdata;
+
+       curl = curl_easy_init ();
+       if (curl == NULL) {
+               ucl_create_err (err, "CURL interface is broken");
+               return false;
+       }
+       if ((r = curl_easy_setopt (curl, CURLOPT_URL, url)) != CURLE_OK) {
+               ucl_create_err (err, "invalid URL %s: %s",
+                               url, curl_easy_strerror (r));
+               curl_easy_cleanup (curl);
+               return false;
+       }
+       curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, ucl_curl_write_callback);
+       cbdata.buf = *buf;
+       cbdata.buflen = *buflen;
+       curl_easy_setopt (curl, CURLOPT_WRITEDATA, &cbdata);
+
+       if ((r = curl_easy_perform (curl)) != CURLE_OK) {
+               ucl_create_err (err, "error fetching URL %s: %s",
+                               url, curl_easy_strerror (r));
+               curl_easy_cleanup (curl);
+               if (buf != NULL) {
+                       free (buf);
+               }
+               return false;
+       }
+       *buf = cbdata.buf;
+       *buflen = cbdata.buflen;
+
+       return true;
+#else
+       ucl_create_err (err, "URL support is disabled");
+       return false;
+#endif
+}
+
+/**
+ * Fetch a file and save results to the memory buffer
+ * @param filename filename to fetch
+ * @param len length of filename
+ * @param buf target buffer
+ * @param buflen target length
+ * @return
+ */
+static bool
+ucl_fetch_file (const unsigned char *filename, unsigned char **buf, size_t *buflen, UT_string **err)
+{
+       int fd;
+       struct stat st;
+
+       if (stat (filename, &st) == -1) {
+               ucl_create_err (err, "cannot stat file %s: %s",
+                               filename, strerror (errno));
+               return false;
+       }
+       if ((fd = open (filename, O_RDONLY)) == -1) {
+               ucl_create_err (err, "cannot open file %s: %s",
+                               filename, strerror (errno));
+               return false;
+       }
+       if ((*buf = mmap (NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0)) == MAP_FAILED) {
+               close (fd);
+               ucl_create_err (err, "cannot mmap file %s: %s",
+                               filename, strerror (errno));
+               return false;
+       }
+       *buflen = st.st_size;
+       close (fd);
+
+       return true;
+}
+
+
+#if (defined(HAVE_OPENSSL) && OPENSSL_VERSION_NUMBER >= 0x10000000L)
+static inline bool
+ucl_sig_check (const unsigned char *data, size_t datalen,
+               const unsigned char *sig, size_t siglen, struct ucl_parser *parser)
+{
+       struct ucl_pubkey *key;
+       char dig[EVP_MAX_MD_SIZE];
+       unsigned int diglen;
+       EVP_PKEY_CTX *key_ctx;
+       EVP_MD_CTX *sign_ctx = NULL;
+
+       sign_ctx = EVP_MD_CTX_create ();
+
+       LL_FOREACH (parser->keys, key) {
+               key_ctx = EVP_PKEY_CTX_new (key->key, NULL);
+               if (key_ctx != NULL) {
+                       if (EVP_PKEY_verify_init (key_ctx) <= 0) {
+                               EVP_PKEY_CTX_free (key_ctx);
+                               continue;
+                       }
+                       if (EVP_PKEY_CTX_set_rsa_padding (key_ctx, RSA_PKCS1_PADDING) <= 0) {
+                               EVP_PKEY_CTX_free (key_ctx);
+                               continue;
+                       }
+                       if (EVP_PKEY_CTX_set_signature_md (key_ctx, EVP_sha256 ()) <= 0) {
+                               EVP_PKEY_CTX_free (key_ctx);
+                               continue;
+                       }
+                       EVP_DigestInit (sign_ctx, EVP_sha256 ());
+                       EVP_DigestUpdate (sign_ctx, data, datalen);
+                       EVP_DigestFinal (sign_ctx, dig, &diglen);
+
+                       if (EVP_PKEY_verify (key_ctx, sig, siglen, dig, diglen) == 1) {
+                               EVP_MD_CTX_destroy (sign_ctx);
+                               EVP_PKEY_CTX_free (key_ctx);
+                               return true;
+                       }
+
+                       EVP_PKEY_CTX_free (key_ctx);
+               }
+       }
+
+       EVP_MD_CTX_destroy (sign_ctx);
+
+       return false;
+}
+#endif
+
+/**
+ * Include an url to configuration
+ * @param data
+ * @param len
+ * @param parser
+ * @param err
+ * @return
+ */
+static bool
+ucl_include_url (const unsigned char *data, size_t len,
+               struct ucl_parser *parser, bool check_signature, UT_string **err)
+{
+
+       bool res;
+       unsigned char *buf = NULL, *sigbuf = NULL;
+       size_t buflen = 0, siglen = 0;
+       struct ucl_chunk *chunk;
+       char urlbuf[PATH_MAX];
+
+       snprintf (urlbuf, sizeof (urlbuf), "%.*s", (int)len, data);
+
+       if (!ucl_fetch_url (urlbuf, &buf, &buflen, err)) {
+               return false;
+       }
+
+       if (check_signature) {
+#if (defined(HAVE_OPENSSL) && OPENSSL_VERSION_NUMBER >= 0x10000000L)
+               /* We need to check signature first */
+               snprintf (urlbuf, sizeof (urlbuf), "%.*s.sig", (int)len, data);
+               if (!ucl_fetch_file (urlbuf, &sigbuf, &siglen, err)) {
+                       return false;
+               }
+               if (!ucl_sig_check (buf, buflen, sigbuf, siglen, parser)) {
+                       ucl_create_err (err, "cannot verify url %s: %s",
+                                                       urlbuf,
+                                                       ERR_error_string (ERR_get_error (), NULL));
+                       munmap (sigbuf, siglen);
+                       return false;
+               }
+               munmap (sigbuf, siglen);
+#endif
+       }
+
+       res = ucl_parser_add_chunk (parser, buf, buflen, err);
+       if (res == true) {
+               /* Remove chunk from the stack */
+               chunk = parser->chunks;
+               if (chunk != NULL) {
+                       parser->chunks = chunk->next;
+                       UCL_FREE (sizeof (struct ucl_chunk), chunk);
+               }
+       }
+       free (buf);
+
+       return res;
+}
+
+/**
+ * Include a file to configuration
+ * @param data
+ * @param len
+ * @param parser
+ * @param err
+ * @return
+ */
+static bool
+ucl_include_file (const unsigned char *data, size_t len,
+               struct ucl_parser *parser, bool check_signature, UT_string **err)
+{
+       bool res;
+       struct ucl_chunk *chunk;
+       unsigned char *buf = NULL, *sigbuf = NULL;
+       size_t buflen, siglen;
+       char filebuf[PATH_MAX], realbuf[PATH_MAX];
+
+       snprintf (filebuf, sizeof (filebuf), "%.*s", (int)len, data);
+       if (realpath (filebuf, realbuf) == NULL) {
+               ucl_create_err (err, "cannot open file %s: %s",
+                                                                       filebuf,
+                                                                       strerror (errno));
+               return false;
+       }
+
+       if (!ucl_fetch_file (realbuf, &buf, &buflen, err)) {
+               return false;
+       }
+
+       if (check_signature) {
+#if (defined(HAVE_OPENSSL) && OPENSSL_VERSION_NUMBER >= 0x10000000L)
+               /* We need to check signature first */
+               snprintf (filebuf, sizeof (filebuf), "%s.sig", realbuf);
+               if (!ucl_fetch_file (filebuf, &sigbuf, &siglen, err)) {
+                       return false;
+               }
+               if (!ucl_sig_check (buf, buflen, sigbuf, siglen, parser)) {
+                       ucl_create_err (err, "cannot verify file %s: %s",
+                                                       filebuf,
+                                                       ERR_error_string (ERR_get_error (), NULL));
+                       munmap (sigbuf, siglen);
+                       return false;
+               }
+               munmap (sigbuf, siglen);
+#endif
+       }
+
+       res = ucl_parser_add_chunk (parser, buf, buflen, err);
+       if (res == true) {
+               /* Remove chunk from the stack */
+               chunk = parser->chunks;
+               if (chunk != NULL) {
+                       parser->chunks = chunk->next;
+                       UCL_FREE (sizeof (struct ucl_chunk), chunk);
+               }
+       }
+       munmap (buf, buflen);
+
+       return res;
+}
+
+/**
+ * Handle include macro
+ * @param data include data
+ * @param len length of data
+ * @param ud user data
+ * @param err error ptr
+ * @return
+ */
+bool
+ucl_include_handler (const unsigned char *data, size_t len, void* ud, UT_string **err)
+{
+       struct ucl_parser *parser = ud;
+
+       if (*data == '/' || *data == '.') {
+               /* Try to load a file */
+               return ucl_include_file (data, len, parser, false, err);
+       }
+
+       return ucl_include_url (data, len, parser, false, err);
+}
+
+/**
+ * Handle includes macro
+ * @param data include data
+ * @param len length of data
+ * @param ud user data
+ * @param err error ptr
+ * @return
+ */
+bool
+ucl_includes_handler (const unsigned char *data, size_t len, void* ud, UT_string **err)
+{
+       struct ucl_parser *parser = ud;
+
+       if (*data == '/' || *data == '.') {
+               /* Try to load a file */
+               return ucl_include_file (data, len, parser, true, err);
+       }
+
+       return ucl_include_url (data, len, parser, true, err);
+}
+
+bool
+ucl_parser_add_file (struct ucl_parser *parser, const char *filename,
+               UT_string **err)
+{
+       unsigned char *buf;
+       size_t len;
+       bool ret;
+
+       if (!ucl_fetch_file (filename, &buf, &len, err)) {
+               return false;
+       }
+
+       ret = ucl_parser_add_chunk (parser, buf, len, err);
+
+       munmap (buf, len);
+
+       return ret;
+}
+
+size_t
+ucl_strlcpy (char *dst, const char *src, size_t siz)
+{
+       char *d = dst;
+       const char *s = src;
+       size_t n = siz;
+
+       /* Copy as many bytes as will fit */
+       if (n != 0) {
+               while (--n != 0) {
+                       if ((*d++ = *s++) == '\0') {
+                               break;
+                       }
+               }
+       }
+
+       if (n == 0 && siz != 0) {
+               *d = '\0';
+       }
+
+       return (s - src - 1);    /* count does not include NUL */
+}
+
+size_t
+ucl_strlcpy_unsafe (char *dst, const char *src, size_t siz)
+{
+       memcpy (dst, src, siz - 1);
+       dst[siz - 1] = '\0';
+
+       return siz - 1;
+}
+
+size_t
+ucl_strlcpy_tolower (char *dst, const char *src, size_t siz)
+{
+       char *d = dst;
+       const char *s = src;
+       size_t n = siz;
+
+       /* Copy as many bytes as will fit */
+       if (n != 0) {
+               while (--n != 0) {
+                       if ((*d++ = tolower (*s++)) == '\0') {
+                               break;
+                       }
+               }
+       }
+
+       if (n == 0 && siz != 0) {
+               *d = '\0';
+       }
+
+       return (s - src);    /* count does not include NUL */
+}
+
+ucl_object_t *
+ucl_object_fromstring_common (const char *str, size_t len, enum ucl_string_flags flags)
+{
+       ucl_object_t *obj;
+       const char *start, *end, *p, *pos;
+       char *dst, *d;
+       size_t escaped_len;
+
+       if (str == NULL) {
+               return NULL;
+       }
+
+       obj = ucl_object_new ();
+       if (obj) {
+               if (len == 0) {
+                       len = strlen (str);
+               }
+               if (flags & UCL_STRING_TRIM) {
+                       /* Skip leading spaces */
+                       for (start = str; (size_t)(start - str) < len; start ++) {
+                               if (!ucl_test_character (*start, UCL_CHARACTER_WHITESPACE_UNSAFE)) {
+                                       break;
+                               }
+                       }
+                       /* Skip trailing spaces */
+                       for (end = str + len - 1; end > start; end --) {
+                               if (!ucl_test_character (*end, UCL_CHARACTER_WHITESPACE_UNSAFE)) {
+                                       break;
+                               }
+                       }
+                       end ++;
+               }
+               else {
+                       start = str;
+                       end = str + len;
+               }
+
+               obj->type = UCL_STRING;
+               if (flags & UCL_STRING_ESCAPE) {
+                       for (p = start, escaped_len = 0; p < end; p ++, escaped_len ++) {
+                               if (ucl_test_character (*p, UCL_CHARACTER_JSON_UNSAFE)) {
+                                       escaped_len ++;
+                               }
+                       }
+                       dst = malloc (escaped_len + 1);
+                       if (dst != NULL) {
+                               for (p = start, d = dst; p < end; p ++, d ++) {
+                                       if (ucl_test_character (*p, UCL_CHARACTER_JSON_UNSAFE)) {
+                                               switch (*p) {
+                                               case '\n':
+                                                       *d++ = '\\';
+                                                       *d = 'n';
+                                                       break;
+                                               case '\r':
+                                                       *d++ = '\\';
+                                                       *d = 'r';
+                                                       break;
+                                               case '\b':
+                                                       *d++ = '\\';
+                                                       *d = 'b';
+                                                       break;
+                                               case '\t':
+                                                       *d++ = '\\';
+                                                       *d = 't';
+                                                       break;
+                                               case '\f':
+                                                       *d++ = '\\';
+                                                       *d = 'f';
+                                                       break;
+                                               case '\\':
+                                                       *d++ = '\\';
+                                                       *d = '\\';
+                                                       break;
+                                               case '"':
+                                                       *d++ = '\\';
+                                                       *d = '"';
+                                                       break;
+                                               }
+                                       }
+                                       else {
+                                               *d = *p;
+                                       }
+                               }
+                               *d = '\0';
+                               obj->value.sv = dst;
+                               obj->trash_stack[UCL_TRASH_VALUE] = dst;
+                               obj->len = escaped_len;
+                       }
+               }
+               else {
+                       dst = malloc (end - start + 1);
+                       if (dst != NULL) {
+                               ucl_strlcpy_unsafe (dst, start, end - start + 1);
+                               obj->value.sv = dst;
+                               obj->trash_stack[UCL_TRASH_VALUE] = dst;
+                               obj->len = end - start;
+                       }
+               }
+               if ((flags & UCL_STRING_PARSE) && dst != NULL) {
+                       /* Parse what we have */
+                       if (flags & UCL_STRING_PARSE_BOOLEAN) {
+                               if (!ucl_maybe_parse_boolean (obj, dst, obj->len) && (flags & UCL_STRING_PARSE_NUMBER)) {
+                                       ucl_maybe_parse_number (obj, dst, dst + obj->len, &pos,
+                                                       flags & UCL_STRING_PARSE_DOUBLE);
+                               }
+                       }
+                       else {
+                               ucl_maybe_parse_number (obj, dst, dst + obj->len, &pos,
+                                               flags & UCL_STRING_PARSE_DOUBLE);
+                       }
+               }
+       }
+
+       return obj;
+}
diff --git a/src/ucl/tests/1.in b/src/ucl/tests/1.in
new file mode 100644 (file)
index 0000000..41b0cfb
--- /dev/null
@@ -0,0 +1,11 @@
+{
+"key1": value;
+"key1": value2;
+"key1": "value;"
+"key1": 1.0,
+"key1": -1e-10,
+"key1": 1
+"key1": true
+"key1": no
+"key1": yes
+}
diff --git a/src/ucl/tests/1.res b/src/ucl/tests/1.res
new file mode 100644 (file)
index 0000000..789c827
--- /dev/null
@@ -0,0 +1,12 @@
+key1 [
+    "value",
+    "value2",
+    "value;",
+    1.0,
+    -1e-10,
+    1,
+    true,
+    false,
+    true,
+]
+
diff --git a/src/ucl/tests/2.in b/src/ucl/tests/2.in
new file mode 100644 (file)
index 0000000..59a4d0c
--- /dev/null
@@ -0,0 +1,18 @@
+section1 { param1 = value; param2 = value, 
+section3 {param = value; param2 = value, param3 = ["value1", value2, 100500]}}
+section2 { param1 = {key = value}, param1 = ["key"]}
+
+# Numbers
+key1 = 1s
+key2 = 1min
+key3 = 1kb
+key4 = 5M
+key5 = 10mS
+key6 = 10y
+
+# Strings
+key1 = "some string";
+key2 = /some/path;
+key3 = 111some,
+key4: s1,
+"key5": "\n\r123"
diff --git a/src/ucl/tests/2.res b/src/ucl/tests/2.res
new file mode 100644 (file)
index 0000000..3a8966f
--- /dev/null
@@ -0,0 +1,45 @@
+section1 {
+    param1 = "value";
+    param2 = "value";
+    section3 {
+        param = "value";
+        param2 = "value";
+        param3 [
+            "value1",
+            "value2",
+            100500,
+        ]
+    }
+}
+section2 {
+    param1 [
+        {
+            key = "value";
+        },
+        [
+            "key",
+        ],
+    ]
+}
+key1 [
+    1.0,
+    "some string",
+]
+key2 [
+    60.0,
+    "/some/path",
+]
+key3 [
+    1024,
+    "111some",
+]
+key4 [
+    5000000,
+    "s1",
+]
+key5 [
+    0.010000,
+    "\n\r123",
+]
+key6 = 2207520000.000000;
+
diff --git a/src/ucl/tests/3.in b/src/ucl/tests/3.in
new file mode 100644 (file)
index 0000000..b3e3696
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * Pkg conf
+ */
+
+#packagesite http//pkg.freebsd.org/freebsd-9-amd64/latest
+#packagesite http//pkg.freebsd.org/freebsd-9-amd64/latest
+packagesite: http://pkg-test.freebsd.org/pkg-test/${ABI}/latest
+squaretest: some[]value
+ALIAS             : {
+  all-depends: query %dn-%dv,
+  annotations: info -A,
+  build-depends: info -qd,
+  download: fetch,
+  iinfo: info -i -g -x,
+  isearch: search -i -g -x,
+  leaf: query -e '%a == 0' '%n-%v',
+  leaf: query -e '%a == 0' '%n-%v',
+  list: info -ql,
+  origin: info -qo,
+  provided-depends: info -qb,
+  raw: info -R,
+  required-depends: info -qr,
+  shared-depends: info -qB,
+  show: info -f -k,
+  size: info -sq,
+  }
+
+repo_dirs : [
+  /home/bapt,
+  /usr/local/etc
+]
diff --git a/src/ucl/tests/3.res b/src/ucl/tests/3.res
new file mode 100644 (file)
index 0000000..2f84ed6
--- /dev/null
@@ -0,0 +1,27 @@
+packagesite = "http://pkg-test.freebsd.org/pkg-test/${ABI}/latest";
+squaretest = "some[]value";
+alias {
+    all-depends = "query %dn-%dv";
+    annotations = "info -A";
+    build-depends = "info -qd";
+    download = "fetch";
+    iinfo = "info -i -g -x";
+    isearch = "search -i -g -x";
+    leaf [
+        "query -e '%a == 0' '%n-%v'",
+        "query -e '%a == 0' '%n-%v'",
+    ]
+    list = "info -ql";
+    origin = "info -qo";
+    provided-depends = "info -qb";
+    raw = "info -R";
+    required-depends = "info -qr";
+    shared-depends = "info -qB";
+    show = "info -f -k";
+    size = "info -sq";
+}
+repo_dirs [
+    "/home/bapt",
+    "/usr/local/etc",
+]
+
diff --git a/src/ucl/tests/4.in b/src/ucl/tests/4.in
new file mode 100644 (file)
index 0000000..2b296ef
--- /dev/null
@@ -0,0 +1,47 @@
+name : "pkgconf"
+version : "0.9.3"
+origin : "devel/pkgconf"
+comment : "Utility to help to configure compiler and linker flags"
+arch : "freebsd:9:x86:64"
+maintainer : "bapt@FreeBSD.org"
+prefix : "/usr/local"
+licenselogic : "single"
+licenses : [
+    "BSD",
+]
+flatsize : 60523
+desc : "pkgconf is a program which helps to configure compiler and linker flags for\ndevelopment frameworks. It is similar to pkg-config, but was written from\nscratch in Summer of 2011 to replace pkg-config, which now needs itself to build\nitself.\n\nWWW: https://github.com/pkgconf/pkgconf"
+categories : [
+    "devel",
+]
+files : {
+    /usr/local/bin/pkg-config : "-",
+    /usr/local/bin/pkgconf : "4a0fc53e5ad64e8085da2e61652d61c50b192a086421d865703f1de9f724da38",
+    /usr/local/share/aclocal/pkg.m4 : "cffab33d659adfe36497ec57665eec36fa6fb7b007e578e6ac2434cc28be8820",
+    /usr/local/share/licenses/pkgconf-0.9.3/BSD : "85e7a53b5e2d3e350e2d084fed2f94b7f63005f8e1168740e1e84aa9fa5d48ce",
+    /usr/local/share/licenses/pkgconf-0.9.3/LICENSE : "d9cce0db43502eb1bd8fbef7e960cfaa43b5647186f7f7379923b336209fd77b",
+    /usr/local/share/licenses/pkgconf-0.9.3/catalog.mk : "e7b131acce7c3d3c61f2214607b11b34526e03b05afe89a608f50586a898e2ef",
+}
+directories : {
+    /usr/local/share/licenses/pkgconf-0.9.3/ : false,
+    /usr/local/share/licenses/ : true,
+}
+scripts : {
+    post-install : "cd /usr/local\nn",
+    pre-deinstall : "cd /usr/local\nn",
+    post-deinstall : "cd /usr/local\nn",
+}
+multiline-key : <<EOD
+test
+test
+test\n
+/* comment like */
+# Some invalid endings
+ EOD
+EOD   
+EOF
+# Valid ending + empty string
+
+EOD
+
+normal-key : <<EODnot
diff --git a/src/ucl/tests/4.res b/src/ucl/tests/4.res
new file mode 100644 (file)
index 0000000..58c3599
--- /dev/null
@@ -0,0 +1,36 @@
+name = "pkgconf";
+version = "0.9.3";
+origin = "devel/pkgconf";
+comment = "Utility to help to configure compiler and linker flags";
+arch = "freebsd:9:x86:64";
+maintainer = "bapt@FreeBSD.org";
+prefix = "/usr/local";
+licenselogic = "single";
+licenses [
+    "BSD",
+]
+flatsize = 60523;
+desc = "pkgconf is a program which helps to configure compiler and linker flags for\ndevelopment frameworks. It is similar to pkg-config, but was written from\nscratch in Summer of 2011 to replace pkg-config, which now needs itself to build\nitself.\n\nWWW: https://github.com/pkgconf/pkgconf";
+categories [
+    "devel",
+]
+files {
+    /usr/local/bin/pkg-config = "-";
+    /usr/local/bin/pkgconf = "4a0fc53e5ad64e8085da2e61652d61c50b192a086421d865703f1de9f724da38";
+    /usr/local/share/aclocal/pkg.m4 = "cffab33d659adfe36497ec57665eec36fa6fb7b007e578e6ac2434cc28be8820";
+    /usr/local/share/licenses/pkgconf-0.9.3/bsd = "85e7a53b5e2d3e350e2d084fed2f94b7f63005f8e1168740e1e84aa9fa5d48ce";
+    /usr/local/share/licenses/pkgconf-0.9.3/license = "d9cce0db43502eb1bd8fbef7e960cfaa43b5647186f7f7379923b336209fd77b";
+    /usr/local/share/licenses/pkgconf-0.9.3/catalog.mk = "e7b131acce7c3d3c61f2214607b11b34526e03b05afe89a608f50586a898e2ef";
+}
+directories {
+    /usr/local/share/licenses/pkgconf-0.9.3/ = false;
+    /usr/local/share/licenses/ = true;
+}
+scripts {
+    post-install = "cd /usr/local\nn";
+    pre-deinstall = "cd /usr/local\nn";
+    post-deinstall = "cd /usr/local\nn";
+}
+multiline-key = "test\ntest\ntest\\n\n/* comment like */\n# Some invalid endings\n EOD\nEOD   \nEOF\n# Valid ending + empty string\n";
+normal-key = "<<EODnot";
+
diff --git a/src/ucl/tests/generate.res b/src/ucl/tests/generate.res
new file mode 100644 (file)
index 0000000..e7659ec
--- /dev/null
@@ -0,0 +1,18 @@
+key1 = "test string";
+key2 = "test \\nstring";
+key3 = "  test string    \n";
+key4 [
+    [
+        10,
+        10.100000,
+    ],
+    true,
+]
+key5 = "";
+key6 = "";
+key7 = "   \\n";
+key8 = 1048576;
+key9 = 3.140000;
+key10 = true;
+key11 = false;
+
diff --git a/src/ucl/tests/rcl_test.json.xz b/src/ucl/tests/rcl_test.json.xz
new file mode 100644 (file)
index 0000000..98c3559
Binary files /dev/null and b/src/ucl/tests/rcl_test.json.xz differ
diff --git a/src/ucl/tests/run_tests.sh b/src/ucl/tests/run_tests.sh
new file mode 100755 (executable)
index 0000000..71e6f1b
--- /dev/null
@@ -0,0 +1,57 @@
+#!/bin/sh
+
+if [ $# -lt 1 ] ; then
+       echo 'Specify binary to run as the first argument'
+       exit 1
+fi
+
+
+for _tin in ${TEST_DIR}/*.in ; do
+       _t=`echo $_tin | sed -e 's/.in$//'`
+       $1 $_t.in $_t.out
+       if [ $? -ne 0 ] ; then
+               echo "Test: $_t failed, output:"
+               cat $_t.out
+               rm $_t.out
+               exit 1
+       fi
+       if [ -f $_t.res ] ; then
+       diff -s $_t.out $_t.res -u 2>/dev/null
+               if [ $? -ne 0 ] ; then
+                       rm $_t.out
+                       echo "Test: $_t output missmatch"
+                       exit 1
+               fi
+       fi
+       rm $_t.out
+done
+
+if [ $# -gt 2 ] ; then
+       $3 ${TEST_DIR}/generate.out
+       diff -s ${TEST_DIR}/generate.out ${TEST_DIR}/generate.res -u 2>/dev/null
+       if [ $? -ne 0 ] ; then
+               rm ${TEST_DIR}/generate.out
+               echo "Test: generate.res output missmatch"
+       exit 1
+       fi
+       rm ${TEST_DIR}/generate.out
+fi
+
+if [ $# -gt 1 -a -x "/usr/bin/xz" ] ; then
+       echo 'Running speed tests'
+       for _tin in ${TEST_DIR}/*.xz ; do
+               echo "Unpacking $_tin..."
+               xz -cd < $_tin > ${TEST_DIR}/test_file
+               # Preread file to cheat benchmark!
+               cat ${TEST_DIR}/test_file > /dev/null
+               echo "Starting benchmarking for $_tin..."
+               $2 ${TEST_DIR}/test_file
+               if [ $? -ne 0 ] ; then
+                       echo "Test: $_tin failed"
+                       rm ${TEST_DIR}/test_file
+                       exit 1
+               fi
+               rm ${TEST_DIR}/test_file
+       done
+fi
+
diff --git a/src/ucl/tests/test_basic.c b/src/ucl/tests/test_basic.c
new file mode 100644 (file)
index 0000000..d4d4786
--- /dev/null
@@ -0,0 +1,113 @@
+/* Copyright (c) 2013, 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 ''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.
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include "ucl.h"
+
+int
+main (int argc, char **argv)
+{
+       char inbuf[8192];
+       struct ucl_parser *parser, *parser2;
+       ucl_object_t *obj;
+       FILE *in, *out;
+       UT_string *err = NULL;
+       unsigned char *emitted;
+       const char *fname_in = NULL, *fname_out = NULL;
+       int ret = 0;
+
+       switch (argc) {
+       case 2:
+               fname_in = argv[1];
+               break;
+       case 3:
+               fname_in = argv[1];
+               fname_out = argv[2];
+               break;
+       }
+
+       if (fname_in != NULL) {
+               in = fopen (fname_in, "r");
+               if (in == NULL) {
+                       exit (-errno);
+               }
+       }
+       else {
+               in = stdin;
+       }
+       parser = ucl_parser_new (UCL_FLAG_KEY_LOWERCASE);
+
+       while (!feof (in)) {
+               fread (inbuf, sizeof (inbuf), 1, in);
+               ucl_parser_add_chunk (parser, inbuf, strlen (inbuf), &err);
+       }
+       fclose (in);
+
+       if (fname_out != NULL) {
+               out = fopen (fname_out, "w");
+               if (out == NULL) {
+                       exit (-errno);
+               }
+       }
+       else {
+               out = stdout;
+       }
+       if (err != NULL) {
+               fprintf (out, "Error occurred: %s\n", err->d);
+               ret = 1;
+               goto end;
+       }
+       obj = ucl_parser_get_object (parser, &err);
+       emitted = ucl_object_emit (obj, UCL_EMIT_CONFIG);
+       ucl_parser_free (parser);
+       ucl_object_unref (obj);
+       parser2 = ucl_parser_new (UCL_FLAG_KEY_LOWERCASE);
+       ucl_parser_add_chunk (parser2, emitted, strlen (emitted), &err);
+
+       if (err != NULL) {
+               fprintf (out, "Error occurred: %s\n", err->d);
+               fprintf (out, "%s\n", emitted);
+               ret = 1;
+               goto end;
+       }
+       if (emitted != NULL) {
+               free (emitted);
+       }
+       obj = ucl_parser_get_object (parser2, &err);
+       emitted = ucl_object_emit (obj, UCL_EMIT_CONFIG);
+
+       fprintf (out, "%s\n", emitted);
+       ucl_object_unref (obj);
+
+end:
+       if (emitted != NULL) {
+               free (emitted);
+       }
+       if (parser2 != NULL) {
+               ucl_parser_free (parser2);
+       }
+       fclose (out);
+
+       return ret;
+}
diff --git a/src/ucl/tests/test_generate.c b/src/ucl/tests/test_generate.c
new file mode 100644 (file)
index 0000000..a61b353
--- /dev/null
@@ -0,0 +1,98 @@
+/* Copyright (c) 2013, 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 ''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.
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include "ucl.h"
+
+int
+main (int argc, char **argv)
+{
+       ucl_object_t *obj, *cur, *ar;
+       FILE *out;
+       unsigned char *emitted;
+       const char *fname_out = NULL;
+       int ret = 0;
+
+       switch (argc) {
+       case 2:
+               fname_out = argv[1];
+               break;
+       }
+
+
+       if (fname_out != NULL) {
+               out = fopen (fname_out, "w");
+               if (out == NULL) {
+                       exit (-errno);
+               }
+       }
+       else {
+               out = stdout;
+       }
+
+       obj = ucl_object_new ();
+       /* Create some strings */
+       cur = ucl_object_fromstring_common ("  test string    ", 0, UCL_STRING_TRIM);
+       obj = ucl_object_insert_key (obj, cur, "key1", 0, false);
+       cur = ucl_object_fromstring_common ("  test \nstring\n    ", 0, UCL_STRING_TRIM | UCL_STRING_ESCAPE);
+       obj = ucl_object_insert_key (obj, cur, "key2", 0, false);
+       cur = ucl_object_fromstring_common ("  test string    \n", 0, 0);
+       obj = ucl_object_insert_key (obj, cur, "key3", 0, false);
+       /* Array of numbers */
+       cur = ucl_object_fromint (10);
+       ar = ucl_array_append (NULL, cur);
+       cur = ucl_object_fromdouble (10.1);
+       ar = ucl_array_append (ar, cur);
+       obj = ucl_object_insert_key (obj, ar, "key4", 0, false);
+       cur = ucl_object_frombool (true);
+       obj = ucl_object_insert_key (obj, cur, "key4", 0, false);
+       /* Empty strings */
+       cur = ucl_object_fromstring_common ("      ", 0, UCL_STRING_TRIM);
+       obj = ucl_object_insert_key (obj, cur, "key5", 0, false);
+       cur = ucl_object_fromstring_common ("", 0, UCL_STRING_ESCAPE);
+       obj = ucl_object_insert_key (obj, cur, "key6", 0, false);
+       cur = ucl_object_fromstring_common ("   \n", 0, UCL_STRING_ESCAPE);
+       obj = ucl_object_insert_key (obj, cur, "key7", 0, false);
+       /* Numbers and booleans */
+       cur = ucl_object_fromstring_common ("1mb", 0, UCL_STRING_ESCAPE | UCL_STRING_PARSE);
+       obj = ucl_object_insert_key (obj, cur, "key8", 0, false);
+       cur = ucl_object_fromstring_common ("3.14", 0, UCL_STRING_PARSE);
+       obj = ucl_object_insert_key (obj, cur, "key9", 0, false);
+       cur = ucl_object_fromstring_common ("true", 0, UCL_STRING_PARSE);
+       obj = ucl_object_insert_key (obj, cur, "key10", 0, false);
+       cur = ucl_object_fromstring_common ("  off  ", 0, UCL_STRING_PARSE | UCL_STRING_TRIM);
+       obj = ucl_object_insert_key (obj, cur, "key11", 0, false);
+
+       emitted = ucl_object_emit (obj, UCL_EMIT_CONFIG);
+
+       fprintf (out, "%s\n", emitted);
+       ucl_object_unref (obj);
+
+       if (emitted != NULL) {
+               free (emitted);
+       }
+       fclose (out);
+
+       return ret;
+}
diff --git a/src/ucl/tests/test_speed.c b/src/ucl/tests/test_speed.c
new file mode 100644 (file)
index 0000000..92a972a
--- /dev/null
@@ -0,0 +1,128 @@
+/* Copyright (c) 2013, 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 ''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.
+ */
+
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <time.h>
+
+#include "ucl.h"
+
+int
+main (int argc, char **argv)
+{
+       void *map;
+       struct ucl_parser *parser;
+       ucl_object_t *obj;
+       int fin;
+       UT_string *err = NULL;
+       unsigned char *emitted;
+       struct stat st;
+       const char *fname_in = NULL;
+       int ret = 0;
+       struct timespec start, end;
+       double seconds;
+
+       switch (argc) {
+       case 2:
+               fname_in = argv[1];
+               break;
+       }
+
+       fin = open (fname_in, O_RDONLY);
+       if (fin == -1) {
+               perror ("open failed");
+               exit (EXIT_FAILURE);
+       }
+       parser = ucl_parser_new (UCL_FLAG_ZEROCOPY);
+
+       (void)fstat (fin, &st);
+       map = mmap (NULL, st.st_size, PROT_READ, MAP_SHARED, fin, 0);
+       if (map == MAP_FAILED) {
+               perror ("mmap failed");
+               exit (EXIT_FAILURE);
+       }
+
+       close (fin);
+
+       clock_gettime (CLOCK_MONOTONIC, &start);
+       ucl_parser_add_chunk (parser, map, st.st_size, &err);
+
+       obj = ucl_parser_get_object (parser, &err);
+       clock_gettime (CLOCK_MONOTONIC, &end);
+
+       seconds = (end.tv_sec - start.tv_sec) + (end.tv_nsec - start.tv_nsec) / 1000000000.;
+       printf ("ucl: parsed input in %.4f seconds\n", seconds);
+       if (err != NULL) {
+               printf ("Error occurred: %s\n", err->d);
+               ret = 1;
+       }
+
+       clock_gettime (CLOCK_MONOTONIC, &start);
+       emitted = ucl_object_emit (obj, UCL_EMIT_CONFIG);
+       clock_gettime (CLOCK_MONOTONIC, &end);
+
+       seconds = (end.tv_sec - start.tv_sec) + (end.tv_nsec - start.tv_nsec) / 1000000000.;
+       printf ("ucl: emitted config in %.4f seconds\n", seconds);
+
+       free (emitted);
+
+       clock_gettime (CLOCK_MONOTONIC, &start);
+       emitted = ucl_object_emit (obj, UCL_EMIT_JSON);
+       clock_gettime (CLOCK_MONOTONIC, &end);
+
+       seconds = (end.tv_sec - start.tv_sec) + (end.tv_nsec - start.tv_nsec) / 1000000000.;
+       printf ("ucl: emitted json in %.4f seconds\n", seconds);
+
+       free (emitted);
+
+       clock_gettime (CLOCK_MONOTONIC, &start);
+       emitted = ucl_object_emit (obj, UCL_EMIT_JSON_COMPACT);
+       clock_gettime (CLOCK_MONOTONIC, &end);
+
+       seconds = (end.tv_sec - start.tv_sec) + (end.tv_nsec - start.tv_nsec) / 1000000000.;
+       printf ("ucl: emitted compact json in %.4f seconds\n", seconds);
+
+       free (emitted);
+
+       clock_gettime (CLOCK_MONOTONIC, &start);
+       emitted = ucl_object_emit (obj, UCL_EMIT_YAML);
+       clock_gettime (CLOCK_MONOTONIC, &end);
+
+       seconds = (end.tv_sec - start.tv_sec) + (end.tv_nsec - start.tv_nsec) / 1000000000.;
+       printf ("ucl: emitted yaml in %.4f seconds\n", seconds);
+
+       free (emitted);
+
+       ucl_parser_free (parser);
+       ucl_object_unref (obj);
+
+       munmap (map, st.st_size);
+
+       return ret;
+}
diff --git a/src/ucl/utils/chargen.c b/src/ucl/utils/chargen.c
new file mode 100644 (file)
index 0000000..724b378
--- /dev/null
@@ -0,0 +1,124 @@
+/* Copyright (c) 2013, 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 ''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.
+ */
+
+/**
+ * @file this utility generates character table for ucl
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <stdbool.h>
+
+static inline int
+print_flag (const char *flag, bool *need_or, char *val)
+{
+       int res;
+       res = sprintf (val, "%s%s", *need_or ? "|" : "", flag);
+
+       *need_or |= true;
+
+       return res;
+}
+
+int
+main (int argc, char **argv)
+{
+       int i, col, r;
+       const char *name = "ucl_chartable";
+       bool need_or;
+       char valbuf[2048];
+
+       col = 0;
+
+       if (argc > 1) {
+               name = argv[1];
+       }
+
+       printf ("static const unsigned int %s[255] = {\n", name);
+
+       for (i = 0; i < 255; i ++) {
+               need_or = false;
+               r = 0;
+               /* UCL_CHARACTER_VALUE_END */
+
+               if (i == ' ' || i == '\t') {
+                       r += print_flag ("UCL_CHARACTER_WHITESPACE", &need_or, valbuf + r);
+               }
+               if (isspace (i)) {
+                       r += print_flag ("UCL_CHARACTER_WHITESPACE_UNSAFE", &need_or, valbuf + r);
+               }
+               if (isalnum (i) || i >= 0x80 || i == '/' || i == '_') {
+                       r += print_flag ("UCL_CHARACTER_KEY_START", &need_or, valbuf + r);
+               }
+               if (isalnum (i) || i == '-' || i == '_' || i == '/' || i == '.' || i >= 0x80) {
+                       r += print_flag ("UCL_CHARACTER_KEY", &need_or, valbuf + r);
+               }
+               if (i == 0 || i == '\r' || i == '\n' || i == ']' || i == '}' || i == ';' || i == ',' || i == '#') {
+                       r += print_flag ("UCL_CHARACTER_VALUE_END", &need_or, valbuf + r);
+               }
+               else {
+                       if (isprint (i) || i >= 0x80) {
+                               r += print_flag ("UCL_CHARACTER_VALUE_STR", &need_or, valbuf + r);
+                       }
+                       if (isdigit (i) || i == '-') {
+                               r += print_flag ("UCL_CHARACTER_VALUE_DIGIT_START", &need_or, valbuf + r);
+                       }
+                       if (isalnum (i) || i == '.' || i == '-' || i == '+') {
+                               r += print_flag ("UCL_CHARACTER_VALUE_DIGIT", &need_or, valbuf + r);
+                       }
+               }
+               if (i == '"' || i == '\\' || i == '/' || i == 'b' ||
+                       i == 'f' || i == 'n' || i == 'r' || i == 't' || i == 'u') {
+                       r += print_flag ("UCL_CHARACTER_ESCAPE", &need_or, valbuf + r);
+               }
+               if (i == ' ' || i == '\t' || i == ':' || i == '=') {
+                       r += print_flag ("UCL_CHARACTER_KEY_SEP", &need_or, valbuf + r);
+               }
+               if (i == '\n' || i == '\r' || i == '\\' || i == '\b' || i == '\t' ||
+                               i == '"' || i == '\f') {
+                       r += print_flag ("UCL_CHARACTER_JSON_UNSAFE", &need_or, valbuf + r);
+               }
+
+               if (!need_or) {
+                       r += print_flag ("UCL_CHARACTER_DENIED", &need_or, valbuf + r);
+               }
+
+               if (isprint (i)) {
+                       r += sprintf (valbuf + r, " /* %c */", i);
+               }
+               if (i != 254) {
+                       r += sprintf (valbuf + r, ", ");
+               }
+               col += r;
+               if (col > 80) {
+                       printf ("\n%s", valbuf);
+                       col = r;
+               }
+               else {
+                       printf ("%s", valbuf);
+               }
+       }
+       printf ("\n}\n");
+
+       return 0;
+}
diff --git a/src/ucl/utils/objdump.c b/src/ucl/utils/objdump.c
new file mode 100644 (file)
index 0000000..985106a
--- /dev/null
@@ -0,0 +1,158 @@
+/* Copyright (c) 2013, Dmitriy V. Reshetnikov
+ * Copyright (c) 2013, 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 ''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.
+ */
+
+#include <stdio.h>
+#include <errno.h>
+
+#include "ucl.h"
+
+void
+ucl_obj_dump(ucl_object_t *obj, unsigned int shift)
+{
+       int num = shift * 4 + 5;
+       char *pre = (char *) malloc (num * sizeof(char));
+       ucl_object_t *cur, *tmp;
+
+       pre[--num] = 0x00;
+       while (num--)
+               pre[num] = 0x20;
+
+       while (obj != NULL ) {
+               printf ("%sucl object address: %p\n", pre + 4, obj);
+               if (obj->hh.key != NULL) {
+                       printf ("%skey: \"%s\"\n", pre, ucl_object_key (obj));
+               }
+               printf ("%sref: %d\n", pre, obj->ref);
+               printf ("%slen: %zd\n", pre, obj->len);
+               printf ("%sprev: %p\n", pre, obj->prev);
+               printf ("%snext: %p\n", pre, obj->next);
+               if (obj->type == UCL_OBJECT) {
+                       printf ("%stype: UCL_OBJECT\n", pre);
+                       printf ("%svalue: %p\n", pre, obj->value.ov);
+                       HASH_ITER (hh, obj->value.ov, cur, tmp) {
+                               ucl_obj_dump (cur, shift + 2);
+                       }
+               }
+               else if (obj->type == UCL_ARRAY) {
+                       printf ("%stype: UCL_ARRAY\n", pre);
+                       printf ("%svalue: %p\n", pre, obj->value.ov);
+                       ucl_obj_dump (obj->value.ov, shift + 2);
+               }
+               else if (obj->type == UCL_INT) {
+                       printf ("%stype: UCL_INT\n", pre);
+                       printf ("%svalue: %ld\n", pre, obj->value.iv);
+               }
+               else if (obj->type == UCL_FLOAT) {
+                       printf ("%stype: UCL_FLOAT\n", pre);
+                       printf ("%svalue: %f\n", pre, obj->value.dv);
+               }
+               else if (obj->type == UCL_STRING) {
+                       printf ("%stype: UCL_STRING\n", pre);
+                        printf ("%svalue: \"%s\"\n", pre, ucl_obj_tostring (obj));
+               }
+               else if (obj->type == UCL_BOOLEAN) {
+                       printf ("%stype: UCL_BOOLEAN\n", pre);
+                       printf ("%svalue: %s\n", pre, (obj->value.iv) ? "true" : "false");
+               }
+               else if (obj->type == UCL_TIME) {
+                       printf ("%stype: UCL_TIME\n", pre);
+                       printf ("%svalue: %f\n", pre, obj->value.dv);
+               }
+               else if (obj->type == UCL_USERDATA) {
+                       printf ("%stype: UCL_USERDATA\n", pre);
+                       printf ("%svalue: %p\n", pre, obj->value.ud);
+               }
+               obj = obj->next;
+       }
+
+       free (pre);
+}
+
+int
+main(int argc, char **argv)
+{
+       const char *fn = NULL;
+       char inbuf[8192];
+       struct ucl_parser *parser;
+       UT_string *err = NULL;
+       int k, ret = 0;
+       ucl_object_t *obj = NULL;
+       ucl_object_t *par;
+       FILE *in;
+
+       if (argc > 1) {
+               fn = argv[1];
+       }
+
+       if (fn != NULL) {
+               in = fopen (fn, "r");
+               if (in == NULL) {
+                       exit (-errno);
+               }
+       }
+       else {
+               in = stdin;
+       }
+
+       parser = ucl_parser_new (0);
+       while (!feof (in)) {
+               fread (inbuf, sizeof (inbuf), 1, in);
+               ucl_parser_add_chunk (parser, inbuf, strlen (inbuf), &err);
+       }
+       fclose (in);
+       if (err != NULL ) {
+               printf ("Error occured: %s\n", err->d);
+               ret = 1;
+               goto end;
+       }
+
+       obj = ucl_parser_get_object (parser, &err);
+       if (err != NULL ) {
+               printf ("Error occured: %s\n", err->d);
+               ret = 1;
+               goto end;
+       }
+
+       if (argc > 2) {
+               for (k = 2; k < argc; k++) {
+                       printf ("search for \"%s\"... ", argv[k]);
+                       par = ucl_obj_get_key (obj, argv[k]);
+                       printf ("%sfound\n", (par == NULL )?"not ":"");
+                       ucl_obj_dump (par, 0);
+               }
+       }
+       else {
+               ucl_obj_dump (obj, 0);
+       }
+
+end:
+       if (parser != NULL ) {
+               ucl_parser_free (parser);
+       }
+       if (obj != NULL ) {
+               ucl_obj_unref (obj);
+       }
+
+       return ret;
+}