libs: add luci-lib-jsonc, a Lua binding for JSON-C
authorJo-Philipp Wich <jow@openwrt.org>
Mon, 26 Jan 2015 15:49:23 +0000 (16:49 +0100)
committerJo-Philipp Wich <jow@openwrt.org>
Mon, 26 Jan 2015 15:49:23 +0000 (16:49 +0100)
Signed-off-by: Jo-Philipp Wich <jow@openwrt.org>
libs/luci-lib-jsonc/Makefile [new file with mode: 0644]
libs/luci-lib-jsonc/src/Makefile [new file with mode: 0644]
libs/luci-lib-jsonc/src/jsonc.c [new file with mode: 0644]

diff --git a/libs/luci-lib-jsonc/Makefile b/libs/luci-lib-jsonc/Makefile
new file mode 100644 (file)
index 0000000..6a63dab
--- /dev/null
@@ -0,0 +1,14 @@
+#
+# Copyright (C) 2015 LuCI Team <luci@lists.subsignal.org>
+#
+# This is free software, licensed under the Apache License, Version 2.0 .
+#
+
+include $(TOPDIR)/rules.mk
+
+LUCI_TITLE:=Lua binding for JSON-C
+LUCI_DEPENDS:=+liblua +libjson-c
+
+include ../../luci.mk
+
+# call BuildPackage - OpenWrt buildroot signature
diff --git a/libs/luci-lib-jsonc/src/Makefile b/libs/luci-lib-jsonc/src/Makefile
new file mode 100644 (file)
index 0000000..e15fbac
--- /dev/null
@@ -0,0 +1,17 @@
+JSONC_CFLAGS = -std=gnu99 -I$(STAGING_DIR)/usr/include/json-c/
+JSONC_LDFLAGS = -llua -lm -ljson-c
+JSONC_OBJ = jsonc.o
+JSONC_LIB = jsonc.so
+
+%.o: %.c
+       $(CC) $(CPPFLAGS) $(CFLAGS) $(LUA_CFLAGS) $(JSONC_CFLAGS) $(FPIC) -c -o $@ $<
+
+compile: $(JSONC_OBJ)
+       $(CC) $(LDFLAGS) -shared -o $(JSONC_LIB) $(JSONC_OBJ) $(JSONC_LDFLAGS)
+
+install: compile
+       mkdir -p $(DESTDIR)/usr/lib/lua/luci
+       cp $(JSONC_LIB) $(DESTDIR)/usr/lib/lua/luci/$(JSONC_LIB)
+
+clean:
+       rm -f *.o *.so
diff --git a/libs/luci-lib-jsonc/src/jsonc.c b/libs/luci-lib-jsonc/src/jsonc.c
new file mode 100644 (file)
index 0000000..49cb21f
--- /dev/null
@@ -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;
+}