From: Paul Eggert Date: Thu, 8 Aug 2024 23:32:49 +0000 (-0700) Subject: Prefer stoint to strtoul and variants X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=d1e72a536f26188230a147d948b9057714fd0b6b;p=thirdparty%2Ftar.git Prefer stoint to strtoul and variants 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. --- 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 + 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 }; } /* 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;