]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
lib/c_strtod; add locale independent strtod()
authorKarel Zak <kzak@redhat.com>
Tue, 25 May 2021 09:31:08 +0000 (11:31 +0200)
committerKarel Zak <kzak@redhat.com>
Tue, 25 May 2021 09:31:08 +0000 (11:31 +0200)
Signed-off-by: Karel Zak <kzak@redhat.com>
configure.ac
include/c_strtod.h [new file with mode: 0644]
lib/Makemodule.am
lib/c_strtod.c [new file with mode: 0644]
lib/meson.build
meson.build

index 26ec01d767dfb5493284e5fa7cd82139fff626d8..720e7d0d6d3671f6d3ef5cdb5a9fded5462f03e4 100644 (file)
@@ -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 (file)
index 0000000..eaa801c
--- /dev/null
@@ -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
index 5d95b37eaf9a01b529e89deee3c9515caef61fb5..1cdd1b146c6fe7c0062ae43f5369c83622b67ebb 100644 (file)
@@ -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 (file)
index 0000000..21b80fd
--- /dev/null
@@ -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 <kzak@redhat.com>
+ */
+#include "c.h"
+
+#include <locale.h>
+#include <stdlib.h>
+#include <string.h>
+
+#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
index af08535d372e7050502d21c7c5be533f5d7fcccc..dcdbdf07dca15251c77a3f7b5bd4b3c22583d844 100644 (file)
@@ -5,6 +5,7 @@ lib_common_sources = '''
        color-names.c
        crc32.c
        crc32c.c
+       c_strtod.c
        encode.c
        env.c
        fileutils.c
index 898c55d7269104ac06068909c7e2842acefd5f81..c53fd36eb6a09106113203b555968884d6ae019a 100644 (file)
@@ -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