# HG changeset patch # User jbe # Date 1406832556 -7200 # Node ID 8e969519f7c722e4d9689f5ea015df49bb6707ee # Parent 581878c613d664ad7ee31056362d18226c311427 JSON pretty printer diff -r 581878c613d6 -r 8e969519f7c7 libraries/json/json.c --- a/libraries/json/json.c Thu Jul 31 19:27:29 2014 +0200 +++ b/libraries/json/json.c Thu Jul 31 20:49:16 2014 +0200 @@ -212,7 +212,7 @@ // limit nested levels: if (level >= JSON_MAXDEPTH) { lua_pushnil(L); - lua_pushliteral(L, "Too many nested JSON levels"); + lua_pushfstring(L, "More than %d nested JSON levels", JSON_MAXDEPTH); return 2; } // additional buffer overflow protection: @@ -711,7 +711,15 @@ #define JSON_TABLETYPE_OBJECT 1 #define JSON_TABLETYPE_ARRAY 2 -static int json_export(lua_State *L) { +#define json_export_internal_indentstring_idx 1 +#define json_export_internal_level_idx 2 +#define json_export_internal_value_idx 3 +#define json_export_internal_tmp_idx 4 + +static int json_export_internal(lua_State *L) { + int level; + int pretty; + int i; lua_Number num; const char *str; unsigned char c; @@ -719,34 +727,36 @@ size_t pos = 0; luaL_Buffer buf; char hexcode[7]; // backslash, character 'u', 4 hex digits, and terminating NULL byte - int luatype; int tabletype = JSON_TABLETYPE_UNKNOWN; - int needsep = 0; + int anyelement = 0; size_t keycount = 0; size_t keypos = 0; json_key_t *keybuf; lua_Integer idx; - lua_settop(L, 1); - if (json_isnullmark(L, 1)) { + lua_settop(L, json_export_internal_value_idx); + if (json_isnullmark(L, json_export_internal_value_idx)) { + lua_pop(L, 1); lua_pushnil(L); - lua_replace(L, 1); } - switch (lua_type(L, 1)) { + switch (lua_type(L, json_export_internal_value_idx)) { case LUA_TNIL: lua_pushliteral(L, "null"); return 1; case LUA_TNUMBER: - num = lua_tonumber(L, 1); + num = lua_tonumber(L, json_export_internal_value_idx); if (isnan(num)) return luaL_error(L, "JSON export not possible for NaN value"); if (isinf(num)) return luaL_error(L, "JSON export not possible for infinite numbers"); - lua_tostring(L, 1); + lua_tostring(L, json_export_internal_value_idx); return 1; case LUA_TBOOLEAN: - if (lua_toboolean(L, 1)) lua_pushliteral(L, "true"); - else lua_pushliteral(L, "false"); + if (lua_toboolean(L, json_export_internal_value_idx)) { + lua_pushliteral(L, "true"); + } else { + lua_pushliteral(L, "false"); + } return 1; case LUA_TSTRING: - str = lua_tolstring(L, 1, &strlen); + str = lua_tolstring(L, 3, &strlen); luaL_buffinit(L, &buf); luaL_addchar(&buf, '"'); while (pos < strlen) { @@ -770,43 +780,55 @@ luaL_pushresult(&buf); return 1; case LUA_TTABLE: - if (lua_getmetatable(L, 1)) { + if (lua_getmetatable(L, json_export_internal_value_idx)) { json_regfetch(L, objectmt); if (lua_rawequal(L, -2, -1)) { tabletype = JSON_TABLETYPE_OBJECT; } else { json_regfetch(L, arraymt); - if (lua_rawequal(L, -3, -1)) tabletype = JSON_TABLETYPE_ARRAY; + if (lua_rawequal(L, -3, -1)) { + tabletype = JSON_TABLETYPE_ARRAY; + } else { + return luaL_error(L, "JSON export not possible for tables with nonsupported metatable"); + } } } json_regfetch(L, shadowtbl); - lua_pushvalue(L, 1); + lua_pushvalue(L, json_export_internal_value_idx); lua_rawget(L, -2); - if (!lua_isnil(L, -1)) lua_replace(L, 1); - lua_settop(L, 1); + if (!lua_isnil(L, -1)) lua_replace(L, json_export_internal_value_idx); + lua_settop(L, json_export_internal_value_idx); if (tabletype == JSON_TABLETYPE_UNKNOWN) { - for (lua_pushnil(L); lua_next(L, 1); lua_pop(L, 1)) { - luatype = lua_type(L, -2); - if (tabletype == JSON_TABLETYPE_UNKNOWN) { - if (luatype == LUA_TSTRING) tabletype = JSON_TABLETYPE_OBJECT; - else if (luatype == LUA_TNUMBER) tabletype = JSON_TABLETYPE_ARRAY; - } else if ( - (tabletype == JSON_TABLETYPE_OBJECT && luatype == LUA_TNUMBER) || - (tabletype == JSON_TABLETYPE_ARRAY && luatype == LUA_TSTRING) - ) { - goto json_export_tabletype_error; + for (lua_pushnil(L); lua_next(L, json_export_internal_value_idx); lua_pop(L, 1)) { + switch (lua_type(L, -2)) { + case LUA_TSTRING: + keycount++; + if (tabletype == JSON_TABLETYPE_UNKNOWN) tabletype = JSON_TABLETYPE_OBJECT; + else if (tabletype == JSON_TABLETYPE_ARRAY) goto json_export_tabletype_error; + break; + case LUA_TNUMBER: + if (tabletype == JSON_TABLETYPE_UNKNOWN) tabletype = JSON_TABLETYPE_ARRAY; + else if (tabletype == JSON_TABLETYPE_OBJECT) goto json_export_tabletype_error; + break; } } } + pretty = lua_toboolean(L, json_export_internal_indentstring_idx); + level = lua_tointeger(L, json_export_internal_level_idx) + 1; + if (level > JSON_MAXDEPTH) { + return luaL_error(L, "More than %d nested JSON levels", JSON_MAXDEPTH); + } switch (tabletype) { case JSON_TABLETYPE_OBJECT: - for (lua_pushnil(L); lua_next(L, 1); lua_pop(L, 1)) { - if (lua_type(L, -2) == LUA_TSTRING) keycount++; + if (!keycount) { + for (lua_pushnil(L); lua_next(L, json_export_internal_value_idx); lua_pop(L, 1)) { + if (lua_type(L, -2) == LUA_TSTRING) keycount++; + } } if (keycount) { keybuf = calloc(keycount, sizeof(json_key_t)); if (!keybuf) return luaL_error(L, "Memory allocation failed in JSON library"); - for (lua_pushnil(L); lua_next(L, 1); lua_pop(L, 1)) { + for (lua_pushnil(L); lua_next(L, json_export_internal_value_idx); lua_pop(L, 1)) { if (lua_type(L, -2) == LUA_TSTRING) { json_key_t *key = keybuf + (keypos++); key->data = lua_tolstring(L, -2, &key->length); @@ -819,46 +841,83 @@ for (keypos=0; keyposdata, key->length); - if (lua_pcall(L, 1, 1, 0)) { + if (lua_pcall(L, 3, 1, 0)) { if (keybuf) free(keybuf); return lua_error(L); } luaL_addvalue(&buf); luaL_addchar(&buf, ':'); - lua_pushcfunction(L, json_export); + if (pretty) luaL_addchar(&buf, ' '); + lua_pushcfunction(L, json_export_internal); + lua_pushvalue(L, json_export_internal_indentstring_idx); + lua_pushinteger(L, level); lua_pushlstring(L, key->data, key->length); - lua_rawget(L, 1); - if (lua_pcall(L, 1, 1, 0)) { + lua_rawget(L, json_export_internal_value_idx); + if (lua_pcall(L, 3, 1, 0)) { if (keybuf) free(keybuf); return lua_error(L); } luaL_addvalue(&buf); } if (keybuf) free(keybuf); + if (pretty && keycount != 0) { + luaL_addchar(&buf, '\n'); + for (i=0; i