diff options
-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) |