diff options
author | zeertzjq <zeertzjq@outlook.com> | 2023-03-05 09:18:42 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-03-05 09:18:42 +0800 |
commit | 419819b6245e120aba8897e3ddea711b2cd0246c (patch) | |
tree | 6a86105155ec9abc9553d3ed69a30321f4be9a01 | |
parent | b44b8e7687ece66f4c535182071223d78ca54ad0 (diff) | |
download | rneovim-419819b6245e120aba8897e3ddea711b2cd0246c.tar.gz rneovim-419819b6245e120aba8897e3ddea711b2cd0246c.tar.bz2 rneovim-419819b6245e120aba8897e3ddea711b2cd0246c.zip |
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)
https://github.com/vim/vim/commit/5fb78c3fa5c996c08a65431d698bd2c251eef5c7
Co-authored-by: Bram Moolenaar <Bram@vim.org>
-rw-r--r-- | src/nvim/charset.c | 12 | ||||
-rw-r--r-- | src/nvim/eval.c | 2 | ||||
-rw-r--r-- | src/nvim/eval/decode.c | 4 | ||||
-rw-r--r-- | src/nvim/eval/funcs.c | 2 | ||||
-rw-r--r-- | src/nvim/eval/typval.c | 2 | ||||
-rw-r--r-- | src/nvim/ex_cmds.c | 4 | ||||
-rw-r--r-- | src/nvim/ex_getln.c | 4 | ||||
-rw-r--r-- | src/nvim/keycodes.c | 4 | ||||
-rw-r--r-- | src/nvim/ops.c | 9 | ||||
-rw-r--r-- | src/nvim/option.c | 2 | ||||
-rw-r--r-- | src/nvim/testdir/test_increment.vim | 16 | ||||
-rw-r--r-- | src/nvim/viml/parser/expressions.c | 4 | ||||
-rw-r--r-- | test/unit/charset/vim_str2nr_spec.lua | 2 |
13 files changed, 48 insertions, 19 deletions
diff --git a/src/nvim/charset.c b/src/nvim/charset.c index bf18d110c0..b792ae5ece 100644 --- a/src/nvim/charset.c +++ b/src/nvim/charset.c @@ -1500,9 +1500,10 @@ bool vim_isblankline(char *lbuf) /// @param strict If true, fail if the number has unexpected trailing /// alphanumeric chars: *len is set to 0 and nothing else is /// 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, 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) { 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; \ } else { \ un = UVARNUMBER_MAX; \ + if (overflow != NULL) { \ + *overflow = true; \ + } \ } \ ptr++; \ } \ @@ -1664,12 +1668,18 @@ vim_str2nr_proceed: // avoid ubsan error for overflow if (un > VARNUMBER_MAX) { *nptr = VARNUMBER_MIN; + if (overflow != NULL) { + *overflow = true; + } } else { *nptr = -(varnumber_T)un; } } else { if (un > VARNUMBER_MAX) { un = VARNUMBER_MAX; + if (overflow != NULL) { + *overflow = true; + } } *nptr = (varnumber_T)un; } diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 67fede1aea..e10607772a 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -3678,7 +3678,7 @@ static int get_number_tv(char **arg, typval_T *rettv, bool evaluate, bool want_s // decimal, hex or octal number int len; 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 (evaluate) { semsg(_(e_invexpr2), *arg); diff --git a/src/nvim/eval/decode.c b/src/nvim/eval/decode.c index f57d06c83b..6ce9309677 100644 --- a/src/nvim/eval/decode.c +++ b/src/nvim/eval/decode.c @@ -439,7 +439,7 @@ static inline int parse_json_string(const char *const buf, const size_t buf_len, t += 4; uvarnumber_T ch; 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) { hasnul = true; } @@ -608,7 +608,7 @@ parse_json_number_check: // Convert integer varnumber_T nr; 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) { semsg(_("E685: internal error: while converting number \"%.*s\" " "to integer vim_str2nr consumed %i bytes in place of %zu"), diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 4208c5ca46..3a7da21606 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -8033,7 +8033,7 @@ static void f_str2nr(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) break; } 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. if (isneg) { rettv->vval.v_number = -n; diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 7d49049b0c..d077998dae 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -3890,7 +3890,7 @@ varnumber_T tv_get_number_chk(const typval_T *const tv, bool *const ret_error) case VAR_STRING: { varnumber_T n = 0; 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; } diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index 67d1a1e2f7..a12c2a15b4 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -580,7 +580,7 @@ void ex_sort(exarg_T *eap) } 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 s2 = s + end_col; c = *s2; @@ -605,7 +605,7 @@ void ex_sort(exarg_T *eap) } else { nrs[lnum - eap->line1].st_u.num.is_number = true; 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 { s = skipwhite(p); diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 4b56dfdd3f..e4cb7a5a76 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -4241,7 +4241,7 @@ int get_list_range(char **str, int *num1, int *num2) *str = skipwhite((*str)); 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; *num1 = (int)num; first = true; @@ -4249,7 +4249,7 @@ int get_list_range(char **str, int *num1, int *num2) *str = skipwhite((*str)); if (**str == ',') { // parse "to" part of range *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) { *num2 = (int)num; *str = skipwhite((*str) + len); diff --git a/src/nvim/keycodes.c b/src/nvim/keycodes.c index a3edd1465a..f3d379c2e9 100644 --- a/src/nvim/keycodes.c +++ b/src/nvim/keycodes.c @@ -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] == '_') { bp += 3; // skip t_xx, xx may be '-' or '>' } 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) { emsg(_(e_invarg)); 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 && ascii_isdigit(last_dash[6])) { // <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) { emsg(_(e_invarg)); return 0; diff --git a/src/nvim/ops.c b/src/nvim/ops.c index 6929189750..1a33ae8bbf 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -4658,11 +4658,12 @@ int do_addsub(int op_type, pos_T *pos, int length, linenr_T Prenum1) : length); } + bool overflow = false; vim_str2nr(ptr + col, &pre, &length, 0 + (do_bin ? STR2NR_BIN : 0) + (do_oct ? STR2NR_OCT : 0) + (do_hex ? STR2NR_HEX : 0), - NULL, &n, maxlen, false); + NULL, &n, maxlen, false, &overflow); // ignore leading '-' for hex, octal and bin numbers if (pre && negative) { @@ -4682,8 +4683,10 @@ int do_addsub(int op_type, pos_T *pos, int length, linenr_T Prenum1) oldn = n; - n = subtract ? n - (uvarnumber_T)Prenum1 - : n + (uvarnumber_T)Prenum1; + if (!overflow) { // if number is too big don't add/subtract + n = subtract ? n - (uvarnumber_T)Prenum1 + : n + (uvarnumber_T)Prenum1; + } // handle wraparound for decimal numbers if (!pre) { diff --git a/src/nvim/option.c b/src/nvim/option.c index 04cf7eede4..be82cf22c7 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -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)) { int i; // 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]))) { *errmsg = e_number_required_after_equal; return; diff --git a/src/nvim/testdir/test_increment.vim b/src/nvim/testdir/test_increment.vim index 3c2b88ef9f..433b2b4471 100644 --- a/src/nvim/testdir/test_increment.vim +++ b/src/nvim/testdir/test_increment.vim @@ -841,6 +841,22 @@ func Test_increment_unsigned() set nrformats-=unsigned 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() set virtualedit=all diff --git a/src/nvim/viml/parser/expressions.c b/src/nvim/viml/parser/expressions.c index 53224f2ee9..35d9de069b 100644 --- a/src/nvim/viml/parser/expressions.c +++ b/src/nvim/viml/parser/expressions.c @@ -376,7 +376,7 @@ LexExprToken viml_pexpr_next_token(ParserState *const pstate, const int flags) } if (exp_start) { 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) { exp_part += frac_size; @@ -394,7 +394,7 @@ LexExprToken viml_pexpr_next_token(ParserState *const pstate, const int flags) int len; int prep; 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; const uint8_t bases[] = { [0] = 10, diff --git a/test/unit/charset/vim_str2nr_spec.lua b/test/unit/charset/vim_str2nr_spec.lua index caf330c378..fc40fa39d4 100644 --- a/test/unit/charset/vim_str2nr_spec.lua +++ b/test/unit/charset/vim_str2nr_spec.lua @@ -63,7 +63,7 @@ local function test_vim_str2nr(s, what, exp, maxlen, strict) cv[k] = args[k] 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 if exp[cck] ~= tonumber(ccv[0]) then error(('Failed check (%s = %d) in test (s=%s, w=%u, m=%d, strict=%s): %d'):format( |