diff options
author | Vsevolod Stakhov <vsevolod@highsecure.ru> | 2016-02-14 13:34:42 +0000 |
---|---|---|
committer | Vsevolod Stakhov <vsevolod@highsecure.ru> | 2016-02-14 13:34:42 +0000 |
commit | d212dc2bf89ec79a85fed3e368d171edd77aa7be (patch) | |
tree | de2ba2d1f0580fdf9313b7a00796357286e2a97f /contrib/libucl/lua_ucl.c | |
parent | b7420cb4cb0a2c9c96abc18ecd4de3c2bfec98d3 (diff) | |
download | rspamd-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.c | 368 |
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); |