patch 8.2.3227: 'virtualedit' can only be set globally

Problem:    'virtualedit' can only be set globally.
Solution:   Make 'virtualedit' global-local. (Gary Johnson, closes #8638)
diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt
index e5a76b7..e0c14a8 100644
--- a/runtime/doc/options.txt
+++ b/runtime/doc/options.txt
@@ -8643,12 +8643,16 @@
 
 					    *'virtualedit'* *'ve'*
 'virtualedit' 've'	string	(default "")
-			global
+			global or local to buffer |global-local|
 	A comma separated list of these words:
 	    block	Allow virtual editing in Visual block mode.
 	    insert	Allow virtual editing in Insert mode.
 	    all		Allow virtual editing in all modes.
 	    onemore	Allow the cursor to move just past the end of the line
+	    none	When used as the local value, do not allow virtual
+			editing even when the global value is set.  When used
+			as the global value, "none" is the same as "".
+	    NONE	Alternative spelling of "none".
 
 	Virtual editing means that the cursor can be positioned where there is
 	no actual character.  This can be halfway into a tab or beyond the end
diff --git a/src/buffer.c b/src/buffer.c
index 25efea1..59397e4 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -2384,6 +2384,7 @@
 #endif
     clear_string_option(&buf->b_p_bkc);
     clear_string_option(&buf->b_p_menc);
+    clear_string_option(&buf->b_p_ve);
 }
 
 /*
diff --git a/src/change.c b/src/change.c
index ee1bd6f..f77b48c 100644
--- a/src/change.c
+++ b/src/change.c
@@ -1257,7 +1257,7 @@
 	// fixpos is TRUE, we don't want to end up positioned at the NUL,
 	// unless "restart_edit" is set or 'virtualedit' contains "onemore".
 	if (col > 0 && fixpos && restart_edit == 0
-					      && (ve_flags & VE_ONEMORE) == 0)
+					      && (get_ve_flags() & VE_ONEMORE) == 0)
 	{
 	    --curwin->w_cursor.col;
 	    curwin->w_cursor.coladd = 0;
diff --git a/src/drawscreen.c b/src/drawscreen.c
index 49615bd..36aad63 100644
--- a/src/drawscreen.c
+++ b/src/drawscreen.c
@@ -2006,21 +2006,21 @@
 	    {
 		colnr_T	    fromc, toc;
 #if defined(FEAT_LINEBREAK)
-		int	    save_ve_flags = ve_flags;
+		int	    save_ve_flags = curbuf->b_ve_flags;
 
 		if (curwin->w_p_lbr)
-		    ve_flags = VE_ALL;
+		    curbuf->b_ve_flags = VE_ALL;
 #endif
 		getvcols(wp, &VIsual, &curwin->w_cursor, &fromc, &toc);
 		++toc;
 #if defined(FEAT_LINEBREAK)
-		ve_flags = save_ve_flags;
+		curbuf->b_ve_flags = save_ve_flags;
 #endif
 		// Highlight to the end of the line, unless 'virtualedit' has
 		// "block".
 		if (curwin->w_curswant == MAXCOL)
 		{
-		    if (ve_flags & VE_BLOCK)
+		    if (get_ve_flags() & VE_BLOCK)
 		    {
 			pos_T	    pos;
 			int	    cursor_above =
diff --git a/src/edit.c b/src/edit.c
index 267c76d..6bdeded 100644
--- a/src/edit.c
+++ b/src/edit.c
@@ -861,7 +861,7 @@
 	    ins_ctrl_o();
 
 	    // don't move the cursor left when 'virtualedit' has "onemore".
-	    if (ve_flags & VE_ONEMORE)
+	    if (get_ve_flags() & VE_ONEMORE)
 	    {
 		ins_at_eol = FALSE;
 		nomove = TRUE;
@@ -2673,7 +2673,7 @@
 
     // move "l" bytes right, but don't end up on the NUL, unless 'virtualedit'
     // contains "onemore".
-    if (ptr[l] == NUL && (ve_flags & VE_ONEMORE) == 0)
+    if (ptr[l] == NUL && (get_ve_flags() & VE_ONEMORE) == 0)
 	return FAIL;
     curwin->w_cursor.col += l;
 
@@ -3656,7 +3656,7 @@
 #endif
 				      )
     {
-	if (curwin->w_cursor.coladd > 0 || ve_flags == VE_ALL)
+	if (curwin->w_cursor.coladd > 0 || get_ve_flags() == VE_ALL)
 	{
 	    oneleft();
 	    if (restart_edit != NUL)
diff --git a/src/misc2.c b/src/misc2.c
index a8e34df..8e99b01 100644
--- a/src/misc2.c
+++ b/src/misc2.c
@@ -22,14 +22,16 @@
     int
 virtual_active(void)
 {
+    unsigned int cur_ve_flags = get_ve_flags();
+
     // While an operator is being executed we return "virtual_op", because
     // VIsual_active has already been reset, thus we can't check for "block"
     // being used.
     if (virtual_op != MAYBE)
 	return virtual_op;
-    return (ve_flags == VE_ALL
-	    || ((ve_flags & VE_BLOCK) && VIsual_active && VIsual_mode == Ctrl_V)
-	    || ((ve_flags & VE_INSERT) && (State & INSERT)));
+    return (cur_ve_flags == VE_ALL
+	    || ((cur_ve_flags & VE_BLOCK) && VIsual_active && VIsual_mode == Ctrl_V)
+	    || ((cur_ve_flags & VE_INSERT) && (State & INSERT)));
 }
 
 /*
@@ -137,7 +139,7 @@
     one_more = (State & INSERT)
 		    || restart_edit != NUL
 		    || (VIsual_active && *p_sel != 'o')
-		    || ((ve_flags & VE_ONEMORE) && wcol < MAXCOL);
+		    || ((get_ve_flags() & VE_ONEMORE) && wcol < MAXCOL);
     line = ml_get_buf(curbuf, pos->lnum, FALSE);
 
     if (wcol >= MAXCOL)
@@ -549,9 +551,10 @@
     void
 check_cursor_col_win(win_T *win)
 {
-    colnr_T len;
-    colnr_T oldcol = win->w_cursor.col;
-    colnr_T oldcoladd = win->w_cursor.col + win->w_cursor.coladd;
+    colnr_T      len;
+    colnr_T      oldcol = win->w_cursor.col;
+    colnr_T      oldcoladd = win->w_cursor.col + win->w_cursor.coladd;
+    unsigned int cur_ve_flags = get_ve_flags();
 
     len = (colnr_T)STRLEN(ml_get_buf(win->w_buffer, win->w_cursor.lnum, FALSE));
     if (len == 0)
@@ -564,7 +567,7 @@
 	// - 'virtualedit' is set
 	if ((State & INSERT) || restart_edit
 		|| (VIsual_active && *p_sel != 'o')
-		|| (ve_flags & VE_ONEMORE)
+		|| (cur_ve_flags & VE_ONEMORE)
 		|| virtual_active())
 	    win->w_cursor.col = len;
 	else
@@ -583,7 +586,7 @@
     // line.
     if (oldcol == MAXCOL)
 	win->w_cursor.coladd = 0;
-    else if (ve_flags == VE_ALL)
+    else if (cur_ve_flags == VE_ALL)
     {
 	if (oldcoladd > win->w_cursor.col)
 	{
diff --git a/src/normal.c b/src/normal.c
index 56f89f5..74c76b2 100644
--- a/src/normal.c
+++ b/src/normal.c
@@ -5757,7 +5757,7 @@
 
     // Corner case: the 0 position in a tab may change when going into
     // virtualedit.  Recalculate curwin->w_cursor to avoid bad highlighting.
-    if (c == Ctrl_V && (ve_flags & VE_BLOCK) && gchar_cursor() == TAB)
+    if (c == Ctrl_V && (get_ve_flags() & VE_BLOCK) && gchar_cursor() == TAB)
     {
 	validate_virtcol();
 	coladvance(curwin->w_virtcol);
@@ -6780,7 +6780,7 @@
     // - 'virtualedit' is not "all" and not "onemore".
     if (curwin->w_cursor.col > 0 && gchar_cursor() == NUL
 		&& (!VIsual_active || *p_sel == 'o')
-		&& !virtual_active() && (ve_flags & VE_ONEMORE) == 0)
+		&& !virtual_active() && (get_ve_flags() & VE_ONEMORE) == 0)
     {
 	--curwin->w_cursor.col;
 	// prevent cursor from moving on the trail byte
@@ -7014,7 +7014,7 @@
 set_cursor_for_append_to_line(void)
 {
     curwin->w_set_curswant = TRUE;
-    if (ve_flags == VE_ALL)
+    if (get_ve_flags() == VE_ALL)
     {
 	int save_State = State;
 
diff --git a/src/ops.c b/src/ops.c
index 59d5bff..75619c5 100644
--- a/src/ops.c
+++ b/src/ops.c
@@ -1474,18 +1474,22 @@
 	// doing block_prep().  When only "block" is used, virtual edit is
 	// already disabled, but still need it when calling
 	// coladvance_force().
+	// coladvance_force() uses get_ve_flags() to get the 'virtualedit'
+	// state for the current buffer.  To override that state, we need to
+	// set the buffer-local value of ve_flags rather than the global value.
 	if (curwin->w_cursor.coladd > 0)
 	{
-	    int		old_ve_flags = ve_flags;
+	    int		old_ve_flags = curbuf->b_ve_flags;
 
-	    ve_flags = VE_ALL;
 	    if (u_save_cursor() == FAIL)
 		return;
+
+	    curbuf->b_ve_flags = VE_ALL;
 	    coladvance_force(oap->op_type == OP_APPEND
 					   ? oap->end_vcol + 1 : getviscol());
 	    if (oap->op_type == OP_APPEND)
 		--curwin->w_cursor.col;
-	    ve_flags = old_ve_flags;
+	    curbuf->b_ve_flags = old_ve_flags;
 	}
 	// Get the info about the block before entering the text
 	block_prep(oap, &bd, oap->start.lnum, TRUE);
@@ -1816,15 +1820,17 @@
     void
 adjust_cursor_eol(void)
 {
+    unsigned int cur_ve_flags = get_ve_flags();
+
     if (curwin->w_cursor.col > 0
 	    && gchar_cursor() == NUL
-	    && (ve_flags & VE_ONEMORE) == 0
+	    && (cur_ve_flags & VE_ONEMORE) == 0
 	    && !(restart_edit || (State & INSERT)))
     {
 	// Put the cursor on the last character in the line.
 	dec_cursor();
 
-	if (ve_flags == VE_ALL)
+	if (cur_ve_flags == VE_ALL)
 	{
 	    colnr_T	    scol, ecol;
 
diff --git a/src/option.c b/src/option.c
index 21d113e..182ff34 100644
--- a/src/option.c
+++ b/src/option.c
@@ -5181,6 +5181,10 @@
 	    set_chars_option((win_T *)from, &((win_T *)from)->w_p_lcs);
 	    redraw_later(NOT_VALID);
 	    break;
+	case PV_VE:
+	    clear_string_option(&buf->b_p_ve);
+	    buf->b_ve_flags = 0;
+	    break;
     }
 }
 #endif
@@ -5239,7 +5243,8 @@
 #endif
 	    case PV_BKC:  return (char_u *)&(curbuf->b_p_bkc);
 	    case PV_MENC: return (char_u *)&(curbuf->b_p_menc);
-	    case PV_LCS: return (char_u *)&(curwin->w_p_lcs);
+	    case PV_LCS:  return (char_u *)&(curwin->w_p_lcs);
+	    case PV_VE:	  return (char_u *)&(curbuf->b_p_ve);
 
 	}
 	return NULL; // "cannot happen"
@@ -5507,6 +5512,8 @@
 	case PV_VSTS:	return (char_u *)&(curbuf->b_p_vsts);
 	case PV_VTS:	return (char_u *)&(curbuf->b_p_vts);
 #endif
+	case PV_VE:	return *curbuf->b_p_ve != NUL
+				    ? (char_u *)&(curbuf->b_p_ve) : p->var;
 	default:	iemsg(_("E356: get_varp ERROR"));
     }
     // always return a valid pointer to avoid a crash!
@@ -6084,6 +6091,8 @@
 	    buf->b_p_lw = empty_option;
 #endif
 	    buf->b_p_menc = empty_option;
+	    buf->b_p_ve = empty_option;
+	    buf->b_ve_flags = 0;
 
 	    /*
 	     * Don't copy the options set by ex_help(), use the saved values,
@@ -7026,6 +7035,16 @@
     return buf->b_bkc_flags ? buf->b_bkc_flags : bkc_flags;
 }
 
+/*
+ * Get the local or global value of the 'virtualedit' flags.
+ */
+    unsigned int
+get_ve_flags(void)
+{
+    return (curbuf->b_ve_flags ? curbuf->b_ve_flags : ve_flags)
+	   & ~(VE_NONE | VE_NONEU);
+}
+
 #if defined(FEAT_LINEBREAK) || defined(PROTO)
 /*
  * Get the local or global value of 'showbreak'.
diff --git a/src/option.h b/src/option.h
index 7be729a..5732e82 100644
--- a/src/option.h
+++ b/src/option.h
@@ -1052,6 +1052,8 @@
 #define VE_INSERT	6	// includes "all"
 #define VE_ALL		4
 #define VE_ONEMORE	8
+#define VE_NONE		16
+#define VE_NONEU	32      // Upper-case NONE
 EXTERN long	p_verbose;	// 'verbose'
 #ifdef IN_OPTION_C
 char_u	*p_vfile = (char_u *)""; // used before options are initialized
@@ -1228,6 +1230,7 @@
     , BV_VSTS
     , BV_VTS
 #endif
+    , BV_VE
     , BV_COUNT	    // must be the last one
 };
 
diff --git a/src/optiondefs.h b/src/optiondefs.h
index 782e4ef..ec89558 100644
--- a/src/optiondefs.h
+++ b/src/optiondefs.h
@@ -153,6 +153,7 @@
 # define PV_VSTS		OPT_BUF(BV_VSTS)
 # define PV_VTS		OPT_BUF(BV_VTS)
 #endif
+#define PV_VE		OPT_BOTH(OPT_BUF(BV_VE))
 
 // Definition of the PV_ values for window-local options.
 // The WV_ values are defined in option.h.
@@ -2806,7 +2807,7 @@
 			    SCTX_INIT},
     {"virtualedit", "ve",   P_STRING|P_ONECOMMA|P_NODUP|P_VI_DEF
 							    |P_VIM|P_CURSWANT,
-			    (char_u *)&p_ve, PV_NONE,
+			    (char_u *)&p_ve, PV_VE,
 			    {(char_u *)"", (char_u *)""}
 			    SCTX_INIT},
     {"visualbell",  "vb",   P_BOOL|P_VI_DEF,
diff --git a/src/optionstr.c b/src/optionstr.c
index 4c1cacc..c22a441 100644
--- a/src/optionstr.c
+++ b/src/optionstr.c
@@ -56,7 +56,7 @@
 #if defined(UNIX) || defined(VMS)
 static char *(p_ttym_values[]) = {"xterm", "xterm2", "dec", "netterm", "jsbterm", "pterm", "urxvt", "sgr", NULL};
 #endif
-static char *(p_ve_values[]) = {"block", "insert", "all", "onemore", NULL};
+static char *(p_ve_values[]) = {"block", "insert", "all", "onemore", "none", "NONE", NULL};
 static char *(p_wop_values[]) = {"tagfile", NULL};
 #ifdef FEAT_WAK
 static char *(p_wak_values[]) = {"yes", "menu", "no", NULL};
@@ -298,6 +298,7 @@
     check_string_option(&buf->b_p_vsts);
     check_string_option(&buf->b_p_vts);
 #endif
+    check_string_option(&buf->b_p_ve);
 }
 
 /*
@@ -2075,16 +2076,31 @@
 #endif
 
     // 'virtualedit'
-    else if (varp == &p_ve)
+    else if (gvarp == &p_ve)
     {
-	if (opt_strings_flags(p_ve, p_ve_values, &ve_flags, TRUE) != OK)
-	    errmsg = e_invarg;
-	else if (STRCMP(p_ve, oldval) != 0)
+	char_u		*ve = p_ve;
+	unsigned int	*flags = &ve_flags;
+
+	if (opt_flags & OPT_LOCAL)
 	{
-	    // Recompute cursor position in case the new 've' setting
-	    // changes something.
-	    validate_virtcol();
-	    coladvance(curwin->w_virtcol);
+	    ve = curbuf->b_p_ve;
+	    flags = &curbuf->b_ve_flags;
+	}
+
+	if ((opt_flags & OPT_LOCAL) && *ve == NUL)
+	    // make the local value empty: use the global value
+	    *flags = 0;
+	else
+	{
+	    if (opt_strings_flags(ve, p_ve_values, flags, TRUE) != OK)
+		errmsg = e_invarg;
+	    else if (STRCMP(p_ve, oldval) != 0)
+	    {
+		// Recompute cursor position in case the new 've' setting
+		// changes something.
+		validate_virtcol();
+		coladvance(curwin->w_virtcol);
+	    }
 	}
     }
 
diff --git a/src/proto/option.pro b/src/proto/option.pro
index 25cfb65..13b9c1b 100644
--- a/src/proto/option.pro
+++ b/src/proto/option.pro
@@ -73,6 +73,7 @@
 long get_scrolloff_value(void);
 long get_sidescrolloff_value(void);
 unsigned int get_bkc_value(buf_T *buf);
+unsigned int get_ve_flags(void);
 char_u *get_showbreak_value(win_T *win);
 dict_T *get_winbuf_options(int bufopt);
 int fill_culopt_flags(char_u *val, win_T *wp);
diff --git a/src/register.c b/src/register.c
index 0c714c6..1615874 100644
--- a/src/register.c
+++ b/src/register.c
@@ -1556,6 +1556,7 @@
     long	cnt;
     pos_T	orig_start = curbuf->b_op_start;
     pos_T	orig_end = curbuf->b_op_end;
+    unsigned int cur_ve_flags = get_ve_flags();
 
 #ifdef FEAT_CLIPBOARD
     // Adjust register name for "unnamed" in 'clipboard'.
@@ -1742,7 +1743,7 @@
 
     yanklen = (int)STRLEN(y_array[0]);
 
-    if (ve_flags == VE_ALL && y_type == MCHAR)
+    if (cur_ve_flags == VE_ALL && y_type == MCHAR)
     {
 	if (gchar_cursor() == TAB)
 	{
@@ -1777,7 +1778,7 @@
 
 	if (dir == FORWARD && c != NUL)
 	{
-	    if (ve_flags == VE_ALL)
+	    if (cur_ve_flags == VE_ALL)
 		getvcol(curwin, &curwin->w_cursor, &col, NULL, &endcol2);
 	    else
 		getvcol(curwin, &curwin->w_cursor, NULL, NULL, &col);
@@ -1786,7 +1787,7 @@
 		// move to start of next multi-byte character
 		curwin->w_cursor.col += (*mb_ptr2len)(ml_get_cursor());
 	    else
-	    if (c != TAB || ve_flags != VE_ALL)
+	    if (c != TAB || cur_ve_flags != VE_ALL)
 		++curwin->w_cursor.col;
 	    ++col;
 	}
@@ -1794,7 +1795,7 @@
 	    getvcol(curwin, &curwin->w_cursor, &col, NULL, &endcol2);
 
 	col += curwin->w_cursor.coladd;
-	if (ve_flags == VE_ALL
+	if (cur_ve_flags == VE_ALL
 		&& (curwin->w_cursor.coladd > 0
 		    || endcol2 == curwin->w_cursor.col))
 	{
diff --git a/src/structs.h b/src/structs.h
index bf8102c..09f0703 100644
--- a/src/structs.h
+++ b/src/structs.h
@@ -2969,6 +2969,8 @@
 #ifdef FEAT_TERMINAL
     long	b_p_twsl;	// 'termwinscroll'
 #endif
+    char_u	*b_p_ve;	// 'virtualedit' local value
+    unsigned	b_ve_flags;     // flags for 'virtualedit'
 
     /*
      * end of buffer options
diff --git a/src/testdir/test_virtualedit.vim b/src/testdir/test_virtualedit.vim
index 7f6e08c..f3cc260 100644
--- a/src/testdir/test_virtualedit.vim
+++ b/src/testdir/test_virtualedit.vim
@@ -402,4 +402,124 @@
   bw!
 endfunc
 
+" After calling s:TryVirtualeditReplace(), line 1 will contain one of these
+" two strings, depending on whether virtual editing is on or off.
+let s:result_ve_on  = 'a      x'
+let s:result_ve_off = 'x'
+
+" Utility function for Test_global_local()
+func s:TryVirtualeditReplace()
+  call setline(1, 'a')
+  normal gg7l
+  normal rx
+endfunc
+
+" Test for :set and :setlocal
+func Test_global_local()
+  new
+
+  " Verify that 'virtualedit' is initialized to empty, can be set globally to
+  " all and to empty, and can be set locally to all and to empty.
+  call s:TryVirtualeditReplace()
+  call assert_equal(s:result_ve_off, getline(1))
+  set ve=all
+  call s:TryVirtualeditReplace()
+  call assert_equal(s:result_ve_on, getline(1))
+  set ve=
+  call s:TryVirtualeditReplace()
+  call assert_equal(s:result_ve_off, getline(1))
+  setlocal ve=all
+  call s:TryVirtualeditReplace()
+  call assert_equal(s:result_ve_on, getline(1))
+  setlocal ve=
+  call s:TryVirtualeditReplace()
+  call assert_equal(s:result_ve_off, getline(1))
+
+  " Verify that :set affects multiple buffers
+  new
+  set ve=all
+  call s:TryVirtualeditReplace()
+  call assert_equal(s:result_ve_on, getline(1))
+  wincmd p
+  call s:TryVirtualeditReplace()
+  call assert_equal(s:result_ve_on, getline(1))
+  set ve=
+  wincmd p
+  call s:TryVirtualeditReplace()
+  call assert_equal(s:result_ve_off, getline(1))
+  bwipe!
+
+  " Verify that :setlocal affects only the current buffer
+  setlocal ve=all
+  new
+  call s:TryVirtualeditReplace()
+  call assert_equal(s:result_ve_off, getline(1))
+  setlocal ve=all
+  wincmd p
+  setlocal ve=
+  wincmd p
+  call s:TryVirtualeditReplace()
+  call assert_equal(s:result_ve_on, getline(1))
+  bwipe!
+  call s:TryVirtualeditReplace()
+  call assert_equal(s:result_ve_off, getline(1))
+
+  " Verify that the buffer 'virtualedit' state follows the global value only
+  " when empty and that "none" works as expected.
+  "
+  "          'virtualedit' State
+  " +--------+--------------------------+
+  " | Local  |          Global          |
+  " |        |                          |
+  " +--------+--------+--------+--------+
+  " |        | ""     | "all"  | "none" |
+  " +--------+--------+--------+--------+
+  " | ""     |  off   |  on    |  off   |
+  " | "all"  |  on    |  on    |  on    |
+  " | "none" |  off   |  off   |  off   |
+  " +--------+--------+--------+--------+
+  new
+
+  setglobal ve=
+  setlocal ve=
+  call s:TryVirtualeditReplace()
+  call assert_equal(s:result_ve_off, getline(1))
+  setlocal ve=all
+  call s:TryVirtualeditReplace()
+  call assert_equal(s:result_ve_on, getline(1))
+  setlocal ve=none
+  call s:TryVirtualeditReplace()
+  call assert_equal(s:result_ve_off, getline(1))
+
+  setglobal ve=all
+  setlocal ve=
+  call s:TryVirtualeditReplace()
+  call assert_equal(s:result_ve_on, getline(1))
+  setlocal ve=all
+  call s:TryVirtualeditReplace()
+  call assert_equal(s:result_ve_on, getline(1))
+  setlocal ve=none
+  call s:TryVirtualeditReplace()
+  call assert_equal(s:result_ve_off, getline(1))
+  setlocal ve=NONE
+  call s:TryVirtualeditReplace()
+  call assert_equal(s:result_ve_off, getline(1))
+
+  setglobal ve=none
+  setlocal ve=
+  call s:TryVirtualeditReplace()
+  call assert_equal(s:result_ve_off, getline(1))
+  setlocal ve=all
+  call s:TryVirtualeditReplace()
+  call assert_equal(s:result_ve_on, getline(1))
+  setlocal ve=none
+  call s:TryVirtualeditReplace()
+  call assert_equal(s:result_ve_off, getline(1))
+
+  bwipe!
+
+  setlocal virtualedit&
+  set virtualedit&
+endfunc
+
 " vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/version.c b/src/version.c
index 0d60cc2..cdf5d4e 100644
--- a/src/version.c
+++ b/src/version.c
@@ -756,6 +756,8 @@
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    3227,
+/**/
     3226,
 /**/
     3225,