]> granicus.if.org Git - vim/commitdiff
patch 9.0.1380: CTRL-X on 2**64 subtracts two v9.0.1380
authorBram Moolenaar <Bram@vim.org>
Sat, 4 Mar 2023 20:47:39 +0000 (20:47 +0000)
committerBram Moolenaar <Bram@vim.org>
Sat, 4 Mar 2023 20:47:39 +0000 (20:47 +0000)
Problem:    CTRL-X on 2**64 subtracts two. (James McCoy)
Solution:   Correct computation for large number. (closes #12103)

12 files changed:
src/charset.c
src/ex_cmds.c
src/ex_getln.c
src/json.c
src/misc2.c
src/ops.c
src/option.c
src/proto/charset.pro
src/strings.c
src/testdir/test_increment.vim
src/typval.c
src/version.c

index 826a768ae06629afdaf969cdfdf857771d14f91d..6b2790f0eacc610a610f6a0803b2f15e63b282ab 100644 (file)
@@ -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;
        }
     }
index 0d8398062db7a0cb0481dec573a7c7ddd3880868..3ccb29ab371f0b5394fdccc5eac513d228a117dd 100644 (file)
@@ -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
index a938bd6b601e06a5cc6a407de796c91a6e4eb15f..dc933d745fe6d0132aff92d828b0dc0123f5f604 100644 (file)
@@ -4338,7 +4338,7 @@ get_list_range(char_u **str, int *num1, int *num2)
     *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, int *num2)
     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;
index 9c084b460524de5662dd902152ed588560c61748..c1602c3d835a5d479fc732020203e5779717b0b7 100644 (file)
@@ -540,7 +540,7 @@ json_decode_string(js_read_T *reader, typval_T *res, int quote)
                    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, typval_T *res, int quote)
 
                        // 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, typval_T *res, int options)
 
                            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);
index 6133a5e93b26e4243d3217b00fb745dee04dbd1a..e793987aa2f3121e22c6b7f68da707b7d0b3b187 100644 (file)
@@ -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(
            {
                // <Char-123> or <Char-033> or <Char-0x33>
                vim_str2nr(last_dash + 6, NULL, &l, STR2NR_ALL, NULL,
-                                                                 &n, 0, TRUE);
+                                                           &n, 0, TRUE, NULL);
                if (l == 0)
                {
                    emsg(_(e_invalid_argument));
index 01d2e95f85d250730f6e3d3697a1b4db24e6ce37..33fedba05a47f003abbcbf2ccd2f24aa2786903f 100644 (file)
--- 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)
        {
index 743546798ff9b800d65a4523949f1268f9e40fa2..f47b51c8746722f7370d72c49260e488ab735754 100644 (file)
@@ -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;
index 7e515be3b25805d0bd96ca071a54f9a00d960113..c915c5802fce543d8e38ef04043a8daa154c80f2 100644 (file)
@@ -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);
index 22361ca31f7180b2e051cf3c688d3b5aa2b9d57f..20997946b9e963dd817d74b8c62b3698ce60a62f 100644 (file)
@@ -1188,7 +1188,7 @@ f_str2nr(typval_T *argvars, typval_T *rettv)
        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;
index c367f2bab585588055a73529b653c9d4fd0c860e..fdd7c0ce01cc183d9130b8a506a7a01b9d671492 100644 (file)
@@ -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\<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
 
index 4033c07ccd734f6d73302f822ab8400ab86c501f..10932e5665eccb783bd18321fd518622aaead2df 100644 (file)
@@ -217,7 +217,7 @@ tv_get_bool_or_number_chk(typval_T *varp, int *denote, int want_bool)
            }
            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)
index 3f40fd180849c2bef4d342008b0e097b4f83d9c0..fbe5346868c2c68921edcbbd2ad7577f2890037f 100644 (file)
@@ -695,6 +695,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1380,
 /**/
     1379,
 /**/