summaryrefslogtreecommitdiff
path: root/lua/lua_ucl.c
diff options
context:
space:
mode:
Diffstat (limited to 'lua/lua_ucl.c')
-rw-r--r--lua/lua_ucl.c450
1 files changed, 383 insertions, 67 deletions
diff --git a/lua/lua_ucl.c b/lua/lua_ucl.c
index 62b0652f564a..b34fd56878b8 100644
--- a/lua/lua_ucl.c
+++ b/lua/lua_ucl.c
@@ -68,16 +68,27 @@ func = "huh";
#define PARSER_META "ucl.parser.meta"
#define EMITTER_META "ucl.emitter.meta"
-#define NULL_META "null.emitter.meta"
+#define NULL_META "ucl.null.meta"
#define OBJECT_META "ucl.object.meta"
+#define UCL_OBJECT_TYPE_META "ucl.type.object"
+#define UCL_ARRAY_TYPE_META "ucl.type.array"
+#define UCL_IMPL_ARRAY_TYPE_META "ucl.type.impl_array"
-static int ucl_object_lua_push_array (lua_State *L, const ucl_object_t *obj);
-static int ucl_object_lua_push_scalar (lua_State *L, const ucl_object_t *obj, bool allow_array);
-static ucl_object_t* ucl_object_lua_fromtable (lua_State *L, int idx);
-static ucl_object_t* ucl_object_lua_fromelt (lua_State *L, int idx);
+static int ucl_object_lua_push_array (lua_State *L, const ucl_object_t *obj, int flags);
+static int ucl_object_lua_push_scalar (lua_State *L, const ucl_object_t *obj, int flags);
+static int ucl_object_push_lua_common (lua_State *L, const ucl_object_t *obj, int flags);
+static ucl_object_t* ucl_object_lua_fromtable (lua_State *L, int idx, ucl_string_flags_t flags);
+static ucl_object_t* ucl_object_lua_fromelt (lua_State *L, int idx, ucl_string_flags_t flags);
static void *ucl_null;
+
+enum lua_ucl_push_flags {
+ LUA_UCL_DEFAULT_FLAGS = 0,
+ LUA_UCL_ALLOW_ARRAY = (1u << 0u),
+ LUA_UCL_CONVERT_NIL = (1u << 1u),
+};
+
/**
* Push a single element of an object to lua
* @param L
@@ -86,10 +97,10 @@ static void *ucl_null;
*/
static void
ucl_object_lua_push_element (lua_State *L, const char *key,
- const ucl_object_t *obj)
+ const ucl_object_t *obj, int flags)
{
lua_pushstring (L, key);
- ucl_object_push_lua (L, obj, true);
+ ucl_object_push_lua_common (L, obj, flags|LUA_UCL_ALLOW_ARRAY);
lua_settable (L, -3);
}
@@ -137,29 +148,26 @@ lua_ucl_userdata_emitter (void *ud)
*/
static int
ucl_object_lua_push_object (lua_State *L, const ucl_object_t *obj,
- bool allow_array)
+ int flags)
{
const ucl_object_t *cur;
ucl_object_iter_t it = NULL;
- int nelt = 0;
- if (allow_array && obj->next != NULL) {
+ if ((flags & LUA_UCL_ALLOW_ARRAY) && obj->next != NULL) {
/* Actually we need to push this as an array */
- return ucl_object_lua_push_array (L, obj);
- }
-
- /* Optimize allocation by preallocation of table */
- while (ucl_object_iterate (obj, &it, true) != NULL) {
- nelt ++;
+ return ucl_object_lua_push_array (L, obj, flags);
}
- lua_createtable (L, 0, nelt);
+ lua_createtable (L, 0, obj->len);
it = NULL;
while ((cur = ucl_object_iterate (obj, &it, true)) != NULL) {
- ucl_object_lua_push_element (L, ucl_object_key (cur), cur);
+ ucl_object_lua_push_element (L, ucl_object_key (cur), cur, flags);
}
+ luaL_getmetatable (L, UCL_OBJECT_TYPE_META);
+ lua_setmetatable (L, -2);
+
return 1;
}
@@ -170,7 +178,7 @@ ucl_object_lua_push_object (lua_State *L, const ucl_object_t *obj,
* @return
*/
static int
-ucl_object_lua_push_array (lua_State *L, const ucl_object_t *obj)
+ucl_object_lua_push_array (lua_State *L, const ucl_object_t *obj, int flags)
{
const ucl_object_t *cur;
ucl_object_iter_t it;
@@ -182,11 +190,14 @@ ucl_object_lua_push_array (lua_State *L, const ucl_object_t *obj)
lua_createtable (L, nelt, 0);
while ((cur = ucl_object_iterate_safe (it, true))) {
- ucl_object_push_lua (L, cur, false);
+ ucl_object_push_lua (L, cur, (flags & ~LUA_UCL_ALLOW_ARRAY));
lua_rawseti (L, -2, i);
i ++;
}
+ luaL_getmetatable (L, UCL_ARRAY_TYPE_META);
+ lua_setmetatable (L, -2);
+
ucl_object_iterate_free (it);
}
else {
@@ -198,10 +209,13 @@ ucl_object_lua_push_array (lua_State *L, const ucl_object_t *obj)
lua_createtable (L, nelt, 0);
LL_FOREACH (obj, cur) {
- ucl_object_push_lua (L, cur, false);
+ ucl_object_push_lua (L, cur, (flags & ~LUA_UCL_ALLOW_ARRAY));
lua_rawseti (L, -2, i);
i ++;
}
+
+ luaL_getmetatable (L, UCL_IMPL_ARRAY_TYPE_META);
+ lua_setmetatable (L, -2);
}
return 1;
@@ -212,13 +226,13 @@ ucl_object_lua_push_array (lua_State *L, const ucl_object_t *obj)
*/
static int
ucl_object_lua_push_scalar (lua_State *L, const ucl_object_t *obj,
- bool allow_array)
+ int flags)
{
struct ucl_lua_funcdata *fd;
- if (allow_array && obj->next != NULL) {
+ if ((flags & LUA_UCL_ALLOW_ARRAY) && obj->next != NULL) {
/* Actually we need to push this as an array */
- return ucl_object_lua_push_array (L, obj);
+ return ucl_object_lua_push_array (L, obj, flags);
}
switch (obj->type) {
@@ -240,7 +254,12 @@ ucl_object_lua_push_scalar (lua_State *L, const ucl_object_t *obj,
lua_pushnumber (L, ucl_obj_todouble (obj));
break;
case UCL_NULL:
- lua_getfield (L, LUA_REGISTRYINDEX, "ucl.null");
+ if (flags & LUA_UCL_CONVERT_NIL) {
+ lua_pushboolean (L, false);
+ }
+ else {
+ lua_getfield (L, LUA_REGISTRYINDEX, "ucl.null");
+ }
break;
case UCL_USERDATA:
fd = (struct ucl_lua_funcdata *)obj->value.ud;
@@ -254,6 +273,19 @@ ucl_object_lua_push_scalar (lua_State *L, const ucl_object_t *obj,
return 1;
}
+static int
+ucl_object_push_lua_common (lua_State *L, const ucl_object_t *obj, int flags)
+{
+ switch (obj->type) {
+ case UCL_OBJECT:
+ return ucl_object_lua_push_object (L, obj, flags);
+ case UCL_ARRAY:
+ return ucl_object_lua_push_array (L, obj, flags);
+ default:
+ return ucl_object_lua_push_scalar (L, obj, flags);
+ }
+}
+
/***
* @function ucl_object_push_lua(L, obj, allow_array)
* This is a `C` function to push `UCL` object as lua variable. This function
@@ -272,14 +304,16 @@ ucl_object_lua_push_scalar (lua_State *L, const ucl_object_t *obj,
int
ucl_object_push_lua (lua_State *L, const ucl_object_t *obj, bool allow_array)
{
- switch (obj->type) {
- case UCL_OBJECT:
- return ucl_object_lua_push_object (L, obj, allow_array);
- case UCL_ARRAY:
- return ucl_object_lua_push_array (L, obj);
- default:
- return ucl_object_lua_push_scalar (L, obj, allow_array);
- }
+ return ucl_object_push_lua_common (L, obj,
+ allow_array ? LUA_UCL_ALLOW_ARRAY : LUA_UCL_DEFAULT_FLAGS);
+}
+
+int
+ucl_object_push_lua_filter_nil (lua_State *L, const ucl_object_t *obj, bool allow_array)
+{
+ return ucl_object_push_lua_common (L, obj,
+ allow_array ? (LUA_UCL_ALLOW_ARRAY|LUA_UCL_CONVERT_NIL) :
+ (LUA_UCL_DEFAULT_FLAGS|LUA_UCL_CONVERT_NIL));
}
/**
@@ -289,55 +323,107 @@ ucl_object_push_lua (lua_State *L, const ucl_object_t *obj, bool allow_array)
* @param idx
*/
static ucl_object_t *
-ucl_object_lua_fromtable (lua_State *L, int idx)
+ucl_object_lua_fromtable (lua_State *L, int idx, ucl_string_flags_t flags)
{
- ucl_object_t *obj, *top = NULL;
+ ucl_object_t *obj, *top = NULL, *cur;
size_t keylen;
const char *k;
- bool is_array = true;
- int max = INT_MIN;
+ bool is_array = true, is_implicit = false, found_mt = false;
+ size_t max = 0, nelts = 0;
if (idx < 0) {
/* For negative indicies we want to invert them */
idx = lua_gettop (L) + idx + 1;
}
- /* Check for array */
- lua_pushnil (L);
- while (lua_next (L, idx) != 0) {
- if (lua_type (L, -2) == LUA_TNUMBER) {
- double num = lua_tonumber (L, -2);
- if (num == (int)num) {
- if (num > max) {
- max = num;
+
+ /* First, we check from metatable */
+ if (luaL_getmetafield (L, idx, "class") != 0) {
+
+ if (lua_type (L, -1) == LUA_TSTRING) {
+ const char *classname = lua_tostring (L, -1);
+
+ if (strcmp (classname, UCL_OBJECT_TYPE_META) == 0) {
+ is_array = false;
+ found_mt = true;
+ } else if (strcmp (classname, UCL_ARRAY_TYPE_META) == 0) {
+ is_array = true;
+ found_mt = true;
+#if LUA_VERSION_NUM >= 502
+ max = lua_rawlen (L, idx);
+#else
+ max = lua_objlen (L, idx);
+#endif
+ nelts = max;
+ } else if (strcmp (classname, UCL_IMPL_ARRAY_TYPE_META) == 0) {
+ is_array = true;
+ is_implicit = true;
+ found_mt = true;
+#if LUA_VERSION_NUM >= 502
+ max = lua_rawlen (L, idx);
+#else
+ max = lua_objlen (L, idx);
+#endif
+ nelts = max;
+ }
+ }
+
+ lua_pop (L, 1);
+ }
+
+ if (!found_mt) {
+ /* Check for array (it is all inefficient) */
+ lua_pushnil (L);
+
+ while (lua_next (L, idx) != 0) {
+ lua_pushvalue (L, -2);
+
+ if (lua_type (L, -1) == LUA_TNUMBER) {
+ double num = lua_tonumber (L, -1);
+ if (num == (int) num) {
+ if (num > max) {
+ max = num;
+ }
+ }
+ else {
+ /* Keys are not integer */
+ is_array = false;
}
}
else {
- /* Keys are not integer */
- lua_pop (L, 2);
+ /* Keys are not numeric */
is_array = false;
- break;
}
- }
- else {
- /* Keys are not numeric */
+
lua_pop (L, 2);
- is_array = false;
- break;
+ nelts ++;
}
- lua_pop (L, 1);
}
/* Table iterate */
if (is_array) {
int i;
- top = ucl_object_typed_new (UCL_ARRAY);
+ if (!is_implicit) {
+ top = ucl_object_typed_new (UCL_ARRAY);
+ ucl_object_reserve (top, nelts);
+ }
+ else {
+ top = NULL;
+ }
+
for (i = 1; i <= max; i ++) {
lua_pushinteger (L, i);
lua_gettable (L, idx);
- obj = ucl_object_lua_fromelt (L, lua_gettop (L));
+
+ obj = ucl_object_lua_fromelt (L, lua_gettop (L), flags);
+
if (obj != NULL) {
- ucl_array_append (top, obj);
+ if (is_implicit) {
+ DL_APPEND (top, obj);
+ }
+ else {
+ ucl_array_append (top, obj);
+ }
}
lua_pop (L, 1);
}
@@ -345,15 +431,25 @@ ucl_object_lua_fromtable (lua_State *L, int idx)
else {
lua_pushnil (L);
top = ucl_object_typed_new (UCL_OBJECT);
+ ucl_object_reserve (top, nelts);
+
while (lua_next (L, idx) != 0) {
/* copy key to avoid modifications */
- k = lua_tolstring (L, -2, &keylen);
- obj = ucl_object_lua_fromelt (L, lua_gettop (L));
+ lua_pushvalue (L, -2);
+ k = lua_tolstring (L, -1, &keylen);
+ obj = ucl_object_lua_fromelt (L, lua_gettop (L) - 1, flags);
if (obj != NULL) {
ucl_object_insert_key (top, obj, k, keylen, true);
+
+ DL_FOREACH (obj, cur) {
+ if (cur->keylen == 0) {
+ cur->keylen = obj->keylen;
+ cur->key = obj->key;
+ }
+ }
}
- lua_pop (L, 1);
+ lua_pop (L, 2);
}
}
@@ -367,18 +463,27 @@ ucl_object_lua_fromtable (lua_State *L, int idx)
* @param idx
*/
static ucl_object_t *
-ucl_object_lua_fromelt (lua_State *L, int idx)
+ucl_object_lua_fromelt (lua_State *L, int idx, ucl_string_flags_t flags)
{
int type;
double num;
ucl_object_t *obj = NULL;
struct ucl_lua_funcdata *fd;
+ const char *str;
+ size_t sz;
type = lua_type (L, idx);
switch (type) {
case LUA_TSTRING:
- obj = ucl_object_fromstring_common (lua_tostring (L, idx), 0, 0);
+ str = lua_tolstring (L, idx, &sz);
+
+ if (str) {
+ obj = ucl_object_fromstring_common (str, sz, flags);
+ }
+ else {
+ obj = ucl_object_typed_new (UCL_NULL);
+ }
break;
case LUA_TNUMBER:
num = lua_tonumber (L, idx);
@@ -406,13 +511,13 @@ ucl_object_lua_fromelt (lua_State *L, int idx)
lua_insert (L, 1); /* func, gen, obj */
lua_insert (L, 2); /* func, obj, gen */
lua_call(L, 2, 1);
- obj = ucl_object_lua_fromelt (L, 1);
+ obj = ucl_object_lua_fromelt (L, 1, flags);
}
lua_pop (L, 2);
}
else {
if (type == LUA_TTABLE) {
- obj = ucl_object_lua_fromtable (L, idx);
+ obj = ucl_object_lua_fromtable (L, idx, flags);
}
else if (type == LUA_TFUNCTION) {
fd = malloc (sizeof (*fd));
@@ -451,10 +556,38 @@ ucl_object_lua_import (lua_State *L, int idx)
t = lua_type (L, idx);
switch (t) {
case LUA_TTABLE:
- obj = ucl_object_lua_fromtable (L, idx);
+ obj = ucl_object_lua_fromtable (L, idx, 0);
break;
default:
- obj = ucl_object_lua_fromelt (L, idx);
+ obj = ucl_object_lua_fromelt (L, idx, 0);
+ break;
+ }
+
+ return obj;
+}
+
+/**
+ * @function ucl_object_lua_import_escape(L, idx)
+ * Extracts ucl object from lua variable at `idx` position escaping JSON strings
+ * @see ucl_object_push_lua for conversion definitions
+ * @param {lua_state} L lua state machine pointer
+ * @param {int} idx index where the source variable is placed
+ * @return {ucl_object_t} new ucl object extracted from lua variable. Reference count of this object is 1,
+ * this object thus needs to be unref'ed after usage.
+ */
+ucl_object_t *
+ucl_object_lua_import_escape (lua_State *L, int idx)
+{
+ ucl_object_t *obj;
+ int t;
+
+ t = lua_type (L, idx);
+ switch (t) {
+ case LUA_TTABLE:
+ obj = ucl_object_lua_fromtable (L, idx, UCL_STRING_RAW);
+ break;
+ default:
+ obj = ucl_object_lua_fromelt (L, idx, UCL_STRING_RAW);
break;
}
@@ -492,6 +625,7 @@ lua_ucl_parser_init (lua_State *L)
parser = ucl_parser_new (flags);
if (parser == NULL) {
lua_pushnil (L);
+ return 1;
}
pparser = lua_newuserdata (L, sizeof (parser));
@@ -590,6 +724,76 @@ lua_ucl_parser_parse_file (lua_State *L)
}
/***
+ * @method parser:register_variable(name, value)
+ * Register parser variable
+ * @param {string} name name of variable
+ * @param {string} value value of variable
+ * @return {bool} success
+@example
+local parser = ucl.parser()
+local res = parser:register_variable('CONFDIR', '/etc/foo')
+ */
+static int
+lua_ucl_parser_register_variable (lua_State *L)
+{
+ struct ucl_parser *parser;
+ const char *name, *value;
+ int ret = 2;
+
+ parser = lua_ucl_parser_get (L, 1);
+ name = luaL_checkstring (L, 2);
+ value = luaL_checkstring (L, 3);
+
+ if (parser != NULL && name != NULL && value != NULL) {
+ ucl_parser_register_variable (parser, name, value);
+ lua_pushboolean (L, true);
+ ret = 1;
+ }
+ else {
+ return luaL_error (L, "invalid arguments");
+ }
+
+ return ret;
+}
+
+/***
+ * @method parser:register_variables(vars)
+ * Register parser variables
+ * @param {table} vars names/values of variables
+ * @return {bool} success
+@example
+local parser = ucl.parser()
+local res = parser:register_variables({CONFDIR = '/etc/foo', VARDIR = '/var'})
+ */
+static int
+lua_ucl_parser_register_variables (lua_State *L)
+{
+ struct ucl_parser *parser;
+ const char *name, *value;
+ int ret = 2;
+
+ parser = lua_ucl_parser_get (L, 1);
+
+ if (parser != NULL && lua_type (L, 2) == LUA_TTABLE) {
+ for (lua_pushnil (L); lua_next (L, 2); lua_pop (L, 1)) {
+ lua_pushvalue (L, -2);
+ name = luaL_checkstring (L, -1);
+ value = luaL_checkstring (L, -2);
+ ucl_parser_register_variable (parser, name, value);
+ lua_pop (L, 1);
+ }
+
+ lua_pushboolean (L, true);
+ ret = 1;
+ }
+ else {
+ return luaL_error (L, "invalid arguments");
+ }
+
+ return ret;
+}
+
+/***
* @method parser:parse_string(input)
* Parse UCL object from file.
* @param {string} input string to parse
@@ -630,6 +834,52 @@ lua_ucl_parser_parse_string (lua_State *L)
return ret;
}
+struct _rspamd_lua_text {
+ const char *start;
+ unsigned int len;
+ unsigned int flags;
+};
+
+/***
+ * @method parser:parse_text(input)
+ * Parse UCL object from text object (Rspamd specific).
+ * @param {rspamd_text} input text to parse
+ * @return {bool[, string]} if res is `true` then file has been parsed successfully, otherwise an error string is also returned
+ */
+static int
+lua_ucl_parser_parse_text (lua_State *L)
+{
+ struct ucl_parser *parser;
+ struct _rspamd_lua_text *t;
+ enum ucl_parse_type type = UCL_PARSE_UCL;
+ int ret = 2;
+
+ parser = lua_ucl_parser_get (L, 1);
+ t = lua_touserdata (L, 2);
+
+ if (lua_type (L, 3) == LUA_TSTRING) {
+ type = lua_ucl_str_to_parse_type (lua_tostring (L, 3));
+ }
+
+ if (parser != NULL && t != NULL) {
+ if (ucl_parser_add_chunk_full (parser, (const unsigned char *)t->start,
+ t->len, 0, UCL_DUPLICATE_APPEND, type)) {
+ lua_pushboolean (L, true);
+ ret = 1;
+ }
+ else {
+ lua_pushboolean (L, false);
+ lua_pushstring (L, ucl_parser_get_error (parser));
+ }
+ }
+ else {
+ lua_pushboolean (L, false);
+ lua_pushstring (L, "invalid arguments");
+ }
+
+ return ret;
+}
+
/***
* @method parser:get_object()
* Get top object from parser and export it to lua representation.
@@ -977,6 +1227,15 @@ lua_ucl_parser_mt (lua_State *L)
lua_pushcfunction (L, lua_ucl_parser_parse_string);
lua_setfield (L, -2, "parse_string");
+ lua_pushcfunction (L, lua_ucl_parser_parse_text);
+ lua_setfield (L, -2, "parse_text");
+
+ lua_pushcfunction (L, lua_ucl_parser_register_variable);
+ lua_setfield (L, -2, "register_variable");
+
+ lua_pushcfunction (L, lua_ucl_parser_register_variables);
+ lua_setfield (L, -2, "register_variables");
+
lua_pushcfunction (L, lua_ucl_parser_get_object);
lua_setfield (L, -2, "get_object");
@@ -1021,6 +1280,49 @@ lua_ucl_object_mt (lua_State *L)
lua_pop (L, 1);
}
+static void
+lua_ucl_types_mt (lua_State *L)
+{
+ luaL_newmetatable (L, UCL_OBJECT_TYPE_META);
+
+ lua_pushcfunction (L, lua_ucl_object_tostring);
+ lua_setfield (L, -2, "__tostring");
+
+ lua_pushcfunction (L, lua_ucl_object_tostring);
+ lua_setfield (L, -2, "tostring");
+
+ lua_pushstring (L, UCL_OBJECT_TYPE_META);
+ lua_setfield (L, -2, "class");
+
+ lua_pop (L, 1);
+
+ luaL_newmetatable (L, UCL_ARRAY_TYPE_META);
+
+ lua_pushcfunction (L, lua_ucl_object_tostring);
+ lua_setfield (L, -2, "__tostring");
+
+ lua_pushcfunction (L, lua_ucl_object_tostring);
+ lua_setfield (L, -2, "tostring");
+
+ lua_pushstring (L, UCL_ARRAY_TYPE_META);
+ lua_setfield (L, -2, "class");
+
+ lua_pop (L, 1);
+
+ luaL_newmetatable (L, UCL_IMPL_ARRAY_TYPE_META);
+
+ lua_pushcfunction (L, lua_ucl_object_tostring);
+ lua_setfield (L, -2, "__tostring");
+
+ lua_pushcfunction (L, lua_ucl_object_tostring);
+ lua_setfield (L, -2, "tostring");
+
+ lua_pushstring (L, UCL_IMPL_ARRAY_TYPE_META);
+ lua_setfield (L, -2, "class");
+
+ lua_pop (L, 1);
+}
+
static int
lua_ucl_to_json (lua_State *L)
{
@@ -1073,7 +1375,7 @@ lua_ucl_to_config (lua_State *L)
* - `yaml` - embedded yaml
*
* If `var` contains function, they are called during output formatting and if
- * they return string value, then this value is used for ouptut.
+ * they return string value, then this value is used for output.
* @param {variant} var any sort of lua variable (if userdata then metafield `__to_ucl` is searched for output)
* @param {string} format any available format
* @return {string} string representation of `var` in the specific `format`.
@@ -1101,6 +1403,7 @@ lua_ucl_to_format (lua_State *L)
{
ucl_object_t *obj;
int format = UCL_EMIT_JSON;
+ bool sort = false;
if (lua_gettop (L) > 1) {
if (lua_type (L, 2) == LUA_TNUMBER) {
@@ -1130,10 +1433,22 @@ lua_ucl_to_format (lua_State *L)
format = UCL_EMIT_MSGPACK;
}
}
+
+ if (lua_isboolean (L, 3)) {
+ sort = lua_toboolean (L, 3);
+ }
}
obj = ucl_object_lua_import (L, 1);
+
if (obj != NULL) {
+
+ if (sort) {
+ if (ucl_object_type (obj) == UCL_OBJECT) {
+ ucl_object_sort_keys (obj, UCL_SORT_KEYS_RECURSIVE);
+ }
+ }
+
lua_ucl_to_string (L, obj, format);
ucl_object_unref (obj);
}
@@ -1168,6 +1483,7 @@ luaopen_ucl (lua_State *L)
lua_ucl_parser_mt (L);
lua_ucl_null_mt (L);
lua_ucl_object_mt (L);
+ lua_ucl_types_mt (L);
/* Create the refs weak table: */
lua_createtable (L, 0, 2);