]> granicus.if.org Git - vim/commitdiff
patch 9.0.0045: reading past end of completion with a long line v9.0.0045
authorBram Moolenaar <Bram@vim.org>
Thu, 7 Jul 2022 18:42:04 +0000 (19:42 +0100)
committerBram Moolenaar <Bram@vim.org>
Thu, 7 Jul 2022 18:42:04 +0000 (19:42 +0100)
Problem:    Reading past end of completion with a long line and 'infercase'
            set.
Solution:   Allocate the string if needed.

src/insexpand.c
src/testdir/test_ins_complete.vim
src/version.c

index 734550ffd231f7f145d555a441783a39e1fa004a..0ecb6560c5fc32a5578db520fb3e036ddf316018 100644 (file)
@@ -524,29 +524,32 @@ ins_compl_accept_char(int c)
 
 /*
  * Get the completed text by inferring the case of the originally typed text.
+ * If the result is in allocated memory "tofree" is set to it.
  */
     static char_u *
 ins_compl_infercase_gettext(
        char_u  *str,
-       int     actual_len,
-       int     actual_compl_length,
-       int     min_len)
+       int     char_len,
+       int     compl_char_len,
+       int     min_len,
+       char_u  **tofree)
 {
     int                *wca;                   // Wide character array.
     char_u     *p;
     int                i, c;
     int                has_lower = FALSE;
     int                was_letter = FALSE;
+    garray_T   gap;
 
     IObuff[0] = NUL;
 
     // Allocate wide character array for the completion and fill it.
-    wca = ALLOC_MULT(int, actual_len);
+    wca = ALLOC_MULT(int, char_len);
     if (wca == NULL)
        return IObuff;
 
     p = str;
-    for (i = 0; i < actual_len; ++i)
+    for (i = 0; i < char_len; ++i)
        if (has_mbyte)
            wca[i] = mb_ptr2char_adv(&p);
        else
@@ -566,7 +569,7 @@ ins_compl_infercase_gettext(
            if (MB_ISUPPER(wca[i]))
            {
                // Rule 1 is satisfied.
-               for (i = actual_compl_length; i < actual_len; ++i)
+               for (i = compl_char_len; i < char_len; ++i)
                    wca[i] = MB_TOLOWER(wca[i]);
                break;
            }
@@ -587,7 +590,7 @@ ins_compl_infercase_gettext(
            if (was_letter && MB_ISUPPER(c) && MB_ISLOWER(wca[i]))
            {
                // Rule 2 is satisfied.
-               for (i = actual_compl_length; i < actual_len; ++i)
+               for (i = compl_char_len; i < char_len; ++i)
                    wca[i] = MB_TOUPPER(wca[i]);
                break;
            }
@@ -610,20 +613,52 @@ ins_compl_infercase_gettext(
     }
 
     // Generate encoding specific output from wide character array.
-    // Multi-byte characters can occupy up to five bytes more than
-    // ASCII characters, and we also need one byte for NUL, so stay
-    // six bytes away from the edge of IObuff.
     p = IObuff;
     i = 0;
-    while (i < actual_len && (p - IObuff + 6) < IOSIZE)
-       if (has_mbyte)
+    ga_init2(&gap, 1, 500);
+    while (i < char_len)
+    {
+       if (gap.ga_data != NULL)
+       {
+           if (ga_grow(&gap, 10) == FAIL)
+           {
+               ga_clear(&gap);
+               return (char_u *)"[failed]";
+           }
+           p = (char_u *)gap.ga_data + gap.ga_len;
+           if (has_mbyte)
+               gap.ga_len += (*mb_char2bytes)(wca[i++], p);
+           else
+           {
+               *p = wca[i++];
+               ++gap.ga_len;
+           }
+       }
+       else if ((p - IObuff) + 6 >= IOSIZE)
+       {
+           // Multi-byte characters can occupy up to five bytes more than
+           // ASCII characters, and we also need one byte for NUL, so when
+           // getting to six bytes from the edge of IObuff switch to using a
+           // growarray.  Add the character in the next round.
+           if (ga_grow(&gap, IOSIZE) == FAIL)
+               return (char_u *)"[failed]";
+           STRCPY(gap.ga_data, IObuff);
+           gap.ga_len = STRLEN(IObuff);
+       }
+       else if (has_mbyte)
            p += (*mb_char2bytes)(wca[i++], p);
        else
            *(p++) = wca[i++];
-    *p = NUL;
-
+    }
     vim_free(wca);
 
+    if (gap.ga_data != NULL)
+    {
+       *tofree = gap.ga_data;
+       return gap.ga_data;
+    }
+
+    *p = NUL;
     return IObuff;
 }
 
@@ -644,10 +679,12 @@ ins_compl_add_infercase(
 {
     char_u     *str = str_arg;
     char_u     *p;
-    int                actual_len;             // Take multi-byte characters
-    int                actual_compl_length;    // into account.
+    int                char_len;               // count multi-byte characters
+    int                compl_char_len;
     int                min_len;
     int                flags = 0;
+    int                res;
+    char_u     *tofree = NULL;
 
     if (p_ic && curbuf->b_p_inf && len > 0)
     {
@@ -657,44 +694,45 @@ ins_compl_add_infercase(
        if (has_mbyte)
        {
            p = str;
-           actual_len = 0;
+           char_len = 0;
            while (*p != NUL)
            {
                MB_PTR_ADV(p);
-               ++actual_len;
+               ++char_len;
            }
        }
        else
-           actual_len = len;
+           char_len = len;
 
        // Find actual length of original text.
        if (has_mbyte)
        {
            p = compl_orig_text;
-           actual_compl_length = 0;
+           compl_char_len = 0;
            while (*p != NUL)
            {
                MB_PTR_ADV(p);
-               ++actual_compl_length;
+               ++compl_char_len;
            }
        }
        else
-           actual_compl_length = compl_length;
+           compl_char_len = compl_length;
 
-       // "actual_len" may be smaller than "actual_compl_length" when using
+       // "char_len" may be smaller than "compl_char_len" when using
        // thesaurus, only use the minimum when comparing.
-       min_len = actual_len < actual_compl_length
-                                          ? actual_len : actual_compl_length;
+       min_len = char_len < compl_char_len ? char_len : compl_char_len;
 
-       str = ins_compl_infercase_gettext(str, actual_len, actual_compl_length,
-                                                               min_len);
+       str = ins_compl_infercase_gettext(str, char_len,
+                                         compl_char_len, min_len, &tofree);
     }
     if (cont_s_ipos)
        flags |= CP_CONT_S_IPOS;
     if (icase)
        flags |= CP_ICASE;
 
-    return ins_compl_add(str, len, fname, NULL, NULL, dir, flags, FALSE);
+    res = ins_compl_add(str, len, fname, NULL, NULL, dir, flags, FALSE);
+    vim_free(tofree);
+    return res;
 }
 
 /*
index baf9ef1711fe581146dd2e7590ec610c4ceec8aa..5e5b1bbece681fa888132d713ebc3111eb98686e 100644 (file)
@@ -2097,4 +2097,20 @@ func Test_complete_overrun()
   bwipe!
 endfunc
 
+func Test_infercase_very_long_line()
+  " this was truncating the line when inferring case
+  new
+  let longLine = "blah "->repeat(300)
+  let verylongLine = "blah "->repeat(400)
+  call setline(1, verylongLine)
+  call setline(2, longLine)
+  set ic infercase
+  exe "normal 2Go\<C-X>\<C-L>\<Esc>"
+  call assert_equal(longLine, getline(3))
+
+  bwipe!
+  set noic noinfercase
+endfunc
+
+
 " vim: shiftwidth=2 sts=2 expandtab
index 31ece0e6b2d42b558e6ccde78da54023a5472a13..75de2fc7b75145f03b7637133a9cb0557bdeaa6b 100644 (file)
@@ -735,6 +735,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    45,
 /**/
     44,
 /**/