From 049691cb11115fbdb750021556cad3507fad4e69 Mon Sep 17 00:00:00 2001 From: Mark Pulford Date: Fri, 7 Oct 2011 23:55:49 +1030 Subject: Add USE_POSIX_SETLOCALE option Add USE_POSIX_SETLOCALE option for platforms missing uselocale(). Document locale options in README, Makefile & rockspec. Also: - Rename USE_POSIX_LOCALE define to USE_POSIX_USELOCALE. - Use uselocale() by default through Makefile (Linux, OSX). - Use setlocale() by default through rockspec (other platforms). --- Makefile | 8 ++++++-- README | 12 ++++++++++++ TODO | 2 -- lua-cjson-1.0.3-1.rockspec | 5 ++++- lua_cjson.c | 38 ++++++++++++++++++++++++++++++-------- 5 files changed, 52 insertions(+), 13 deletions(-) diff --git a/Makefile b/Makefile index 5857aee..082c720 100644 --- a/Makefile +++ b/Makefile @@ -23,8 +23,12 @@ override CFLAGS += -fpic -I$(LUA_INCLUDE_DIR) -DVERSION=\"$(CJSON_VERSION)\" # Handle Solaris platforms that are missing isinf(). #override CFLAGS += -DUSE_INTERNAL_ISINF # Handle locales that use comma as a decimal separator on locale aware -# platforms. Requires POSIX-1.2008 support. -override CFLAGS += -DUSE_POSIX_LOCALE +# platforms (optional, but recommended). +# USE_POSIX_USELOCALE: Linux, OSX. Thread safe. Recommended option. +# USE_POSIX_SETLOCALE: Works on all ANSI C platforms. May be used when +# thread-safety isn't required. +override CFLAGS += -DUSE_POSIX_USELOCALE +#override CFLAGS += -DUSE_POSIX_SETLOCALE INSTALL ?= install diff --git a/README b/README index e8c3abe..f863d73 100644 --- a/README +++ b/README @@ -309,6 +309,18 @@ Lua CJSON will generate an error if asked to serialise Lua functions, userdata, lightuserdata or threads. +Locale handling +--------------- + +Lua CJSON uses strtod() and snprintf() to perform numeric conversion +as they are usually well supported, fast and bug free. + +To ensure JSON encoding/decoding works correctly for locales using +comma decimal separators, Lua CJSON must be compiled with either +USE_POSIX_USELOCALE or USE_POSIX_SETLOCALE. See the Makefile or the +rockspec for details. + + References ========== diff --git a/TODO b/TODO index 9dbde9c..1345448 100644 --- a/TODO +++ b/TODO @@ -2,5 +2,3 @@ - Optionally create an object for settings. Clone function. - Convert documentation into structured source format - -- Add setlocale() support for non-POSIX 2008 operating systems diff --git a/lua-cjson-1.0.3-1.rockspec b/lua-cjson-1.0.3-1.rockspec index 3f8e981..28514e1 100644 --- a/lua-cjson-1.0.3-1.rockspec +++ b/lua-cjson-1.0.3-1.rockspec @@ -25,9 +25,12 @@ build = { cjson = { sources = { "lua_cjson.c", "strbuf.c" }, -- Optional workarounds: +-- USE_POSIX_USELOCALE: Linux, OSX. Thread safe. Recommended. +-- USE_POSIX_SETLOCALE: Works on all ANSI C platforms. May be used when +-- thread-safety isn't required. -- USE_INTERNAL_ISINF: Provide internal isinf() implementation. Required -- on some Solaris platforms. - defines = { "VERSION=\"1.0.3\"" } + defines = { "VERSION=\"1.0.3\"", "USE_POSIX_SETLOCALE" } } }, copy_directories = { "tests" } diff --git a/lua_cjson.c b/lua_cjson.c index ad3818d..8efd127 100644 --- a/lua_cjson.c +++ b/lua_cjson.c @@ -44,26 +44,46 @@ #include "strbuf.h" -#ifdef USE_POSIX_LOCALE -/* Reset locale to POSIX for strtod() / sprintf(). +/* Support to reset locale to POSIX for strtod() / sprintf(). * Some locales use comma as a decimal separator. This breaks JSON. */ +#ifdef USE_POSIX_USELOCALE + +#ifdef USE_POSIX_SETLOCALE +#error Must not define USE_POSIX_USELOCALE and USE_POSIX_SETLOCALE simultaneously. +#endif /* unistd.h defines _POSIX_VERSION */ #include + #if _POSIX_VERSION >= 200809L /* POSIX.1-2008 adds threadsafe locale support */ #include #elif defined(_POSIX_VERSION) -/* Some pre-POSIX.1-2008 operating systems use xlocale.h instead */ +/* Some pre-POSIX.1-2008 operating systems offer xlocale.h instead */ #include #else #error Missing _POSIX_VERSION define -#endif +#endif /* _POSIX_VERSION */ + #define LOCALE_SET_POSIX(x) (x)->saved_locale = uselocale((x)->posix_locale) #define LOCALE_RESTORE(x) uselocale((x)->saved_locale) -#else + +#elif defined(USE_POSIX_SETLOCALE) +/* ANSI C / ISO C90 implementation. Not thread-safe, affects entire process. */ +#include + +#define LOCALE_SET_POSIX(x) \ + do { \ + (x)->saved_locale = setlocale(LC_NUMERIC, NULL); \ + setlocale(LC_NUMERIC, "C"); \ + } while(0) +#define LOCALE_RESTORE(x) setlocale(LC_NUMERIC, (x)->saved_locale) + +#else /* Do not work around locale support in strtod() / sprintf() */ + #define LOCALE_SET_POSIX(x) do { } while(0) #define LOCALE_RESTORE(x) do { } while(0) + #endif /* Some Solaris platforms are missing isinf(). */ @@ -122,9 +142,11 @@ typedef struct { char *char2escape[256]; /* Encoding */ #endif strbuf_t encode_buf; -#if USE_POSIX_LOCALE +#if defined(USE_POSIX_USELOCALE) locale_t saved_locale; locale_t posix_locale; +#elif defined(USE_POSIX_SETLOCALE) + const char *saved_locale; #endif char number_fmt[8]; /* "%.XXg\0" */ int current_depth; @@ -369,7 +391,7 @@ static int json_destroy_config(lua_State *l) json_config_t *cfg; cfg = lua_touserdata(l, 1); -#ifdef USE_POSIX_LOCALE +#ifdef USE_POSIX_USELOCALE if (cfg->posix_locale) freelocale(cfg->posix_locale); #endif @@ -394,7 +416,7 @@ static void json_create_config(lua_State *l) lua_setmetatable(l, -2); strbuf_init(&cfg->encode_buf, 0); -#if USE_POSIX_LOCALE +#ifdef USE_POSIX_USELOCALE cfg->saved_locale = NULL; /* Must not lua_error() before cfg->posix_locale has been initialised */ cfg->posix_locale = newlocale(LC_ALL_MASK, "C", NULL); -- cgit v1.2.3-55-g6feb