From bef1c5cc433b89add2cf0292e1098e1bd74ac640 Mon Sep 17 00:00:00 2001 From: Youngbok Shin Date: Wed, 22 Aug 2018 01:53:11 +0000 Subject: [PATCH] eina: add locale-independent eina_convert_strtod_c function strtod's behavior is changed by system locale. http://man7.org/linux/man-pages/man3/strtod.3.html https://en.wikipedia.org/wiki/Decimal_separator Because of this, strtod(0.5) returns 0.0 in some locales. When a given value string is locale-independent, strtod has to be replaced to eina_convert_strtod_c function. Internally, it calls strtod_l function with "C" locale. @feature Reviewed-by: Cedric BAIL Reviewed-by: Vincent Torri Differential Revision: https://phab.enlightenment.org/D6644 --- src/lib/eina/eina_convert.c | 10 ++++++++++ src/lib/eina/eina_convert.h | 16 ++++++++++++++++ src/lib/eina/eina_main.c | 19 +++++++++++++++++++ src/lib/eina/eina_private.h | 6 ++++++ src/tests/eina/eina_test_convert.c | 21 +++++++++++++++++++++ 5 files changed, 72 insertions(+) diff --git a/src/lib/eina/eina_convert.c b/src/lib/eina/eina_convert.c index 2943b37343..152ef5be4a 100644 --- a/src/lib/eina/eina_convert.c +++ b/src/lib/eina/eina_convert.c @@ -453,6 +453,16 @@ eina_convert_atofp(const char *src, int length, Eina_F32p32 *fp) return EINA_TRUE; } +EAPI double +eina_convert_strtod_c(const char *nptr, char **endptr) +{ +#ifdef _WIN32 + return _strtod_l(nptr, endptr, _eina_c_locale_get()); +#else + return strtod_l(nptr, endptr, _eina_c_locale_get()); +#endif +} + /** * @} */ diff --git a/src/lib/eina/eina_convert.h b/src/lib/eina/eina_convert.h index 026fc6c8f6..337cd88ad8 100644 --- a/src/lib/eina/eina_convert.h +++ b/src/lib/eina/eina_convert.h @@ -333,6 +333,22 @@ EAPI Eina_Bool eina_convert_atofp(const char *src, int length, Eina_F32p32 *fp) EINA_ARG_NONNULL(1, 3); +/** + * @brief Converts a string to a floating point number. + * + * @param[in] nptr a string to convert. It shouldn't be NULL. + * @param[out] endptr If endptr is not NULL, a pointer to the character after the last + * character used in the conversion is stored in the location referenced + * by endptr. + * @return a double type floating point number. + * + * This function returns converted floating point number with locale-independency. + * Actually, it use "C" locale for strtod_l function internally. If you need strtod + * without locale-dependency, this function can replace strtod. + * For more information, please refer documents of strtod, strtod_l. + */ +EAPI double eina_convert_strtod_c(const char *nptr, char **endptr); + /** * @} */ diff --git a/src/lib/eina/eina_main.c b/src/lib/eina/eina_main.c index 024d1e3eaa..535e4e7611 100644 --- a/src/lib/eina/eina_main.c +++ b/src/lib/eina/eina_main.c @@ -85,6 +85,7 @@ static int _eina_main_count = 0; static int _eina_main_thread_count = 0; #endif static int _eina_log_dom = -1; +static locale_t _eina_c_locale; #ifdef ERR #undef ERR @@ -285,6 +286,12 @@ eina_init(void) if (EINA_LIKELY(_eina_main_count > 0)) return ++_eina_main_count; +#ifdef _WIN32 + _eina_c_locale = _create_locale(LC_ALL, "C"); +#else + _eina_c_locale = newlocale(LC_ALL_MASK, "C", NULL); +#endif + srand(time(NULL)); while (eina_seed == 0) eina_seed = rand(); @@ -348,6 +355,12 @@ eina_init(void) return 1; } +locale_t +_eina_c_locale_get(void) +{ + return _eina_c_locale; +} + EAPI int eina_shutdown(void) { @@ -359,6 +372,12 @@ eina_shutdown(void) _eina_main_count--; if (EINA_UNLIKELY(_eina_main_count == 0)) { +#ifdef _WIN32 + _free_locale(_eina_c_locale); +#else + freelocale(_eina_c_locale); +#endif + eina_log_timing(_eina_log_dom, EINA_LOG_STATE_START, EINA_LOG_STATE_SHUTDOWN); diff --git a/src/lib/eina/eina_private.h b/src/lib/eina/eina_private.h index eab59586c1..be826e0fb0 100644 --- a/src/lib/eina/eina_private.h +++ b/src/lib/eina/eina_private.h @@ -20,6 +20,7 @@ #define EINA_PRIVATE_H_ #include +#include #ifdef _WIN32 # include @@ -151,6 +152,11 @@ Eina_Stringshare *eina_file_sanitize(const char *path); void eina_freeq_main_set(Eina_FreeQ *fq); +#ifdef _WIN32 +typedef _locale_t locale_t; +#endif +locale_t _eina_c_locale_get(void); + #include "eina_inline_private.h" #endif /* EINA_PRIVATE_H_ */ diff --git a/src/tests/eina/eina_test_convert.c b/src/tests/eina/eina_test_convert.c index 1b08a00a19..c8ccfcb772 100644 --- a/src/tests/eina/eina_test_convert.c +++ b/src/tests/eina/eina_test_convert.c @@ -160,10 +160,31 @@ _eina_convert_fp_check(double d, Eina_F32p32 fp, int length) } EFL_END_TEST +static void +_eina_convert_strtod_c_check(const char *str, double expected_result) +{ + double result = eina_convert_strtod_c(str, NULL); + + fail_if(result != expected_result); +} + +EFL_START_TEST(eina_convert_strtod_c_simple) +{ + _eina_convert_strtod_c_check("0.0", 0.0); + _eina_convert_strtod_c_check("0.5", 0.5); + _eina_convert_strtod_c_check("1.0", 1.0); + _eina_convert_strtod_c_check("-0.5", -0.5); + _eina_convert_strtod_c_check("-1.0", -1.0); + _eina_convert_strtod_c_check("3.45e-2", 0.0345); + _eina_convert_strtod_c_check("3.45e+2", 345.0); +} +EFL_END_TEST + void eina_test_convert(TCase *tc) { tcase_add_test(tc, eina_convert_simple); tcase_add_test(tc, eina_convert_double); tcase_add_test(tc, eina_convert_fp); + tcase_add_test(tc, eina_convert_strtod_c_simple); }