From 670b10ae363d40fbe7dfd3c2e9c3f8044bf630f1 Mon Sep 17 00:00:00 2001 From: Karel Zak Date: Tue, 25 May 2021 11:31:08 +0200 Subject: [PATCH] lib/c_strtod; add locale independent strtod() Signed-off-by: Karel Zak --- configure.ac | 3 ++ include/c_strtod.h | 6 +++ lib/Makemodule.am | 7 ++- lib/c_strtod.c | 105 +++++++++++++++++++++++++++++++++++++++++++++ lib/meson.build | 1 + meson.build | 3 ++ 6 files changed, 124 insertions(+), 1 deletion(-) create mode 100644 include/c_strtod.h create mode 100644 lib/c_strtod.c diff --git a/configure.ac b/configure.ac index 26ec01d767..720e7d0d6d 100644 --- a/configure.ac +++ b/configure.ac @@ -540,6 +540,7 @@ AC_CHECK_FUNCS([ \ jrand48 \ lchown \ llseek \ + newlocale \ mempcpy \ mkostemp \ nanosleep \ @@ -565,10 +566,12 @@ AC_CHECK_FUNCS([ \ strnchr \ strndup \ strnlen \ + strtod_l \ sysconf \ sysinfo \ timegm \ usleep \ + uselocale \ utimensat \ vwarnx \ warn \ diff --git a/include/c_strtod.h b/include/c_strtod.h new file mode 100644 index 0000000000..eaa801c408 --- /dev/null +++ b/include/c_strtod.h @@ -0,0 +1,6 @@ +#ifndef UTIL_LINUX_C_STRTOD_H +#define UTIL_LINUX_C_STRTOD_H + +extern double c_strtod(char const *str, char **end); + +#endif diff --git a/lib/Makemodule.am b/lib/Makemodule.am index 5d95b37eaf..1cdd1b146c 100644 --- a/lib/Makemodule.am +++ b/lib/Makemodule.am @@ -18,6 +18,7 @@ libcommon_la_SOURCES = \ lib/color-names.c \ lib/crc32.c \ lib/crc32c.c \ + lib/c_strtod.c \ lib/encode.c \ lib/env.c \ lib/fileutils.c \ @@ -91,7 +92,8 @@ check_PROGRAMS += \ test_remove_env \ test_strutils \ test_ttyutils \ - test_timeutils + test_timeutils \ + test_c_strtod if LINUX @@ -135,6 +137,9 @@ test_mangle_CFLAGS = $(AM_CFLAGS) -DTEST_PROGRAM_MANGLE test_strutils_SOURCES = lib/strutils.c test_strutils_CFLAGS = $(AM_CFLAGS) -DTEST_PROGRAM_STRUTILS +test_c_strtod_SOURCES = lib/c_strtod.c +test_c_strtod_CFLAGS = $(AM_CFLAGS) -DTEST_PROGRAM + test_colors_SOURCES = lib/colors.c lib/color-names.c test_colors_CFLAGS = $(AM_CFLAGS) -DTEST_PROGRAM_COLORS test_colors_LDADD = $(LDADD) libtcolors.la diff --git a/lib/c_strtod.c b/lib/c_strtod.c new file mode 100644 index 0000000000..21b80fd35d --- /dev/null +++ b/lib/c_strtod.c @@ -0,0 +1,105 @@ +/* + * Locale-independent strtod(). + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + * + * Copyright (C) 2021 Karel Zak + */ +#include "c.h" + +#include +#include +#include + +#include "c_strtod.h" + +#if defined(HAVE_NEWLOCALE) && (defined(HAVE_STRTOD_L) || defined(HAVE_USELOCALE)) +# define USE_CLOCALE +#endif + +#if defined(USE_CLOCALE) +static volatile locale_t c_locale; + +static locale_t get_c_locale(void) +{ + if (!c_locale) + c_locale = newlocale(LC_ALL_MASK, "C", (locale_t) 0); + return c_locale; +} +#endif + + +double c_strtod(char const *str, char **end) +{ + double res; + int errsv; + +#if defined(USE_CLOCALE) + locale_t cl = get_c_locale(); + +#if defined(HAVE_STRTOD_L) + /* + * A) try strtod_l() for "C" locale + */ + if (cl) + return strtod_l(str, end, cl); +#elif defined(HAVE_USELOCALE) + /* + * B) classic strtod(), but switch to "C" locale by uselocal() + */ + if (cl) { + locale_t org_cl = uselocale(locale); + if (!org_cl) + return 0; + + res = strtod(str, end); + errsv = errno; + + uselocale(org_cl); + errno = errsv; + return res; + } +#endif /* HAVE_USELOCALE */ +#endif /* USE_CLOCALE */ + /* + * C) classic strtod(), but switch to "C" locale by setlocale() + */ + char *org_locale = setlocale(LC_NUMERIC, NULL); + + if (org_locale) { + org_locale = strdup(org_locale); + if (!org_locale) + return 0; + + setlocale(LC_NUMERIC, "C"); + } + res = strtod(str, end); + errsv = errno; + + if (org_locale) { + setlocale(LC_NUMERIC, org_locale); + free(org_locale); + } + errno = errsv; + return res; +} + +#ifdef TEST_PROGRAM +int main(int argc, char *argv[]) +{ + double res; + char *end; + + if (argc < 2) { + fprintf(stderr, "usage: %s decimal.number\n", + program_invocation_short_name); + return EXIT_FAILURE; + } + + res = c_strtod(argv[1], &end); + printf("Result: %g, errno: %d, endptr: '%s'\n", res, errno, end); + + return errno ? EXIT_FAILURE : EXIT_SUCCESS; +} +#endif diff --git a/lib/meson.build b/lib/meson.build index af08535d37..dcdbdf07dc 100644 --- a/lib/meson.build +++ b/lib/meson.build @@ -5,6 +5,7 @@ lib_common_sources = ''' color-names.c crc32.c crc32c.c + c_strtod.c encode.c env.c fileutils.c diff --git a/meson.build b/meson.build index 898c55d726..c53fd36eb6 100644 --- a/meson.build +++ b/meson.build @@ -460,6 +460,7 @@ funcs = ''' jrand48 lchown llseek + newlocale mempcpy mkostemp nanosleep @@ -484,6 +485,7 @@ funcs = ''' strnchr strndup strnlen + strtod_l sysconf sysinfo swapon @@ -491,6 +493,7 @@ funcs = ''' timegm unshare usleep + uselocale utimensat vwarnx warn -- 2.47.3