aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/libucl/lua_ucl.c
diff options
context:
space:
mode:
authorVsevolod Stakhov <vsevolod@highsecure.ru>2016-02-14 13:34:42 +0000
committerVsevolod Stakhov <vsevolod@highsecure.ru>2016-02-14 13:34:42 +0000
commitd212dc2bf89ec79a85fed3e368d171edd77aa7be (patch)
treede2ba2d1f0580fdf9313b7a00796357286e2a97f /contrib/libucl/lua_ucl.c
parentb7420cb4cb0a2c9c96abc18ecd4de3c2bfec98d3 (diff)
downloadrspamd-d212dc2bf89ec79a85fed3e368d171edd77aa7be.tar.gz
rspamd-d212dc2bf89ec79a85fed3e368d171edd77aa7be.zip
Backport fixes from libucl
Diffstat (limited to 'contrib/libucl/lua_ucl.c')
-rw-r--r--contrib/libucl/lua_ucl.c368
1 files changed, 353 insertions, 15 deletions
diff --git a/contrib/libucl/lua_ucl.c b/contrib/libucl/lua_ucl.c
index 1cb716b24..3c42adbfe 100644
--- a/contrib/libucl/lua_ucl.c
+++ b/contrib/libucl/lua_ucl.c
@@ -29,6 +29,7 @@
#include "ucl_internal.h"
#include "lua_ucl.h"
#include <strings.h>
+#include <zconf.h>
/***
* @module ucl
@@ -69,6 +70,7 @@ func = "huh";
#define PARSER_META "ucl.parser.meta"
#define EMITTER_META "ucl.emitter.meta"
#define NULL_META "null.emitter.meta"
+#define OBJECT_META "ucl.object.meta"
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);
@@ -420,9 +422,7 @@ ucl_object_lua_fromelt (lua_State *L, int idx)
fd->idx = luaL_ref (L, LUA_REGISTRYINDEX);
obj = ucl_object_new_userdata (lua_ucl_userdata_dtor,
- lua_ucl_userdata_emitter);
- obj->type = UCL_USERDATA;
- obj->value.ud = (void *)fd;
+ lua_ucl_userdata_emitter, (void *)fd);
}
}
}
@@ -461,6 +461,24 @@ ucl_object_lua_import (lua_State *L, int idx)
}
static int
+lua_ucl_to_string (lua_State *L, const ucl_object_t *obj, enum ucl_emitter type)
+{
+ unsigned char *result;
+
+ result = ucl_object_emit (obj, type);
+
+ if (result != NULL) {
+ lua_pushstring (L, (const char *)result);
+ free (result);
+ }
+ else {
+ lua_pushnil (L);
+ }
+
+ return 1;
+}
+
+static int
lua_ucl_parser_init (lua_State *L)
{
struct ucl_parser *parser, **pparser;
@@ -489,6 +507,23 @@ lua_ucl_parser_get (lua_State *L, int index)
return *((struct ucl_parser **) luaL_checkudata(L, index, PARSER_META));
}
+static ucl_object_t *
+lua_ucl_object_get (lua_State *L, int index)
+{
+ return *((ucl_object_t **) luaL_checkudata(L, index, OBJECT_META));
+}
+
+static void
+lua_ucl_push_opaque (lua_State *L, ucl_object_t *obj)
+{
+ ucl_object_t **pobj;
+
+ pobj = lua_newuserdata (L, sizeof (*pobj));
+ *pobj = obj;
+ luaL_getmetatable (L, OBJECT_META);
+ lua_setmetatable (L, -2);
+}
+
/***
* @method parser:parse_file(name)
* Parse UCL object from file.
@@ -594,6 +629,105 @@ lua_ucl_parser_get_object (lua_State *L)
return ret;
}
+/***
+ * @method parser:get_object_wrapped()
+ * Get top object from parser and export it to userdata object without
+ * unwrapping to lua.
+ * @return {ucl.object or nil} ucl object wrapped variable
+ */
+static int
+lua_ucl_parser_get_object_wrapped (lua_State *L)
+{
+ struct ucl_parser *parser;
+ ucl_object_t *obj;
+ int ret = 1;
+
+ parser = lua_ucl_parser_get (L, 1);
+ obj = ucl_parser_get_object (parser);
+
+ if (obj != NULL) {
+ lua_ucl_push_opaque (L, obj);
+ }
+ else {
+ lua_pushnil (L);
+ }
+
+ return ret;
+}
+
+/***
+ * @method parser:validate(schema)
+ * Validates the top object in the parser against schema. Schema might be
+ * another object or a string that represents file to load schema from.
+ *
+ * @param {string/table} schema input schema
+ * @return {result,err} two values: boolean result and the corresponding error
+ *
+ */
+static int
+lua_ucl_parser_validate (lua_State *L)
+{
+ struct ucl_parser *parser, *schema_parser;
+ ucl_object_t *schema;
+ const char *schema_file;
+ struct ucl_schema_error err;
+
+ parser = lua_ucl_parser_get (L, 1);
+
+ if (parser && parser->top_obj) {
+ if (lua_type (L, 2) == LUA_TTABLE) {
+ schema = ucl_object_lua_import (L, 2);
+
+ if (schema == NULL) {
+ lua_pushboolean (L, false);
+ lua_pushstring (L, "cannot load schema from lua table");
+
+ return 2;
+ }
+ }
+ else if (lua_type (L, 2) == LUA_TSTRING) {
+ schema_parser = ucl_parser_new (0);
+ schema_file = luaL_checkstring (L, 2);
+
+ if (!ucl_parser_add_file (schema_parser, schema_file)) {
+ lua_pushboolean (L, false);
+ lua_pushfstring (L, "cannot parse schema file \"%s\": "
+ "%s", schema_file, ucl_parser_get_error (parser));
+ ucl_parser_free (schema_parser);
+
+ return 2;
+ }
+
+ schema = ucl_parser_get_object (schema_parser);
+ ucl_parser_free (schema_parser);
+ }
+ else {
+ lua_pushboolean (L, false);
+ lua_pushstring (L, "invalid schema argument");
+
+ return 2;
+ }
+
+ if (!ucl_object_validate (schema, parser->top_obj, &err)) {
+ lua_pushboolean (L, false);
+ lua_pushfstring (L, "validation error: "
+ "%s", err.msg);
+ }
+ else {
+ lua_pushboolean (L, true);
+ lua_pushnil (L);
+ }
+
+ ucl_object_unref (schema);
+ }
+ else {
+ lua_pushboolean (L, false);
+ lua_pushstring (L, "invalid parser or empty top object");
+ }
+
+ return 2;
+}
+
static int
lua_ucl_parser_gc (lua_State *L)
{
@@ -605,6 +739,189 @@ lua_ucl_parser_gc (lua_State *L)
return 0;
}
+/***
+ * @method object:unwrap()
+ * Unwraps opaque ucl object to the native lua object (performing copying)
+ * @return {variant} any lua object
+ */
+static int
+lua_ucl_object_unwrap (lua_State *L)
+{
+ ucl_object_t *obj;
+
+ obj = lua_ucl_object_get (L, 1);
+
+ if (obj) {
+ ucl_object_push_lua (L, obj, true);
+ }
+ else {
+ lua_pushnil (L);
+ }
+
+ return 1;
+}
+
+/***
+ * @method object:tostring(type)
+ * Unwraps opaque ucl object to string (json by default). Optionally you can
+ * specify output format:
+ *
+ * - `json` - fine printed json
+ * - `json-compact` - compacted json
+ * - `config` - fine printed configuration
+ * - `ucl` - same as `config`
+ * - `yaml` - embedded yaml
+ * @param {string} type optional
+ * @return {string} string representation of the opaque ucl object
+ */
+static int
+lua_ucl_object_tostring (lua_State *L)
+{
+ ucl_object_t *obj;
+ enum ucl_emitter format = UCL_EMIT_JSON_COMPACT;
+
+ obj = lua_ucl_object_get (L, 1);
+
+ if (obj) {
+ if (lua_gettop (L) > 1) {
+ if (lua_type (L, 2) == LUA_TSTRING) {
+ const char *strtype = lua_tostring (L, 2);
+
+ if (strcasecmp (strtype, "json") == 0) {
+ format = UCL_EMIT_JSON;
+ }
+ else if (strcasecmp (strtype, "json-compact") == 0) {
+ format = UCL_EMIT_JSON_COMPACT;
+ }
+ else if (strcasecmp (strtype, "yaml") == 0) {
+ format = UCL_EMIT_YAML;
+ }
+ else if (strcasecmp (strtype, "config") == 0 ||
+ strcasecmp (strtype, "ucl") == 0) {
+ format = UCL_EMIT_CONFIG;
+ }
+ }
+ }
+
+ return lua_ucl_to_string (L, obj, format);
+ }
+ else {
+ lua_pushnil (L);
+ }
+
+ return 1;
+}
+
+/***
+ * @method object:validate(schema[, path[, ext_refs]])
+ * Validates the given ucl object using schema object represented as another
+ * opaque ucl object. You can also specify path in the form `#/path/def` to
+ * specify the specific schema element to perform validation.
+ *
+ * @param {ucl.object} schema schema object
+ * @param {string} path optional path for validation procedure
+ * @return {result,err} two values: boolean result and the corresponding
+ * error, if `ext_refs` are also specified, then they are returned as opaque
+ * ucl object as {result,err,ext_refs}
+ */
+static int
+lua_ucl_object_validate (lua_State *L)
+{
+ ucl_object_t *obj, *schema, *ext_refs = NULL;
+ const ucl_object_t *schema_elt;
+ bool res = false;
+ struct ucl_schema_error err;
+ const char *path = NULL;
+
+ obj = lua_ucl_object_get (L, 1);
+ schema = lua_ucl_object_get (L, 2);
+
+ if (schema && obj && ucl_object_type (schema) == UCL_OBJECT) {
+ if (lua_gettop (L) > 2) {
+ if (lua_type (L, 3) == LUA_TSTRING) {
+ path = lua_tostring (L, 3);
+ if (path[0] == '#') {
+ path++;
+ }
+ }
+ else if (lua_type (L, 3) == LUA_TUSERDATA || lua_type (L, 3) ==
+ LUA_TTABLE) {
+ /* External refs */
+ ext_refs = lua_ucl_object_get (L, 3);
+ }
+
+ if (lua_gettop (L) > 3) {
+ if (lua_type (L, 4) == LUA_TUSERDATA || lua_type (L, 4) ==
+ LUA_TTABLE) {
+ /* External refs */
+ ext_refs = lua_ucl_object_get (L, 4);
+ }
+ }
+ }
+
+ if (path) {
+ schema_elt = ucl_lookup_path_char (schema, path, '/');
+ }
+ else {
+ /* Use the top object */
+ schema_elt = schema;
+ }
+
+ if (schema_elt) {
+ res = ucl_object_validate_root_ext (schema_elt, obj, schema,
+ ext_refs, &err);
+
+ if (res) {
+ lua_pushboolean (L, res);
+ lua_pushnil (L);
+
+ if (ext_refs) {
+ lua_ucl_push_opaque (L, ext_refs);
+ }
+ }
+ else {
+ lua_pushboolean (L, res);
+ lua_pushfstring (L, "validation error: %s", err.msg);
+
+ if (ext_refs) {
+ lua_ucl_push_opaque (L, ext_refs);
+ }
+ }
+ }
+ else {
+ lua_pushboolean (L, res);
+
+ lua_pushfstring (L, "cannot find the requested path: %s", path);
+
+ if (ext_refs) {
+ lua_ucl_push_opaque (L, ext_refs);
+ }
+ }
+ }
+ else {
+ lua_pushboolean (L, res);
+ lua_pushstring (L, "invalid object or schema");
+ }
+
+ if (ext_refs) {
+ return 3;
+ }
+
+ return 2;
+}
+
+static int
+lua_ucl_object_gc (lua_State *L)
+{
+ ucl_object_t *obj;
+
+ obj = lua_ucl_object_get (L, 1);
+
+ ucl_object_unref (obj);
+
+ return 0;
+}
+
static void
lua_ucl_parser_mt (lua_State *L)
{
@@ -625,25 +942,45 @@ lua_ucl_parser_mt (lua_State *L)
lua_pushcfunction (L, lua_ucl_parser_get_object);
lua_setfield (L, -2, "get_object");
+ lua_pushcfunction (L, lua_ucl_parser_get_object_wrapped);
+ lua_setfield (L, -2, "get_object_wrapped");
+
+ lua_pushcfunction (L, lua_ucl_parser_validate);
+ lua_setfield (L, -2, "validate");
+
lua_pop (L, 1);
}
-static int
-lua_ucl_to_string (lua_State *L, const ucl_object_t *obj, enum ucl_emitter type)
+static void
+lua_ucl_object_mt (lua_State *L)
{
- unsigned char *result;
+ luaL_newmetatable (L, OBJECT_META);
- result = ucl_object_emit (obj, type);
+ lua_pushvalue(L, -1);
+ lua_setfield(L, -2, "__index");
- if (result != NULL) {
- lua_pushstring (L, (const char *)result);
- free (result);
- }
- else {
- lua_pushnil (L);
- }
+ lua_pushcfunction (L, lua_ucl_object_gc);
+ lua_setfield (L, -2, "__gc");
- return 1;
+ 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_pushcfunction (L, lua_ucl_object_unwrap);
+ lua_setfield (L, -2, "unwrap");
+
+ lua_pushcfunction (L, lua_ucl_object_unwrap);
+ lua_setfield (L, -2, "tolua");
+
+ lua_pushcfunction (L, lua_ucl_object_validate);
+ lua_setfield (L, -2, "validate");
+
+ lua_pushstring (L, OBJECT_META);
+ lua_setfield (L, -2, "class");
+
+ lua_pop (L, 1);
}
static int
@@ -789,6 +1126,7 @@ luaopen_ucl (lua_State *L)
{
lua_ucl_parser_mt (L);
lua_ucl_null_mt (L);
+ lua_ucl_object_mt (L);
/* Create the refs weak table: */
lua_createtable (L, 0, 2);