mirror of
https://github.com/json-c/json-c.git
synced 2026-09-07 08:36:50 +08:00
fix locale-dependent strtod in json_object_get_double
This commit is contained in:
+38
-2
@@ -21,6 +21,9 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#ifdef HAVE_LOCALE_H
|
||||||
|
#include <locale.h>
|
||||||
|
#endif /* HAVE_LOCALE_H */
|
||||||
|
|
||||||
#include "arraylist.h"
|
#include "arraylist.h"
|
||||||
#include "debug.h"
|
#include "debug.h"
|
||||||
@@ -1381,12 +1384,42 @@ double json_object_get_double(const struct json_object *jso)
|
|||||||
}
|
}
|
||||||
case json_type_boolean: return JC_BOOL_C(jso)->c_boolean;
|
case json_type_boolean: return JC_BOOL_C(jso)->c_boolean;
|
||||||
case json_type_string:
|
case json_type_string:
|
||||||
|
{
|
||||||
|
const char *cstr = get_string_component(jso);
|
||||||
|
const char *parse = cstr;
|
||||||
|
char *radixconv = NULL;
|
||||||
|
#ifdef HAVE_LOCALE_H
|
||||||
|
/* A json_type_string holds the value with a '.' radix, as it
|
||||||
|
* appears in JSON, but strtod() honours the current locale. In a
|
||||||
|
* locale whose decimal point is not '.' the '.' is treated as a
|
||||||
|
* stray character, so e.g. "19.95" is read as 0. Parse a copy
|
||||||
|
* with the radix translated so the value comes back unchanged. */
|
||||||
|
const char *decimal_point = localeconv()->decimal_point;
|
||||||
|
if (decimal_point && decimal_point[0] != '.' && decimal_point[1] == '\0')
|
||||||
|
{
|
||||||
|
const char *dot = strchr(cstr, '.');
|
||||||
|
if (dot)
|
||||||
|
{
|
||||||
|
size_t slen = strlen(cstr);
|
||||||
|
radixconv = (char *)malloc(slen + 1);
|
||||||
|
if (radixconv == NULL)
|
||||||
|
{
|
||||||
|
errno = ENOMEM;
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
memcpy(radixconv, cstr, slen + 1);
|
||||||
|
radixconv[dot - cstr] = decimal_point[0];
|
||||||
|
parse = radixconv;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
errno = 0;
|
errno = 0;
|
||||||
cdouble = strtod(get_string_component(jso), &errPtr);
|
cdouble = strtod(parse, &errPtr);
|
||||||
|
|
||||||
/* if conversion stopped at the first character, return 0.0 */
|
/* if conversion stopped at the first character, return 0.0 */
|
||||||
if (errPtr == get_string_component(jso))
|
if (errPtr == parse)
|
||||||
{
|
{
|
||||||
|
free(radixconv);
|
||||||
errno = EINVAL;
|
errno = EINVAL;
|
||||||
return 0.0;
|
return 0.0;
|
||||||
}
|
}
|
||||||
@@ -1398,6 +1431,7 @@ double json_object_get_double(const struct json_object *jso)
|
|||||||
*/
|
*/
|
||||||
if (*errPtr != '\0')
|
if (*errPtr != '\0')
|
||||||
{
|
{
|
||||||
|
free(radixconv);
|
||||||
errno = EINVAL;
|
errno = EINVAL;
|
||||||
return 0.0;
|
return 0.0;
|
||||||
}
|
}
|
||||||
@@ -1415,7 +1449,9 @@ double json_object_get_double(const struct json_object *jso)
|
|||||||
*/
|
*/
|
||||||
if ((HUGE_VAL == cdouble || -HUGE_VAL == cdouble) && (ERANGE == errno))
|
if ((HUGE_VAL == cdouble || -HUGE_VAL == cdouble) && (ERANGE == errno))
|
||||||
cdouble = 0.0;
|
cdouble = 0.0;
|
||||||
|
free(radixconv);
|
||||||
return cdouble;
|
return cdouble;
|
||||||
|
}
|
||||||
default: errno = EINVAL; return 0.0;
|
default: errno = EINVAL; return 0.0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,6 +40,18 @@ int main(int argc, char **argv)
|
|||||||
if (strcmp(buf1, buf2) != 0)
|
if (strcmp(buf1, buf2) != 0)
|
||||||
printf("ERROR: Original locale not restored \"%s\" != \"%s\"", buf1, buf2);
|
printf("ERROR: Original locale not restored \"%s\" != \"%s\"", buf1, buf2);
|
||||||
|
|
||||||
|
// json_object_get_double() on a json_type_string must not depend on the
|
||||||
|
// locale's decimal point. Under a comma-radix locale such as de_DE the
|
||||||
|
// '.' used to be treated as a stray char, so "19.95" came back as 0.
|
||||||
|
// (If the de_DE locale isn't installed we're still in "C" here, where
|
||||||
|
// this passes anyway, so the check is safe to always run.)
|
||||||
|
{
|
||||||
|
json_object *numstr = json_object_new_string("19.95");
|
||||||
|
double d = json_object_get_double(numstr);
|
||||||
|
assert(d > 19.94 && d < 19.96);
|
||||||
|
json_object_put(numstr);
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef HAVE_SETLOCALE
|
#ifdef HAVE_SETLOCALE
|
||||||
setlocale(LC_NUMERIC, "C");
|
setlocale(LC_NUMERIC, "C");
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
Reference in New Issue
Block a user