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.
This commit is contained in:
Paul Eggert
2024-08-08 16:32:49 -07:00
parent 3ffe2eb073
commit d1e72a536f
10 changed files with 232 additions and 278 deletions

View 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

View 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);
}

View 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 };

View 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))

View File

@@ -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;
}

View 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 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. */

View 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

109
src/tar.c
View File

@@ -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")));
}

View 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")));

View 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;