]> git.ipfire.org Git - thirdparty/coreutils.git/commitdiff
maint: distinguish EOVERFLOW vs ERANGE better
authorPaul Eggert <eggert@cs.ucla.edu>
Sat, 10 Aug 2024 20:00:33 +0000 (13:00 -0700)
committerPaul Eggert <eggert@cs.ucla.edu>
Sun, 11 Aug 2024 02:30:01 +0000 (19:30 -0700)
Also, prepare for allowing some arguments to overflow
without that being an error.
* gl/lib/xdectoint.c: Do not include stddef.h,
since we no longer use ‘unreachable’.
(xnumtoimax, xnumtoumax, __xnumtoint):
New arg FLAGS.  All callers changed.
Stop using __xdectoint_signed.  All definers removed.
* gl/lib/xdectoint.h (XTOINT_MIN_QUIET, XTOINT_MAX_QUIET)
(XTOINT_MIN_RANGE, XTOINT_MAX_RANGE): New flag constants.
* src/fmt.c (main):
* src/fold.c (main):
* src/nl.c (main):
* src/pr.c (getoptnum):
* src/split.c (main):
Use XTOINT_MIN_RANGE and XTOINT_MAX_RANGE if appropriate.
* src/pr.c (getoptnum): Return int rather than returning void
and storing through int *.
* src/stty.c (apply_settings):
Use ckd_add to check for overflow instead of doing it by hand.
(integer_arg): Accept and return uintmax_t, not unsigned long.

12 files changed:
gl/lib/xdectoimax.c
gl/lib/xdectoint.c
gl/lib/xdectoint.h
gl/lib/xdectoumax.c
src/fmt.c
src/fold.c
src/ls.c
src/nl.c
src/pr.c
src/shred.c
src/split.c
src/stty.c

index d4bb18fa1393eab9123723bb41fa9528ec390393..4b0a33b752d21c1f16242ec142f003a9a73be250 100644 (file)
@@ -2,5 +2,4 @@
 #define __xnumtoint xnumtoimax
 #define __xdectoint_t intmax_t
 #define __xstrtol xstrtoimax
-#define __xdectoint_signed 1
 #include "xdectoint.c"
index 8a7efbcdaf9592f612a223486832b701c282e2dc..bb38431f3909f8afb191dbdd8de3b444dee7bc8c 100644 (file)
@@ -21,7 +21,6 @@
 
 #include <errno.h>
 #include <inttypes.h>
-#include <stddef.h>
 #include <stdlib.h>
 
 #include <error.h>
 #include <xstrtol.h>
 
 /* Parse numeric string N_STR of base BASE, and return the value.
-   Exit on parse error or if MIN or MAX are exceeded.
+   The value is between MIN and MAX.
    Strings can have multiplicative SUFFIXES if specified.
-   ERR is printed along with N_STR on error.  */
+   On a parse error or out-of-range number, diagnose with N_STR and ERR, and
+   exit with status ERR_EXIT if nonzero, EXIT_FAILURE otherwise.
+   However, if FLAGS & XTOINT_MIN_QUIET, do not diagnose or exit
+   for too-low numbers; return MIN and set errno instead.
+   Similarly for XTOINT_MAX_QUIET and too-high numbers and MAX.
+   The errno value and diagnostic for out-of-range values depend on
+   whether FLAGS & XTOINT_MIN_RANGE and FLAGS & XTOINT_MAX_RANGE are set.  */
 
 __xdectoint_t
 __xnumtoint (char const *n_str, int base, __xdectoint_t min, __xdectoint_t max,
-             char const *suffixes, char const *err, int err_exit)
+             char const *suffixes, char const *err, int err_exit,
+             int flags)
 {
-  strtol_error s_err;
+  __xdectoint_t tnum, r;
+  strtol_error s_err = __xstrtol (n_str, nullptr, base, &tnum, suffixes);
 
-  __xdectoint_t tnum;
-  s_err = __xstrtol (n_str, nullptr, base, &tnum, suffixes);
+  /* Errno value to report if there is an overflow.  */
+  int overflow_errno;
 
-  if (s_err == LONGINT_OK)
+  if (tnum < min)
     {
-      if (tnum < min || max < tnum)
-        {
-          s_err = LONGINT_OVERFLOW;
-          /* Use have the INT range as a heuristic to distinguish
-             type overflow rather than other min/max limits.  */
-          if (tnum > INT_MAX / 2)
-            errno = EOVERFLOW;
-#if __xdectoint_signed
-          else if (tnum < INT_MIN / 2)
-            errno = EOVERFLOW;
-#endif
-          else
-            errno = ERANGE;
-        }
+      r = min;
+      overflow_errno = flags & XTOINT_MIN_RANGE ? ERANGE : EOVERFLOW;
+      if (s_err == LONGINT_OK)
+        s_err = LONGINT_OVERFLOW;
     }
-  else if (s_err == LONGINT_OVERFLOW)
-    errno = EOVERFLOW;
-  else if (s_err == LONGINT_INVALID_SUFFIX_CHAR_WITH_OVERFLOW)
-    errno = 0; /* Don't show ERANGE errors for invalid numbers.  */
-
-  if (s_err != LONGINT_OK)
+  else if (max < tnum)
     {
-      /* EINVAL error message is redundant in this context.  */
-      error (err_exit ? err_exit : EXIT_FAILURE, errno == EINVAL ? 0 : errno,
-             "%s: %s", err, quote (n_str));
-      unreachable ();
+      r = max;
+      overflow_errno = flags & XTOINT_MAX_RANGE ? ERANGE : EOVERFLOW;
+      if (s_err == LONGINT_OK)
+        s_err = LONGINT_OVERFLOW;
     }
+  else
+    {
+      r = tnum;
+      overflow_errno = EOVERFLOW;
+    }
+
+  int e = s_err == LONGINT_OVERFLOW ? overflow_errno : 0;
+
+  if (! (s_err == LONGINT_OK
+         || (s_err == LONGINT_OVERFLOW
+             && flags & (tnum < 0 ? XTOINT_MIN_QUIET : XTOINT_MAX_QUIET))))
+    error (err_exit ? err_exit : EXIT_FAILURE, e, "%s: %s", err, quote (n_str));
 
-  return tnum;
+  errno = e;
+  return r;
 }
 
 /* Parse decimal string N_STR, and return the value.
@@ -84,5 +89,5 @@ __xdectoint_t
 __xdectoint (char const *n_str, __xdectoint_t min, __xdectoint_t max,
              char const *suffixes, char const *err, int err_exit)
 {
-  return __xnumtoint (n_str, 10, min, max, suffixes, err, err_exit);
+  return __xnumtoint (n_str, 10, min, max, suffixes, err, err_exit, 0);
 }
index 4550474b4d53680a07651d7db65deac365fb6596..2c738753af15ac25d749b6dc4688231784da6dec 100644 (file)
    along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
 
 #ifndef XDECTOINT_H_
-# define XDECTOINT_H_ 1
+#define XDECTOINT_H_ 1
 
-# include <inttypes.h>
+#include <inttypes.h>
 
-# define _DECLARE_XDECTOINT(name, type) \
+/* Flags for xnumtoimax and xnumtoumax.  They can be ORed togethar.  */
+enum
+  {
+    /* If the number is less than MIN, do not diagnose the problem;
+       instead, return MIN and set errno to EOVERFLOW or ERANGE.  */
+    XTOINT_MIN_QUIET = 1 << 0,
+
+    /* Likewise for the MAX argument.  */
+    XTOINT_MAX_QUIET = 1 << 1,
+
+    /* The MIN argument is imposed by the caller, not by the type of
+       the result.  This causes the function to use ERANGE rather
+       than EOVERFLOW behavior when issuing diagnostics or setting errno.  */
+    XTOINT_MIN_RANGE = 1 << 2,
+
+    /* Likewise for the MAX argument.  */
+    XTOINT_MAX_RANGE = 1 << 3
+  };
+
+#define _DECLARE_XDECTOINT(name, type) \
   type name (char const *n_str, type min, type max, \
              char const *suffixes, char const *err, int err_exit) \
     _GL_ATTRIBUTE_NONNULL ((1, 5));
-# define _DECLARE_XNUMTOINT(name, type) \
+#define _DECLARE_XNUMTOINT(name, type) \
   type name (char const *n_str, int base, type min, type max, \
-             char const *suffixes, char const *err, int err_exit) \
+             char const *suffixes, char const *err, int err_exit, int flags) \
     _GL_ATTRIBUTE_NONNULL ((1, 6));
 
 _DECLARE_XDECTOINT (xdectoimax, intmax_t)
index 412ddf962dc228eb14f8eaf258d03f7633bcf6f9..4b43fa406ff83edecdb6acd4c1509e7d333dde06 100644 (file)
@@ -2,5 +2,4 @@
 #define __xnumtoint xnumtoumax
 #define __xdectoint_t uintmax_t
 #define __xstrtol xstrtoumax
-#define __xdectoint_signed 0
 #include "xdectoint.c"
index 73ef64ee4022762c764c74b94cb4eb33b8efc066..59086f35e0e3ae9c6a6073f0ee5661a38517b2b2 100644 (file)
--- a/src/fmt.c
+++ b/src/fmt.c
@@ -395,8 +395,8 @@ main (int argc, char **argv)
     {
       /* Limit max_width to MAXCHARS / 2; otherwise, the resulting
          output can be quite ugly.  */
-      max_width = xdectoumax (max_width_option, 0, MAXCHARS / 2, "",
-                              _("invalid width"), 0);
+      max_width = xnumtoumax (max_width_option, 10, 0, MAXCHARS / 2, "",
+                              _("invalid width"), 0, XTOINT_MAX_RANGE);
     }
 
   if (goal_width_option)
index 941ad11c61cb1a5e8e93174d9eb627510941e027..7231e877420abf987bfab9ebbf43652b5480a8fd 100644 (file)
@@ -279,8 +279,9 @@ main (int argc, char **argv)
             }
           FALLTHROUGH;
         case 'w':              /* Line width. */
-          width = xdectoumax (optarg, 1, SIZE_MAX - TAB_WIDTH - 1, "",
-                              _("invalid number of columns"), 0);
+          width = xnumtoumax (optarg, 10, 1, SIZE_MAX - TAB_WIDTH - 1, "",
+                              _("invalid number of columns"), 0,
+                              XTOINT_MIN_RANGE | XTOINT_MAX_RANGE);
           break;
 
         case_GETOPT_HELP_CHAR;
index f7ffe1086ef188afebf403c2634abd9486ead7d2..de4b02a20103ae7b81415023e2c7f289644a1ffe 100644 (file)
--- a/src/ls.c
+++ b/src/ls.c
@@ -2109,7 +2109,7 @@ decode_switches (int argc, char **argv)
 
         case 'T':
           tabsize_opt = xnumtoumax (optarg, 0, 0, MIN (PTRDIFF_MAX, SIZE_MAX),
-                                    "", _("invalid tab size"), LS_FAILURE);
+                                    "", _("invalid tab size"), LS_FAILURE, 0);
           break;
 
         case 'U':
index f77a2aa9e85768381a77b960749814be29d85abe..b30579b4b3b971c8c47d42515a176b081398abd1 100644 (file)
--- a/src/nl.c
+++ b/src/nl.c
@@ -531,15 +531,17 @@ main (int argc, char **argv)
           reset_numbers = false;
           break;
         case 'l':
-          blank_join = xdectoimax (optarg, 1, INTMAX_MAX, "",
-                                   _("invalid line number of blank lines"), 0);
+          blank_join = xnumtoimax (optarg, 10, 1, INTMAX_MAX, "",
+                                   _("invalid line number of blank lines"),
+                                   0, XTOINT_MIN_RANGE);
           break;
         case 's':
           separator_str = optarg;
           break;
         case 'w':
-          lineno_width = xdectoimax (optarg, 1, INT_MAX, "",
-                                     _("invalid line number field width"), 0);
+          lineno_width = xnumtoimax (optarg, 10, 1, INT_MAX, "",
+                                     _("invalid line number field width"),
+                                     0, XTOINT_MIN_RANGE);
           break;
         case 'n':
           if (STREQ (optarg, "ln"))
index 09c6fa8acfbac1a6e9affda05dd55ff6783ea795..88a330bc0c34961298b8ebb04b5c29102c40b6e1 100644 (file)
--- a/src/pr.c
+++ b/src/pr.c
@@ -425,8 +425,7 @@ static bool skip_to_page (uintmax_t page);
 static void print_header (void);
 static void pad_across_to (int position);
 static void add_line_number (COLUMN *p);
-static void getoptnum (char const *n_str, int min, int *num,
-                       char const *errfmt);
+static int getoptnum (char const *n_str, int min, char const *errfmt);
 static void getoptarg (char *arg, char switch_char, char *character,
                        int *number);
 static void print_files (int number_of_files, char **av);
@@ -838,7 +837,7 @@ first_last_page (int oi, char c, char const *pages)
 static void
 parse_column_count (char const *s)
 {
-  getoptnum (s, 1, &columns, _("invalid number of columns"));
+  columns = getoptnum (s, 1, _("invalid number of columns"));
   explicit_columns = true;
 }
 
@@ -975,8 +974,9 @@ main (int argc, char **argv)
           join_lines = true;
           break;
         case 'l':
-          getoptnum (optarg, 1, &lines_per_page,
-                     _("'-l PAGE_LENGTH' invalid number of lines"));
+          lines_per_page
+            = getoptnum (optarg, 1,
+                         _("'-l PAGE_LENGTH' invalid number of lines"));
           break;
         case 'm':
           parallel_files = true;
@@ -990,12 +990,13 @@ main (int argc, char **argv)
           break;
         case 'N':
           skip_count = false;
-          getoptnum (optarg, INT_MIN, &start_line_num,
-                     _("'-N NUMBER' invalid starting line number"));
+          start_line_num
+            = getoptnum (optarg, INT_MIN,
+                         _("'-N NUMBER' invalid starting line number"));
           break;
         case 'o':
-          getoptnum (optarg, 0, &chars_per_margin,
-                     _("'-o MARGIN' invalid line offset"));
+          chars_per_margin = getoptnum (optarg, 0,
+                                        _("'-o MARGIN' invalid line offset"));
           break;
         case 'r':
           ignore_failed_opens = true;
@@ -1030,9 +1031,9 @@ main (int argc, char **argv)
           old_options = true;
           old_w = true;
           {
-            int tmp_cpl;
-            getoptnum (optarg, 1, &tmp_cpl,
-                       _("'-w PAGE_WIDTH' invalid number of characters"));
+            int tmp_cpl
+              = getoptnum (optarg, 1,
+                           _("'-w PAGE_WIDTH' invalid number of characters"));
             if (! truncate_lines)
               chars_per_line = tmp_cpl;
           }
@@ -1040,8 +1041,9 @@ main (int argc, char **argv)
         case 'W':
           old_w = false;                       /* dominates -w */
           truncate_lines = true;
-          getoptnum (optarg, 1, &chars_per_line,
-                     _("'-W PAGE_WIDTH' invalid number of characters"));
+          chars_per_line
+            = getoptnum (optarg, 1,
+                         _("'-W PAGE_WIDTH' invalid number of characters"));
           break;
         case_GETOPT_HELP_CHAR;
         case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
@@ -1151,11 +1153,11 @@ main (int argc, char **argv)
 
 /* Parse numeric arguments, ensuring MIN <= number <= INT_MAX.  */
 
-static void
-getoptnum (char const *n_str, int min, int *num, char const *err)
+static int
+getoptnum (char const *n_str, int min, char const *err)
 {
-  intmax_t tnum = xdectoimax (n_str, min, INT_MAX, "", err, 0);
-  *num = tnum;
+  return xnumtoimax (n_str, 10, min, INT_MAX, "", err, 0,
+                     min <= 0 ? 0 : XTOINT_MIN_RANGE);
 }
 
 /* Parse options of the form -scNNN.
index ecb207a8b42c45cddf0e44e547e45f1db7dc0d65..b7cd289b1ada59170c0adf2fb17cb09357bd0d44 100644 (file)
@@ -1210,7 +1210,7 @@ main (int argc, char **argv)
 
         case 's':
           flags.size = xnumtoumax (optarg, 0, 0, OFF_T_MAX, "cbBkKMGTPEZYRQ0",
-                                   _("invalid file size"), 0);
+                                   _("invalid file size"), 0, 0);
           break;
 
         case 'v':
index f82a7f74b40160c27cbed0a4cc219220d5c5cefb..47abce848a8462fc227a9f44a0dce50e84b6c6ff 100644 (file)
@@ -1536,10 +1536,11 @@ main (int argc, char **argv)
           break;
 
         case IO_BLKSIZE_OPTION:
-          in_blk_size = xdectoumax (optarg, 1,
+          in_blk_size = xnumtoumax (optarg, 10, 1,
                                     MIN (SYS_BUFSIZE_MAX,
                                          MIN (IDX_MAX, SIZE_MAX) - 1),
-                                    multipliers, _("invalid IO block size"), 0);
+                                    multipliers, _("invalid IO block size"),
+                                    0, XTOINT_MIN_RANGE);
           break;
 
         case VERBOSE_OPTION:
index c3cd75cc5c3d6d733b1dc7e471312fa603cfdbdf..95091744aa48eae51f7b19b910ee7cda319e9cdb 100644 (file)
@@ -442,7 +442,7 @@ static int screen_columns (void);
 static bool set_mode (struct mode_info const *info, bool reversed,
                       struct termios *mode);
 static bool eq_mode (struct termios *mode1, struct termios *mode2);
-static unsigned long int integer_arg (char const *s, unsigned long int max);
+static uintmax_t integer_arg (char const *s, uintmax_t max);
 static speed_t string_to_baud (char const *arg);
 static tcflag_t *mode_type_flag (enum mode_type type, struct termios *mode);
 static void display_all (struct termios *mode, char const *device_name);
@@ -1243,12 +1243,11 @@ apply_settings (bool checking, char const *device_name,
 #ifdef HAVE_C_LINE
           else if (STREQ (arg, "line"))
             {
-              unsigned long int value;
               check_argument (arg);
               ++k;
-              mode->c_line = value = integer_arg (settings[k], ULONG_MAX);
-              if (mode->c_line != value)
-                error (0, 0, _("invalid line discipline %s"),
+              uintmax_t value = integer_arg (settings[k], UINTMAX_MAX);
+              if (ckd_add (&mode->c_line, value, 0))
+                error (0, EOVERFLOW, _("invalid line discipline %s"),
                        quote (settings[k]));
               *require_set_attr = true;
             }
@@ -2359,8 +2358,9 @@ visible (cc_t ch)
    but allowing octal and hex numbers as in C.  Reject values
    larger than MAXVAL.  */
 
-static unsigned long int
-integer_arg (char const *s, unsigned long int maxval)
+static uintmax_t
+integer_arg (char const *s, uintmax_t maxval)
 {
-  return xnumtoumax (s, 0, 0, maxval, "bB", _("invalid integer argument"), 0);
+  return xnumtoumax (s, 0, 0, maxval, "bB", _("invalid integer argument"),
+                     0, 0);
 }