From: Eric Haszlakiewicz Date: Sun, 31 Jul 2016 01:34:58 +0000 (-0400) Subject: Issue #195: use uselocale() instead of setlocale() in json_tokener to behave better... X-Git-Tag: json-c-0.13-20171207~143 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=4091b9c87ea293fd8bc3b05bc5b032e09697eefe;p=thirdparty%2Fjson-c.git Issue #195: use uselocale() instead of setlocale() in json_tokener to behave better in threaded environments. --- diff --git a/configure.ac b/configure.ac index 18f91dc6..a08fa168 100644 --- a/configure.ac +++ b/configure.ac @@ -45,7 +45,7 @@ AC_TYPE_SIZE_T AC_FUNC_VPRINTF AC_FUNC_MEMCMP AC_CHECK_FUNCS([realloc]) -AC_CHECK_FUNCS(strcasecmp strdup strerror snprintf vsnprintf vasprintf open vsyslog strncasecmp setlocale) +AC_CHECK_FUNCS(strcasecmp strdup strerror snprintf vsnprintf vasprintf open vsyslog strncasecmp setlocale uselocale) AC_CHECK_DECLS([INFINITY], [], [], [[#include ]]) AC_CHECK_DECLS([nan], [], [], [[#include ]]) AC_CHECK_DECLS([isnan], [], [], [[#include ]]) diff --git a/json_tokener.c b/json_tokener.c index 8b65ee02..233ac92c 100644 --- a/json_tokener.c +++ b/json_tokener.c @@ -235,12 +235,11 @@ struct json_object* json_tokener_parse_ex(struct json_tokener *tok, { struct json_object *obj = NULL; char c = '\1'; -#ifdef HAVE_SETLOCALE - char *oldlocale=NULL, *tmplocale; - - tmplocale = setlocale(LC_NUMERIC, NULL); - if (tmplocale) oldlocale = strdup(tmplocale); - setlocale(LC_NUMERIC, "C"); +#ifdef HAVE_USELOCALE + locale_t oldlocale = uselocale(NULL); + locale_t newloc; +#elif defined(HAVE_SETLOCALE) + char *oldlocale = NULL; #endif tok->char_offset = 0; @@ -253,12 +252,32 @@ struct json_object* json_tokener_parse_ex(struct json_tokener *tok, the string length is less than INT32_MAX (2GB) */ if ((len < -1) || (len == -1 && strlen(str) > INT32_MAX)) { tok->err = json_tokener_error_size; -#ifdef HAVE_SETLOCALE - free(oldlocale); -#endif return NULL; } +#ifdef HAVE_USELOCALE + { + locale_t duploc = duplocale(oldlocale); + newloc = newlocale(LC_NUMERIC, "C", duploc); + // XXX at least Debian 8.4 has a bug in newlocale where it doesn't + // change the decimal separator unless you set LC_TIME! + if (newloc) + newloc = newlocale(LC_TIME, "C", newloc); + if (newloc == NULL) + { + freelocale(duploc); + return NULL; + } + } +#elif defined(HAVE_SETLOCALE) + { + char *tmplocale; + tmplocale = setlocale(LC_NUMERIC, NULL); + if (tmplocale) oldlocale = strdup(tmplocale); + setlocale(LC_NUMERIC, "C"); + } +#endif + while (PEEK_CHAR(c, tok)) { redo_char: @@ -887,6 +906,7 @@ struct json_object* json_tokener_parse_ex(struct json_tokener *tok, goto redo_char; case json_tokener_state_object_sep: + /* { */ if(c == '}') { saved_state = json_tokener_state_finish; state = json_tokener_state_eatws; @@ -918,7 +938,10 @@ struct json_object* json_tokener_parse_ex(struct json_tokener *tok, tok->err = json_tokener_error_parse_eof; } -#ifdef HAVE_SETLOCALE +#ifdef HAVE_USELOCALE + uselocale(oldlocale); + freelocale(newloc); +#elif defined(HAVE_SETLOCALE) setlocale(LC_NUMERIC, oldlocale); free(oldlocale); #endif diff --git a/tests/test_locale.c b/tests/test_locale.c index da070cf5..e6d09566 100644 --- a/tests/test_locale.c +++ b/tests/test_locale.c @@ -21,11 +21,22 @@ int main(int argc, char **argv) printf("No locale\n"); #endif + char buf1[10], buf2[10]; + // Should result in "0,1", if the locale is installed. + // Regardless of what it generates, we check that it's + // consistent below. + (void)snprintf(buf1, sizeof(buf1), "%f", 0.1); + MC_SET_DEBUG(1); new_obj = json_tokener_parse("[1.2,3.4,123456.78,5.0,2.3e10]"); printf("new_obj.to_string()=%s\n", json_object_to_json_string(new_obj)); printf("new_obj.to_string()=%s\n", json_object_to_json_string_ext(new_obj,JSON_C_TO_STRING_NOZERO)); json_object_put(new_obj); + + (void)snprintf(buf2, sizeof(buf2), "%f", 0.1); + if (strcmp(buf1, buf2) != 0) + printf("ERROR: Original locale not restored \"%s\" != \"%s\"", + buf1, buf2); }