changeset 25749:24b80aa4dd2e v8.2.3410

patch 8.2.3410: crash with linebreak, listchars and large tabstop Commit: https://github.com/vim/vim/commit/89a54b413a8c96206ce7e038dde81a6eff6cd6b8 Author: Bram Moolenaar <Bram@vim.org> Date: Tue Sep 7 20:45:31 2021 +0200 patch 8.2.3410: crash with linebreak, listchars and large tabstop Problem: Crash with linebreak, listchars and large tabstop. Solution: Account for different size listchars for a tab. (closes https://github.com/vim/vim/issues/8841)
author Bram Moolenaar <Bram@vim.org>
date Tue, 07 Sep 2021 21:00:05 +0200
parents 8d0ecfbcc62a
children 06e0400d9d69
files src/drawline.c src/testdir/test_listlbr_utf8.vim src/version.c
diffstat 3 files changed, 47 insertions(+), 30 deletions(-) [+]
line wrap: on
line diff
--- a/src/drawline.c
+++ b/src/drawline.c
@@ -2109,55 +2109,60 @@ win_line(
 			int	i;
 			int	saved_nextra = n_extra;
 
-#ifdef FEAT_CONCEAL
+# ifdef FEAT_CONCEAL
 			if (vcol_off > 0)
 			    // there are characters to conceal
 			    tab_len += vcol_off;
+
 			// boguscols before FIX_FOR_BOGUSCOLS macro from above
 			if (wp->w_p_list && wp->w_lcs_chars.tab1
 							&& old_boguscols > 0
 							&& n_extra > tab_len)
 			    tab_len += n_extra - tab_len;
-#endif
-
-			// if n_extra > 0, it gives the number of chars, to
+# endif
+			// If n_extra > 0, it gives the number of chars, to
 			// use for a tab, else we need to calculate the width
-			// for a tab
+			// for a tab.
 			len = (tab_len * mb_char2len(wp->w_lcs_chars.tab2));
+			if (wp->w_lcs_chars.tab3)
+			    len += mb_char2len(wp->w_lcs_chars.tab3);
 			if (n_extra > 0)
 			    len += n_extra - tab_len;
 			c = wp->w_lcs_chars.tab1;
 			p = alloc(len + 1);
-			vim_memset(p, ' ', len);
-			p[len] = NUL;
-			vim_free(p_extra_free);
-			p_extra_free = p;
-			for (i = 0; i < tab_len; i++)
+			if (p == NULL)
+			    n_extra = 0;
+			else
 			{
-			    int lcs = wp->w_lcs_chars.tab2;
-
-			    if (*p == NUL)
+			    vim_memset(p, ' ', len);
+			    p[len] = NUL;
+			    vim_free(p_extra_free);
+			    p_extra_free = p;
+			    for (i = 0; i < tab_len; i++)
 			    {
-				tab_len = i;
-				break;
-			    }
+				int lcs = wp->w_lcs_chars.tab2;
 
-			    // if tab3 is given, need to change the char
-			    // for tab
-			    if (wp->w_lcs_chars.tab3 && i == tab_len - 1)
-				lcs = wp->w_lcs_chars.tab3;
-			    mb_char2bytes(lcs, p);
-			    p += mb_char2len(lcs);
-			    n_extra += mb_char2len(lcs)
+				if (*p == NUL)
+				{
+				    tab_len = i;
+				    break;
+				}
+
+				// if tab3 is given, use it for the last char
+				if (wp->w_lcs_chars.tab3 && i == tab_len - 1)
+				    lcs = wp->w_lcs_chars.tab3;
+				p += mb_char2bytes(lcs, p);
+				n_extra += mb_char2len(lcs)
 						  - (saved_nextra > 0 ? 1 : 0);
+			    }
+			    p_extra = p_extra_free;
+# ifdef FEAT_CONCEAL
+			    // n_extra will be increased by FIX_FOX_BOGUSCOLS
+			    // macro below, so need to adjust for that here
+			    if (vcol_off > 0)
+				n_extra -= vcol_off;
+# endif
 			}
-			p_extra = p_extra_free;
-#ifdef FEAT_CONCEAL
-			// n_extra will be increased by FIX_FOX_BOGUSCOLS
-			// macro below, so need to adjust for that here
-			if (vcol_off > 0)
-			    n_extra -= vcol_off;
-#endif
 		    }
 #endif
 #ifdef FEAT_CONCEAL
--- a/src/testdir/test_listlbr_utf8.vim
+++ b/src/testdir/test_listlbr_utf8.vim
@@ -70,6 +70,16 @@ func Test_nolinebreak_with_list()
   call s:close_windows()
 endfunc
 
+" this was causing a crash
+func Test_linebreak_with_list_and_tabs()
+  set linebreak list listchars=tab:⇤\ ⇥ tabstop=100
+  new
+  call setline(1, "\t\t\ttext")
+  redraw
+  bwipe!
+  set nolinebreak nolist listchars&vim tabstop=8
+endfunc
+
 func Test_linebreak_with_nolist()
   call s:test_windows('setl nolist')
   call setline(1, "\t*mask = nil;")
--- a/src/version.c
+++ b/src/version.c
@@ -756,6 +756,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    3410,
+/**/
     3409,
 /**/
     3408,