From 6b1801063b7078c8d9357f0ac8447202f3335f1b Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Mon, 29 Aug 2016 20:11:40 -0700 Subject: [PATCH] fix raw_string_to_scalar_type locale handling Fixes #3334 --- src/builtin_printf.cpp | 17 +++++++++++++---- tests/printf.err | 1 + tests/printf.in | 9 ++++++++- tests/printf.out | 3 +++ 4 files changed, 25 insertions(+), 5 deletions(-) diff --git a/src/builtin_printf.cpp b/src/builtin_printf.cpp index 9f3ea7bc5..35e5b18d4 100644 --- a/src/builtin_printf.cpp +++ b/src/builtin_printf.cpp @@ -264,11 +264,20 @@ uintmax_t raw_string_to_scalar_type(const wchar_t *s, wchar_t **end) { template <> long double raw_string_to_scalar_type(const wchar_t *s, wchar_t **end) { - // Forcing the locale to C is questionable but it's what the old C_STRTOD() that I inlined here - // as part of changing how locale management is done by fish. - char *old_locale = setlocale(LC_NUMERIC, "C"); double val = wcstod(s, end); - setlocale(LC_NUMERIC, old_locale); + if (**end == L'\0') return val; + + // The conversion using the user's locale failed. That may be due to the string not being a + // valid floating point value. It could also be due to the locale using different separator + // characters than the normal english convention. So try again by forcing the use of a locale + // that employs the english convention for writing floating point numbers. + // + // TODO: switch to the wcstod_l() function to avoid changing the global locale. + char *saved_locale = strdup(setlocale(LC_NUMERIC, NULL)); + setlocale(LC_NUMERIC, "C"); + val = wcstod(s, end); + setlocale(LC_NUMERIC, saved_locale); + free(saved_locale); return val; } diff --git a/tests/printf.err b/tests/printf.err index e69de29bb..8ba50d3ff 100644 --- a/tests/printf.err +++ b/tests/printf.err @@ -0,0 +1 @@ +2,34: value not completely converted diff --git a/tests/printf.in b/tests/printf.in index 78c0d9771..01f8b2645 100644 --- a/tests/printf.in +++ b/tests/printf.in @@ -34,4 +34,11 @@ printf "%5" 10 ^ /dev/null # \376 is 0xFE printf '\376' | xxd -p -true +# Verify that floating point conversions and output work correctly with +# different combinations of locales and floating point strings. See issue +# #3334. This starts by assuming an locale using english conventions. +printf '%e\n' "1.23" # should succeed, output should be 1.230000e+00 +printf '%e\n' "2,34" # should fail +set -x LC_NUMERIC fr_FR.UTF-8 +printf '%e\n' "3,45" # should succeed, output should be 3,450000e+00 +printf '%e\n' "4.56" # should succeed, output should be 4,560000e+00 diff --git a/tests/printf.out b/tests/printf.out index f1c1960ba..5349b0fbf 100644 --- a/tests/printf.out +++ b/tests/printf.out @@ -14,3 +14,6 @@ I P Q R Test escapes a fe +1.230000e+00 +3,450000e+00 +4,560000e+00