mirror of
https://github.com/neovim/neovim.git
synced 2025-02-25 18:55:25 -06:00
vim-patch:9.0.1380: CTRL-X on 2**64 subtracts two (#22530)
Problem: CTRL-X on 2**64 subtracts two. (James McCoy)
Solution: Correct computation for large number. (closes vim/vim#12103)
5fb78c3fa5
Co-authored-by: Bram Moolenaar <Bram@vim.org>
This commit is contained in:
parent
b44b8e7687
commit
419819b624
@ -1500,9 +1500,10 @@ bool vim_isblankline(char *lbuf)
|
|||||||
/// @param strict If true, fail if the number has unexpected trailing
|
/// @param strict If true, fail if the number has unexpected trailing
|
||||||
/// alphanumeric chars: *len is set to 0 and nothing else is
|
/// alphanumeric chars: *len is set to 0 and nothing else is
|
||||||
/// returned.
|
/// returned.
|
||||||
|
/// @param overflow When not NULL, set to true for overflow.
|
||||||
void vim_str2nr(const char *const start, int *const prep, int *const len, const int what,
|
void vim_str2nr(const char *const start, int *const prep, int *const len, const int what,
|
||||||
varnumber_T *const nptr, uvarnumber_T *const unptr, const int maxlen,
|
varnumber_T *const nptr, uvarnumber_T *const unptr, const int maxlen,
|
||||||
const bool strict)
|
const bool strict, bool *const overflow)
|
||||||
FUNC_ATTR_NONNULL_ARG(1)
|
FUNC_ATTR_NONNULL_ARG(1)
|
||||||
{
|
{
|
||||||
const char *ptr = start;
|
const char *ptr = start;
|
||||||
@ -1626,6 +1627,9 @@ void vim_str2nr(const char *const start, int *const prep, int *const len, const
|
|||||||
un = (base) * un + digit; \
|
un = (base) * un + digit; \
|
||||||
} else { \
|
} else { \
|
||||||
un = UVARNUMBER_MAX; \
|
un = UVARNUMBER_MAX; \
|
||||||
|
if (overflow != NULL) { \
|
||||||
|
*overflow = true; \
|
||||||
|
} \
|
||||||
} \
|
} \
|
||||||
ptr++; \
|
ptr++; \
|
||||||
} \
|
} \
|
||||||
@ -1664,12 +1668,18 @@ vim_str2nr_proceed:
|
|||||||
// avoid ubsan error for overflow
|
// avoid ubsan error for overflow
|
||||||
if (un > VARNUMBER_MAX) {
|
if (un > VARNUMBER_MAX) {
|
||||||
*nptr = VARNUMBER_MIN;
|
*nptr = VARNUMBER_MIN;
|
||||||
|
if (overflow != NULL) {
|
||||||
|
*overflow = true;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
*nptr = -(varnumber_T)un;
|
*nptr = -(varnumber_T)un;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (un > VARNUMBER_MAX) {
|
if (un > VARNUMBER_MAX) {
|
||||||
un = VARNUMBER_MAX;
|
un = VARNUMBER_MAX;
|
||||||
|
if (overflow != NULL) {
|
||||||
|
*overflow = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
*nptr = (varnumber_T)un;
|
*nptr = (varnumber_T)un;
|
||||||
}
|
}
|
||||||
|
@ -3678,7 +3678,7 @@ static int get_number_tv(char **arg, typval_T *rettv, bool evaluate, bool want_s
|
|||||||
// decimal, hex or octal number
|
// decimal, hex or octal number
|
||||||
int len;
|
int len;
|
||||||
varnumber_T n;
|
varnumber_T n;
|
||||||
vim_str2nr(*arg, NULL, &len, STR2NR_ALL, &n, NULL, 0, true);
|
vim_str2nr(*arg, NULL, &len, STR2NR_ALL, &n, NULL, 0, true, NULL);
|
||||||
if (len == 0) {
|
if (len == 0) {
|
||||||
if (evaluate) {
|
if (evaluate) {
|
||||||
semsg(_(e_invexpr2), *arg);
|
semsg(_(e_invexpr2), *arg);
|
||||||
|
@ -439,7 +439,7 @@ static inline int parse_json_string(const char *const buf, const size_t buf_len,
|
|||||||
t += 4;
|
t += 4;
|
||||||
uvarnumber_T ch;
|
uvarnumber_T ch;
|
||||||
vim_str2nr(ubuf, NULL, NULL,
|
vim_str2nr(ubuf, NULL, NULL,
|
||||||
STR2NR_HEX | STR2NR_FORCE, NULL, &ch, 4, true);
|
STR2NR_HEX | STR2NR_FORCE, NULL, &ch, 4, true, NULL);
|
||||||
if (ch == 0) {
|
if (ch == 0) {
|
||||||
hasnul = true;
|
hasnul = true;
|
||||||
}
|
}
|
||||||
@ -608,7 +608,7 @@ parse_json_number_check:
|
|||||||
// Convert integer
|
// Convert integer
|
||||||
varnumber_T nr;
|
varnumber_T nr;
|
||||||
int num_len;
|
int num_len;
|
||||||
vim_str2nr(s, NULL, &num_len, 0, &nr, NULL, (int)(p - s), true);
|
vim_str2nr(s, NULL, &num_len, 0, &nr, NULL, (int)(p - s), true, NULL);
|
||||||
if ((int)exp_num_len != num_len) {
|
if ((int)exp_num_len != num_len) {
|
||||||
semsg(_("E685: internal error: while converting number \"%.*s\" "
|
semsg(_("E685: internal error: while converting number \"%.*s\" "
|
||||||
"to integer vim_str2nr consumed %i bytes in place of %zu"),
|
"to integer vim_str2nr consumed %i bytes in place of %zu"),
|
||||||
|
@ -8033,7 +8033,7 @@ static void f_str2nr(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
varnumber_T n;
|
varnumber_T n;
|
||||||
vim_str2nr(p, NULL, NULL, what, &n, NULL, 0, false);
|
vim_str2nr(p, NULL, NULL, what, &n, NULL, 0, false, NULL);
|
||||||
// Text after the number is silently ignored.
|
// Text after the number is silently ignored.
|
||||||
if (isneg) {
|
if (isneg) {
|
||||||
rettv->vval.v_number = -n;
|
rettv->vval.v_number = -n;
|
||||||
|
@ -3890,7 +3890,7 @@ varnumber_T tv_get_number_chk(const typval_T *const tv, bool *const ret_error)
|
|||||||
case VAR_STRING: {
|
case VAR_STRING: {
|
||||||
varnumber_T n = 0;
|
varnumber_T n = 0;
|
||||||
if (tv->vval.v_string != NULL) {
|
if (tv->vval.v_string != NULL) {
|
||||||
vim_str2nr(tv->vval.v_string, NULL, NULL, STR2NR_ALL, &n, NULL, 0, false);
|
vim_str2nr(tv->vval.v_string, NULL, NULL, STR2NR_ALL, &n, NULL, 0, false, NULL);
|
||||||
}
|
}
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
@ -580,7 +580,7 @@ void ex_sort(exarg_T *eap)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (sort_nr || sort_flt) {
|
if (sort_nr || sort_flt) {
|
||||||
// Make sure vim_str2nr doesn't read any digits past the end
|
// Make sure vim_str2nr() doesn't read any digits past the end
|
||||||
// of the match, by temporarily terminating the string there
|
// of the match, by temporarily terminating the string there
|
||||||
s2 = s + end_col;
|
s2 = s + end_col;
|
||||||
c = *s2;
|
c = *s2;
|
||||||
@ -605,7 +605,7 @@ void ex_sort(exarg_T *eap)
|
|||||||
} else {
|
} else {
|
||||||
nrs[lnum - eap->line1].st_u.num.is_number = true;
|
nrs[lnum - eap->line1].st_u.num.is_number = true;
|
||||||
vim_str2nr(s, NULL, NULL, sort_what,
|
vim_str2nr(s, NULL, NULL, sort_what,
|
||||||
&nrs[lnum - eap->line1].st_u.num.value, NULL, 0, false);
|
&nrs[lnum - eap->line1].st_u.num.value, NULL, 0, false, NULL);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
s = skipwhite(p);
|
s = skipwhite(p);
|
||||||
|
@ -4241,7 +4241,7 @@ int get_list_range(char **str, int *num1, int *num2)
|
|||||||
|
|
||||||
*str = skipwhite((*str));
|
*str = skipwhite((*str));
|
||||||
if (**str == '-' || ascii_isdigit(**str)) { // parse "from" part of range
|
if (**str == '-' || ascii_isdigit(**str)) { // parse "from" part of range
|
||||||
vim_str2nr(*str, NULL, &len, 0, &num, NULL, 0, false);
|
vim_str2nr(*str, NULL, &len, 0, &num, NULL, 0, false, NULL);
|
||||||
*str += len;
|
*str += len;
|
||||||
*num1 = (int)num;
|
*num1 = (int)num;
|
||||||
first = true;
|
first = true;
|
||||||
@ -4249,7 +4249,7 @@ int get_list_range(char **str, int *num1, int *num2)
|
|||||||
*str = skipwhite((*str));
|
*str = skipwhite((*str));
|
||||||
if (**str == ',') { // parse "to" part of range
|
if (**str == ',') { // parse "to" part of range
|
||||||
*str = skipwhite((*str) + 1);
|
*str = skipwhite((*str) + 1);
|
||||||
vim_str2nr(*str, NULL, &len, 0, &num, NULL, 0, false);
|
vim_str2nr(*str, NULL, &len, 0, &num, NULL, 0, false, NULL);
|
||||||
if (len > 0) {
|
if (len > 0) {
|
||||||
*num2 = (int)num;
|
*num2 = (int)num;
|
||||||
*str = skipwhite((*str) + len);
|
*str = skipwhite((*str) + len);
|
||||||
|
@ -673,7 +673,7 @@ int find_special_key(const char **const srcp, const size_t src_len, int *const m
|
|||||||
if (end - bp > 3 && bp[0] == 't' && bp[1] == '_') {
|
if (end - bp > 3 && bp[0] == 't' && bp[1] == '_') {
|
||||||
bp += 3; // skip t_xx, xx may be '-' or '>'
|
bp += 3; // skip t_xx, xx may be '-' or '>'
|
||||||
} else if (end - bp > 4 && STRNICMP(bp, "char-", 5) == 0) {
|
} else if (end - bp > 4 && STRNICMP(bp, "char-", 5) == 0) {
|
||||||
vim_str2nr(bp + 5, NULL, &l, STR2NR_ALL, NULL, NULL, 0, true);
|
vim_str2nr(bp + 5, NULL, &l, STR2NR_ALL, NULL, NULL, 0, true, NULL);
|
||||||
if (l == 0) {
|
if (l == 0) {
|
||||||
emsg(_(e_invarg));
|
emsg(_(e_invarg));
|
||||||
return 0;
|
return 0;
|
||||||
@ -704,7 +704,7 @@ int find_special_key(const char **const srcp, const size_t src_len, int *const m
|
|||||||
if (STRNICMP(last_dash + 1, "char-", 5) == 0
|
if (STRNICMP(last_dash + 1, "char-", 5) == 0
|
||||||
&& ascii_isdigit(last_dash[6])) {
|
&& ascii_isdigit(last_dash[6])) {
|
||||||
// <Char-123> or <Char-033> or <Char-0x33>
|
// <Char-123> or <Char-033> or <Char-0x33>
|
||||||
vim_str2nr(last_dash + 6, NULL, &l, STR2NR_ALL, NULL, &n, 0, true);
|
vim_str2nr(last_dash + 6, NULL, &l, STR2NR_ALL, NULL, &n, 0, true, NULL);
|
||||||
if (l == 0) {
|
if (l == 0) {
|
||||||
emsg(_(e_invarg));
|
emsg(_(e_invarg));
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -4658,11 +4658,12 @@ int do_addsub(int op_type, pos_T *pos, int length, linenr_T Prenum1)
|
|||||||
: length);
|
: length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool overflow = false;
|
||||||
vim_str2nr(ptr + col, &pre, &length,
|
vim_str2nr(ptr + col, &pre, &length,
|
||||||
0 + (do_bin ? STR2NR_BIN : 0)
|
0 + (do_bin ? STR2NR_BIN : 0)
|
||||||
+ (do_oct ? STR2NR_OCT : 0)
|
+ (do_oct ? STR2NR_OCT : 0)
|
||||||
+ (do_hex ? STR2NR_HEX : 0),
|
+ (do_hex ? STR2NR_HEX : 0),
|
||||||
NULL, &n, maxlen, false);
|
NULL, &n, maxlen, false, &overflow);
|
||||||
|
|
||||||
// ignore leading '-' for hex, octal and bin numbers
|
// ignore leading '-' for hex, octal and bin numbers
|
||||||
if (pre && negative) {
|
if (pre && negative) {
|
||||||
@ -4682,8 +4683,10 @@ int do_addsub(int op_type, pos_T *pos, int length, linenr_T Prenum1)
|
|||||||
|
|
||||||
oldn = n;
|
oldn = n;
|
||||||
|
|
||||||
n = subtract ? n - (uvarnumber_T)Prenum1
|
if (!overflow) { // if number is too big don't add/subtract
|
||||||
: n + (uvarnumber_T)Prenum1;
|
n = subtract ? n - (uvarnumber_T)Prenum1
|
||||||
|
: n + (uvarnumber_T)Prenum1;
|
||||||
|
}
|
||||||
|
|
||||||
// handle wraparound for decimal numbers
|
// handle wraparound for decimal numbers
|
||||||
if (!pre) {
|
if (!pre) {
|
||||||
|
@ -814,7 +814,7 @@ static void do_set_num(int opt_idx, int opt_flags, char **argp, int nextchar, co
|
|||||||
} else if (*arg == '-' || ascii_isdigit(*arg)) {
|
} else if (*arg == '-' || ascii_isdigit(*arg)) {
|
||||||
int i;
|
int i;
|
||||||
// Allow negative, octal and hex numbers.
|
// Allow negative, octal and hex numbers.
|
||||||
vim_str2nr(arg, NULL, &i, STR2NR_ALL, &value, NULL, 0, true);
|
vim_str2nr(arg, NULL, &i, STR2NR_ALL, &value, NULL, 0, true, NULL);
|
||||||
if (i == 0 || (arg[i] != NUL && !ascii_iswhite(arg[i]))) {
|
if (i == 0 || (arg[i] != NUL && !ascii_iswhite(arg[i]))) {
|
||||||
*errmsg = e_number_required_after_equal;
|
*errmsg = e_number_required_after_equal;
|
||||||
return;
|
return;
|
||||||
|
@ -841,6 +841,22 @@ func Test_increment_unsigned()
|
|||||||
set nrformats-=unsigned
|
set nrformats-=unsigned
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
|
func Test_in_decrement_large_number()
|
||||||
|
" NOTE: 18446744073709551616 == 2^64
|
||||||
|
call setline(1, '18446744073709551616')
|
||||||
|
exec "norm! gg0\<C-X>"
|
||||||
|
call assert_equal('18446744073709551615', getline(1))
|
||||||
|
|
||||||
|
exec "norm! gg0\<C-X>"
|
||||||
|
call assert_equal('18446744073709551614', getline(1))
|
||||||
|
|
||||||
|
exec "norm! gg0\<C-A>"
|
||||||
|
call assert_equal('18446744073709551615', getline(1))
|
||||||
|
|
||||||
|
exec "norm! gg0\<C-A>"
|
||||||
|
call assert_equal('-18446744073709551615', getline(1))
|
||||||
|
endfunc
|
||||||
|
|
||||||
func Test_normal_increment_with_virtualedit()
|
func Test_normal_increment_with_virtualedit()
|
||||||
set virtualedit=all
|
set virtualedit=all
|
||||||
|
|
||||||
|
@ -376,7 +376,7 @@ LexExprToken viml_pexpr_next_token(ParserState *const pstate, const int flags)
|
|||||||
}
|
}
|
||||||
if (exp_start) {
|
if (exp_start) {
|
||||||
vim_str2nr(pline.data + exp_start, NULL, NULL, 0, NULL, &exp_part,
|
vim_str2nr(pline.data + exp_start, NULL, NULL, 0, NULL, &exp_part,
|
||||||
(int)(ret.len - exp_start), false);
|
(int)(ret.len - exp_start), false, NULL);
|
||||||
}
|
}
|
||||||
if (exp_negative) {
|
if (exp_negative) {
|
||||||
exp_part += frac_size;
|
exp_part += frac_size;
|
||||||
@ -394,7 +394,7 @@ LexExprToken viml_pexpr_next_token(ParserState *const pstate, const int flags)
|
|||||||
int len;
|
int len;
|
||||||
int prep;
|
int prep;
|
||||||
vim_str2nr(pline.data, &prep, &len, STR2NR_ALL, NULL,
|
vim_str2nr(pline.data, &prep, &len, STR2NR_ALL, NULL,
|
||||||
&ret.data.num.val.integer, (int)pline.size, false);
|
&ret.data.num.val.integer, (int)pline.size, false, NULL);
|
||||||
ret.len = (size_t)len;
|
ret.len = (size_t)len;
|
||||||
const uint8_t bases[] = {
|
const uint8_t bases[] = {
|
||||||
[0] = 10,
|
[0] = 10,
|
||||||
|
@ -63,7 +63,7 @@ local function test_vim_str2nr(s, what, exp, maxlen, strict)
|
|||||||
cv[k] = args[k]
|
cv[k] = args[k]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
lib.vim_str2nr(s, cv.pre, cv.len, what, cv.num, cv.unum, maxlen, strict)
|
lib.vim_str2nr(s, cv.pre, cv.len, what, cv.num, cv.unum, maxlen, strict, nil)
|
||||||
for cck, ccv in pairs(cv) do
|
for cck, ccv in pairs(cv) do
|
||||||
if exp[cck] ~= tonumber(ccv[0]) then
|
if exp[cck] ~= tonumber(ccv[0]) then
|
||||||
error(('Failed check (%s = %d) in test (s=%s, w=%u, m=%d, strict=%s): %d'):format(
|
error(('Failed check (%s = %d) in test (s=%s, w=%u, m=%d, strict=%s): %d'):format(
|
||||||
|
Loading…
Reference in New Issue
Block a user