]> git.ipfire.org Git - thirdparty/tar.git/commitdiff
Prefer stoint to strtoul and variants
authorPaul Eggert <eggert@cs.ucla.edu>
Thu, 8 Aug 2024 23:32:49 +0000 (16:32 -0700)
committerPaul Eggert <eggert@cs.ucla.edu>
Thu, 15 Aug 2024 06:25:46 +0000 (23:25 -0700)
When parsing numbers prefer using strtosysint (renamed stoint)
to using strtoul and its variants.
This is simpler and faster and likely more reliable than
relying on quirks of the system strtoul etc,
and it standardizes how tar deals with parsing integers.
Among other things, the C standard and POSIX don’t specify
what strtol does to errno when conversions cannot be performed,
and it requires strtoul to support "-" before unsigned numbers.
* gnulib.modules (strtoimax, strtol, strtoumax, xstrtoimax):
Remove.
* src/checkpoint.c (checkpoint_compile_action, getwidth)
(format_checkpoint_string):
* src/incremen.c (read_incr_db_01, read_num)
* src/map.c (parse_id):
* src/misc.c (decode_timespec):
* src/sparse.c (decode_num):
* src/tar.c (parse_owner_group, parse_opt):
* src/transform.c (parse_transform_expr):
* src/xheader.c (decode_record, decode_signed_num)
(sparse_map_decoder):
Prefer stoint to strtol etc.
Don’t rely on errno == EINVAL as the standards don’t guarantee it.
* src/checkpoint.c (getwidth, format_checkpoint_string):
Check for invalid string suffix.
* src/checkpoint.c (getwidth):
Return intmax_t, not long.  All callers changed.
* src/incremen.c (read_directory_file):
It’s just a one-digit number, so just subtract '0'.
* src/map.c (parse_id): Return bool not int.  All callers changed.
* src/misc.c (stoint): Rename from strtosysint, and add
a bool * argument for reporting overflow.  All callers changed.
(decode_timespec): Simplify by using ckd_sub rather than
checking for overflow by hand.
* src/tar.c (incremental_level): Now signed char to
emphasize that it can be only -1, 0, 1.  All uses changed.
* src/xheader.c (decode_record): Avoid giant diagnostics.

gnulib.modules
src/checkpoint.c
src/common.h
src/incremen.c
src/map.c
src/misc.c
src/sparse.c
src/tar.c
src/transform.c
src/xheader.c

index dc826b437bc83d8c06ae4c8b9ca41d35f8accd2e..bd3b6f5a3a333cbcd163807a20c2b746bd2e06ca 100644 (file)
@@ -106,9 +106,6 @@ stdopen
 strdup-posix
 strerror
 strnlen
-strtoimax
-strtol
-strtoumax
 symlinkat
 sys_stat
 timespec
@@ -122,7 +119,6 @@ xalignalloc
 xalloc
 xalloc-die
 xgetcwd
-xstrtoimax
 xstrtoumax
 xvasprintf
 year2038-recommended
index 28e4c22a92a5e56ba98c39c7925246d582ea7fc0..5f82ef20d323a5cb41a17609773987a40c8588dc 100644 (file)
@@ -127,13 +127,12 @@ checkpoint_compile_action (const char *str)
     }
   else if (strncmp (str, "sleep=", 6) == 0)
     {
+      char const *arg = str + 6;
       char *p;
-      intmax_t sleepsec = strtoimax (str + 6, &p, 10);
-      if (*p || sleepsec < 0)
-       FATAL_ERROR ((0, 0, _("%s: not a valid timeout"), str));
-      time_t n = ckd_add (&n, sleepsec, 0) ? TYPE_MAXIMUM (time_t) : n;
       act = alloc_action (cop_sleep);
-      act->v.time = n;
+      act->v.time = stoint (arg, &p, NULL, 0, TYPE_MAXIMUM (time_t));
+      if ((p == arg) | *p)
+       FATAL_ERROR ((0, 0, _("%s: not a valid timeout"), str));
     }
   else if (strcmp (str, "totals") == 0)
     alloc_action (cop_totals);
@@ -176,7 +175,7 @@ static const char *checkpoint_total_format[] = {
   "D"
 };
 
-static long
+static intmax_t
 getwidth (FILE *fp)
 {
   char const *columns;
@@ -190,8 +189,9 @@ getwidth (FILE *fp)
   columns = getenv ("COLUMNS");
   if (columns)
     {
-      long int col = strtol (columns, NULL, 10);
-      if (0 < col)
+      char *end;
+      intmax_t col = stoint (columns, &end, NULL, 0, INTMAX_MAX);
+      if (! (*end | !col))
        return col;
     }
 
@@ -337,7 +337,16 @@ format_checkpoint_string (FILE *fp, size_t len,
 
            case '*':
              {
-               long w = arg ? strtol (arg, NULL, 10) : getwidth (fp);
+               intmax_t w;
+               if (!arg)
+                 w = getwidth (fp);
+               else
+                 {
+                   char *end;
+                   w = stoint (arg, &end, NULL, 0, INTMAX_MAX);
+                   if ((end == arg) | *end)
+                     w = 80;
+                 }
                for (; w > len; len++)
                  fputc (' ', fp);
              }
index 0c1b139e9414b89d3616d3e2bad02922dbc1f0b1..bfabfe9853354bbca18fa444d284f21555463d5d 100644 (file)
@@ -190,8 +190,8 @@ extern bool keep_directory_symlink_option;
 
 /* Specified file name for incremental list.  */
 extern const char *listed_incremental_option;
-/* Incremental dump level */
-extern int incremental_level;
+/* Incremental dump level: either -1, 0, or 1.  */
+extern signed char incremental_level;
 /* Check device numbers when doing incremental dumps. */
 extern bool check_device_option;
 
@@ -701,7 +701,7 @@ enum { UINTMAX_STRSIZE_BOUND = INT_BUFSIZE_BOUND (intmax_t) };
 enum { SYSINT_BUFSIZE =
         max (UINTMAX_STRSIZE_BOUND, INT_BUFSIZE_BOUND (intmax_t)) };
 char *sysinttostr (uintmax_t, intmax_t, uintmax_t, char buf[SYSINT_BUFSIZE]);
-intmax_t strtosysint (char const *, char **, intmax_t, uintmax_t);
+intmax_t stoint (char const *, char **, bool *, intmax_t, uintmax_t);
 char *timetostr (time_t, char buf[SYSINT_BUFSIZE]);
 void code_ns_fraction (int ns, char *p);
 enum { BILLION = 1000000000, LOG10_BILLION = 9 };
index 5d14e5b876da20c8e0acfcb71b51a51fd4689406..6e6b14670b1438b524dca0370610c03ecc494d08 100644 (file)
@@ -976,7 +976,6 @@ static void
 read_incr_db_01 (int version, const char *initbuf)
 {
   int n;
-  uintmax_t u;
   char *buf = NULL;
   size_t bufsize = 0;
   char *ebuf;
@@ -1010,21 +1009,18 @@ read_incr_db_01 (int version, const char *initbuf)
       if (version == 1 && *ebuf)
        {
          char const *buf_ns = ebuf + 1;
-         errno = 0;
-         u = strtoumax (buf_ns, &ebuf, 10);
-         if (!errno && BILLION <= u)
-           errno = ERANGE;
-         if (errno || buf_ns == ebuf)
+         bool overflow;
+         newer_mtime_option.tv_nsec
+           = stoint (buf_ns, &ebuf, &overflow, 0, BILLION - 1);
+         if ((ebuf == buf_ns) | *ebuf | overflow)
            {
-             ERROR ((0, errno, "%s:%ld: %s",
+             ERROR ((0, 0, "%s:%ld: %s",
                      quotearg_colon (listed_incremental_option),
                      lineno,
                      _("Invalid time stamp")));
              newer_mtime_option.tv_sec = TYPE_MINIMUM (time_t);
              newer_mtime_option.tv_nsec = -1;
            }
-         else
-           newer_mtime_option.tv_nsec = u;
        }
     }
 
@@ -1050,41 +1046,37 @@ read_incr_db_01 (int version, const char *initbuf)
                          quotearg_colon (listed_incremental_option), lineno,
                          _("Invalid modification time")));
 
-         errno = 0;
-         u = strtoumax (strp, &ebuf, 10);
-         if (!errno && BILLION <= u)
-           errno = ERANGE;
-         if (errno || strp == ebuf || *ebuf != ' ')
+         bool overflow;
+         mtime.tv_nsec = stoint (strp, &ebuf, &overflow, 0, BILLION - 1);
+         if ((ebuf == strp) | (*ebuf != ' ') | overflow)
            {
-             FATAL_ERROR ((0, errno, "%s:%ld: %s",
+             FATAL_ERROR ((0, 0, "%s:%ld: %s",
                            quotearg_colon (listed_incremental_option), lineno,
                            _("Invalid modification time (nanoseconds)")));
              mtime.tv_nsec = -1;
            }
-         else
-           mtime.tv_nsec = u;
          strp = ebuf;
        }
       else
        mtime.tv_sec = mtime.tv_nsec = 0;
 
-      dev = strtosysint (strp, &ebuf,
-                        TYPE_MINIMUM (dev_t), TYPE_MAXIMUM (dev_t));
-      strp = ebuf;
-      if (errno || *strp != ' ')
-       FATAL_ERROR ((0, errno, "%s:%ld: %s",
+      bool overflow;
+      dev = stoint (strp, &ebuf, &overflow,
+                   TYPE_MINIMUM (dev_t), TYPE_MAXIMUM (dev_t));
+      if ((ebuf == strp) | (*ebuf != ' ') | overflow)
+       FATAL_ERROR ((0, 0, "%s:%ld: %s",
                quotearg_colon (listed_incremental_option), lineno,
                      _("Invalid device number")));
+      strp = ebuf + 1;
 
-      ino = strtosysint (strp, &ebuf,
-                        TYPE_MINIMUM (ino_t), TYPE_MAXIMUM (ino_t));
-      strp = ebuf;
-      if (errno || *strp != ' ')
-       FATAL_ERROR ((0, errno, "%s:%ld: %s",
+      ino = stoint (strp, &ebuf, &overflow,
+                   TYPE_MINIMUM (ino_t), TYPE_MAXIMUM (ino_t));
+      if ((ebuf == strp) | (*ebuf != ' ') | overflow)
+       FATAL_ERROR ((0, 0, "%s:%ld: %s",
                      quotearg_colon (listed_incremental_option), lineno,
                      _("Invalid inode number")));
+      strp = ebuf + 1;
 
-      strp++;
       unquote_string (strp);
       note_directory (strp, mtime, dev, ino, nfs, false, NULL);
     }
@@ -1126,7 +1118,6 @@ read_num (FILE *fp, char const *fieldname,
 {
   int i;
   char buf[INT_BUFSIZE_BOUND (intmax_t)];
-  int conversion_errno;
   int c = getc (fp);
   bool negative = c == '-';
 
@@ -1165,24 +1156,20 @@ read_num (FILE *fp, char const *fieldname,
                    fieldname, buf, uc));
     }
 
-  *pval = strtosysint (buf, NULL, min_val, max_val);
-  conversion_errno = errno;
+  char *bufend;
+  bool overflow;
+  *pval = stoint (buf, &bufend, &overflow, min_val, max_val);
 
-  switch (conversion_errno)
-    {
-    case ERANGE:
-      FATAL_ERROR ((0, conversion_errno,
-                   _("%s: byte %jd: (valid range %jd..%ju)\n\t%s %s"),
-                   quotearg_colon (listed_incremental_option),
-                   intmax (ftello (fp)), min_val, max_val, fieldname, buf));
-    default:
-      FATAL_ERROR ((0, conversion_errno,
-                   _("%s: byte %jd: %s %s"),
-                   quotearg_colon (listed_incremental_option),
-                   intmax (ftello (fp)), fieldname, buf));
-    case 0:
-      break;
-    }
+  if (buf == bufend)
+    FATAL_ERROR ((0, EINVAL,
+                 _("%s: byte %jd: %s %s"),
+                 quotearg_colon (listed_incremental_option),
+                 intmax (ftello (fp)), fieldname, buf));
+  if (overflow)
+    FATAL_ERROR ((0, ERANGE,
+                 _("%s: byte %jd: (valid range %jd..%ju)\n\t%s %s"),
+                 quotearg_colon (listed_incremental_option),
+                 intmax (ftello (fp)), min_val, max_val, fieldname, buf));
 
   return true;
 }
@@ -1361,7 +1348,7 @@ read_directory_file (void)
   if (0 < getline (&buf, &bufsize, listed_incremental_stream))
     {
       char *ebuf;
-      uintmax_t incremental_version;
+      int incremental_version;
 
       if (strncmp (buf, PACKAGE_NAME, sizeof PACKAGE_NAME - 1) == 0)
        {
@@ -1372,7 +1359,12 @@ read_directory_file (void)
            if (!*ebuf)
              ERROR((1, 0, _("Bad incremental file format")));
 
-         incremental_version = strtoumax (ebuf + 1, NULL, 10);
+         ebuf++;
+         if (! ('0' <= *ebuf && *ebuf <= '0' + TAR_INCREMENTAL_VERSION
+                && !c_isdigit (ebuf[1])))
+           ERROR ((1, 0, _("Unsupported incremental format version: %s"),
+                   ebuf));
+         incremental_version = *ebuf - '0';
        }
       else
        incremental_version = 0;
@@ -1389,10 +1381,8 @@ read_directory_file (void)
          break;
 
        default:
-         ERROR ((1, 0, _("Unsupported incremental format version: %"PRIuMAX),
-                 incremental_version));
+         unreachable ();
        }
-
     }
 
   if (ferror (listed_incremental_stream))
index b112e36e5ecac7ef8d4c49c7d81befb43d17dea0..1ff5d077418a0f0500fc3e84cc3f2c7da4953e54 100644 (file)
--- a/src/map.c
+++ b/src/map.c
@@ -45,28 +45,26 @@ map_compare (void const *entry1, void const *entry2)
   return map1->orig_id == map2->orig_id;
 }
 
-static int
+static bool
 parse_id (uintmax_t *retval,
          char const *arg, char const *what, uintmax_t maxval,
          char const *file, unsigned line)
 {
-  uintmax_t v;
   char *p;
-  
-  errno = 0;
-  v = strtoumax (arg, &p, 10);
-  if (*p || errno)
+  bool overflow;
+  *retval = stoint (arg, &p, &overflow, 0, maxval);
+
+  if ((p == arg) | *p)
     {
       error (0, 0, _("%s:%u: invalid %s: %s"),  file, line, what, arg);
-      return -1;
+      return false;
     }
-  if (v > maxval)
+  if (overflow)
     {
       error (0, 0, _("%s:%u: %s out of range: %s"), file, line, what, arg);
-      return -1;
+      return false;
     }
-  *retval = v;
-  return 0;
+  return true;
 }
 
 static void
@@ -82,7 +80,7 @@ map_read (Hash_table **ptab, char const *file,
   int wsopt;
   unsigned line;
   int err = 0;
-  
+
   fp = fopen (file, "r");
   if (!fp)
     open_fatal (file);
@@ -97,7 +95,7 @@ map_read (Hash_table **ptab, char const *file,
       uintmax_t orig_id, new_id;
       char *name = NULL;
       char *colon;
-      
+
       ++line;
       if (wordsplit (buf, &ws, wsopt))
        FATAL_ERROR ((0, 0, _("%s:%u: cannot split line: %s"),
@@ -114,7 +112,7 @@ map_read (Hash_table **ptab, char const *file,
 
       if (ws.ws_wordv[0][0] == '+')
        {
-         if (parse_id (&orig_id, ws.ws_wordv[0]+1, what, maxval, file, line)) 
+         if (!parse_id (&orig_id, ws.ws_wordv[0]+1, what, maxval, file, line))
            {
              err = 1;
              continue;
@@ -138,7 +136,7 @@ map_read (Hash_table **ptab, char const *file,
          if (colon > ws.ws_wordv[1])
            name = ws.ws_wordv[1];
          *colon++ = 0;
-         if (parse_id (&new_id, colon, what, maxval, file, line)) 
+         if (!parse_id (&new_id, colon, what, maxval, file, line))
            {
              err = 1;
              continue;
@@ -146,7 +144,7 @@ map_read (Hash_table **ptab, char const *file,
        }
       else if (ws.ws_wordv[1][0] == '+')
        {
-         if (parse_id (&new_id, ws.ws_wordv[1], what, maxval, file, line)) 
+         if (!parse_id (&new_id, ws.ws_wordv[1], what, maxval, file, line))
            {
              err = 1;
              continue;
@@ -169,7 +167,7 @@ map_read (Hash_table **ptab, char const *file,
       ent->orig_id = orig_id;
       ent->new_id = new_id;
       ent->new_name = name ? xstrdup (name) : NULL;
-      
+
       if (!((*ptab
             || (*ptab = hash_initialize (0, 0, map_hash, map_compare, 0)))
            && hash_insert (*ptab, ent)))
@@ -203,11 +201,11 @@ int
 owner_map_translate (uid_t uid, uid_t *new_uid, char const **new_name)
 {
   int rc = 1;
-  
+
   if (owner_map)
     {
       struct mapentry ent, *res;
-  
+
       ent.orig_id = uid;
       res = hash_lookup (owner_map, &ent);
       if (res)
@@ -253,11 +251,11 @@ int
 group_map_translate (gid_t gid, gid_t *new_gid, char const **new_name)
 {
   int rc = 1;
-  
+
   if (group_map)
     {
       struct mapentry ent, *res;
-  
+
       ent.orig_id = gid;
       res = hash_lookup (group_map, &ent);
       if (res)
@@ -278,6 +276,6 @@ group_map_translate (gid_t gid, gid_t *new_gid, char const **new_name)
       *new_name = group_name_option;
       rc = 0;
     }
-  
+
   return rc;
 }
index dffd531f49af594205cf8405ba504f4de6227478..8de75aaedb53fd170dd64e52d0c416718295952c 100644 (file)
@@ -400,51 +400,102 @@ timetostr (time_t t, char buf[SYSINT_BUFSIZE])
   return sysinttostr (t, TYPE_MINIMUM (time_t), TYPE_MAXIMUM (time_t), buf);
 }
 
-/* Convert a prefix of the string ARG to a system integer type whose
-   minimum value is MINVAL and maximum MAXVAL.  If MINVAL is negative,
+/* Convert a prefix of the string ARG to a system integer type.
+   If ARGLIM, set *ARGLIM to point to just after the prefix.
+   If OVERFLOW, set *OVERFLOW to true or false
+   depending on whether the input is out of MINVAL..MAXVAL range.
+   If the input is out of that range, return an extreme value.
+   MINVAL must not be positive.
+
+   If MINVAL is negative, MAXVAL can be at most INTMAX_MAX, and
    negative integers MINVAL .. -1 are assumed to be represented using
    leading '-' in the usual way.  If the represented value exceeds
    INTMAX_MAX, return a negative integer V such that (uintmax_t) V
-   yields the represented value.  If ARGLIM is nonnull, store into
-   *ARGLIM a pointer to the first character after the prefix.
+   yields the represented value.
+
+   On conversion error: if ARGLIM set *ARGLIM = ARG; if OVERFLOW set
+   *OVERFLOW = false; then return 0.
 
    This is the inverse of sysinttostr.
 
-   On a normal return, set errno = 0.
-   On conversion error, return 0 and set errno = EINVAL.
-   On overflow, return an extreme value and set errno = ERANGE.  */
+   Sample call to this function:
+
+      char *s_end;
+      bool overflow;
+      idx_t i = stoint (s, &s_end, &overflow, 0, IDX_MAX);
+      if ((s_end == s) | *s_end | overflow)
+        diagnose_invalid (s);
+
+   This example uses "|" instead of "||" for fewer branches at runtime,
+   which tends to be more efficient on modern processors.
+
+   This function is named "stoint" instead of "strtoint" because
+   <string.h> reserves names beginning with "str".  */
+
 intmax_t
-strtosysint (char const *arg, char **arglim, intmax_t minval, uintmax_t maxval)
+stoint (char const *arg, char **arglim, bool *overflow,
+       intmax_t minval, uintmax_t maxval)
 {
   static_assert (INTMAX_MAX <= UINTMAX_MAX);
+  char const *p = arg;
+  intmax_t i;
+  bool v = false;
 
-  errno = 0;
-  if (maxval <= INTMAX_MAX)
+  if (c_isdigit (*p))
     {
-      if (c_isdigit (arg[*arg == '-']))
+      if (minval < 0)
        {
-         intmax_t i = strtoimax (arg, arglim, 10);
-         intmax_t imaxval = maxval;
-         if (minval <= i && i <= imaxval)
-           return i;
-         errno = ERANGE;
-         return i < minval ? minval : maxval;
+         i = *p - '0';
+
+         while (c_isdigit (*++p))
+           {
+             v |= ckd_mul (&i, i, 10);
+             v |= ckd_add (&i, i, *p - '0');
+           }
+
+         v |= maxval < i;
+         if (v)
+           i = maxval;
+       }
+      else
+       {
+         uintmax_t u = *p - '0';
+
+         while (c_isdigit (*++p))
+           {
+             v |= ckd_mul (&u, u, 10);
+             v |= ckd_add (&u, u, *p - '0');
+           }
+
+         v |= maxval < u;
+         if (v)
+           u = maxval;
+         i = represent_uintmax (u);
        }
     }
-  else
+  else if (minval < 0 && *p == '-' && c_isdigit (p[1]))
     {
-      if (c_isdigit (*arg))
+      p++;
+      i = - (*p - '0');
+
+      while (c_isdigit (*++p))
        {
-         uintmax_t i = strtoumax (arg, arglim, 10);
-         if (i <= maxval)
-           return represent_uintmax (i);
-         errno = ERANGE;
-         return maxval;
+         v |= ckd_mul (&i, i, 10);
+         v |= ckd_sub (&i, i, *p - '0');
        }
+
+      v |= i < minval;
+      if (v)
+       i = minval;
     }
+  else
+    i = 0;
 
-  errno = EINVAL;
-  return 0;
+  if (arglim)
+    *arglim = (char *) p;
+  if (overflow)
+    *overflow = v;
+  return i;
 }
 
 /* Output fraction and trailing digits appropriate for a nanoseconds
@@ -507,36 +558,13 @@ code_timespec (struct timespec t, char tsbuf[TIMESPEC_STRSIZE_BOUND])
 struct timespec
 decode_timespec (char const *arg, char **arg_lim, bool parse_fraction)
 {
-  time_t s = TYPE_MINIMUM (time_t);
   int ns = -1;
-  char const *p = arg;
-  bool negative = *arg == '-';
-  struct timespec r;
-
-  if (! c_isdigit (arg[negative]))
-    errno = EINVAL;
-  else
+  bool overflow;
+  time_t s = stoint (arg, arg_lim, &overflow,
+                    TYPE_MINIMUM (time_t), TYPE_MAXIMUM (time_t));
+  char const *p = *arg_lim;
+  if (p != arg)
     {
-      errno = 0;
-
-      if (negative)
-       {
-         intmax_t i = strtoimax (arg, arg_lim, 10);
-         if (TYPE_SIGNED (time_t) ? TYPE_MINIMUM (time_t) <= i : 0 <= i)
-           s = i;
-         else
-           errno = ERANGE;
-       }
-      else
-       {
-         uintmax_t i = strtoumax (arg, arg_lim, 10);
-         if (i <= TYPE_MAXIMUM (time_t))
-           s = i;
-         else
-           errno = ERANGE;
-       }
-
-      p = *arg_lim;
       ns = 0;
 
       if (parse_fraction && *p == '.')
@@ -550,36 +578,27 @@ decode_timespec (char const *arg, char **arg_lim, bool parse_fraction)
            else
              trailing_nonzero |= *p != '0';
 
+         *arg_lim = (char *) p;
+
          while (digits < LOG10_BILLION)
            digits++, ns *= 10;
 
-         if (negative)
+         if (*arg == '-')
            {
              /* Convert "-1.10000000000001" to s == -2, ns == 89999999.
                 I.e., truncate time stamps towards minus infinity while
                 converting them to internal form.  */
              ns += trailing_nonzero;
              if (ns != 0)
-               {
-                 if (s == TYPE_MINIMUM (time_t))
-                   ns = -1;
-                 else
-                   {
-                     s--;
-                     ns = BILLION - ns;
-                   }
-               }
+               ns = ckd_sub (&s, s, 1) ? -1 : BILLION - ns;
            }
        }
 
-      if (errno == ERANGE)
+      if (overflow)
        ns = -1;
     }
 
-  *arg_lim = (char *) p;
-  r.tv_sec = s;
-  r.tv_nsec = ns;
-  return r;
+  return (struct timespec) { .tv_sec = s, .tv_nsec = ns };
 }
 \f
 /* File handling.  */
index 66d17d4dd20919604c8de3ada2ed75ee31672ef9..5fa192aba1cc5ed2293d465b5c8924473cc7b4d7 100644 (file)
@@ -1249,20 +1249,10 @@ pax_dump_header (struct tar_sparse_file *file)
 static bool
 decode_num (uintmax_t *num, char const *arg, uintmax_t maxval)
 {
-  uintmax_t u;
   char *arg_lim;
-
-  if (!c_isdigit (*arg))
-    return false;
-
-  errno = 0;
-  u = strtoumax (arg, &arg_lim, 10);
-
-  if (! (u <= maxval && errno != ERANGE) || *arg_lim)
-    return false;
-
-  *num = u;
-  return true;
+  bool overflow;
+  *num = stoint (arg, &arg_lim, &overflow, 0, maxval);
+  return ! ((arg_lim == arg) | *arg_lim | overflow);
 }
 
 static bool
index ca8860bbbf5712ec9c3e639fcd86eefdd8386593..79b713a89a837c6681e7021faba0de210d099f8d 100644 (file)
--- a/src/tar.c
+++ b/src/tar.c
@@ -60,7 +60,7 @@ uintmax_t occurrence_option;
 enum old_files old_files_option;
 bool keep_directory_symlink_option;
 const char *listed_incremental_option;
-int incremental_level;
+signed char incremental_level;
 bool check_device_option;
 struct mode_change *mode_option;
 mode_t initial_umask;
@@ -1345,50 +1345,24 @@ expand_pax_option (struct tar_args *targs, const char *arg)
 static uintmax_t
 parse_owner_group (char *arg, uintmax_t field_max, char const **name_option)
 {
-  uintmax_t u = UINTMAX_MAX;
-  char *end;
-  char const *name = 0;
-  char const *invalid_num = 0;
+  char const *name = NULL;
+  char const *num = arg;
   char *colon = strchr (arg, ':');
-
   if (colon)
     {
-      char const *num = colon + 1;
+      num = colon + 1;
       *colon = '\0';
-      if (*arg)
+      if (arg != colon)
        name = arg;
-      if (num && (! (xstrtoumax (num, &end, 10, &u, "") == LONGINT_OK
-                    && u <= field_max)))
-       invalid_num = num;
     }
-  else
-    {
-      uintmax_t u1;
-      switch ('0' <= *arg && *arg <= '9'
-             ? xstrtoumax (arg, &end, 10, &u1, "")
-             : LONGINT_INVALID)
-       {
-       default:
-         name = arg;
-         break;
 
-       case LONGINT_OK:
-         if (u1 <= field_max)
-           {
-             u = u1;
-             break;
-           }
-         FALLTHROUGH;
-       case LONGINT_OVERFLOW:
-         invalid_num = arg;
-         break;
-       }
-    }
-
-  if (invalid_num)
-    FATAL_ERROR ((0, 0, "%s: %s", quotearg_colon (invalid_num),
+  bool overflow;
+  char *end;
+  uintmax_t u = stoint (num, &end, &overflow, 0, field_max);
+  if ((end == num) | *end | overflow)
+    FATAL_ERROR ((0, 0, "%s: %s", quotearg_colon (num),
                  _("Invalid owner or group ID")));
-  if (name)
+  if (name_option)
     *name_option = name;
   return u;
 }
@@ -1498,14 +1472,15 @@ parse_opt (int key, char *arg, struct argp_state *state)
 
     case 'b':
       {
-       uintmax_t u;
-       if (! (xstrtoumax (arg, 0, 10, &u, "") == LONGINT_OK
-              && !ckd_add (&blocking_factor, u, 0)
-              && 0 < blocking_factor
-              && !ckd_mul (&record_size, u, BLOCKSIZE)
-              && record_size <= min (SSIZE_MAX, SIZE_MAX)))
+       bool overflow;
+       char *end;
+       blocking_factor = stoint (arg, &end, &overflow, 0,
+                                 (min (IDX_MAX, min (SSIZE_MAX, SIZE_MAX))
+                                  / BLOCKSIZE));
+       if ((end == arg) | *end | overflow | !blocking_factor)
          USAGE_ERROR ((0, 0, "%s: %s", quotearg_colon (arg),
                        _("Invalid blocking factor")));
+       record_size = blocking_factor * BLOCKSIZE;
       }
       break;
 
@@ -1712,9 +1687,9 @@ parse_opt (int key, char *arg, struct argp_state *state)
 
     case LEVEL_OPTION:
       {
-       uintmax_t u;
-       if (! (xstrtoumax (arg, nullptr, 10, &u, "") == LONGINT_OK
-              && ckd_add (&incremental_level, u, 0)))
+       char *end;
+       incremental_level = stoint (arg, &end, NULL, 0, 1);
+       if ((end == arg) | *end)
          USAGE_ERROR ((0, 0, _("Invalid incremental level value")));
       }
       break;
@@ -1832,26 +1807,11 @@ parse_opt (int key, char *arg, struct argp_state *state)
       sparse_option = true;
       {
        char *p;
-       bool ok;
-       switch (xstrtoimax (arg, &p, 10, &tar_sparse_major, ""))
-         {
-         case LONGINT_OK:
-           tar_sparse_minor = 0;
-           ok = 0 <= tar_sparse_major;
-           break;
-
-         case LONGINT_INVALID_SUFFIX_CHAR:
-           ok = (*p == '.'
-                 && (xstrtoimax (p + 1, nullptr, 10, &tar_sparse_minor, "")
-                     == LONGINT_OK)
-                 && 0 <= tar_sparse_minor && 0 <= tar_sparse_major);
-           break;
-
-         default:
-           ok = false;
-           break;
-         }
-       if (!ok)
+       bool vmajor, vminor;
+       tar_sparse_major = stoint (arg, &p, &vmajor, 0, INTMAX_MAX);
+       if ((p != arg) & (*p == '.'))
+         tar_sparse_minor = stoint (p + 1, &p, &vminor, 0, INTMAX_MAX);
+       if ((p == arg) | *p | vmajor | vminor)
          USAGE_ERROR ((0, 0, _("Invalid sparse version value")));
       }
       break;
@@ -1949,8 +1909,8 @@ parse_opt (int key, char *arg, struct argp_state *state)
              checkpoint_compile_action (".");
              arg++;
            }
-         checkpoint_option = strtoimax (arg, &p, 0);
-         if (*p || checkpoint_option <= 0)
+         checkpoint_option = stoint (arg, &p, NULL, 0, INTMAX_MAX);
+         if (*p | (checkpoint_option <= 0))
            FATAL_ERROR ((0, 0,
                          _("invalid --checkpoint value")));
        }
@@ -2058,10 +2018,9 @@ parse_opt (int key, char *arg, struct argp_state *state)
        occurrence_option = 1;
       else
        {
-         uintmax_t u;
-         if (xstrtoumax (arg, 0, 10, &u, "") == LONGINT_OK)
-           occurrence_option = u;
-         else
+         char *end;
+         occurrence_option = stoint (arg, &end, NULL, 0, UINTMAX_MAX);
+         if (*end)
            FATAL_ERROR ((0, 0, "%s: %s", quotearg_colon (arg),
                          _("Invalid number")));
        }
@@ -2172,9 +2131,9 @@ parse_opt (int key, char *arg, struct argp_state *state)
 
     case STRIP_COMPONENTS_OPTION:
       {
-       uintmax_t u;
-       if (! (xstrtoumax (arg, 0, 10, &u, "") == LONGINT_OK
-              && !ckd_add (&strip_name_components, u, 0)))
+       char *end;
+       strip_name_components = stoint (arg, &end, NULL, 0, SIZE_MAX);
+       if (*end)
          USAGE_ERROR ((0, 0, "%s: %s", quotearg_colon (arg),
                        _("Invalid number of elements")));
       }
index 2fc97b31f807b93d38d21f0c590a8a4195962621..3926301b3c8be0a2d213a1e71d69d67471878d3a 100644 (file)
@@ -250,10 +250,7 @@ parse_transform_expr (const char *expr)
       case '5': case '6': case '7': case '8': case '9':
        {
          char *endp;
-         intmax_t match_number = strtoimax (p, &endp, 10);
-         assume (0 <= match_number);
-         if (ckd_add (&tf->match_number, match_number, 0))
-           tf->match_number = IDX_MAX;
+         tf->match_number = stoint (p, &endp, NULL, 0, IDX_MAX);
          p = endp - 1;
        }
        break;
@@ -302,8 +299,7 @@ parse_transform_expr (const char *expr)
            case '0': case '1': case '2': case '3': case '4':
            case '5': case '6': case '7': case '8': case '9':
              {
-               intmax_t n = strtoimax (cur, &cur, 10);
-               assume (0 <= n);
+               idx_t n = stoint (cur, &cur, NULL, 0, IDX_MAX);
                if (tf->regex.re_nsub < n)
                  USAGE_ERROR ((0, 0, _("Invalid transform replacement:"
                                        " back reference out of range")));
index b306accdd7c34b69e6d153402135c94056ce6a41..0fe1ce14561e9be8bc2916d005c31c047fc3ecbb 100644 (file)
@@ -608,27 +608,30 @@ decode_record (struct xheader *xhdr,
 {
   char *start = *ptr;
   char *p = start;
-  size_t len;
   char *len_lim;
   char const *keyword;
   char *nextp;
-  size_t len_max = xhdr->buffer + xhdr->size - start;
+  idx_t len_max = xhdr->buffer + xhdr->size - start;
 
   while (*p == ' ' || *p == '\t')
     p++;
 
-  if (! c_isdigit (*p))
+  idx_t len = stoint (p, &len_lim, NULL, 0, IDX_MAX);
+
+  if (len_lim == p)
     {
+      /* The length is missing.
+        FIXME: Comment why this is diagnosed only if (*p), or change code.  */
       if (*p)
        ERROR ((0, 0, _("Malformed extended header: missing length")));
       return false;
     }
 
-  len = strtoumax (p, &len_lim, 10);
-
   if (len_max < len)
     {
-      int len_len = len_lim - p;
+      /* Avoid giant diagnostics, as this won't help user.  */
+      int len_len = min (len_lim - p, 1000);
+
       ERROR ((0, 0, _("Extended header length %.*s is out of range"),
              len_len, p));
       return false;
@@ -1087,16 +1090,17 @@ decode_signed_num (intmax_t *num, char const *arg,
                   char const *keyword)
 {
   char *arg_lim;
-  intmax_t u = strtosysint (arg, &arg_lim, minval, maxval);
+  bool overflow;
+  intmax_t u = stoint (arg, &arg_lim, &overflow, minval, maxval);
 
-  if (errno == EINVAL || *arg_lim)
+  if ((arg_lim == arg) | *arg_lim)
     {
       ERROR ((0, 0, _("Malformed extended header: invalid %s=%s"),
              keyword, arg));
       return false;
     }
 
-  if (errno == ERANGE)
+  if (overflow)
     {
       out_of_range_header (keyword, arg, minval, maxval);
       return false;
@@ -1433,33 +1437,26 @@ sparse_map_decoder (struct tar_stat_info *st,
                    char const *arg,
                    MAYBE_UNUSED size_t size)
 {
-  int offset = 1;
+  bool offset = true;
   struct sp_array e;
 
   st->sparse_map_avail = 0;
-  while (1)
+  while (true)
     {
-      intmax_t u;
       char *delim;
-
-      if (!c_isdigit (*arg))
+      bool overflow;
+      off_t u = stoint (arg, &delim, &overflow, 0, TYPE_MAXIMUM (off_t));
+      if (delim == arg)
        {
          ERROR ((0, 0, _("Malformed extended header: invalid %s=%s"),
                  keyword, arg));
          return;
        }
 
-      errno = 0;
-      u = strtoimax (arg, &delim, 10);
-      if (TYPE_MAXIMUM (off_t) < u)
-       {
-         u = TYPE_MAXIMUM (off_t);
-         errno = ERANGE;
-       }
       if (offset)
        {
          e.offset = u;
-         if (errno == ERANGE)
+         if (overflow)
            {
              out_of_range_header (keyword, arg, 0, TYPE_MAXIMUM (off_t));
              return;
@@ -1468,7 +1465,7 @@ sparse_map_decoder (struct tar_stat_info *st,
       else
        {
          e.numbytes = u;
-         if (errno == ERANGE)
+         if (overflow)
            {
              out_of_range_header (keyword, arg, 0, TYPE_MAXIMUM (off_t));
              return;