diff options
Diffstat (limited to '')
| -rw-r--r-- | Makefile | 10 | ||||
| -rw-r--r-- | NEWS | 3 | ||||
| -rw-r--r-- | TODO | 2 | ||||
| -rw-r--r-- | lua_cjson.c | 51 | ||||
| -rwxr-xr-x | tests/test.lua | 16 |
5 files changed, 79 insertions, 3 deletions
| @@ -15,13 +15,17 @@ LDFLAGS += -shared | |||
| 15 | LUA_INCLUDE_DIR ?= $(PREFIX)/include | 15 | LUA_INCLUDE_DIR ?= $(PREFIX)/include |
| 16 | LUA_LIB_DIR ?= $(PREFIX)/lib/lua/$(LUA_VERSION) | 16 | LUA_LIB_DIR ?= $(PREFIX)/lib/lua/$(LUA_VERSION) |
| 17 | 17 | ||
| 18 | # Some versions of Solaris are missing isinf(). Add -DMISSING_ISINF to | ||
| 19 | # CFLAGS to work around this bug. | ||
| 20 | |||
| 21 | #CFLAGS ?= -g -Wall -pedantic -fno-inline | 18 | #CFLAGS ?= -g -Wall -pedantic -fno-inline |
| 22 | CFLAGS ?= -g -O3 -Wall -pedantic | 19 | CFLAGS ?= -g -O3 -Wall -pedantic |
| 23 | override CFLAGS += -fpic -I$(LUA_INCLUDE_DIR) -DVERSION=\"$(CJSON_VERSION)\" | 20 | override CFLAGS += -fpic -I$(LUA_INCLUDE_DIR) -DVERSION=\"$(CJSON_VERSION)\" |
| 24 | 21 | ||
| 22 | ## Conditional work arounds | ||
| 23 | # Handle Solaris platforms that are missing isinf(). | ||
| 24 | #override CFLAGS += -DMISSING_ISINF | ||
| 25 | # Handle locales that use comma as a decimal separator on locale aware | ||
| 26 | # platforms. Requires POSIX-1.2008 support. | ||
| 27 | override CFLAGS += -DUSE_POSIX_LOCALE | ||
| 28 | |||
| 25 | INSTALL ?= install | 29 | INSTALL ?= install |
| 26 | 30 | ||
| 27 | .PHONY: all clean install package | 31 | .PHONY: all clean install package |
| @@ -1,3 +1,6 @@ | |||
| 1 | Version 1.0.4 (?) | ||
| 2 | * Handle locales with a comma decimal separator | ||
| 3 | |||
| 1 | Version 1.0.3 (Sep 15 2011) | 4 | Version 1.0.3 (Sep 15 2011) |
| 2 | * Fixed detection of objects with numeric string keys | 5 | * Fixed detection of objects with numeric string keys |
| 3 | * Provided work around for missing isinf() on Solaris | 6 | * Provided work around for missing isinf() on Solaris |
| @@ -2,3 +2,5 @@ | |||
| 2 | - Optionally create an object for settings. Clone function. | 2 | - Optionally create an object for settings. Clone function. |
| 3 | 3 | ||
| 4 | - Convert documentation into structured source format | 4 | - Convert documentation into structured source format |
| 5 | |||
| 6 | - Add setlocale() support for non-POSIX 2008 operating systems | ||
diff --git a/lua_cjson.c b/lua_cjson.c index f765883..151fa39 100644 --- a/lua_cjson.c +++ b/lua_cjson.c | |||
| @@ -44,7 +44,30 @@ | |||
| 44 | 44 | ||
| 45 | #include "strbuf.h" | 45 | #include "strbuf.h" |
| 46 | 46 | ||
| 47 | #ifdef USE_POSIX_LOCALE | ||
| 48 | /* Reset locale to POSIX for strtod() / sprintf(). | ||
| 49 | * Some locales use comma as a decimal separator. This breaks JSON. */ | ||
| 50 | |||
| 51 | /* unistd.h defines _POSIX_VERSION */ | ||
| 52 | #include <unistd.h> | ||
| 53 | #if _POSIX_VERSION >= 200809L | ||
| 54 | /* POSIX.1-2008 adds threadsafe locale support */ | ||
| 55 | #include <locale.h> | ||
| 56 | #elif defined(_POSIX_VERSION) | ||
| 57 | /* Some pre-POSIX.1-2008 operating systems use xlocale.h instead */ | ||
| 58 | #include <xlocale.h> | ||
| 59 | #else | ||
| 60 | #error Missing _POSIX_VERSION define | ||
| 61 | #endif | ||
| 62 | #define LOCALE_SET_POSIX(x) (x)->saved_locale = uselocale((x)->posix_locale) | ||
| 63 | #define LOCALE_RESTORE(x) uselocale((x)->saved_locale) | ||
| 64 | #else | ||
| 65 | #define LOCALE_SET_POSIX(x) do { } while(0) | ||
| 66 | #define LOCALE_RESTORE(x) do { } while(0) | ||
| 67 | #endif | ||
| 68 | |||
| 47 | #ifdef MISSING_ISINF | 69 | #ifdef MISSING_ISINF |
| 70 | /* Some Solaris platforms are missing isinf(). Define here. */ | ||
| 48 | #define isinf(x) (!isnan(x) && isnan((x) - (x))) | 71 | #define isinf(x) (!isnan(x) && isnan((x) - (x))) |
| 49 | #endif | 72 | #endif |
| 50 | 73 | ||
| @@ -99,6 +122,10 @@ typedef struct { | |||
| 99 | char *char2escape[256]; /* Encoding */ | 122 | char *char2escape[256]; /* Encoding */ |
| 100 | #endif | 123 | #endif |
| 101 | strbuf_t encode_buf; | 124 | strbuf_t encode_buf; |
| 125 | #if USE_POSIX_LOCALE | ||
| 126 | locale_t saved_locale; | ||
| 127 | locale_t posix_locale; | ||
| 128 | #endif | ||
| 102 | char number_fmt[8]; /* "%.XXg\0" */ | 129 | char number_fmt[8]; /* "%.XXg\0" */ |
| 103 | int current_depth; | 130 | int current_depth; |
| 104 | 131 | ||
| @@ -342,6 +369,10 @@ static int json_destroy_config(lua_State *l) | |||
| 342 | json_config_t *cfg; | 369 | json_config_t *cfg; |
| 343 | 370 | ||
| 344 | cfg = lua_touserdata(l, 1); | 371 | cfg = lua_touserdata(l, 1); |
| 372 | #ifdef USE_POSIX_LOCALE | ||
| 373 | if (cfg->posix_locale) | ||
| 374 | freelocale(cfg->posix_locale); | ||
| 375 | #endif | ||
| 345 | if (cfg) | 376 | if (cfg) |
| 346 | strbuf_free(&cfg->encode_buf); | 377 | strbuf_free(&cfg->encode_buf); |
| 347 | cfg = NULL; | 378 | cfg = NULL; |
| @@ -363,6 +394,13 @@ static void json_create_config(lua_State *l) | |||
| 363 | lua_setmetatable(l, -2); | 394 | lua_setmetatable(l, -2); |
| 364 | 395 | ||
| 365 | strbuf_init(&cfg->encode_buf, 0); | 396 | strbuf_init(&cfg->encode_buf, 0); |
| 397 | #if USE_POSIX_LOCALE | ||
| 398 | cfg->saved_locale = NULL; | ||
| 399 | /* Must not lua_error() before cfg->posix_locale has been initialised */ | ||
| 400 | cfg->posix_locale = newlocale(LC_ALL_MASK, "C", NULL); | ||
| 401 | if (!cfg->posix_locale) | ||
| 402 | luaL_error(l, "Failed to create POSIX locale for JSON"); | ||
| 403 | #endif | ||
| 366 | 404 | ||
| 367 | cfg->encode_sparse_convert = DEFAULT_SPARSE_CONVERT; | 405 | cfg->encode_sparse_convert = DEFAULT_SPARSE_CONVERT; |
| 368 | cfg->encode_sparse_ratio = DEFAULT_SPARSE_RATIO; | 406 | cfg->encode_sparse_ratio = DEFAULT_SPARSE_RATIO; |
| @@ -452,6 +490,7 @@ static void json_encode_exception(lua_State *l, json_config_t *cfg, int lindex, | |||
| 452 | { | 490 | { |
| 453 | if (!cfg->encode_keep_buffer) | 491 | if (!cfg->encode_keep_buffer) |
| 454 | strbuf_free(&cfg->encode_buf); | 492 | strbuf_free(&cfg->encode_buf); |
| 493 | LOCALE_RESTORE(cfg); | ||
| 455 | luaL_error(l, "Cannot serialise %s: %s", | 494 | luaL_error(l, "Cannot serialise %s: %s", |
| 456 | lua_typename(l, lua_type(l, lindex)), reason); | 495 | lua_typename(l, lua_type(l, lindex)), reason); |
| 457 | } | 496 | } |
| @@ -542,6 +581,7 @@ static void json_encode_descend(lua_State *l, json_config_t *cfg) | |||
| 542 | if (cfg->current_depth > cfg->encode_max_depth) { | 581 | if (cfg->current_depth > cfg->encode_max_depth) { |
| 543 | if (!cfg->encode_keep_buffer) | 582 | if (!cfg->encode_keep_buffer) |
| 544 | strbuf_free(&cfg->encode_buf); | 583 | strbuf_free(&cfg->encode_buf); |
| 584 | LOCALE_RESTORE(cfg); | ||
| 545 | luaL_error(l, "Cannot serialise, excessive nesting (%d)", | 585 | luaL_error(l, "Cannot serialise, excessive nesting (%d)", |
| 546 | cfg->current_depth); | 586 | cfg->current_depth); |
| 547 | } | 587 | } |
| @@ -701,9 +741,13 @@ static int json_encode(lua_State *l) | |||
| 701 | else | 741 | else |
| 702 | strbuf_init(&cfg->encode_buf, 0); | 742 | strbuf_init(&cfg->encode_buf, 0); |
| 703 | 743 | ||
| 744 | LOCALE_SET_POSIX(cfg); | ||
| 745 | |||
| 704 | json_append_data(l, cfg, &cfg->encode_buf); | 746 | json_append_data(l, cfg, &cfg->encode_buf); |
| 705 | json = strbuf_string(&cfg->encode_buf, &len); | 747 | json = strbuf_string(&cfg->encode_buf, &len); |
| 706 | 748 | ||
| 749 | LOCALE_RESTORE(cfg); | ||
| 750 | |||
| 707 | lua_pushlstring(l, json, len); | 751 | lua_pushlstring(l, json, len); |
| 708 | 752 | ||
| 709 | if (!cfg->encode_keep_buffer) | 753 | if (!cfg->encode_keep_buffer) |
| @@ -1084,6 +1128,8 @@ static void json_throw_parse_error(lua_State *l, json_parse_t *json, | |||
| 1084 | else | 1128 | else |
| 1085 | found = json_token_type_name[token->type]; | 1129 | found = json_token_type_name[token->type]; |
| 1086 | 1130 | ||
| 1131 | LOCALE_RESTORE(json->cfg); | ||
| 1132 | |||
| 1087 | /* Note: token->index is 0 based, display starting from 1 */ | 1133 | /* Note: token->index is 0 based, display starting from 1 */ |
| 1088 | luaL_error(l, "Expected %s but found %s at character %d", | 1134 | luaL_error(l, "Expected %s but found %s at character %d", |
| 1089 | exp, found, token->index + 1); | 1135 | exp, found, token->index + 1); |
| @@ -1095,6 +1141,7 @@ static void json_decode_checkstack(lua_State *l, json_parse_t *json, int n) | |||
| 1095 | return; | 1141 | return; |
| 1096 | 1142 | ||
| 1097 | strbuf_free(json->tmp); | 1143 | strbuf_free(json->tmp); |
| 1144 | LOCALE_RESTORE(json->cfg); | ||
| 1098 | luaL_error(l, "Too many nested data structures"); | 1145 | luaL_error(l, "Too many nested data structures"); |
| 1099 | } | 1146 | } |
| 1100 | 1147 | ||
| @@ -1224,6 +1271,8 @@ static void lua_json_decode(lua_State *l, const char *json_text, int json_len) | |||
| 1224 | * string must be smaller than the entire json string */ | 1271 | * string must be smaller than the entire json string */ |
| 1225 | json.tmp = strbuf_new(json_len); | 1272 | json.tmp = strbuf_new(json_len); |
| 1226 | 1273 | ||
| 1274 | LOCALE_SET_POSIX(json.cfg); | ||
| 1275 | |||
| 1227 | json_next_token(&json, &token); | 1276 | json_next_token(&json, &token); |
| 1228 | json_process_value(l, &json, &token); | 1277 | json_process_value(l, &json, &token); |
| 1229 | 1278 | ||
| @@ -1233,6 +1282,8 @@ static void lua_json_decode(lua_State *l, const char *json_text, int json_len) | |||
| 1233 | if (token.type != T_END) | 1282 | if (token.type != T_END) |
| 1234 | json_throw_parse_error(l, &json, "the end", &token); | 1283 | json_throw_parse_error(l, &json, "the end", &token); |
| 1235 | 1284 | ||
| 1285 | LOCALE_RESTORE(json.cfg); | ||
| 1286 | |||
| 1236 | strbuf_free(json.tmp); | 1287 | strbuf_free(json.tmp); |
| 1237 | } | 1288 | } |
| 1238 | 1289 | ||
diff --git a/tests/test.lua b/tests/test.lua index 7a75243..d80dcf0 100755 --- a/tests/test.lua +++ b/tests/test.lua | |||
| @@ -210,6 +210,21 @@ local escape_tests = { | |||
| 210 | { json.decode, { utf16_escaped }, true, { utf8_raw } } | 210 | { json.decode, { utf16_escaped }, true, { utf8_raw } } |
| 211 | } | 211 | } |
| 212 | 212 | ||
| 213 | -- The standard Lua interpreter is ANSI C online doesn't support locales | ||
| 214 | -- by default. Force a known problematic locale to test strtod()/sprintf(). | ||
| 215 | local locale_tests = { | ||
| 216 | function () | ||
| 217 | os.setlocale("cs_CZ") | ||
| 218 | return "Setting locale to cs_CZ (comma separator)" | ||
| 219 | end, | ||
| 220 | { json.encode, { 1.5 }, true, { '1.5' } }, | ||
| 221 | { json.decode, { "[ 10, \"test\" ]" }, true, { { 10, "test" } } }, | ||
| 222 | function () | ||
| 223 | os.setlocale("C") | ||
| 224 | return "Reverting locale to POSIX" | ||
| 225 | end | ||
| 226 | } | ||
| 227 | |||
| 213 | print(string.format("Testing CJSON v%s\n", cjson.version)) | 228 | print(string.format("Testing CJSON v%s\n", cjson.version)) |
| 214 | 229 | ||
| 215 | run_test_group("decode simple value", decode_simple_tests) | 230 | run_test_group("decode simple value", decode_simple_tests) |
| @@ -225,6 +240,7 @@ run_test_group("encode table", encode_table_tests) | |||
| 225 | run_test_group("decode error", decode_error_tests) | 240 | run_test_group("decode error", decode_error_tests) |
| 226 | run_test_group("encode error", encode_error_tests) | 241 | run_test_group("encode error", encode_error_tests) |
| 227 | run_test_group("escape", escape_tests) | 242 | run_test_group("escape", escape_tests) |
| 243 | run_test_group("locale", locale_tests) | ||
| 228 | 244 | ||
| 229 | cjson.refuse_invalid_numbers(false) | 245 | cjson.refuse_invalid_numbers(false) |
| 230 | cjson.encode_max_depth(20) | 246 | cjson.encode_max_depth(20) |
