diff options
Diffstat (limited to 'libs/luci-lib-jsonc/src/jsonc.c')
-rw-r--r-- | libs/luci-lib-jsonc/src/jsonc.c | 388 |
1 files changed, 388 insertions, 0 deletions
diff --git a/libs/luci-lib-jsonc/src/jsonc.c b/libs/luci-lib-jsonc/src/jsonc.c new file mode 100644 index 0000000000..49cb21f5bc --- /dev/null +++ b/libs/luci-lib-jsonc/src/jsonc.c @@ -0,0 +1,388 @@ +/* +Copyright 2015 Jo-Philipp Wich <jow@openwrt.org> + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +#define _GNU_SOURCE + +#include <math.h> +#include <stdbool.h> +#include <json-c/json.h> + +#include <lua.h> +#include <lualib.h> +#include <lauxlib.h> + +#define LUCI_JSONC "luci.jsonc" +#define LUCI_JSONC_PARSER "luci.jsonc.parser" + +struct json_state { + struct json_object *obj; + struct json_tokener *tok; + enum json_tokener_error err; +}; + +static void _json_to_lua(lua_State *L, struct json_object *obj); +static struct json_object * _lua_to_json(lua_State *L, int index); + +static int json_new(lua_State *L) +{ + struct json_state *s; + struct json_tokener *tok = json_tokener_new(); + + if (!tok) + return 0; + + s = lua_newuserdata(L, sizeof(*s)); + + if (!s) + { + json_tokener_free(tok); + return 0; + } + + s->tok = tok; + s->obj = NULL; + s->err = json_tokener_continue; + + luaL_getmetatable(L, LUCI_JSONC_PARSER); + lua_setmetatable(L, -2); + + return 1; +} + +static int json_parse(lua_State *L) +{ + size_t len; + const char *json = luaL_checklstring(L, 1, &len); + struct json_state s = { + .tok = json_tokener_new() + }; + + if (!s.tok) + return 0; + + s.obj = json_tokener_parse_ex(s.tok, json, len); + s.err = json_tokener_get_error(s.tok); + + if (s.obj) + { + _json_to_lua(L, s.obj); + json_object_put(s.obj); + } + else + { + lua_pushnil(L); + } + + if (s.err == json_tokener_continue) + s.err = json_tokener_error_parse_eof; + + if (s.err) + lua_pushstring(L, json_tokener_error_desc(s.err)); + + json_tokener_free(s.tok); + return (1 + !!s.err); +} + +static int json_stringify(lua_State *L) +{ + struct json_object *obj = _lua_to_json(L, 1); + bool pretty = lua_toboolean(L, 2); + int flags = 0; + + if (pretty) + flags |= JSON_C_TO_STRING_PRETTY | JSON_C_TO_STRING_SPACED; + + lua_pushstring(L, json_object_to_json_string_ext(obj, flags)); + return 1; +} + + +static int json_parse_chunk(lua_State *L) +{ + size_t len; + struct json_state *s = luaL_checkudata(L, 1, LUCI_JSONC_PARSER); + const char *chunk = luaL_checklstring(L, 2, &len); + + s->obj = json_tokener_parse_ex(s->tok, chunk, len); + s->err = json_tokener_get_error(s->tok); + + if (!s->err) + { + lua_pushboolean(L, true); + return 1; + } + else if (s->err == json_tokener_continue) + { + lua_pushboolean(L, false); + return 1; + } + + lua_pushnil(L); + lua_pushstring(L, json_tokener_error_desc(s->err)); + return 2; +} + +static void _json_to_lua(lua_State *L, struct json_object *obj) +{ + int n; + + switch (json_object_get_type(obj)) + { + case json_type_object: + lua_newtable(L); + json_object_object_foreach(obj, key, val) + { + _json_to_lua(L, val); + lua_setfield(L, -2, key); + } + break; + + case json_type_array: + lua_newtable(L); + for (n = 0; n < json_object_array_length(obj); n++) + { + _json_to_lua(L, json_object_array_get_idx(obj, n)); + lua_rawseti(L, -2, n + 1); + } + break; + + case json_type_boolean: + lua_pushboolean(L, json_object_get_boolean(obj)); + break; + + case json_type_int: + lua_pushinteger(L, json_object_get_int(obj)); + break; + + case json_type_double: + lua_pushnumber(L, json_object_get_double(obj)); + break; + + case json_type_string: + lua_pushstring(L, json_object_get_string(obj)); + break; + + case json_type_null: + lua_pushnil(L); + break; + } +} + +static int json_parse_get(lua_State *L) +{ + struct json_state *s = luaL_checkudata(L, 1, LUCI_JSONC_PARSER); + + if (!s->obj || s->err) + lua_pushnil(L); + else + _json_to_lua(L, s->obj); + + return 1; +} + +static int _lua_test_array(lua_State *L, int index) +{ + int max = 0; + lua_Number idx; + + lua_pushnil(L); + + /* check for non-integer keys */ + while (lua_next(L, index)) + { + if (lua_type(L, -2) != LUA_TNUMBER) + goto out; + + idx = lua_tonumber(L, -2); + + if (idx != (lua_Number)(lua_Integer)idx) + goto out; + + if (idx <= 0) + goto out; + + if (idx > max) + max = idx; + + lua_pop(L, 1); + continue; + +out: + lua_pop(L, 2); + return 0; + } + + /* check for holes */ + //for (i = 1; i <= max; i++) + //{ + // lua_rawgeti(L, index, i); + // + // if (lua_isnil(L, -1)) + // { + // lua_pop(L, 1); + // return 0; + // } + // + // lua_pop(L, 1); + //} + + return max; +} + +static struct json_object * _lua_to_json(lua_State *L, int index) +{ + lua_Number nd, ni; + struct json_object *obj; + const char *key; + int i, max; + + switch (lua_type(L, index)) + { + case LUA_TTABLE: + max = _lua_test_array(L, index); + + if (max > 0) + { + obj = json_object_new_array(); + + if (!obj) + return NULL; + + for (i = 1; i <= max; i++) + { + lua_rawgeti(L, index, i); + + json_object_array_put_idx(obj, i - 1, + _lua_to_json(L, lua_gettop(L))); + + lua_pop(L, 1); + } + + return obj; + } + + obj = json_object_new_object(); + + if (!obj) + return NULL; + + lua_pushnil(L); + + while (lua_next(L, index)) + { + lua_pushvalue(L, -2); + key = lua_tostring(L, -1); + + json_object_object_add(obj, key, + _lua_to_json(L, lua_gettop(L) - 1)); + + lua_pop(L, 2); + } + + return obj; + + case LUA_TNIL: + return NULL; + + case LUA_TBOOLEAN: + return json_object_new_boolean(lua_toboolean(L, index)); + + case LUA_TNUMBER: + nd = lua_tonumber(L, index); + ni = lua_tointeger(L, index); + + if (nd == ni) + return json_object_new_int(nd); + + return json_object_new_double(nd); + + case LUA_TSTRING: + return json_object_new_string(lua_tostring(L, index)); + } + + return NULL; +} + +static int json_parse_set(lua_State *L) +{ + struct json_state *s = luaL_checkudata(L, 1, LUCI_JSONC_PARSER); + + s->err = 0; + s->obj = _lua_to_json(L, 2); + + return 0; +} + +static int json_tostring(lua_State *L) +{ + struct json_state *s = luaL_checkudata(L, 1, LUCI_JSONC_PARSER); + bool pretty = lua_toboolean(L, 2); + int flags = 0; + + if (pretty) + flags |= JSON_C_TO_STRING_PRETTY | JSON_C_TO_STRING_SPACED; + + lua_pushstring(L, json_object_to_json_string_ext(s->obj, flags)); + return 1; +} + +static int json_gc(lua_State *L) +{ + struct json_state *s = luaL_checkudata(L, 1, LUCI_JSONC_PARSER); + + if (s->obj) + json_object_put(s->obj); + + if (s->tok) + json_tokener_free(s->tok); + + return 0; +} + + +static const luaL_reg jsonc_methods[] = { + { "new", json_new }, + { "parse", json_parse }, + { "stringify", json_stringify }, + + { } +}; + +static const luaL_reg jsonc_parser_methods[] = { + { "parse", json_parse_chunk }, + { "get", json_parse_get }, + { "set", json_parse_set }, + { "stringify", json_tostring }, + + { "__gc", json_gc }, + { "__tostring", json_tostring }, + + { } +}; + + +int luaopen_luci_jsonc(lua_State *L) +{ + luaL_register(L, LUCI_JSONC, jsonc_methods); + + luaL_newmetatable(L, LUCI_JSONC_PARSER); + luaL_register(L, NULL, jsonc_parser_methods); + lua_pushvalue(L, -1); + lua_setfield(L, -2, "__index"); + lua_pop(L, 1); + + return 1; +} |