gluon-packages/libs/lua-jsonc/src/lua-jsonc.c

231 lines
4.5 KiB
C

/*
Copyright 2015 Jo-Philipp Wich <jow@openwrt.org>
Copyright 2018 Matthias Schiffer <mschiffer@universe-factory.net>
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.
*/
#include "lua-jsonc.h"
#include <limits.h>
#include <lualib.h>
#include <lauxlib.h>
void lua_jsonc_push_json(lua_State *L, struct json_object *obj) {
int64_t i;
switch (json_object_get_type(obj)) {
case json_type_object:
lua_newtable(L);
json_object_object_foreach(obj, key, val) {
lua_jsonc_push_json(L, val);
lua_setfield(L, -2, key);
}
break;
case json_type_array:
lua_newtable(L);
for (size_t n = 0; n < json_object_array_length(obj); n++) {
lua_jsonc_push_json(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:
i = json_object_get_int64(obj);
if (i == (int64_t)(lua_Integer)i)
lua_pushinteger(L, i);
else
lua_pushnumber(L, i);
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 lua_jsonc_lua_test_array(lua_State *L, int index) {
int max = 0;
lua_pushnil(L);
/* check for non-integer keys */
while (lua_next(L, index)) {
if (lua_type(L, -2) != LUA_TNUMBER)
goto out;
lua_Number idx = lua_tonumber(L, -2);
if (idx != (lua_Number)(lua_Integer)idx)
goto out;
/* We only allow INT_MAX-1 keys to avoid overflows */
if (idx <= 0 || idx >= INT_MAX)
goto out;
if (idx > max)
max = idx;
lua_pop(L, 1);
}
/* check for holes */
for (int i = 1; i <= max; i++) {
lua_rawgeti(L, index, i);
int isnil = lua_isnil(L, -1);
lua_pop(L, 1);
if (isnil)
return -1;
}
return max;
out:
lua_pop(L, 2);
return -1;
}
struct json_object * lua_jsonc_tojson(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_jsonc_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_jsonc_tojson(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);
if (key)
json_object_object_add(
obj, key, lua_jsonc_tojson(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 lua_jsonc_load(lua_State *L) {
const char *filename = luaL_checkstring(L, 1);
struct json_object *obj = json_object_from_file(filename);
lua_jsonc_push_json(L, obj);
json_object_put(obj);
return 1;
}
static int lua_jsonc_parse(lua_State *L) {
const char *input = luaL_checkstring(L, 1);
struct json_object *obj = json_tokener_parse(input);
lua_jsonc_push_json(L, obj);
json_object_put(obj);
return 1;
}
static int lua_jsonc_stringify(lua_State *L) {
struct json_object *obj = lua_jsonc_tojson(L, 1);
int 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));
json_object_put(obj);
return 1;
}
static const luaL_reg R[] = {
{ "load", lua_jsonc_load },
{ "parse", lua_jsonc_parse },
{ "stringify", lua_jsonc_stringify },
{}
};
int luaopen_jsonc(lua_State *L) {
luaL_register(L, "jsonc", R);
return 1;
}