changeset 31263:d8e7d725a666 v9.0.0965

patch 9.0.0965: using one window for executing autocommands is insufficient Commit: https://github.com/vim/vim/commit/e76062c078debed0df818f70e4db14ad7a7cb53a Author: Bram Moolenaar <Bram@vim.org> Date: Mon Nov 28 18:51:43 2022 +0000 patch 9.0.0965: using one window for executing autocommands is insufficient Problem: Using one window for executing autocommands is insufficient. Solution: Use up to five windows for executing autocommands.
author Bram Moolenaar <Bram@vim.org>
date Mon, 28 Nov 2022 20:00:05 +0100
parents 7f766b7e17d5
children a79a8c911704
files src/autocmd.c src/buffer.c src/bufwrite.c src/channel.c src/diff.c src/eval.c src/evalbuffer.c src/evalvars.c src/evalwindow.c src/ex_cmds2.c src/ex_docmd.c src/fileio.c src/globals.h src/if_perl.xs src/if_py_both.h src/if_ruby.c src/main.c src/os_unix.c src/os_win32.c src/proto/autocmd.pro src/quickfix.c src/screen.c src/structs.h src/term.c src/terminal.c src/testdir/test_autocmd.vim src/version.c src/window.c
diffstat 28 files changed, 461 insertions(+), 270 deletions(-) [+]
line wrap: on
line diff
--- a/src/autocmd.c
+++ b/src/autocmd.c
@@ -629,28 +629,52 @@ do_augroup(char_u *arg, int del_group)
     }
 }
 
+    void
+autocmd_init(void)
+{
+    CLEAR_FIELD(aucmd_win);
+}
+
 #if defined(EXITFREE) || defined(PROTO)
     void
 free_all_autocmds(void)
 {
-    int		i;
     char_u	*s;
 
     for (current_augroup = -1; current_augroup < augroups.ga_len;
 							    ++current_augroup)
 	do_autocmd(NULL, (char_u *)"", TRUE);
 
-    for (i = 0; i < augroups.ga_len; ++i)
+    for (int i = 0; i < augroups.ga_len; ++i)
     {
 	s = ((char_u **)(augroups.ga_data))[i];
 	if (s != get_deleted_augroup())
 	    vim_free(s);
     }
     ga_clear(&augroups);
+
+    for (int i = 0; i < AUCMD_WIN_COUNT; ++i)
+	if (aucmd_win[i].auc_win_used)
+	{
+	    aucmd_win[i].auc_win_used = FALSE;
+	    win_remove(aucmd_win[i].auc_win, NULL);
+	}
 }
 #endif
 
 /*
+ * Return TRUE if "win" is an active entry in aucmd_win[].
+ */
+    int
+is_aucmd_win(win_T *win)
+{
+    for (int i = 0; i < AUCMD_WIN_COUNT; ++i)
+	if (aucmd_win[i].auc_win_used && aucmd_win[i].auc_win == win)
+	    return TRUE;
+    return FALSE;
+}
+
+/*
  * Return the event number for event name "start".
  * Return NUM_EVENTS if the event name was not found.
  * Return a pointer to the next event name in "end".
@@ -1438,8 +1462,16 @@ ex_doautoall(exarg_T *eap)
 	if (buf->b_ml.ml_mfp == NULL || buf == curbuf)
 	    continue;
 
-	// find a window for this buffer and save some values
+	// Find a window for this buffer and save some values.
 	aucmd_prepbuf(&aco, buf);
+	if (curbuf != buf)
+	{
+	    // Failed to find a window for this buffer.  Better not execute
+	    // autocommands then.
+	    retval = FAIL;
+	    break;
+	}
+
 	set_bufref(&bufref, buf);
 
 	// execute the autocommands for this buffer
@@ -1449,7 +1481,7 @@ ex_doautoall(exarg_T *eap)
 	    // Execute the modeline settings, but don't set window-local
 	    // options if we are using the current window for another
 	    // buffer.
-	    do_modelines(curwin == aucmd_win ? OPT_NOWIN : 0);
+	    do_modelines(is_aucmd_win(curwin) ? OPT_NOWIN : 0);
 
 	// restore the current window
 	aucmd_restbuf(&aco);
@@ -1490,8 +1522,9 @@ check_nomodeline(char_u **argp)
 /*
  * Prepare for executing autocommands for (hidden) buffer "buf".
  * Search for a visible window containing the current buffer.  If there isn't
- * one then use "aucmd_win".
+ * one then use an entry in "aucmd_win[]".
  * Set "curbuf" and "curwin" to match "buf".
+ * When this fails "curbuf" is not equal "buf".
  */
     void
 aucmd_prepbuf(
@@ -1512,18 +1545,29 @@ aucmd_prepbuf(
 	    if (win->w_buffer == buf)
 		break;
 
-    // Allocate "aucmd_win" when needed.  If this fails (out of memory) fall
-    // back to using the current window.
-    if (win == NULL && aucmd_win == NULL)
+    // Allocate a window when needed.
+    win_T *auc_win = NULL;
+    int auc_idx = AUCMD_WIN_COUNT;
+    if (win == NULL)
     {
-	aucmd_win = win_alloc_popup_win();
-	if (aucmd_win == NULL)
-	    win = curwin;
+	for (auc_idx = 0; auc_idx < AUCMD_WIN_COUNT; ++auc_idx)
+	    if (!aucmd_win[auc_idx].auc_win_used)
+	    {
+		auc_win = win_alloc_popup_win();
+		if (auc_win != NULL)
+		{
+		    aucmd_win[auc_idx].auc_win = auc_win;
+		    aucmd_win[auc_idx].auc_win_used = TRUE;
+		}
+		break;
+	    }
+
+	// If this fails (out of memory or using all AUCMD_WIN_COUNT
+	// entries) then we can't reliable execute the autocmd, return with
+	// "curbuf" unequal "buf".
+	if (auc_win == NULL)
+	    return;
     }
-    if (win == NULL && aucmd_win_used)
-	// Strange recursive autocommand, fall back to using the current
-	// window.  Expect a few side effects...
-	win = curwin;
 
     aco->save_curwin_id = curwin->w_id;
     aco->save_curbuf = curbuf;
@@ -1533,24 +1577,23 @@ aucmd_prepbuf(
 	// There is a window for "buf" in the current tab page, make it the
 	// curwin.  This is preferred, it has the least side effects (esp. if
 	// "buf" is curbuf).
-	aco->use_aucmd_win = FALSE;
+	aco->use_aucmd_win_idx = -1;
 	curwin = win;
     }
     else
     {
-	// There is no window for "buf", use "aucmd_win".  To minimize the side
+	// There is no window for "buf", use "auc_win".  To minimize the side
 	// effects, insert it in the current tab page.
 	// Anything related to a window (e.g., setting folds) may have
 	// unexpected results.
-	aco->use_aucmd_win = TRUE;
-	aucmd_win_used = TRUE;
-
-	win_init_popup_win(aucmd_win, buf);
+	aco->use_aucmd_win_idx = auc_idx;
+
+	win_init_popup_win(auc_win, buf);
 
 	aco->globaldir = globaldir;
 	globaldir = NULL;
 
-	// Split the current window, put the aucmd_win in the upper half.
+	// Split the current window, put the auc_win in the upper half.
 	// We don't want the BufEnter or WinEnter autocommands.
 	block_autocmds();
 	make_snapshot(SNAP_AUCMD_IDX);
@@ -1565,7 +1608,7 @@ aucmd_prepbuf(
 
 	// no redrawing and don't set the window title
 	++RedrawingDisabled;
-	(void)win_split_ins(0, WSP_TOP, aucmd_win, 0);
+	(void)win_split_ins(0, WSP_TOP, auc_win, 0);
 	--RedrawingDisabled;
 	(void)win_comp_pos();   // recompute window positions
 	p_ea = save_ea;
@@ -1573,7 +1616,7 @@ aucmd_prepbuf(
 	p_acd = save_acd;
 #endif
 	unblock_autocmds();
-	curwin = aucmd_win;
+	curwin = auc_win;
     }
     curbuf = buf;
     aco->new_curwin_id = curwin->w_id;
@@ -1595,24 +1638,26 @@ aucmd_restbuf(
     int	    dummy;
     win_T   *save_curwin;
 
-    if (aco->use_aucmd_win)
+    if (aco->use_aucmd_win_idx >= 0)
     {
+	win_T *awp = aucmd_win[aco->use_aucmd_win_idx].auc_win;
+
 	--curbuf->b_nwindows;
-	// Find "aucmd_win", it can't be closed, but it may be in another tab
+	// Find "awp", it can't be closed, but it may be in another tab
 	// page. Do not trigger autocommands here.
 	block_autocmds();
-	if (curwin != aucmd_win)
+	if (curwin != awp)
 	{
 	    tabpage_T	*tp;
 	    win_T	*wp;
 
 	    FOR_ALL_TAB_WINDOWS(tp, wp)
 	    {
-		if (wp == aucmd_win)
+		if (wp == awp)
 		{
 		    if (tp != curtab)
 			goto_tabpage_tp(tp, TRUE, TRUE);
-		    win_goto(aucmd_win);
+		    win_goto(awp);
 		    goto win_found;
 		}
 	    }
@@ -1622,7 +1667,7 @@ win_found:
 	// Remove the window and frame from the tree of frames.
 	(void)winframe_remove(curwin, &dummy, NULL);
 	win_remove(curwin, NULL);
-	aucmd_win_used = FALSE;
+	aucmd_win[aco->use_aucmd_win_idx].auc_win_used = FALSE;
 	last_status(FALSE);	    // may need to remove last status line
 
 	if (!valid_tabpage_win(curtab))
@@ -1646,8 +1691,8 @@ win_found:
 #endif
 	prevwin = win_find_by_id(aco->save_prevwin_id);
 #ifdef FEAT_EVAL
-	vars_clear(&aucmd_win->w_vars->dv_hashtab);  // free all w: variables
-	hash_init(&aucmd_win->w_vars->dv_hashtab);   // re-use the hashtab
+	vars_clear(&awp->w_vars->dv_hashtab);  // free all w: variables
+	hash_init(&awp->w_vars->dv_hashtab);   // re-use the hashtab
 #endif
 	vim_free(globaldir);
 	globaldir = aco->globaldir;
@@ -1664,11 +1709,9 @@ win_found:
 #if defined(FEAT_GUI)
 	if (gui.in_use)
 	{
-	    // Hide the scrollbars from the aucmd_win and update.
-	    gui_mch_enable_scrollbar(
-				   &aucmd_win->w_scrollbars[SBAR_LEFT], FALSE);
-	    gui_mch_enable_scrollbar(
-				  &aucmd_win->w_scrollbars[SBAR_RIGHT], FALSE);
+	    // Hide the scrollbars from the "awp" and update.
+	    gui_mch_enable_scrollbar(&awp->w_scrollbars[SBAR_LEFT], FALSE);
+	    gui_mch_enable_scrollbar(&awp->w_scrollbars[SBAR_RIGHT], FALSE);
 	    gui_may_update_scrollbars();
 	}
 #endif
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -150,11 +150,15 @@ buffer_ensure_loaded(buf_T *buf)
     {
 	aco_save_T	aco;
 
+	// Make sure the buffer is in a window.  If not then skip it.
 	aucmd_prepbuf(&aco, buf);
-	if (swap_exists_action != SEA_READONLY)
-	    swap_exists_action = SEA_NONE;
-	open_buffer(FALSE, NULL, 0);
-	aucmd_restbuf(&aco);
+	if (curbuf == buf)
+	{
+	    if (swap_exists_action != SEA_READONLY)
+		swap_exists_action = SEA_NONE;
+	    open_buffer(FALSE, NULL, 0);
+	    aucmd_restbuf(&aco);
+	}
     }
 }
 #endif
@@ -361,21 +365,26 @@ open_buffer(
 	{
 	    aco_save_T	aco;
 
-	    // Go to the buffer that was opened.
+	    // Go to the buffer that was opened, make sure it is in a window.
+	    // If not then skip it.
 	    aucmd_prepbuf(&aco, old_curbuf.br_buf);
-	    do_modelines(0);
-	    curbuf->b_flags &= ~(BF_CHECK_RO | BF_NEVERLOADED);
-
-	    if ((flags & READ_NOWINENTER) == 0)
+	    if (curbuf == old_curbuf.br_buf)
+	    {
+		do_modelines(0);
+		curbuf->b_flags &= ~(BF_CHECK_RO | BF_NEVERLOADED);
+
+		if ((flags & READ_NOWINENTER) == 0)
 #ifdef FEAT_EVAL
-		apply_autocmds_retval(EVENT_BUFWINENTER, NULL, NULL, FALSE,
-							      curbuf, &retval);
+		    apply_autocmds_retval(EVENT_BUFWINENTER, NULL, NULL,
+						       FALSE, curbuf, &retval);
 #else
-		apply_autocmds(EVENT_BUFWINENTER, NULL, NULL, FALSE, curbuf);
-#endif
-
-	    // restore curwin/curbuf and a few other things
-	    aucmd_restbuf(&aco);
+		    apply_autocmds(EVENT_BUFWINENTER, NULL, NULL,
+								FALSE, curbuf);
+#endif
+
+		// restore curwin/curbuf and a few other things
+		aucmd_restbuf(&aco);
+	    }
 	}
     }
 
@@ -5942,8 +5951,14 @@ buf_contents_changed(buf_T *buf)
 	return TRUE;
     }
 
-    // set curwin/curbuf to buf and save a few things
+    // Set curwin/curbuf to buf and save a few things.
     aucmd_prepbuf(&aco, newbuf);
+    if (curbuf != newbuf)
+    {
+	// Failed to find a window for "newbuf".
+	wipe_buffer(newbuf, FALSE);
+	return TRUE;
+    }
 
     if (ml_open(curbuf) == OK
 	    && readfile(buf->b_ffname, buf->b_fname,
--- a/src/bufwrite.c
+++ b/src/bufwrite.c
@@ -802,8 +802,15 @@ buf_write(
 	if (fname == buf->b_sfname)
 	    buf_fname_s = TRUE;
 
-	// set curwin/curbuf to buf and save a few things
+	// Set curwin/curbuf to buf and save a few things.
 	aucmd_prepbuf(&aco, buf);
+	if (curbuf != buf)
+	{
+	    // Could not find a window for "buf".  Doing more might cause
+	    // problems, better bail out.
+	    return FAIL;
+	}
+
 	set_bufref(&bufref, buf);
 
 	if (append)
@@ -2592,23 +2599,26 @@ nofail:
 
 	// Apply POST autocommands.
 	// Careful: The autocommands may call buf_write() recursively!
+	// Only do this when a window was found for "buf".
 	aucmd_prepbuf(&aco, buf);
+	if (curbuf == buf)
+	{
+	    if (append)
+		apply_autocmds_exarg(EVENT_FILEAPPENDPOST, fname, fname,
+							   FALSE, curbuf, eap);
+	    else if (filtering)
+		apply_autocmds_exarg(EVENT_FILTERWRITEPOST, NULL, fname,
+							   FALSE, curbuf, eap);
+	    else if (reset_changed && whole)
+		apply_autocmds_exarg(EVENT_BUFWRITEPOST, fname, fname,
+							   FALSE, curbuf, eap);
+	    else
+		apply_autocmds_exarg(EVENT_FILEWRITEPOST, fname, fname,
+							   FALSE, curbuf, eap);
 
-	if (append)
-	    apply_autocmds_exarg(EVENT_FILEAPPENDPOST, fname, fname,
-							  FALSE, curbuf, eap);
-	else if (filtering)
-	    apply_autocmds_exarg(EVENT_FILTERWRITEPOST, NULL, fname,
-							  FALSE, curbuf, eap);
-	else if (reset_changed && whole)
-	    apply_autocmds_exarg(EVENT_BUFWRITEPOST, fname, fname,
-							  FALSE, curbuf, eap);
-	else
-	    apply_autocmds_exarg(EVENT_FILEWRITEPOST, fname, fname,
-							  FALSE, curbuf, eap);
-
-	// restore curwin/curbuf and a few other things
-	aucmd_restbuf(&aco);
+	    // restore curwin/curbuf and a few other things
+	    aucmd_restbuf(&aco);
+	}
 
 #ifdef FEAT_EVAL
 	if (aborting())	    // autocmds may abort script processing
--- a/src/channel.c
+++ b/src/channel.c
@@ -2872,8 +2872,14 @@ append_to_buffer(buf_T *buffer, char_u *
 
     buffer->b_p_ma = TRUE;
 
-    // set curbuf to be our buf, temporarily
+    // Set curbuf to "buffer", temporarily.
     aucmd_prepbuf(&aco, buffer);
+    if (curbuf != buffer)
+    {
+	// Could not find a window for this buffer, the following might cause
+	// trouble, better bail out.
+	return;
+    }
 
     u_sync(TRUE);
     // ignore undo failure, undo is not very useful here
--- a/src/diff.c
+++ b/src/diff.c
@@ -2786,8 +2786,12 @@ ex_diffgetput(exarg_T *eap)
 	idx_to = idx_other;
 	// Need to make the other buffer the current buffer to be able to make
 	// changes in it.
-	// set curwin/curbuf to buf and save a few things
+	// Set curwin/curbuf to buf and save a few things.
 	aucmd_prepbuf(&aco, curtab->tp_diffbuf[idx_other]);
+	if (curbuf != curtab->tp_diffbuf[idx_other])
+	    // Could not find a window for this buffer, the rest is likely to
+	    // fail.
+	    goto theend;
     }
 
     // May give the warning for a changed buffer here, which can trigger the
--- a/src/eval.c
+++ b/src/eval.c
@@ -5084,9 +5084,10 @@ garbage_collect(int testing)
     FOR_ALL_TAB_WINDOWS(tp, wp)
 	abort = abort || set_ref_in_item(&wp->w_winvar.di_tv, copyID,
 								  NULL, NULL);
-    if (aucmd_win != NULL)
-	abort = abort || set_ref_in_item(&aucmd_win->w_winvar.di_tv, copyID,
-								  NULL, NULL);
+    for (int i = 0; i < AUCMD_WIN_COUNT; ++i)
+	if (aucmd_win[i].auc_win_used)
+	    abort = abort || set_ref_in_item(
+		    &aucmd_win[i].auc_win->w_winvar.di_tv, copyID, NULL, NULL);
 #ifdef FEAT_PROP_POPUP
     FOR_ALL_POPUPWINS(wp)
 	abort = abort || set_ref_in_item(&wp->w_winvar.di_tv, copyID,
--- a/src/evalbuffer.c
+++ b/src/evalbuffer.c
@@ -136,6 +136,8 @@ typedef struct {
  *
  * Information is saved in "cob" and MUST be restored by calling
  * change_other_buffer_restore().
+ *
+ * If this fails then "curbuf" will not be equal to "buf".
  */
     static void
 change_other_buffer_prepare(cob_T *cob, buf_T *buf)
@@ -156,7 +158,8 @@ change_other_buffer_prepare(cob_T *cob, 
 	// curwin->w_buffer differ from "curbuf", use the autocmd window.
 	curbuf = curwin->w_buffer;
 	aucmd_prepbuf(&cob->cob_aco, buf);
-	cob->cob_using_aco = TRUE;
+	if (curbuf == buf)
+	    cob->cob_using_aco = TRUE;
     }
 }
 
--- a/src/evalvars.c
+++ b/src/evalvars.c
@@ -4761,13 +4761,16 @@ f_setbufvar(typval_T *argvars, typval_T 
 	{
 	    aco_save_T	aco;
 
-	    // set curbuf to be our buf, temporarily
+	    // Set curbuf to be our buf, temporarily.
 	    aucmd_prepbuf(&aco, buf);
-
-	    set_option_from_tv(varname + 1, varp);
-
-	    // reset notion of buffer
-	    aucmd_restbuf(&aco);
+	    if (curbuf == buf)
+	    {
+		// Only when it worked to set "curbuf".
+		set_option_from_tv(varname + 1, varp);
+
+		// reset notion of buffer
+		aucmd_restbuf(&aco);
+	    }
 	}
 	else
 	{
--- a/src/evalwindow.c
+++ b/src/evalwindow.c
@@ -1064,7 +1064,7 @@ f_win_gettype(typval_T *argvars, typval_
 	    return;
 	}
     }
-    if (wp == aucmd_win)
+    if (is_aucmd_win(wp))
 	rettv->vval.v_string = vim_strsave((char_u *)"autocmd");
 #if defined(FEAT_QUICKFIX)
     else if (wp->w_p_pvw)
--- a/src/ex_cmds2.c
+++ b/src/ex_cmds2.c
@@ -705,9 +705,12 @@ ex_listdo(exarg_T *eap)
 		else
 		{
 		    aucmd_prepbuf(&aco, buf);
-		    apply_autocmds(EVENT_SYNTAX, buf->b_p_syn,
+		    if (curbuf == buf)
+		    {
+			apply_autocmds(EVENT_SYNTAX, buf->b_p_syn,
 						      buf->b_fname, TRUE, buf);
-		    aucmd_restbuf(&aco);
+			aucmd_restbuf(&aco);
+		    }
 		}
 
 		// start over, in case autocommands messed things up.
--- a/src/ex_docmd.c
+++ b/src/ex_docmd.c
@@ -6050,7 +6050,7 @@ ex_win_close(
     buf_T	*buf = win->w_buffer;
 
     // Never close the autocommand window.
-    if (win == aucmd_win)
+    if (is_aucmd_win(win))
     {
 	emsg(_(e_cannot_close_autocmd_or_popup_window));
 	return;
--- a/src/fileio.c
+++ b/src/fileio.c
@@ -4369,8 +4369,14 @@ buf_reload(buf_T *buf, int orig_mode, in
     int		flags = READ_NEW;
     int		prepped = OK;
 
-    // set curwin/curbuf for "buf" and save some things
+    // Set curwin/curbuf for "buf" and save some things.
     aucmd_prepbuf(&aco, buf);
+    if (curbuf != buf)
+    {
+	// Failed to find a window for "buf", it is dangerous to continue,
+	// better bail out.
+	return;
+    }
 
     // Unless reload_options is set, we only want to read the text from the
     // file, not reset the syntax highlighting, clear marks, diff status, etc.
--- a/src/globals.h
+++ b/src/globals.h
@@ -977,8 +977,18 @@ EXTERN win_T	*prevwin INIT(= NULL);	// p
 
 EXTERN win_T	*curwin;	// currently active window
 
-EXTERN win_T	*aucmd_win;	// window used in aucmd_prepbuf()
-EXTERN int	aucmd_win_used INIT(= FALSE);	// aucmd_win is being used
+// When executing autocommands for a buffer that is not in any window, a
+// special window is created to handle the side effects.  When autocommands
+// nest we may need more than one.  Allow for up to five, if more are needed
+// something crazy is happening.
+#define AUCMD_WIN_COUNT 5
+
+typedef struct {
+  win_T	*auc_win;	// window used in aucmd_prepbuf()
+  int	auc_win_used;	// this auc_win is being used
+} aucmdwin_T;
+
+EXTERN aucmdwin_T aucmd_win[AUCMD_WIN_COUNT];
 
 #ifdef FEAT_PROP_POPUP
 EXTERN win_T    *first_popupwin;		// first global popup window
--- a/src/if_perl.xs
+++ b/src/if_perl.xs
@@ -1869,18 +1869,21 @@ Set(vimbuf, ...)
 	    {
 		aco_save_T	aco;
 
-		/* set curwin/curbuf for "vimbuf" and save some things */
+		/* Set curwin/curbuf for "vimbuf" and save some things. */
 		aucmd_prepbuf(&aco, vimbuf);
-
-		if (u_savesub(lnum) == OK)
+		if (curbuf == vimbuf)
 		{
-		    ml_replace(lnum, (char_u *)line, TRUE);
-		    changed_bytes(lnum, 0);
-		}
+		    /* Only when a window was found. */
+		    if (u_savesub(lnum) == OK)
+		    {
+			ml_replace(lnum, (char_u *)line, TRUE);
+			changed_bytes(lnum, 0);
+		    }
 
-		/* restore curwin/curbuf and a few other things */
-		aucmd_restbuf(&aco);
-		/* Careful: autocommands may have made "vimbuf" invalid! */
+		    /* restore curwin/curbuf and a few other things */
+		    aucmd_restbuf(&aco);
+		    /* Careful: autocommands may have made "vimbuf" invalid! */
+		}
 	    }
 	}
     }
@@ -1921,18 +1924,22 @@ Delete(vimbuf, ...)
 
 		    /* set curwin/curbuf for "vimbuf" and save some things */
 		    aucmd_prepbuf(&aco, vimbuf);
-
-		    if (u_savedel(lnum, 1) == OK)
+		    if (curbuf == vimbuf)
 		    {
-			ml_delete(lnum);
-			check_cursor();
-			deleted_lines_mark(lnum, 1L);
+			/* Only when a window was found. */
+			if (u_savedel(lnum, 1) == OK)
+			{
+			    ml_delete(lnum);
+			    check_cursor();
+			    deleted_lines_mark(lnum, 1L);
+			}
+
+			/* restore curwin/curbuf and a few other things */
+			aucmd_restbuf(&aco);
+			/* Careful: autocommands may have made "vimbuf"
+			 * invalid! */
 		    }
 
-		    /* restore curwin/curbuf and a few other things */
-		    aucmd_restbuf(&aco);
-		    /* Careful: autocommands may have made "vimbuf" invalid! */
-
 		    update_curbuf(UPD_VALID);
 		}
 	    }
@@ -1963,16 +1970,19 @@ Append(vimbuf, ...)
 
 		/* set curwin/curbuf for "vimbuf" and save some things */
 		aucmd_prepbuf(&aco, vimbuf);
-
-		if (u_inssub(lnum + 1) == OK)
+		if (curbuf == vimbuf)
 		{
-		    ml_append(lnum, (char_u *)line, (colnr_T)0, FALSE);
-		    appended_lines_mark(lnum, 1L);
-		}
+		    /* Only when a window for "vimbuf" was found. */
+		    if (u_inssub(lnum + 1) == OK)
+		    {
+			ml_append(lnum, (char_u *)line, (colnr_T)0, FALSE);
+			appended_lines_mark(lnum, 1L);
+		    }
 
-		/* restore curwin/curbuf and a few other things */
-		aucmd_restbuf(&aco);
-		/* Careful: autocommands may have made "vimbuf" invalid! */
+		    /* restore curwin/curbuf and a few other things */
+		    aucmd_restbuf(&aco);
+		    /* Careful: autocommands may have made "vimbuf" invalid! */
+		    }
 
 		update_curbuf(UPD_VALID);
 	    }
--- a/src/if_py_both.h
+++ b/src/if_py_both.h
@@ -5283,8 +5283,11 @@ BufferSetattr(BufferObject *self, char *
 	VimTryStart();
 	// Using aucmd_*: autocommands will be executed by rename_buffer
 	aucmd_prepbuf(&aco, self->buf);
-	ren_ret = rename_buffer(val);
-	aucmd_restbuf(&aco);
+	if (curbuf == self->buf)
+	{
+	    ren_ret = rename_buffer(val);
+	    aucmd_restbuf(&aco);
+	}
 	Py_XDECREF(todecref);
 	if (VimTryEnd())
 	    return -1;
--- a/src/if_ruby.c
+++ b/src/if_ruby.c
@@ -1371,21 +1371,24 @@ set_buffer_line(buf_T *buf, linenr_T n, 
 
     if (n > 0 && n <= buf->b_ml.ml_line_count && line != NULL)
     {
-	// set curwin/curbuf for "buf" and save some things
+	// Set curwin/curbuf for "buf" and save some things.
 	aucmd_prepbuf(&aco, buf);
-
-	if (u_savesub(n) == OK)
+	if (curbuf == buf)
 	{
-	    ml_replace(n, (char_u *)line, TRUE);
-	    changed();
+	    // Only when it worked to set "curbuf".
+	    if (u_savesub(n) == OK)
+	    {
+		ml_replace(n, (char_u *)line, TRUE);
+		changed();
 #ifdef SYNTAX_HL
-	    syn_changed(n); // recompute syntax hl. for this line
+		syn_changed(n); // recompute syntax hl. for this line
 #endif
-	}
+	    }
 
-	// restore curwin/curbuf and a few other things
-	aucmd_restbuf(&aco);
-	// Careful: autocommands may have made "buf" invalid!
+	    // restore curwin/curbuf and a few other things
+	    aucmd_restbuf(&aco);
+	    // Careful: autocommands may have made "buf" invalid!
+	}
 
 	update_curbuf(UPD_NOT_VALID);
     }
@@ -1415,23 +1418,26 @@ buffer_delete(VALUE self, VALUE num)
 
     if (n > 0 && n <= buf->b_ml.ml_line_count)
     {
-	// set curwin/curbuf for "buf" and save some things
+	// Set curwin/curbuf for "buf" and save some things.
 	aucmd_prepbuf(&aco, buf);
-
-	if (u_savedel(n, 1) == OK)
+	if (curbuf == buf)
 	{
-	    ml_delete(n);
+	    // Only when it worked to set "curbuf".
+	    if (u_savedel(n, 1) == OK)
+	    {
+		ml_delete(n);
 
-	    // Changes to non-active buffers should properly refresh
-	    //   SegPhault - 01/09/05
-	    deleted_lines_mark(n, 1L);
+		// Changes to non-active buffers should properly refresh
+		//   SegPhault - 01/09/05
+		deleted_lines_mark(n, 1L);
 
-	    changed();
-	}
+		changed();
+	    }
 
-	// restore curwin/curbuf and a few other things
-	aucmd_restbuf(&aco);
-	// Careful: autocommands may have made "buf" invalid!
+	    // restore curwin/curbuf and a few other things
+	    aucmd_restbuf(&aco);
+	    // Careful: autocommands may have made "buf" invalid!
+	}
 
 	update_curbuf(UPD_NOT_VALID);
     }
@@ -1458,21 +1464,24 @@ buffer_append(VALUE self, VALUE num, VAL
     {
 	// set curwin/curbuf for "buf" and save some things
 	aucmd_prepbuf(&aco, buf);
-
-	if (u_inssub(n + 1) == OK)
+	if (curbuf == buf)
 	{
-	    ml_append(n, (char_u *) line, (colnr_T) 0, FALSE);
+	    // Only when it worked to set "curbuf".
+	    if (u_inssub(n + 1) == OK)
+	    {
+		ml_append(n, (char_u *) line, (colnr_T) 0, FALSE);
 
-	    //  Changes to non-active buffers should properly refresh screen
-	    //    SegPhault - 12/20/04
-	    appended_lines_mark(n, 1L);
+		//  Changes to non-active buffers should properly refresh screen
+		//    SegPhault - 12/20/04
+		appended_lines_mark(n, 1L);
 
-	    changed();
-	}
+		changed();
+	    }
 
-	// restore curwin/curbuf and a few other things
-	aucmd_restbuf(&aco);
-	// Careful: autocommands may have made "buf" invalid!
+	    // restore curwin/curbuf and a few other things
+	    aucmd_restbuf(&aco);
+	    // Careful: autocommands may have made "buf" invalid!
+	}
 
 	update_curbuf(UPD_NOT_VALID);
     }
--- a/src/main.c
+++ b/src/main.c
@@ -123,6 +123,8 @@ main
 #endif
     params.window_count = -1;
 
+    autocmd_init();
+
 #ifdef FEAT_RUBY
     {
 	int ruby_stack_start;
--- a/src/os_unix.c
+++ b/src/os_unix.c
@@ -4490,27 +4490,30 @@ mch_call_shell_terminal(
 
     // Find a window to make "buf" curbuf.
     aucmd_prepbuf(&aco, buf);
-
-    clear_oparg(&oa);
-    while (term_use_loop())
-    {
-	if (oa.op_type == OP_NOP && oa.regname == NUL && !VIsual_active)
-	{
-	    // If terminal_loop() returns OK we got a key that is handled
-	    // in Normal model. We don't do redrawing anyway.
-	    if (terminal_loop(TRUE) == OK)
+    if (curbuf == buf)
+    {
+	// Only when managed to find a window for "buf",
+	clear_oparg(&oa);
+	while (term_use_loop())
+	{
+	    if (oa.op_type == OP_NOP && oa.regname == NUL && !VIsual_active)
+	    {
+		// If terminal_loop() returns OK we got a key that is handled
+		// in Normal model. We don't do redrawing anyway.
+		if (terminal_loop(TRUE) == OK)
+		    normal_cmd(&oa, TRUE);
+	    }
+	    else
 		normal_cmd(&oa, TRUE);
 	}
-	else
-	    normal_cmd(&oa, TRUE);
-    }
-    retval = job->jv_exitval;
-    ch_log(NULL, "system command finished");
-
-    job_unref(job);
-
-    // restore curwin/curbuf and a few other things
-    aucmd_restbuf(&aco);
+	retval = job->jv_exitval;
+	ch_log(NULL, "system command finished");
+
+	job_unref(job);
+
+	// restore curwin/curbuf and a few other things
+	aucmd_restbuf(&aco);
+    }
 
     // Only require pressing Enter when redrawing, to avoid that system() gets
     // the hit-enter prompt even though it didn't output anything.
--- a/src/os_win32.c
+++ b/src/os_win32.c
@@ -4878,27 +4878,30 @@ mch_call_shell_terminal(
 
     // Find a window to make "buf" curbuf.
     aucmd_prepbuf(&aco, buf);
-
-    clear_oparg(&oa);
-    while (term_use_loop())
-    {
-	if (oa.op_type == OP_NOP && oa.regname == NUL && !VIsual_active)
-	{
-	    // If terminal_loop() returns OK we got a key that is handled
-	    // in Normal model. We don't do redrawing anyway.
-	    if (terminal_loop(TRUE) == OK)
+    if (curbuf == buf)
+    {
+	// Only do this when a window was found for "buf".
+	clear_oparg(&oa);
+	while (term_use_loop())
+	{
+	    if (oa.op_type == OP_NOP && oa.regname == NUL && !VIsual_active)
+	    {
+		// If terminal_loop() returns OK we got a key that is handled
+		// in Normal model. We don't do redrawing anyway.
+		if (terminal_loop(TRUE) == OK)
+		    normal_cmd(&oa, TRUE);
+	    }
+	    else
 		normal_cmd(&oa, TRUE);
 	}
-	else
-	    normal_cmd(&oa, TRUE);
-    }
-    retval = job->jv_exitval;
-    ch_log(NULL, "system command finished");
-
-    job_unref(job);
-
-    // restore curwin/curbuf and a few other things
-    aucmd_restbuf(&aco);
+	retval = job->jv_exitval;
+	ch_log(NULL, "system command finished");
+
+	job_unref(job);
+
+	// restore curwin/curbuf and a few other things
+	aucmd_restbuf(&aco);
+    }
 
     wait_return(TRUE);
     do_buffer(DOBUF_WIPE, DOBUF_FIRST, FORWARD, buf->b_fnum, TRUE);
--- a/src/proto/autocmd.pro
+++ b/src/proto/autocmd.pro
@@ -2,7 +2,9 @@
 void aubuflocal_remove(buf_T *buf);
 int au_has_group(char_u *name);
 void do_augroup(char_u *arg, int del_group);
+void autocmd_init(void);
 void free_all_autocmds(void);
+int is_aucmd_win(win_T *win);
 int check_ei(void);
 char_u *au_event_disable(char *what);
 void au_event_restore(char_u *old_ei);
--- a/src/quickfix.c
+++ b/src/quickfix.c
@@ -4598,21 +4598,29 @@ qf_update_buffer(qf_info_T *qi, qfline_T
 	// autocommands may cause trouble
 	incr_quickfix_busy();
 
+	int do_fill = TRUE;
 	if (old_last == NULL)
+	{
 	    // set curwin/curbuf to buf and save a few things
 	    aucmd_prepbuf(&aco, buf);
-
-	qf_update_win_titlevar(qi);
-
-	qf_fill_buffer(qf_get_curlist(qi), buf, old_last, qf_winid);
-	++CHANGEDTICK(buf);
-
-	if (old_last == NULL)
+	    if (curbuf != buf)
+		do_fill = FALSE;  // failed to find a window for "buf"
+	}
+
+	if (do_fill)
 	{
-	    (void)qf_win_pos_update(qi, 0);
-
-	    // restore curwin/curbuf and a few other things
-	    aucmd_restbuf(&aco);
+	    qf_update_win_titlevar(qi);
+
+	    qf_fill_buffer(qf_get_curlist(qi), buf, old_last, qf_winid);
+	    ++CHANGEDTICK(buf);
+
+	    if (old_last == NULL)
+	    {
+		(void)qf_win_pos_update(qi, 0);
+
+		// restore curwin/curbuf and a few other things
+		aucmd_restbuf(&aco);
+	    }
 	}
 
 	// Only redraw when added lines are visible.  This avoids flickering
@@ -6395,12 +6403,15 @@ vgr_process_files(
 		    // need to be done (again).  But not the window-local
 		    // options!
 		    aucmd_prepbuf(&aco, buf);
+		    if (curbuf == buf)
+		    {
 #if defined(FEAT_SYN_HL)
-		    apply_autocmds(EVENT_FILETYPE, buf->b_p_ft,
+			apply_autocmds(EVENT_FILETYPE, buf->b_p_ft,
 						     buf->b_fname, TRUE, buf);
 #endif
-		    do_modelines(OPT_NOWIN);
-		    aucmd_restbuf(&aco);
+			do_modelines(OPT_NOWIN);
+			aucmd_restbuf(&aco);
+		    }
 		}
 	    }
 	}
@@ -6593,40 +6604,43 @@ load_dummy_buffer(
 
 	// set curwin/curbuf to buf and save a few things
 	aucmd_prepbuf(&aco, newbuf);
-
-	// Need to set the filename for autocommands.
-	(void)setfname(curbuf, fname, NULL, FALSE);
-
-	// Create swap file now to avoid the ATTENTION message.
-	check_need_swap(TRUE);
-
-	// Remove the "dummy" flag, otherwise autocommands may not
-	// work.
-	curbuf->b_flags &= ~BF_DUMMY;
-
-	newbuf_to_wipe.br_buf = NULL;
-	readfile_result = readfile(fname, NULL,
-		    (linenr_T)0, (linenr_T)0, (linenr_T)MAXLNUM,
-		    NULL, READ_NEW | READ_DUMMY);
-	--newbuf->b_locked;
-	if (readfile_result == OK
-		&& !got_int
-		&& !(curbuf->b_flags & BF_NEW))
+	if (curbuf == newbuf)
 	{
-	    failed = FALSE;
-	    if (curbuf != newbuf)
+	    // Need to set the filename for autocommands.
+	    (void)setfname(curbuf, fname, NULL, FALSE);
+
+	    // Create swap file now to avoid the ATTENTION message.
+	    check_need_swap(TRUE);
+
+	    // Remove the "dummy" flag, otherwise autocommands may not
+	    // work.
+	    curbuf->b_flags &= ~BF_DUMMY;
+
+	    newbuf_to_wipe.br_buf = NULL;
+	    readfile_result = readfile(fname, NULL,
+			(linenr_T)0, (linenr_T)0, (linenr_T)MAXLNUM,
+			NULL, READ_NEW | READ_DUMMY);
+	    --newbuf->b_locked;
+	    if (readfile_result == OK
+		    && !got_int
+		    && !(curbuf->b_flags & BF_NEW))
 	    {
-		// Bloody autocommands changed the buffer!  Can happen when
-		// using netrw and editing a remote file.  Use the current
-		// buffer instead, delete the dummy one after restoring the
-		// window stuff.
-		set_bufref(&newbuf_to_wipe, newbuf);
-		newbuf = curbuf;
+		failed = FALSE;
+		if (curbuf != newbuf)
+		{
+		    // Bloody autocommands changed the buffer!  Can happen when
+		    // using netrw and editing a remote file.  Use the current
+		    // buffer instead, delete the dummy one after restoring the
+		    // window stuff.
+		    set_bufref(&newbuf_to_wipe, newbuf);
+		    newbuf = curbuf;
+		}
 	    }
+
+	    // restore curwin/curbuf and a few other things
+	    aucmd_restbuf(&aco);
 	}
 
-	// restore curwin/curbuf and a few other things
-	aucmd_restbuf(&aco);
 	if (newbuf_to_wipe.br_buf != NULL && bufref_valid(&newbuf_to_wipe))
 	    wipe_buffer(newbuf_to_wipe.br_buf, FALSE);
 
--- a/src/screen.c
+++ b/src/screen.c
@@ -2369,7 +2369,6 @@ screenalloc(int doclear)
     u8char_T	    *new_ScreenLinesUC = NULL;
     u8char_T	    *new_ScreenLinesC[MAX_MCO];
     schar_T	    *new_ScreenLines2 = NULL;
-    int		    i;
     sattr_T	    *new_ScreenAttrs;
     colnr_T	    *new_ScreenCols;
     unsigned	    *new_LineOffset;
@@ -2438,8 +2437,9 @@ retry:
      */
     FOR_ALL_TAB_WINDOWS(tp, wp)
 	win_free_lsize(wp);
-    if (aucmd_win != NULL)
-	win_free_lsize(aucmd_win);
+    for (int i = 0; i < AUCMD_WIN_COUNT; ++i)
+	if (aucmd_win[i].auc_win_used)
+	    win_free_lsize(aucmd_win[i].auc_win);
 #ifdef FEAT_PROP_POPUP
     // global popup windows
     FOR_ALL_POPUPWINS(wp)
@@ -2455,7 +2455,7 @@ retry:
     if (enc_utf8)
     {
 	new_ScreenLinesUC = LALLOC_MULT(u8char_T, (Rows + 1) * Columns);
-	for (i = 0; i < p_mco; ++i)
+	for (int i = 0; i < p_mco; ++i)
 	    new_ScreenLinesC[i] = LALLOC_CLEAR_MULT(u8char_T,
 							 (Rows + 1) * Columns);
     }
@@ -2482,9 +2482,14 @@ retry:
 	    goto give_up;
 	}
     }
-    if (aucmd_win != NULL && aucmd_win->w_lines == NULL
-					&& win_alloc_lines(aucmd_win) == FAIL)
-	outofmem = TRUE;
+    for (int i = 0; i < AUCMD_WIN_COUNT; ++i)
+	if (aucmd_win[i].auc_win_used
+		&& aucmd_win[i].auc_win->w_lines == NULL
+		&& win_alloc_lines(aucmd_win[i].auc_win) == FAIL)
+	{
+	    outofmem = TRUE;
+	    break;
+	}
 #ifdef FEAT_PROP_POPUP
     // global popup windows
     FOR_ALL_POPUPWINS(wp)
@@ -2505,11 +2510,15 @@ retry:
 
 give_up:
 
-    for (i = 0; i < p_mco; ++i)
+    int found_null = FALSE;
+    for (int i = 0; i < p_mco; ++i)
 	if (new_ScreenLinesC[i] == NULL)
+	{
+	    found_null = TRUE;
 	    break;
+	}
     if (new_ScreenLines == NULL
-	    || (enc_utf8 && (new_ScreenLinesUC == NULL || i != p_mco))
+	    || (enc_utf8 && (new_ScreenLinesUC == NULL || found_null))
 	    || (enc_dbcs == DBCS_JPNU && new_ScreenLines2 == NULL)
 	    || new_ScreenAttrs == NULL
 	    || new_ScreenCols == NULL
@@ -2534,7 +2543,7 @@ give_up:
 	}
 	VIM_CLEAR(new_ScreenLines);
 	VIM_CLEAR(new_ScreenLinesUC);
-	for (i = 0; i < p_mco; ++i)
+	for (int i = 0; i < p_mco; ++i)
 	    VIM_CLEAR(new_ScreenLinesC[i]);
 	VIM_CLEAR(new_ScreenLines2);
 	VIM_CLEAR(new_ScreenAttrs);
@@ -2571,7 +2580,7 @@ give_up:
 		{
 		    (void)vim_memset(new_ScreenLinesUC + new_row * Columns,
 				       0, (size_t)Columns * sizeof(u8char_T));
-		    for (i = 0; i < p_mco; ++i)
+		    for (int i = 0; i < p_mco; ++i)
 			(void)vim_memset(new_ScreenLinesC[i]
 							  + new_row * Columns,
 				       0, (size_t)Columns * sizeof(u8char_T));
@@ -2603,7 +2612,7 @@ give_up:
 			mch_memmove(new_ScreenLinesUC + new_LineOffset[new_row],
 				ScreenLinesUC + LineOffset[old_row],
 				(size_t)len * sizeof(u8char_T));
-			for (i = 0; i < p_mco; ++i)
+			for (int i = 0; i < p_mco; ++i)
 			    mch_memmove(new_ScreenLinesC[i]
 						    + new_LineOffset[new_row],
 				ScreenLinesC[i] + LineOffset[old_row],
@@ -2636,7 +2645,7 @@ give_up:
     // NOTE: this may result in all pointers to become NULL.
     ScreenLines = new_ScreenLines;
     ScreenLinesUC = new_ScreenLinesUC;
-    for (i = 0; i < p_mco; ++i)
+    for (int i = 0; i < p_mco; ++i)
 	ScreenLinesC[i] = new_ScreenLinesC[i];
     Screen_mco = p_mco;
     ScreenLines2 = new_ScreenLines2;
--- a/src/structs.h
+++ b/src/structs.h
@@ -4160,7 +4160,7 @@ typedef int vimmenu_T;
 typedef struct
 {
     buf_T	*save_curbuf;	    // saved curbuf
-    int		use_aucmd_win;	    // using aucmd_win
+    int		use_aucmd_win_idx;  // index in aucmd_win[] if >= 0
     int		save_curwin_id;	    // ID of saved curwin
     int		new_curwin_id;	    // ID of new curwin
     int		save_prevwin_id;    // ID of saved prevwin
--- a/src/term.c
+++ b/src/term.c
@@ -2232,10 +2232,13 @@ set_termname(char_u *term)
 		if (curbuf->b_ml.ml_mfp != NULL)
 		{
 		    aucmd_prepbuf(&aco, buf);
-		    apply_autocmds(EVENT_TERMCHANGED, NULL, NULL, FALSE,
+		    if (curbuf == buf)
+		    {
+			apply_autocmds(EVENT_TERMCHANGED, NULL, NULL, FALSE,
 								      curbuf);
-		    // restore curwin/curbuf and a few other things
-		    aucmd_restbuf(&aco);
+			// restore curwin/curbuf and a few other things
+			aucmd_restbuf(&aco);
+		    }
 		}
 	    }
 	}
--- a/src/terminal.c
+++ b/src/terminal.c
@@ -3560,15 +3560,18 @@ term_after_channel_closed(term_T *term)
 	    // ++close or term_finish == "close"
 	    ch_log(NULL, "terminal job finished, closing window");
 	    aucmd_prepbuf(&aco, term->tl_buffer);
-	    // Avoid closing the window if we temporarily use it.
-	    if (curwin == aucmd_win)
-		do_set_w_closing = TRUE;
-	    if (do_set_w_closing)
-		curwin->w_closing = TRUE;
-	    do_bufdel(DOBUF_WIPE, (char_u *)"", 1, fnum, fnum, FALSE);
-	    if (do_set_w_closing)
-		curwin->w_closing = FALSE;
-	    aucmd_restbuf(&aco);
+	    if (curbuf == term->tl_buffer)
+	    {
+		// Avoid closing the window if we temporarily use it.
+		if (is_aucmd_win(curwin))
+		    do_set_w_closing = TRUE;
+		if (do_set_w_closing)
+		    curwin->w_closing = TRUE;
+		do_bufdel(DOBUF_WIPE, (char_u *)"", 1, fnum, fnum, FALSE);
+		if (do_set_w_closing)
+		    curwin->w_closing = FALSE;
+		aucmd_restbuf(&aco);
+	    }
 #ifdef FEAT_PROP_POPUP
 	    if (pwin != NULL)
 		popup_close_with_retval(pwin, 0);
--- a/src/testdir/test_autocmd.vim
+++ b/src/testdir/test_autocmd.vim
@@ -4068,4 +4068,27 @@ func Test_autocmd_split_dummy()
   call delete('Xerr')
 endfunc
 
+" This was crashing because there was only one window to execute autocommands
+" in.
+func Test_autocmd_nested_setbufvar()
+  CheckFeature python3
+
+  set hidden
+  edit Xaaa
+  edit Xbbb
+  call setline(1, 'bar')
+  enew
+  au BufWriteCmd Xbbb ++nested call setbufvar('Xaaa', '&ft', 'foo') | bw! Xaaa
+  au FileType foo call py3eval('vim.current.buffer.options["cindent"]')
+  wall
+
+  au! BufWriteCmd
+  au! FileType foo
+  set nohidden
+  call delete('Xaaa')
+  call delete('Xbbb')
+  %bwipe!
+endfunc
+
+
 " vim: shiftwidth=2 sts=2 expandtab
--- 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 */
 /**/
+    965,
+/**/
     964,
 /**/
     963,
--- a/src/window.c
+++ b/src/window.c
@@ -1362,7 +1362,7 @@ win_split_ins(
 	win_equal(wp, TRUE,
 		(flags & WSP_VERT) ? (dir == 'v' ? 'b' : 'h')
 		: dir == 'h' ? 'b' : 'v');
-    else if (*p_spk != 'c' && wp != aucmd_win)
+    else if (*p_spk != 'c' && !is_aucmd_win(wp))
 	win_fix_scroll(FALSE);
 
     // Don't change the window height/width to 'winheight' / 'winwidth' if a
@@ -1962,7 +1962,7 @@ win_equal(
     win_equal_rec(next_curwin == NULL ? curwin : next_curwin, current,
 		      topframe, dir, 0, tabline_height(),
 					   (int)Columns, topframe->fr_height);
-    if (*p_spk != 'c' && next_curwin != aucmd_win)
+    if (*p_spk != 'c' && !is_aucmd_win(next_curwin))
 	win_fix_scroll(TRUE);
 }
 
@@ -2426,7 +2426,7 @@ close_windows(
 
 /*
  * Return TRUE if the current window is the only window that exists (ignoring
- * "aucmd_win").
+ * "aucmd_win[]").
  * Returns FALSE if there is a window, possibly in another tab page.
  */
     static int
@@ -2436,7 +2436,7 @@ last_window(void)
 }
 
 /*
- * Return TRUE if there is only one window other than "aucmd_win" in the
+ * Return TRUE if there is only one window other than "aucmd_win[]" in the
  * current tab page.
  */
     int
@@ -2447,7 +2447,7 @@ one_window(void)
 
     FOR_ALL_WINDOWS(wp)
     {
-	if (wp != aucmd_win)
+	if (!is_aucmd_win(wp))
 	{
 	    if (seen_one)
 		return FALSE;
@@ -2588,7 +2588,7 @@ win_close(win_T *win, int free_buf)
 	emsg(_(e_cannot_close_autocmd_or_popup_window));
 	return FAIL;
     }
-    if ((firstwin == aucmd_win || lastwin == aucmd_win) && one_window())
+    if ((is_aucmd_win(firstwin) || is_aucmd_win(lastwin)) && one_window())
     {
 	emsg(_(e_cannot_close_window_only_autocmd_window_would_remain));
 	return FAIL;
@@ -3292,11 +3292,12 @@ win_free_all(void)
     while (first_tabpage->tp_next != NULL)
 	tabpage_close(TRUE);
 
-    if (aucmd_win != NULL)
-    {
-	(void)win_free_mem(aucmd_win, &dummy, NULL);
-	aucmd_win = NULL;
-    }
+    for (int i = 0; i < AUCMD_WIN_COUNT; ++i)
+	if (aucmd_win[i].auc_win_used)
+	{
+	    (void)win_free_mem(aucmd_win[i].auc_win, &dummy, NULL);
+	    aucmd_win[i].auc_win_used = FALSE;
+	}
 
     while (firstwin != NULL)
 	(void)win_free_mem(firstwin, &dummy, NULL);
@@ -5663,7 +5664,7 @@ win_free(
     int
 win_unlisted(win_T *wp)
 {
-    return wp == aucmd_win || WIN_IS_POPUP(wp);
+    return is_aucmd_win(wp) || WIN_IS_POPUP(wp);
 }
 
 #if defined(FEAT_PROP_POPUP) || defined(PROTO)
@@ -7257,7 +7258,7 @@ only_one_window(void)
 # ifdef FEAT_QUICKFIX
 		    || wp->w_p_pvw
 # endif
-	     ) || wp == curwin) && wp != aucmd_win)
+	     ) || wp == curwin) && !is_aucmd_win(wp))
 	    ++count;
     return (count <= 1);
 }