getdigits: introduce strict, def parameters

Problem:
During a refactor long ago, we changed the `getdigits_*` familiy of
functions to abort on overflow.  But this is often wrong, because many
of these codepaths are handling user input.

Solution:
Decide at each call-site whether to use "strict" mode.

fix #5555
This commit is contained in:
Justin M. Keyes
2019-09-13 18:15:09 -07:00
parent 0a24a2c314
commit 3344cffe7b
22 changed files with 150 additions and 113 deletions

View File

@@ -166,7 +166,7 @@ int buf_init_chartab(buf_T *buf, int global)
}
if (ascii_isdigit(*p)) {
c = getdigits_int((char_u **)&p);
c = getdigits_int((char_u **)&p, true, 0);
} else {
c = mb_ptr2char_adv(&p);
}
@@ -176,7 +176,7 @@ int buf_init_chartab(buf_T *buf, int global)
++p;
if (ascii_isdigit(*p)) {
c2 = getdigits_int((char_u **)&p);
c2 = getdigits_int((char_u **)&p, true, 0);
} else {
c2 = mb_ptr2char_adv(&p);
}
@@ -1595,7 +1595,7 @@ char_u* skiptowhite_esc(char_u *p) {
return p;
}
/// Get a number from a string and skip over it, signalling overflows
/// Gets a number from a string and skips over it, signalling overflow.
///
/// @param[out] pp A pointer to a pointer to char_u.
/// It will be advanced past the read number.
@@ -1606,48 +1606,58 @@ bool try_getdigits(char_u **pp, intmax_t *nr)
{
errno = 0;
*nr = strtoimax((char *)(*pp), (char **)pp, 10);
if ((*nr == INTMAX_MIN || *nr == INTMAX_MAX)
&& errno == ERANGE) {
if (errno == ERANGE && (*nr == INTMAX_MIN || *nr == INTMAX_MAX)) {
return false;
}
return true;
}
/// Get a number from a string and skip over it.
/// Gets a number from a string and skips over it.
///
/// @param[out] pp A pointer to a pointer to char_u.
/// @param[out] pp Pointer to a pointer to char_u.
/// It will be advanced past the read number.
/// @param strict Abort on overflow.
/// @param def Default value, if parsing fails or overflow occurs.
///
/// @return Number read from the string.
intmax_t getdigits(char_u **pp)
/// @return Number read from the string, or `def` on parse failure or overflow.
intmax_t getdigits(char_u **pp, bool strict, intmax_t def)
{
intmax_t number;
int ok = try_getdigits(pp, &number);
(void)ok; // Avoid "unused variable" warning in Release build
assert(ok);
return number;
if (strict && !ok) {
abort();
}
return ok ? number : def;
}
/// Get an int number from a string. Like getdigits(), but restricted to `int`.
int getdigits_int(char_u **pp)
/// Gets an int number from a string.
///
/// @see getdigits
int getdigits_int(char_u **pp, bool strict, int def)
{
intmax_t number = getdigits(pp);
intmax_t number = getdigits(pp, strict, def);
#if SIZEOF_INTMAX_T > SIZEOF_INT
assert(number >= INT_MIN && number <= INT_MAX);
if (strict) {
assert(number >= INT_MIN && number <= INT_MAX);
} else if (!(number >= INT_MIN && number <= INT_MAX)) {
return def;
}
#endif
return (int)number;
}
/// Get a long number from a string. Like getdigits(), but restricted to `long`.
long getdigits_long(char_u **pp)
/// Gets a long number from a string.
///
/// @see getdigits
long getdigits_long(char_u **pp, bool strict, long def)
{
intmax_t number = getdigits(pp);
intmax_t number = getdigits(pp, strict, def);
#if SIZEOF_INTMAX_T > SIZEOF_LONG
assert(number >= LONG_MIN && number <= LONG_MAX);
if (strict) {
assert(number >= LONG_MIN && number <= LONG_MAX);
} else if (!(number >= LONG_MIN && number <= LONG_MAX)) {
return def;
}
#endif
return (long)number;
}