summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt7
-rw-r--r--lib/CMakeLists.txt2
-rw-r--r--src/cfg_file.h8
-rw-r--r--src/cfg_rcl.c192
-rw-r--r--src/cfg_rcl.h22
-rw-r--r--src/cfg_utils.c10
-rw-r--r--src/lua/lua_cfg_file.c7
-rw-r--r--src/lua/lua_common.h6
-rw-r--r--src/lua/lua_config.c6
-rw-r--r--src/lua/lua_rcl.c78
-rw-r--r--src/plugins/chartable.c6
-rw-r--r--src/plugins/dkim_check.c28
-rw-r--r--src/plugins/fuzzy_check.c46
-rw-r--r--src/plugins/regexp.c40
-rw-r--r--src/plugins/spf.c14
-rw-r--r--src/plugins/surbl.c50
-rw-r--r--src/rcl/CMakeLists.txt20
-rw-r--r--src/rcl/rcl.h391
-rw-r--r--src/rcl/rcl_chartable.h256
-rw-r--r--src/rcl/rcl_emitter.c451
-rw-r--r--src/rcl/rcl_internal.h146
-rw-r--r--src/rcl/rcl_parser.c1380
-rw-r--r--src/rcl/rcl_util.c623
-rw-r--r--src/ucl/.gitignore3
-rw-r--r--src/ucl/CMakeLists.txt20
-rw-r--r--src/ucl/README.md236
-rw-r--r--src/ucl/include/ucl.h731
-rw-r--r--src/ucl/src/ucl_chartable.h266
-rw-r--r--src/ucl/src/ucl_emitter.c629
-rw-r--r--src/ucl/src/ucl_internal.h249
-rw-r--r--src/ucl/src/ucl_parser.c1420
-rw-r--r--src/ucl/src/ucl_util.c841
-rw-r--r--src/ucl/tests/1.in11
-rw-r--r--src/ucl/tests/1.res12
-rw-r--r--src/ucl/tests/2.in18
-rw-r--r--src/ucl/tests/2.res45
-rw-r--r--src/ucl/tests/3.in31
-rw-r--r--src/ucl/tests/3.res27
-rw-r--r--src/ucl/tests/4.in47
-rw-r--r--src/ucl/tests/4.res36
-rw-r--r--src/ucl/tests/generate.res18
-rw-r--r--src/ucl/tests/rcl_test.json.xzbin0 -> 1853872 bytes
-rwxr-xr-xsrc/ucl/tests/run_tests.sh57
-rw-r--r--src/ucl/tests/test_basic.c113
-rw-r--r--src/ucl/tests/test_generate.c98
-rw-r--r--src/ucl/tests/test_speed.c128
-rw-r--r--src/ucl/utils/chargen.c124
-rw-r--r--src/ucl/utils/objdump.c158
48 files changed, 5577 insertions, 3530 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 52f42203d..7bac51678 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -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)
diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt
index 561058e1a..9f4636dd7 100644
--- a/lib/CMakeLists.txt
+++ b/lib/CMakeLists.txt
@@ -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)
diff --git a/src/cfg_file.h b/src/cfg_file.h
index e7e122977..875ce3373 100644
--- a/src/cfg_file.h
+++ b/src/cfg_file.h
@@ -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);
/**
diff --git a/src/cfg_rcl.c b/src/cfg_rcl.c
index 2c4bb2845..5320e17ba 100644
--- a/src/cfg_rcl.c
+++ b/src/cfg_rcl.c
@@ -29,23 +29,23 @@
* 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 {
diff --git a/src/cfg_rcl.h b/src/cfg_rcl.h
index fb0fea7e3..2c1441fbb 100644
--- a/src/cfg_rcl.h
+++ b/src/cfg_rcl.h
@@ -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);
/**
diff --git a/src/cfg_utils.c b/src/cfg_utils.c
index 906f4406f..8a49237ac 100644
--- a/src/cfg_utils.c
+++ b/src/cfg_utils.c
@@ -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;
diff --git a/src/lua/lua_cfg_file.c b/src/lua/lua_cfg_file.c
index 4f7a3bca5..0e22ec8f2 100644
--- a/src/lua/lua_cfg_file.c
+++ b/src/lua/lua_cfg_file.c
@@ -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);
}
}
}
diff --git a/src/lua/lua_common.h b/src/lua/lua_common.h
index c6ba06160..c878a50f3 100644
--- a/src/lua/lua_common.h
+++ b/src/lua/lua_common.h
@@ -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
diff --git a/src/lua/lua_config.c b/src/lua/lua_config.c
index d3b7c6af9..df8526d0f 100644
--- a/src/lua/lua_config.c
+++ b/src/lua/lua_config.c
@@ -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);
}
diff --git a/src/lua/lua_rcl.c b/src/lua/lua_rcl.c
index 91f1fff79..b695f4f11 100644
--- a/src/lua/lua_rcl.c
+++ b/src/lua/lua_rcl.c
@@ -22,16 +22,15 @@
*/
#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);
diff --git a/src/plugins/chartable.c b/src/plugins/chartable.c
index a140c5893..69197dfd6 100644
--- a/src/plugins/chartable.c
+++ b/src/plugins/chartable.c
@@ -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;
}
diff --git a/src/plugins/dkim_check.c b/src/plugins/dkim_check.c
index 7ae11631c..8e3fa4c23 100644
--- a/src/plugins/dkim_check.c
+++ b/src/plugins/dkim_check.c
@@ -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;
diff --git a/src/plugins/fuzzy_check.c b/src/plugins/fuzzy_check.c
index 4a900d4db..60549b849 100644
--- a/src/plugins/fuzzy_check.c
+++ b/src/plugins/fuzzy_check.c
@@ -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) {
diff --git a/src/plugins/regexp.c b/src/plugins/regexp.c
index f3eadfa45..a1c4fa049 100644
--- a/src/plugins/regexp.c
+++ b/src/plugins/regexp.c
@@ -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;
diff --git a/src/plugins/spf.c b/src/plugins/spf.c
index 254190961..cf1fbb180 100644
--- a/src/plugins/spf.c
+++ b/src/plugins/spf.c
@@ -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);
diff --git a/src/plugins/surbl.c b/src/plugins/surbl.c
index 8b5121aa8..5d49eccec 100644
--- a/src/plugins/surbl.c
+++ b/src/plugins/surbl.c
@@ -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
index 134db7269..000000000
--- a/src/rcl/CMakeLists.txt
+++ /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
index 48f055414..000000000
--- a/src/rcl/rcl.h
+++ /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
index 2c02c9546..000000000
--- a/src/rcl/rcl_chartable.h
+++ /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
index bf394ee1d..000000000
--- a/src/rcl/rcl_emitter.c
+++ /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
index c2928da9d..000000000
--- a/src/rcl/rcl_internal.h
+++ /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
index b601df310..000000000
--- a/src/rcl/rcl_parser.c
+++ /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
index c351fa859..000000000
--- a/src/rcl/rcl_util.c
+++ /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
index 000000000..ea72388b3
--- /dev/null
+++ b/src/ucl/.gitignore
@@ -0,0 +1,3 @@
+.cproject
+.project
+.settings
diff --git a/src/ucl/CMakeLists.txt b/src/ucl/CMakeLists.txt
new file mode 100644
index 000000000..73937d199
--- /dev/null
+++ b/src/ucl/CMakeLists.txt
@@ -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
index 000000000..18779a835
--- /dev/null
+++ b/src/ucl/README.md
@@ -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
index 000000000..1b8fa8631
--- /dev/null
+++ b/src/ucl/include/ucl.h
@@ -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
index 000000000..232043c1b
--- /dev/null
+++ b/src/ucl/src/ucl_chartable.h
@@ -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
index 000000000..78e321c53
--- /dev/null
+++ b/src/ucl/src/ucl_emitter.c
@@ -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
index 000000000..97613c824
--- /dev/null
+++ b/src/ucl/src/ucl_internal.h
@@ -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
index 000000000..30106ebd3
--- /dev/null
+++ b/src/ucl/src/ucl_parser.c
@@ -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
index 000000000..510035c55
--- /dev/null
+++ b/src/ucl/src/ucl_util.c
@@ -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
index 000000000..41b0cfb3c
--- /dev/null
+++ b/src/ucl/tests/1.in
@@ -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
index 000000000..789c82769
--- /dev/null
+++ b/src/ucl/tests/1.res
@@ -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
index 000000000..59a4d0c38
--- /dev/null
+++ b/src/ucl/tests/2.in
@@ -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
index 000000000..3a8966f65
--- /dev/null
+++ b/src/ucl/tests/2.res
@@ -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
index 000000000..b3e369670
--- /dev/null
+++ b/src/ucl/tests/3.in
@@ -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
index 000000000..2f84ed69e
--- /dev/null
+++ b/src/ucl/tests/3.res
@@ -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
index 000000000..2b296efff
--- /dev/null
+++ b/src/ucl/tests/4.in
@@ -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
index 000000000..58c359943
--- /dev/null
+++ b/src/ucl/tests/4.res
@@ -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
index 000000000..e7659ec3a
--- /dev/null
+++ b/src/ucl/tests/generate.res
@@ -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
index 000000000..98c3559ad
--- /dev/null
+++ b/src/ucl/tests/rcl_test.json.xz
Binary files differ
diff --git a/src/ucl/tests/run_tests.sh b/src/ucl/tests/run_tests.sh
new file mode 100755
index 000000000..71e6f1b08
--- /dev/null
+++ b/src/ucl/tests/run_tests.sh
@@ -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
index 000000000..d4d4786d6
--- /dev/null
+++ b/src/ucl/tests/test_basic.c
@@ -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
index 000000000..a61b35396
--- /dev/null
+++ b/src/ucl/tests/test_generate.c
@@ -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
index 000000000..92a972aa5
--- /dev/null
+++ b/src/ucl/tests/test_speed.c
@@ -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
index 000000000..724b37899
--- /dev/null
+++ b/src/ucl/utils/chargen.c
@@ -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
index 000000000..985106a09
--- /dev/null
+++ b/src/ucl/utils/objdump.c
@@ -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;
+}