# Checks for libraries.
# Checks for header files.
+AC_CHECK_HEADERS([locale.h])
# Checks for typedefs, structures, and compiler characteristics.
AC_TYPE_INT32_T
AC_SUBST([json_inline])
# Checks for library functions.
+AC_CHECK_FUNCS([setlocale localeconv])
+case "$ac_cv_header_locale_h$ac_cv_func_localeconv" in
+ yesyes) json_have_localeconv=1;;
+ *) json_have_localeconv=0;;
+esac
+AC_SUBST([json_have_localeconv])
AC_CONFIG_FILES([
jansson.pc
are implicitly handled via the ordinary C type coercion rules (subject
to overflow semantics). Also, no support or hooks are provided for any
supplemental "bignum" type add-on packages.
+
+
+Locale
+======
+
+Jansson works fine under any locale.
+
+However, if the host program is multithreaded and uses ``setlocale()``
+to switch the locale in one thread while Jansson is currently encoding
+or decoding JSON data in another thread, the result may be wrong or
+the program may even crash.
+
+Jansson uses locale specific functions for certain string conversions
+in the encoder and decoder, and then converts the locale specific
+values to/from the JSON representation. This fails if the locale
+changes between the string conversion and the locale-to-JSON
+conversion. This can only happen in multithreaded programs that use
+``setlocale()``, that switches the locale for all running threads, not
+only the thread that calls ``setlocale()``.
+
+If your program uses ``setlocale()`` as described above, consider
+using the thread-safe ``uselocale()`` instead.
pack_unpack.c \
strbuffer.c \
strbuffer.h \
+ strconv.c \
utf.c \
utf.h \
value.c
{
char buffer[MAX_REAL_STR_LENGTH];
int size;
+ double value = json_real_value(json);
- size = snprintf(buffer, MAX_REAL_STR_LENGTH, "%.17g",
- json_real_value(json));
- if(size >= MAX_REAL_STR_LENGTH)
+ size = jsonp_dtostr(buffer, MAX_REAL_STR_LENGTH, value);
+ if(size < 0)
return -1;
- /* Make sure there's a dot or 'e' in the output. Otherwise
- a real is converted to an integer when decoding */
- if(strchr(buffer, '.') == NULL &&
- strchr(buffer, 'e') == NULL)
- {
- if(size + 2 >= MAX_REAL_STR_LENGTH) {
- /* No space to append ".0" */
- return -1;
- }
- buffer[size] = '.';
- buffer[size + 1] = '0';
- size += 2;
- }
-
return dump(buffer, size, data);
}
JSON_INTEGER_IS_LONG_LONG is defined to 1, otherwise to 0. */
#define JSON_INTEGER_IS_LONG_LONG @json_have_long_long@
+/* If locale.h and localeconv() are available, define to 1,
+ otherwise to 0. */
+#define JSON_HAVE_LOCALECONV @json_have_localeconv@
+
#endif
JSON_INTEGER_IS_LONG_LONG is defined to 1, otherwise to 0. */
#define JSON_INTEGER_IS_LONG_LONG 1
+/* If locale.h and localeconv() are available, define to 1,
+ otherwise to 0. */
+#define JSON_HAVE_LOCALECONV 1
+
#endif
#include <stddef.h>
#include "jansson.h"
#include "hashtable.h"
+#include "strbuffer.h"
#define container_of(ptr_, type_, member_) \
((type_ *)((char *)ptr_ - offsetof(type_, member_)))
void jsonp_error_vset(json_error_t *error, int line, int column,
size_t position, const char *msg, va_list ap);
+/* Locale independent string<->double conversions */
+int jsonp_strtod(strbuffer_t *strbuffer, double *out);
+int jsonp_dtostr(char *buffer, size_t size, double value);
+
/* Wrappers for custom memory functions */
void* jsonp_malloc(size_t size);
void jsonp_free(void *ptr);
lex_unget_unsave(lex, c);
- saved_text = strbuffer_value(&lex->saved_text);
- errno = 0;
- value = strtod(saved_text, &end);
- assert(end == saved_text + lex->saved_text.length);
-
- if(errno == ERANGE && value != 0) {
+ if(jsonp_strtod(&lex->saved_text, &value)) {
error_set(error, lex, "real number overflow");
goto out;
}
--- /dev/null
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include "jansson_private.h"
+#include "strbuffer.h"
+
+#if JSON_HAVE_LOCALECONV
+#include <locale.h>
+
+/*
+ - This code assumes that the decimal separator is exactly one
+ character.
+
+ - If setlocale() is called by another thread between the call to
+ localeconv() and the call to sprintf() or strtod(), the result may
+ be wrong. setlocale() is not thread-safe and should not be used
+ this way. Multi-threaded programs should use uselocale() instead.
+*/
+
+static void to_locale(strbuffer_t *strbuffer)
+{
+ const char *point;
+ char *pos;
+
+ point = localeconv()->decimal_point;
+ if(*point == '.') {
+ /* No conversion needed */
+ return;
+ }
+
+ pos = strstr(strbuffer->value, ".");
+ if(pos)
+ *pos = *point;
+}
+
+static void from_locale(char *buffer)
+{
+ const char *point;
+ char *pos;
+
+ point = localeconv()->decimal_point;
+ if(*point == '.') {
+ /* No conversion needed */
+ return;
+ }
+
+ pos = strstr(buffer, point);
+ if(pos)
+ *pos = '.';
+}
+#endif
+
+int jsonp_strtod(strbuffer_t *strbuffer, double *out)
+{
+ double value;
+ char *end;
+
+#if JSON_HAVE_LOCALECONV
+ to_locale(strbuffer);
+#endif
+
+ errno = 0;
+ value = strtod(strbuffer->value, &end);
+ assert(end == strbuffer->value + strbuffer->length);
+
+ if(errno == ERANGE && value != 0) {
+ /* Overflow */
+ return -1;
+ }
+
+ *out = value;
+ return 0;
+}
+
+int jsonp_dtostr(char *buffer, size_t size, double value)
+{
+ int ret;
+ size_t length;
+
+ ret = snprintf(buffer, size, "%.17g", value);
+ if(ret < 0)
+ return -1;
+
+ length = (size_t)ret;
+ if(length >= size)
+ return -1;
+
+#if JSON_HAVE_LOCALECONV
+ from_locale(buffer);
+#endif
+
+ /* Make sure there's a dot or 'e' in the output. Otherwise
+ a real is converted to an integer when decoding */
+ if(strchr(buffer, '.') == NULL &&
+ strchr(buffer, 'e') == NULL)
+ {
+ if(length + 2 >= size) {
+ /* No space to append ".0" */
+ return -1;
+ }
+ buffer[length] = '.';
+ buffer[length + 1] = '0';
+ length += 2;
+ }
+
+ return (int)length;
+}
* it under the terms of the MIT license. See LICENSE for details.
*/
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <jansson.h>
+#if HAVE_LOCALE_H
+#include <locale.h>
+#endif
+
static int getenv_int(const char *name)
{
char *value, *end;
json_t *json;
json_error_t error;
+#if HAVE_SETLOCALE
+ setlocale(LC_ALL, "");
+#endif
+
if(argc != 1) {
fprintf(stderr, "usage: %s\n", argv[0]);
return 2;
}
-int main()
+static void run_tests()
{
test_misc();
test_insert();
test_clear();
test_extend();
test_circular();
-
- return 0;
}
json_decref(copy);
}
-int main()
+static void run_tests()
{
test_copy_simple();
test_deep_copy_simple();
test_deep_copy_array();
test_copy_object();
test_deep_copy_object();
- return 0;
}
}
-int main()
+static void run_tests()
{
encode_twice();
circular_references();
encode_other_than_array_or_object();
- return 0;
}
return 0;
}
-int main(void)
+static void run_tests()
{
struct my_sink s;
json_t *json;
json_decref(json);
free(dumped_to_string);
free(s.buf);
- return EXIT_SUCCESS;
}
-
/* TODO: There's no negative test case here */
}
-int main()
+static void run_tests()
{
test_equal_simple();
test_equal_array();
test_equal_object();
test_equal_complex();
- return 0;
}
json_decref(json);
}
-int main()
+static void run_tests()
{
file_not_found();
reject_duplicates();
disable_eof_check();
-
- return 0;
}
#include <string.h>
#include "util.h"
-int main()
+static void run_tests()
{
json_t *json;
json_error_t error;
if(strcmp(error.text, "']' expected near end of file") != 0) {
fail("json_loadb returned an invalid error message for an unclosed top-level array");
}
-
- return 0;
}
create_and_free_complex_object();
}
-int main()
+static void run_tests()
{
test_simple();
test_secure_funcs();
-
- return 0;
}
#include <jansson.h>
#include "util.h"
-int main()
+static void run_tests()
{
json_t *integer, *real;
int i;
json_decref(integer);
json_decref(real);
-
- return 0;
}
json_decref(object);
}
-int main()
+static void run_tests()
{
test_misc();
test_clear();
test_set_nocheck();
test_iterators();
test_preserve_order();
-
- return 0;
}
#include <stdio.h>
#include "util.h"
-int main()
+static void run_tests()
{
json_t *value;
int i;
if(json_pack_ex(&error, 0, "{s:s}", "foo", "\xff\xff"))
fail("json_pack failed to catch invalid UTF-8 in a string");
check_error("Invalid UTF-8 string", "<args>", 1, 4, 4);
-
- return 0;
}
#include "util.h"
/* Call the simple functions not covered by other tests of the public API */
-int main()
+static void run_tests()
{
json_t *value;
json_incref(value);
if(value->refcount != (size_t)-1)
fail("refcounting null works incorrectly");
-
- return 0;
}
#include <stdio.h>
#include "util.h"
-int main()
+static void run_tests()
{
json_t *j, *j2;
int i1, i2, i3;
fail("json_unpack nested array with strict validation failed");
check_error("1 array item(s) left unpacked", "<validation>", 1, 5, 5);
json_decref(j);
-
- return 0;
}
#ifndef UTIL_H
#define UTIL_H
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
#include <stdio.h>
#include <stdlib.h>
+#if HAVE_LOCALE_H
+#include <locale.h>
+#endif
+
#include <jansson.h>
#define failhdr fprintf(stderr, "%s:%s:%d: ", __FILE__, __FUNCTION__, __LINE__)
} \
} while(0)
+
+static void run_tests();
+
+int main() {
+#ifdef HAVE_SETLOCALE
+ setlocale(LC_ALL, "");
+#endif
+ run_tests();
+ return 0;
+}
+
#endif