diff --git a/gnulib.modules b/gnulib.modules index dc826b43..bd3b6f5a 100644 --- a/gnulib.modules +++ b/gnulib.modules @@ -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 diff --git a/src/checkpoint.c b/src/checkpoint.c index 28e4c22a..5f82ef20 100644 --- a/src/checkpoint.c +++ b/src/checkpoint.c @@ -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); } diff --git a/src/common.h b/src/common.h index 0c1b139e..bfabfe98 100644 --- a/src/common.h +++ b/src/common.h @@ -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 }; diff --git a/src/incremen.c b/src/incremen.c index 5d14e5b8..6e6b1467 100644 --- a/src/incremen.c +++ b/src/incremen.c @@ -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)) diff --git a/src/map.c b/src/map.c index b112e36e..1ff5d077 100644 --- 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; } diff --git a/src/misc.c b/src/misc.c index dffd531f..8de75aae 100644 --- a/src/misc.c +++ b/src/misc.c @@ -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 + 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 if (minval < 0 && *p == '-' && c_isdigit (p[1])) + { + p++; + i = - (*p - '0'); + + while (c_isdigit (*++p)) + { + v |= ckd_mul (&i, i, 10); + v |= ckd_sub (&i, i, *p - '0'); + } + + v |= i < minval; + if (v) + i = minval; } else - { - if (c_isdigit (*arg)) - { - uintmax_t i = strtoumax (arg, arglim, 10); - if (i <= maxval) - return represent_uintmax (i); - errno = ERANGE; - return maxval; - } - } + 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 }; } /* File handling. */ diff --git a/src/sparse.c b/src/sparse.c index 66d17d4d..5fa192ab 100644 --- a/src/sparse.c +++ b/src/sparse.c @@ -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 diff --git a/src/tar.c b/src/tar.c index ca8860bb..79b713a8 100644 --- 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"))); } diff --git a/src/transform.c b/src/transform.c index 2fc97b31..3926301b 100644 --- a/src/transform.c +++ b/src/transform.c @@ -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"))); diff --git a/src/xheader.c b/src/xheader.c index b306accd..0fe1ce14 100644 --- a/src/xheader.c +++ b/src/xheader.c @@ -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;