From 55b006efbf594766c440354616b0f643c14be070 Mon Sep 17 00:00:00 2001 From: MikalaiR Date: Sat, 9 Jan 2016 14:00:31 +0000 Subject: MirLua: JSON module added git-svn-id: http://svn.miranda-ng.org/main/trunk@16065 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c --- plugins/MirLua/Modules/JSON/src/json.c | 591 +++++++++++++++++++++++++++++ plugins/MirLua/Modules/JSON/src/stdafx.cxx | 1 + plugins/MirLua/Modules/JSON/src/stdafx.h | 59 +++ 3 files changed, 651 insertions(+) create mode 100644 plugins/MirLua/Modules/JSON/src/json.c create mode 100644 plugins/MirLua/Modules/JSON/src/stdafx.cxx create mode 100644 plugins/MirLua/Modules/JSON/src/stdafx.h (limited to 'plugins/MirLua/Modules/JSON/src') diff --git a/plugins/MirLua/Modules/JSON/src/json.c b/plugins/MirLua/Modules/JSON/src/json.c new file mode 100644 index 0000000000..165780488f --- /dev/null +++ b/plugins/MirLua/Modules/JSON/src/json.c @@ -0,0 +1,591 @@ +#include "stdafx.h" + +int vasprintf(char **strp, const char *fmt, va_list ap) +{ + int r = -1, size = _vscprintf(fmt, ap); + + if ((size >= 0) && (size < INT_MAX)) + { + *strp = (char *)malloc(size + 1); //+1 for null + if (*strp) + { + r = vsnprintf(*strp, size + 1, fmt, ap); //+1 for null + if ((r < 0) || (r > size)) + { + free(*strp); + *strp = 0; + r = -1; + } + } + } + else { *strp = 0; } + + return(r); +} + +static int +json_error(lua_State *L, const char *fmt, ...) +{ + va_list ap; + int len; + char *msg; + + va_start(ap, fmt); + len = vasprintf(&msg, fmt, ap); + va_end(ap); + + lua_pushnil(L); + if (len != -1) { + lua_pushstring(L, msg); + free(msg); + } + else + lua_pushstring(L, "internal error: vasprintf failed"); + longjmp(env, 0); +} + +static unsigned int +digit2int(lua_State *L, const unsigned char digit) +{ + unsigned int val = 0; + + if (digit >= '0' && digit <= '9') + val = digit - '0'; + else if (digit >= 'a' || digit <= 'f') + val = digit - 'a' + 10; + else if (digit >= 'A' || digit <= 'F') + val = digit - 'A' + 10; + else + json_error(L, "Invalid hex digit"); + return val; +} + +static unsigned int +fourhex2int(lua_State *L, const unsigned char *code) +{ + unsigned int utf = 0; + + utf += digit2int(L, code[0]) * 4096; + utf += digit2int(L, code[1]) * 256; + utf += digit2int(L, code[2]) * 16; + utf += digit2int(L, code[3]); + return utf; +} + +static const char * +code2utf8(lua_State *L, const unsigned char *code, char buf[4]) +{ + unsigned int utf = 0; + + utf = fourhex2int(L, code); + if (utf < 128) { + buf[0] = utf & 0x7F; + buf[1] = buf[2] = buf[3] = 0; + } + else if (utf < 2048) { + buf[0] = ((utf >> 6) & 0x1F) | 0xC0; + buf[1] = (utf & 0x3F) | 0x80; + buf[2] = buf[3] = 0; + } + else { + buf[0] = ((utf >> 12) & 0x0F) | 0xE0; + buf[1] = ((utf >> 6) & 0x3F) | 0x80; + buf[2] = (utf & 0x3F) | 0x80; + buf[3] = 0; + } + return buf; +} + +static void +skip_ws(char **s) +{ + while (isspace(**s)) + (*s)++; +} + +static void +decode_array(lua_State *L, char **s, int null) +{ + int i = 1; + + (*s)++; + lua_newtable(L); + for (i = 1; 1; i++) { + skip_ws(s); + lua_pushinteger(L, i); + decode_value(L, s, null); + lua_settable(L, -3); + skip_ws(s); + if (**s == ',') { + (*s)++; + skip_ws(s); + } + else + break; + } + skip_ws(s); + if (**s == ']') + (*s)++; + else + json_error(L, "array does not end with ']'"); +} + +static void +decode_object(lua_State *L, char **s, int null) +{ + (*s)++; + lua_newtable(L); + skip_ws(s); + + if (**s != '}') { + while (1) { + skip_ws(s); + decode_string(L, s); + skip_ws(s); + if (**s != ':') + json_error(L, "object lacks separator ':'"); + (*s)++; + skip_ws(s); + decode_value(L, s, null); + lua_settable(L, -3); + skip_ws(s); + if (**s == ',') { + (*s)++; + skip_ws(s); + } + else + break; + } + skip_ws(s); + } + if (**s == '}') + (*s)++; + else + json_error(L, "objects does not end with '}'"); +} + +static void +decode_string(lua_State *L, char **s) +{ + size_t len; + char *newstr = NULL, *newc, *beginning, *end, *nextEscape = NULL; + char utfbuf[4] = ""; + + (*s)++; + beginning = *s; + for (end = NULL; **s != '\0' && end == NULL; (*s)++) { + if (**s == '"' && (*((*s) - 1) != '\\')) + end = *s; + } + *s = beginning; + len = strlen(*s); + newstr = (char*)malloc(len + 1); + memset(newstr, 0, len + 1); + newc = newstr; + while (*s != end) { + nextEscape = strchr(*s, '\\'); + if (nextEscape > end) + nextEscape = NULL; + if (nextEscape == *s) { + switch (*((*s) + 1)) { + case '"': + *newc = '"'; + newc++; + (*s) += 2; + break; + case '\\': + *newc = '\\'; + newc++; + (*s) += 2; + break; + case '/': + *newc = '/'; + newc++; + (*s) += 2; + break; + case 'b': + *newc = '\b'; + newc++; + (*s) += 2; + break; + case 'f': + *newc = '\f'; + newc++; + (*s) += 2; + break; + case 'n': + *newc = '\n'; + newc++; + (*s) += 2; + break; + case 'r': + *newc = '\r'; + newc++; + (*s) += 2; + break; + case 't': + *newc = '\t'; + newc++; + (*s) += 2; + break; + case 'u': + code2utf8(L, (unsigned char *)(*s) + 2, utfbuf); + size_t len = strlen(utfbuf); + strcpy(newc, utfbuf); + newc += len; + (*s) += 6; + break; +/* default: + json_error(L, "invalid escape character"); + break;*/ + } + } + else if (nextEscape != NULL) { + size_t len = nextEscape - *s; + strncpy(newc, *s, len); + newc += len; + (*s) += len; + } + else { + size_t len = end - *s; + strncpy(newc, *s, len); + newc += len; + (*s) += len; + } + } + *newc = 0; + lua_pushstring(L, newstr); + (*s)++; + free(newstr); +} + +static void +decode_value(lua_State *L, char **s, int null) +{ + skip_ws(s); + + if (!strncmp(*s, "false", 5)) { + lua_pushboolean(L, 0); + *s += 5; + } + else if (!strncmp(*s, "true", 4)) { + lua_pushboolean(L, 1); + *s += 4; + } + else if (!strncmp(*s, "null", 4)) { + switch (null) { + case 0: + lua_pushstring(L, ""); + break; + case 1: + lua_newtable(L); + luaL_getmetatable(L, JSON_NULL_METATABLE); + lua_setmetatable(L, -2); + break; + case 2: + lua_pushnil(L); + break; + } + *s += 4; + } + else if (isdigit(**s) || **s == '+' || **s == '-') { + lua_pushnumber(L, atof(*s)); + /* advance pointer past the number */ + while (isdigit(**s) || **s == '+' || **s == '-' + || **s == 'e' || **s == 'E' || **s == '.') + (*s)++; + } + else { + switch (**s) { + case '[': + decode_array(L, s, null); + break; + case '{': + decode_object(L, s, null); + break; + case '"': + decode_string(L, s); + break; + case ']': /* ignore end of empty array */ + lua_pushnil(L); + break; + default: + json_error(L, "syntax error"); + break; + } + } +} + +static int +json_decode(lua_State *L) +{ + char *s; + int null; + const char *const options[] = { + "empty-string", + "json-null", + "nil", + NULL + }; + + s = (char *)luaL_checkstring(L, 1); + + null = luaL_checkoption(L, 2, "json-null", options); + + if (!setjmp(env)) { + decode_value(L, &s, null); + return 1; + } + else + return 2; +} + +/* encode JSON */ + +/* encode_string assumes an UTF-8 string */ +static void +encode_string(lua_State *L, luaL_Buffer *b, unsigned char *s) +{ + char hexbuf[6]; + + luaL_addchar(b, '"'); + for (; *s; s++) { + switch (*s) { + case '\\': + luaL_addstring(b, "\\\\"); + break; + case '"': + luaL_addstring(b, "\\\""); + break; + case '\b': + luaL_addstring(b, "\\b"); + break; + case '\f': + luaL_addstring(b, "\\f"); + break; + case '\n': + luaL_addstring(b, "\\n"); + break; + case '\r': + luaL_addstring(b, "\\r"); + break; + case '\t': + luaL_addstring(b, "\\t"); + break; + default: + /* Convert UTF-8 to unicode + * 00000000 - 0000007F: 0xxxxxxx + * 00000080 - 000007FF: 110xxxxx 10xxxxxx + * 00000800 - 0000FFFF: 1110xxxx 10xxxxxx 10xxxxxx + * 00010000 - 001FFFFF: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + */ + if ((*s & 0x80) == 0) + luaL_addchar(b, *s); + else if (((*s >> 5) & 0x07) == 0x06) { + luaL_addstring(b, "\\u"); + _snprintf(hexbuf, sizeof hexbuf, "%04x", + ((*s & 0x1f) << 6) | (*(s + 1) & 0x3f)); + luaL_addstring(b, hexbuf); + s++; + } + else if (((*s >> 4) & 0x0f) == 0x0e) { + luaL_addstring(b, "\\u"); + _snprintf(hexbuf, sizeof hexbuf, "%04x", + ((*s & 0x0f) << 12) | + ((*(s + 1) & 0x3f) << 6) | + (*(s + 2) & 0x3f)); + s += 2; + } + else if (((*s >> 3) & 0x1f) == 0x1e) { + /* deliberately ignored */ + s += 3; + } + break; + } + } + luaL_addchar(b, '"'); +} + +static void +encode(lua_State *L, luaL_Buffer *b) +{ + int e, t, n, m; + + switch (lua_type(L, -1)) { + case LUA_TBOOLEAN: + luaL_addstring(b, lua_toboolean(L, -1) ? "true" : "false"); + lua_pop(L, 1); + break; + case LUA_TNUMBER: + luaL_addvalue(b); + break; + case LUA_TSTRING: + encode_string(L, b, (unsigned char *)lua_tostring(L, -1)); + lua_pop(L, 1); + break; + case LUA_TTABLE: + /* check if this is the null value */ + lua_checkstack(L, 2); + if (lua_getmetatable(L, -1)) { + luaL_getmetatable(L, JSON_NULL_METATABLE); +#if LUA_VERSION_NUM >= 502 + if (lua_compare(L, -2, -1, LUA_OPEQ)) { +#else + if (lua_equal(L, -2, -1)) { +#endif + lua_pop(L, 2); + luaL_addstring(b, "null"); + lua_pop(L, 1); + break; + } + lua_pop(L, 2); + } + /* if there are t[1] .. t[n], output them as array */ + n = 0; + e = 1; + for (m = 1;; m++) { + lua_pushnumber(L, m); + lua_gettable(L, -2); + if (lua_isnil(L, -1)) { + lua_pop(L, 1); + break; + } + luaL_addchar(b, n ? ',' : '['); + encode(L, b); + n++; + } + if (n) { + luaL_addchar(b, ']'); + lua_pop(L, 1); + e = 0; + break; + } + + /* output non-numerical indices as object */ + t = lua_gettop(L); + lua_pushnil(L); + n = 0; + while (lua_next(L, t) != 0) { + if (lua_type(L, -2) == LUA_TNUMBER) { + lua_pop(L, 1); + continue; + } + luaL_addstring(b, n ? ",\"" : "{\""); + luaL_addstring(b, lua_tostring(L, -2)); + luaL_addstring(b, "\":"); + encode(L, b); + n++; + } + if (n) { + luaL_addchar(b, '}'); + e = 0; + } + + if (e) + luaL_addstring(b, "[]"); + lua_pop(L, 1); + break; + case LUA_TNIL: + luaL_addstring(b, "null"); + lua_pop(L, 1); + break; + default: + json_error(L, "Lua type %s is incompatible with JSON", + luaL_typename(L, -1)); + lua_pop(L, 1); + } +} + +static int +json_encode(lua_State *L) +{ + luaL_Buffer b; + + luaL_buffinit(L, &b); + encode(L, &b); + luaL_pushresult(&b); + return 1; +} + +static int +json_isnull(lua_State *L) +{ + if (lua_getmetatable(L, -1)) { + luaL_getmetatable(L, JSON_NULL_METATABLE); +#if LUA_VERSION_NUM >= 502 + if (lua_compare(L, -2, -1, LUA_OPEQ)) { +#else + if (lua_equal(L, -2, -1)) { +#endif + lua_pop(L, 2); + lua_pushboolean(L, 1); + goto done; + } + lua_pop(L, 2); + } + lua_pushboolean(L, 0); +done: + return 1; +} + +static void +json_set_info(lua_State *L) +{ + lua_pushliteral(L, "_COPYRIGHT"); + lua_pushliteral(L, "Copyright (C) 2011 - 2015 " + "micro systems marc balmer"); + lua_settable(L, -3); + lua_pushliteral(L, "_DESCRIPTION"); + lua_pushliteral(L, "JSON encoder/decoder for Lua"); + lua_settable(L, -3); + lua_pushliteral(L, "_VERSION"); + lua_pushliteral(L, "json 1.2.3"); + lua_settable(L, -3); +} + +static int +json_null(lua_State *L) +{ + lua_pushstring(L, "null"); + return 1; +} + +LUA_LIBRARY_EXPORT +luaopen_json(lua_State* L) +{ + static const struct luaL_Reg methods[] = { + { "decode", json_decode }, + { "encode", json_encode }, + { "isnull", json_isnull }, + { NULL, NULL } + }; + static const struct luaL_Reg null_methods[] = { + { "__tostring", json_null }, + { "__call", json_null }, + { NULL, NULL } + }; + +#if LUA_VERSION_NUM >= 502 + luaL_newlib(L, methods); +#else + luaL_register(L, "json", methods); +#endif + json_set_info(L); + + lua_newtable(L); + /* The null metatable */ + if (luaL_newmetatable(L, JSON_NULL_METATABLE)) { +#if LUA_VERSION_NUM >= 502 + luaL_setfuncs(L, null_methods, 0); +#else + luaL_register(L, NULL, null_methods); +#endif + lua_pushliteral(L, "__metatable"); + lua_pushliteral(L, "must not access this metatable"); + lua_settable(L, -3); + + } + lua_setmetatable(L, -2); + lua_setfield(L, -2, "null"); + return 1; +} \ No newline at end of file diff --git a/plugins/MirLua/Modules/JSON/src/stdafx.cxx b/plugins/MirLua/Modules/JSON/src/stdafx.cxx new file mode 100644 index 0000000000..1577c4e3bc --- /dev/null +++ b/plugins/MirLua/Modules/JSON/src/stdafx.cxx @@ -0,0 +1 @@ +#include "stdafx.h" \ No newline at end of file diff --git a/plugins/MirLua/Modules/JSON/src/stdafx.h b/plugins/MirLua/Modules/JSON/src/stdafx.h new file mode 100644 index 0000000000..7853175a06 --- /dev/null +++ b/plugins/MirLua/Modules/JSON/src/stdafx.h @@ -0,0 +1,59 @@ +/* +* Copyright (c) 2011 - 2015, Micro Systems Marc Balmer, CH-5073 Gipf-Oberfrick +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of Micro Systems Marc Balmer nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +* ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/* JSON interface for Lua */ + +/* +* This code has been derived from the public domain LuaJSON Library 1.1 +* written by Nathaniel Musgrove (proton.zero@gmail.com), for the original +* code see http://luaforge.net/projects/luajsonlib/ +*/ + +#ifdef __cplusplus +# define LUA_LIBRARY_EXPORT extern "C" int __declspec(dllexport) +#else +# define LUA_LIBRARY_EXPORT int __declspec(dllexport) +#endif + +#include + +#include + +#include +#include +#include +#include +#include +#include + +#define JSON_NULL_METATABLE "JSON null object methods" + +static void decode_value(lua_State *, char **, int); +static void decode_string(lua_State *, char **); +static void encode(lua_State *, luaL_Buffer *); + +static jmp_buf env; -- cgit v1.2.3