[Top][All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH] printf,seq,sleep,tail,timeout: accept current-locale floats

From: Paul Eggert
Subject: [PATCH] printf,seq,sleep,tail,timeout: accept current-locale floats
Date: Sat, 26 Jan 2019 22:41:28 -0800

These commands now accept floating-point numbers in the
current locale, as well as in the C locale.
Compatibility problem reported by Robert Elz.
* NEWS: Document this.
* bootstrap.conf (gnulib_modules): Add cl-strtod, cl-strtold.
Remove c-strtold.
* doc/coreutils.texi (Floating point, tail invocation)
(printf invocation, timeout invocation, sleep invocation)
(seq invocation): Document this.
* gl/lib/cl-strtod.c, gl/lib/cl-strtod.h, gl/lib/cl-strtold.c:
* gl/modules/cl-strtod, gl/modules/cl-strtold: New files.
* src/printf.c, src/seq.c, src/sleep.c, src/tail.c, src/timeout.c:
Include cl-strtod.h instead of c-strtod.
* src/printf.c (vstrtold):
* src/seq.c (scan_arg, print_numbers):
* src/sleep.c (main):
* src/tail.c (parse_options):
* src/timeout.c (parse_duration):
Use cl_strtold instead of c_strtold.
 NEWS                  |  7 ++++
 bootstrap.conf        |  3 +-
 doc/coreutils.texi    | 34 ++++++++++++--------
 gl/lib/cl-strtod.c    | 74 +++++++++++++++++++++++++++++++++++++++++++
 gl/lib/cl-strtod.h    |  2 ++
 gl/lib/cl-strtold.c   |  2 ++
 gl/modules/cl-strtod  | 24 ++++++++++++++
 gl/modules/cl-strtold | 23 ++++++++++++++
 src/printf.c          |  4 +--
 src/seq.c             |  6 ++--
 src/sleep.c           |  4 +--
 src/tail.c            |  4 +--
 src/timeout.c         |  6 ++--
 13 files changed, 166 insertions(+), 27 deletions(-)
 create mode 100644 gl/lib/cl-strtod.c
 create mode 100644 gl/lib/cl-strtod.h
 create mode 100644 gl/lib/cl-strtold.c
 create mode 100644 gl/modules/cl-strtod
 create mode 100644 gl/modules/cl-strtold

diff --git a/NEWS b/NEWS
index a6a02d8e7..4b6b8bff8 100644
--- a/NEWS
+++ b/NEWS
@@ -53,6 +53,13 @@ GNU coreutils NEWS                                    -*- 
outline -*-
   id now supports specifying multiple users.
+  printf, seq, sleep, tail, and timeout now accept floating point
+  numbers in either the current or the C locale.  For example, if the
+  current locale's decimal point is ',', 'sleep 0,1' and 'sleep 0.1'
+  now mean the same thing.  Previously, these commands accepted only
+  C-locale syntax with '.' as the decimal point.  The new behavior is
+  more compatible with other implementations in non-C locales.
   test now supports the '-N FILE' unary operator (like e.g. bash) to check
   whether FILE exists and has been modified since it was last read.
diff --git a/bootstrap.conf b/bootstrap.conf
index 37ed49a66..02e70b379 100644
--- a/bootstrap.conf
+++ b/bootstrap.conf
@@ -41,7 +41,8 @@ gnulib_modules="
-  c-strtold
+  cl-strtod
+  cl-strtold
diff --git a/doc/coreutils.texi b/doc/coreutils.texi
index fa82d6546..be35de490 100644
--- a/doc/coreutils.texi
+++ b/doc/coreutils.texi
@@ -1088,7 +1088,6 @@ information, please see David Goldberg's paper
 What Every Computer Scientist Should Know About Floating-Point Arithmetic}.
address@hidden LC_NUMERIC
 Commands that accept floating point numbers as options, operands or
 input use the standard C functions @code{strtod} and @code{strtold} to
 convert from text to floating point numbers.  These floating point
@@ -1098,10 +1097,16 @@ case-insensitive @code{inf}, @code{infinity}, and 
@code{NaN}, although
 whether such values are useful depends on the command in question.
 Modern C implementations also accept hexadecimal floating point
 numbers such as @code{-0x.ep-3}, which stands for @minus{}14/16 times
address@hidden, which equals @minus{}0.109375.  The @env{LC_NUMERIC}
-locale determines the decimal-point character.  @xref{Parsing of
address@hidden, which equals @minus{}0.109375.  @xref{Parsing of
 Floats,,, libc, The GNU C Library Reference Manual}.
address@hidden LC_NUMERIC
+Normally the @env{LC_NUMERIC} locale determines the decimal-point
+character.  However, some commands' descriptions specify that they
+accept numbers in either the current or the C locale; for example,
+they treat @samp{3.14} like @samp{3,14} if the current locale uses
+comma as a decimal point.
 @node Signal specifications
 @section Signal specifications
 @cindex signals, specifying
@@ -3187,13 +3192,12 @@ never checks it again.
 Change the number of seconds to wait between iterations (the default is 1.0).
 During one iteration, every specified file is checked to see if it has
 changed size.
-Historical implementations of @command{tail} have required that
address@hidden be an integer.  However, GNU @command{tail} accepts
-an arbitrary floating point number.  @xref{Floating point}.
 When @command{tail} uses inotify, this polling-related option
 is usually ignored.  However, if you also specify @address@hidden,
 @command{tail} checks whether process @var{p} is alive at least
 every @var{number} seconds.
+The @var{number} must be non-negative and can be a floating-point number
+in either the current or the C locale.  @xref{Floating point}.
 @item -v
 @itemx --verbose
@@ -12829,11 +12833,11 @@ warning is printed.  For example, @samp{printf "%d" 
"'a"} outputs
 @end itemize
 @vindex LC_NUMERIC
-A floating-point argument must use a period before any fractional
-digits, but is printed according to the @env{LC_NUMERIC} category of the
-current locale.  For example, in a locale whose radix character is a
-comma, the command @samp{printf %g 3.14} outputs @samp{3,14} whereas
-the command @samp{printf %g 3,14} is an error.
+A floating point argument is interpreted according to
+the @env{LC_NUMERIC} category of either the current or the C locale,
+and is printed according to the current locale.
+For example, in a locale whose decimal point character is a comma,
+the command @samp{printf '%g %g' 2,5 2.5} outputs @samp{2,5 2,5}.
 @xref{Floating point}.
 @kindex address@hidden
@@ -17985,7 +17989,8 @@ Diagnose to stderr, any signal sent upon timeout.
 @end table
 @cindex time units
address@hidden is a floating point number followed by an optional unit:
address@hidden is a floating point number in either the current or the
+C locale (@pxref{Floating point}) followed by an optional unit:
 @samp{s} for seconds (the default)
 @samp{m} for minutes
@@ -18138,7 +18143,7 @@ days
 Although portable POSIX scripts must give @command{sleep} a single
 non-negative integer argument without a suffix, GNU @command{sleep}
 also accepts two or more arguments, unit suffixes, and floating-point
-numbers.  @xref{Floating point}.
+numbers in either the current or the C locale.  @xref{Floating point}.
 The only options are @option{--help} and @option{--version}.  @xref{Common
@@ -18587,7 +18592,8 @@ so @code{seq 1 10 10} only produces @samp{1}.
 @var{increment} must not be @samp{0}; use @command{yes} to get
 repeated output of a constant number.
 @var{first}, @var{increment} and @var{last} must not be @code{NaN}.
-Floating-point numbers may be specified.  @xref{Floating point}.
+Floating-point numbers may be specified in either the current or
+the C locale.  @xref{Floating point}.
 The program accepts the following options.  Also see @ref{Common options}.
 Options must precede operands.
diff --git a/gl/lib/cl-strtod.c b/gl/lib/cl-strtod.c
new file mode 100644
index 000000000..fa77235ba
--- /dev/null
+++ b/gl/lib/cl-strtod.c
@@ -0,0 +1,74 @@
+/* Convert string to double in the current locale, falling back on the C 
+   Copyright 2019 Free Software Foundation, Inc.
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   GNU General Public License for more details.
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <>.  */
+/* Written by Paul Eggert.  */
+#include <config.h>
+#include "cl-strtod.h"
+#include <c-strtod.h>
+#include <errno.h>
+#include <stdlib.h>
+#if LONG
+# define CL_STRTOD cl_strtold
+# define DOUBLE long double
+# define C_STRTOD c_strtold
+# define CL_STRTOD cl_strtod
+# define DOUBLE double
+# define C_STRTOD c_strtod
+/* This function acts like strtod or strtold, except that it falls
+   back on the C locale if the initial prefix is not parsable in
+   the current locale.  If the prefix is parsable in both locales,
+   it uses the longer parse, breaking ties in favor of the current locale.
+   Parse the initial prefix of NPTR as a floating-point number in the
+   current locale or in the C locale (preferring the locale that
+   yields the longer parse, or the current locale if there is a tie).
+   If ENDPTR is not NULL, set *ENDPTR to the first unused byte, or to
+   NPTR if the prefix cannot be parsed.
+   If successful, return a number without changing errno.
+   If the prefix cannot be parsed, return 0 and possibly set errno to EINVAL.
+   If the number overflows, return an extreme value and set errno to ERANGE.
+   If the number underflows, return a value close to 0 and set errno to ERANGE.
+   If there is some other error, return 0 and set errno.  */
+CL_STRTOD (char const *nptr, char **restrict endptr)
+  char *end;
+  DOUBLE d = strtod (nptr, &end);
+  if (*end)
+    {
+      int strtod_errno = errno;
+      char *c_end;
+      DOUBLE c = C_STRTOD (nptr, &c_end);
+      if (end < c_end)
+        d = c, end = c_end;
+      else
+        errno = strtod_errno;
+    }
+  if (endptr)
+    *endptr = end;
+  return d;
diff --git a/gl/lib/cl-strtod.h b/gl/lib/cl-strtod.h
new file mode 100644
index 000000000..51becd3cd
--- /dev/null
+++ b/gl/lib/cl-strtod.h
@@ -0,0 +1,2 @@
+double cl_strtod (char const *, char **restrict);
+long double cl_strtold (char const *, char **restrict);
diff --git a/gl/lib/cl-strtold.c b/gl/lib/cl-strtold.c
new file mode 100644
index 000000000..95ae46dca
--- /dev/null
+++ b/gl/lib/cl-strtold.c
@@ -0,0 +1,2 @@
+#define LONG 1
+#include "cl-strtod.c"
diff --git a/gl/modules/cl-strtod b/gl/modules/cl-strtod
new file mode 100644
index 000000000..a2b43c11f
--- /dev/null
+++ b/gl/modules/cl-strtod
@@ -0,0 +1,24 @@
+Convert string to double in current or C locale.
+lib_SOURCES += cl-strtod.c
diff --git a/gl/modules/cl-strtold b/gl/modules/cl-strtold
new file mode 100644
index 000000000..61cc43820
--- /dev/null
+++ b/gl/modules/cl-strtold
@@ -0,0 +1,23 @@
+Convert string to long double in current or C locale.
+lib_SOURCES += cl-strtold.c
diff --git a/src/printf.c b/src/printf.c
index 28e058f50..3260868cb 100644
--- a/src/printf.c
+++ b/src/printf.c
@@ -55,7 +55,7 @@
 #include <sys/types.h>
 #include "system.h"
-#include "c-strtod.h"
+#include "cl-strtod.h"
 #include "die.h"
 #include "error.h"
 #include "quote.h"
@@ -188,7 +188,7 @@ FUNC_NAME (char const *s)                                   
 STRTOX (intmax_t,    vstrtoimax, strtoimax (s, &end, 0))
 STRTOX (uintmax_t,   vstrtoumax, strtoumax (s, &end, 0))
-STRTOX (long double, vstrtold,   c_strtold (s, &end))
+STRTOX (long double, vstrtold,   cl_strtold (s, &end))
 /* Output a single-character \ escape.  */
diff --git a/src/seq.c b/src/seq.c
index 21c32096b..61d20fe62 100644
--- a/src/seq.c
+++ b/src/seq.c
@@ -23,7 +23,7 @@
 #include "system.h"
 #include "die.h"
-#include "c-strtod.h"
+#include "cl-strtod.h"
 #include "error.h"
 #include "quote.h"
 #include "xstrtod.h"
@@ -142,7 +142,7 @@ scan_arg (const char *arg)
   operand ret;
-  if (! xstrtold (arg, NULL, &ret.value, c_strtold))
+  if (! xstrtold (arg, NULL, &ret.value, cl_strtold))
       error (0, 0, _("invalid floating point argument: %s"), quote (arg));
       usage (EXIT_FAILURE);
@@ -331,7 +331,7 @@ print_numbers (char const *fmt, struct layout layout,
                 xalloc_die ();
               x_str[x_strlen - layout.suffix_len] = '\0';
-              if (xstrtold (x_str + layout.prefix_len, NULL, &x_val, c_strtold)
+              if (xstrtold (x_str + layout.prefix_len, NULL, &x_val, 
                   && x_val == last)
                   char *x0_str = NULL;
diff --git a/src/sleep.c b/src/sleep.c
index 23a941636..a634f1ae6 100644
--- a/src/sleep.c
+++ b/src/sleep.c
@@ -20,7 +20,7 @@
 #include <getopt.h>
 #include "system.h"
-#include "c-strtod.h"
+#include "cl-strtod.h"
 #include "die.h"
 #include "error.h"
 #include "long-options.h"
@@ -128,7 +128,7 @@ main (int argc, char **argv)
       double s;
       const char *p;
-      if (! (xstrtod (argv[i], &p, &s, c_strtod) || errno == ERANGE)
+      if (! (xstrtod (argv[i], &p, &s, cl_strtod) || errno == ERANGE)
           /* Nonnegative interval.  */
           || ! (0 <= s)
           /* No extra chars after the number and an optional s,m,h,d char.  */
diff --git a/src/tail.c b/src/tail.c
index b8064853f..dee827bc8 100644
--- a/src/tail.c
+++ b/src/tail.c
@@ -36,7 +36,7 @@
 #include "system.h"
 #include "argmatch.h"
-#include "c-strtod.h"
+#include "cl-strtod.h"
 #include "die.h"
 #include "error.h"
 #include "fcntl--.h"
@@ -2244,7 +2244,7 @@ parse_options (int argc, char **argv,
         case 's':
             double s;
-            if (! (xstrtod (optarg, NULL, &s, c_strtod) && 0 <= s))
+            if (! (xstrtod (optarg, NULL, &s, cl_strtod) && 0 <= s))
               die (EXIT_FAILURE, 0,
                    _("invalid number of seconds: %s"), quote (optarg));
             *sleep_interval = s;
diff --git a/src/timeout.c b/src/timeout.c
index 560af1a7c..748832f8a 100644
--- a/src/timeout.c
+++ b/src/timeout.c
@@ -55,7 +55,7 @@
 #include <sys/wait.h>
 #include "system.h"
-#include "c-strtod.h"
+#include "cl-strtod.h"
 #include "xstrtod.h"
 #include "sig2str.h"
 #include "operand2sig.h"
@@ -316,12 +316,12 @@ apply_time_suffix (double *x, char suffix_char)
 static double
-parse_duration (const char* str)
+parse_duration (const char *str)
   double duration;
   const char *ep;
-  if (! (xstrtod (str, &ep, &duration, c_strtod) || errno == ERANGE)
+  if (! (xstrtod (str, &ep, &duration, cl_strtod) || errno == ERANGE)
       /* Nonnegative interval.  */
       || ! (0 <= duration)
       /* No extra chars after the number and an optional s,m,h,d char.  */

reply via email to

[Prev in Thread] Current Thread [Next in Thread]