# HG changeset patch # User Bram Moolenaar # Date 1677963603 -3600 # Node ID 39f4126d2a0d82d27d23c7ee17810750d9fdc17c # Parent 79439f353fa03c813e5a31ed61269ea3d2d90564 patch 9.0.1380: CTRL-X on 2**64 subtracts two Commit: https://github.com/vim/vim/commit/5fb78c3fa5c996c08a65431d698bd2c251eef5c7 Author: Bram Moolenaar Date: Sat Mar 4 20:47:39 2023 +0000 patch 9.0.1380: CTRL-X on 2**64 subtracts two Problem: CTRL-X on 2**64 subtracts two. (James McCoy) Solution: Correct computation for large number. (closes https://github.com/vim/vim/issues/12103) diff --git a/src/charset.c b/src/charset.c --- a/src/charset.c +++ b/src/charset.c @@ -2138,7 +2138,8 @@ vim_str2nr( varnumber_T *nptr, // return: signed result uvarnumber_T *unptr, // return: unsigned result int maxlen, // max length of string to check - int strict) // check strictly + int strict, // check strictly + int *overflow) // when not NULL set to TRUE for overflow { char_u *ptr = start; int pre = 0; // default is decimal @@ -2209,7 +2210,11 @@ vim_str2nr( if (un <= UVARNUM_MAX / 2) un = 2 * un + (uvarnumber_T)(*ptr - '0'); else + { un = UVARNUM_MAX; + if (overflow != NULL) + *overflow = TRUE; + } ++ptr; if (n++ == maxlen) break; @@ -2234,7 +2239,11 @@ vim_str2nr( if (un <= UVARNUM_MAX / 8) un = 8 * un + (uvarnumber_T)(*ptr - '0'); else + { un = UVARNUM_MAX; + if (overflow != NULL) + *overflow = TRUE; + } ++ptr; if (n++ == maxlen) break; @@ -2258,7 +2267,11 @@ vim_str2nr( if (un <= UVARNUM_MAX / 16) un = 16 * un + (uvarnumber_T)hex2nr(*ptr); else + { un = UVARNUM_MAX; + if (overflow != NULL) + *overflow = TRUE; + } ++ptr; if (n++ == maxlen) break; @@ -2282,7 +2295,11 @@ vim_str2nr( || (un == UVARNUM_MAX / 10 && digit <= UVARNUM_MAX % 10)) un = 10 * un + digit; else + { un = UVARNUM_MAX; + if (overflow != NULL) + *overflow = TRUE; + } ++ptr; if (n++ == maxlen) break; @@ -2310,7 +2327,11 @@ vim_str2nr( { // avoid ubsan error for overflow if (un > VARNUM_MAX) + { *nptr = VARNUM_MIN; + if (overflow != NULL) + *overflow = TRUE; + } else *nptr = -(varnumber_T)un; } @@ -2318,7 +2339,11 @@ vim_str2nr( { // prevent a large unsigned number to become negative if (un > VARNUM_MAX) + { un = VARNUM_MAX; + if (overflow != NULL) + *overflow = TRUE; + } *nptr = (varnumber_T)un; } } diff --git a/src/ex_cmds.c b/src/ex_cmds.c --- a/src/ex_cmds.c +++ b/src/ex_cmds.c @@ -511,7 +511,7 @@ 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; @@ -539,7 +539,7 @@ ex_sort(exarg_T *eap) 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); + NULL, 0, FALSE, NULL); } } else diff --git a/src/ex_getln.c b/src/ex_getln.c --- a/src/ex_getln.c +++ b/src/ex_getln.c @@ -4338,7 +4338,7 @@ get_list_range(char_u **str, int *num1, *str = skipwhite(*str); if (**str == '-' || vim_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; @@ -4347,7 +4347,7 @@ get_list_range(char_u **str, int *num1, 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; diff --git a/src/json.c b/src/json.c --- a/src/json.c +++ b/src/json.c @@ -540,7 +540,7 @@ json_decode_string(js_read_T *reader, ty nr = 0; len = 0; vim_str2nr(p + 2, NULL, &len, - STR2NR_HEX + STR2NR_FORCE, &nr, NULL, 4, TRUE); + STR2NR_HEX + STR2NR_FORCE, &nr, NULL, 4, TRUE, NULL); if (len == 0) { if (res != NULL) @@ -556,8 +556,8 @@ json_decode_string(js_read_T *reader, ty // decode surrogate pair: \ud812\u3456 len = 0; - vim_str2nr(p + 2, NULL, &len, - STR2NR_HEX + STR2NR_FORCE, &nr2, NULL, 4, TRUE); + vim_str2nr(p + 2, NULL, &len, STR2NR_HEX + STR2NR_FORCE, + &nr2, NULL, 4, TRUE, NULL); if (len == 0) { if (res != NULL) @@ -882,7 +882,7 @@ json_decode_item(js_read_T *reader, typv vim_str2nr(reader->js_buf + reader->js_used, NULL, &len, 0, // what - &nr, NULL, 0, TRUE); + &nr, NULL, 0, TRUE, NULL); if (len == 0) { semsg(_(e_json_decode_error_at_str), p); diff --git a/src/misc2.c b/src/misc2.c --- a/src/misc2.c +++ b/src/misc2.c @@ -1410,7 +1410,7 @@ find_special_key( bp += 3; // skip t_xx, xx may be '-' or '>' else if (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_invalid_argument)); @@ -1448,7 +1448,7 @@ find_special_key( { // or or vim_str2nr(last_dash + 6, NULL, &l, STR2NR_ALL, NULL, - &n, 0, TRUE); + &n, 0, TRUE, NULL); if (l == 0) { emsg(_(e_invalid_argument)); diff --git a/src/ops.c b/src/ops.c --- a/src/ops.c +++ b/src/ops.c @@ -2781,11 +2781,12 @@ do_addsub( ? (int)STRLEN(ptr) - col : length); + int 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 and octal and bin numbers if (pre && negative) @@ -2802,10 +2803,14 @@ do_addsub( subtract ^= TRUE; oldn = n; - if (subtract) - n -= (uvarnumber_T)Prenum1; - else - n += (uvarnumber_T)Prenum1; + if (!overflow) // if number is too big don't add/subtract + { + if (subtract) + n -= (uvarnumber_T)Prenum1; + else + n += (uvarnumber_T)Prenum1; + } + // handle wraparound for decimal numbers if (!pre) { diff --git a/src/option.c b/src/option.c --- a/src/option.c +++ b/src/option.c @@ -2157,7 +2157,7 @@ do_set_option_numeric( else if (*arg == '-' || VIM_ISDIGIT(*arg)) { // Allow negative (for 'undolevels'), 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 && !VIM_ISWHITE(arg[i]))) { errmsg = e_number_required_after_equal; diff --git a/src/proto/charset.pro b/src/proto/charset.pro --- a/src/proto/charset.pro +++ b/src/proto/charset.pro @@ -64,7 +64,7 @@ char_u *skiptowhite_esc(char_u *p); long getdigits(char_u **pp); long getdigits_quoted(char_u **pp); int vim_isblankline(char_u *lbuf); -void vim_str2nr(char_u *start, int *prep, int *len, int what, varnumber_T *nptr, uvarnumber_T *unptr, int maxlen, int strict); +void vim_str2nr(char_u *start, int *prep, int *len, int what, varnumber_T *nptr, uvarnumber_T *unptr, int maxlen, int strict, int *overflow); int hex2nr(int c); int hexhex2nr(char_u *p); int rem_backslash(char_u *str); diff --git a/src/strings.c b/src/strings.c --- a/src/strings.c +++ b/src/strings.c @@ -1188,7 +1188,7 @@ f_str2nr(typval_T *argvars, typval_T *re case 8: what |= STR2NR_OCT + STR2NR_OOCT + STR2NR_FORCE; break; case 16: what |= STR2NR_HEX + STR2NR_FORCE; break; } - 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/testdir/test_increment.vim b/src/testdir/test_increment.vim --- a/src/testdir/test_increment.vim +++ b/src/testdir/test_increment.vim @@ -840,6 +840,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\" + call assert_equal('18446744073709551615', getline(1)) + + exec "norm! gg0\" + call assert_equal('18446744073709551614', getline(1)) + + exec "norm! gg0\" + call assert_equal('18446744073709551615', getline(1)) + + exec "norm! gg0\" + call assert_equal('-18446744073709551615', getline(1)) +endfunc + func Test_normal_increment_with_virtualedit() set virtualedit=all diff --git a/src/typval.c b/src/typval.c --- a/src/typval.c +++ b/src/typval.c @@ -217,7 +217,7 @@ tv_get_bool_or_number_chk(typval_T *varp } if (varp->vval.v_string != NULL) vim_str2nr(varp->vval.v_string, NULL, NULL, - STR2NR_ALL, &n, NULL, 0, FALSE); + STR2NR_ALL, &n, NULL, 0, FALSE, NULL); return n; case VAR_LIST: emsg(_(e_using_list_as_number)); @@ -2230,7 +2230,7 @@ eval_number( // decimal, hex or octal number vim_str2nr(*arg, NULL, &len, skip_quotes ? STR2NR_NO_OCT + STR2NR_QUOTE - : STR2NR_ALL, &n, NULL, 0, TRUE); + : STR2NR_ALL, &n, NULL, 0, TRUE, NULL); if (len == 0) { if (evaluate) diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -696,6 +696,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 1380, +/**/ 1379, /**/ 1378,