Skip to content

Instantly share code, notes, and snippets.

@h-east
Last active August 24, 2016 09:04
Show Gist options
  • Save h-east/9f2322ec5225f54d61071d040e1351a5 to your computer and use it in GitHub Desktop.
Save h-east/9f2322ec5225f54d61071d040e1351a5 to your computer and use it in GitHub Desktop.
diff --git a/src/buffer.c b/src/buffer.c
index 75de736..db632fa 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -499,6 +499,10 @@ close_buffer(
/* When the buffer is no longer in a window, trigger BufWinLeave */
if (buf->b_nwindows == 1)
{
+# ifdef FEAT_WINDOWS
+ tabpage_T *save_curtab = curtab;
+# endif
+
buf->b_closing = TRUE;
if (apply_autocmds(EVENT_BUFWINLEAVE, buf->b_fname, buf->b_fname,
FALSE, buf)
@@ -533,6 +537,10 @@ aucmd_abort:
if (aborting()) /* autocmds may abort script processing */
return;
# endif
+# ifdef FEAT_WINDOWS
+ if (save_curtab != curtab)
+ return;
+# endif
}
nwindows = buf->b_nwindows;
#endif
@@ -561,7 +569,9 @@ aucmd_abort:
buf->b_nwindows = nwindows;
#endif
- buf_freeall(buf, (del_buf ? BFA_DEL : 0) + (wipe_buf ? BFA_WIPE : 0));
+ if (buf_freeall(buf, (del_buf ? BFA_DEL : 0) + (wipe_buf ? BFA_WIPE : 0))
+ == FAIL)
+ return;
#ifdef FEAT_AUTOCMD
/* Autocommands may have deleted the buffer. */
@@ -670,13 +680,17 @@ buf_clear_file(buf_T *buf)
* BFA_DEL buffer is going to be deleted
* BFA_WIPE buffer is going to be wiped out
* BFA_KEEP_UNDO do not free undo information
+ * Return FAIL for failure, OK otherwise.
*/
- void
+ int
buf_freeall(buf_T *buf, int flags)
{
#ifdef FEAT_AUTOCMD
int is_curbuf = (buf == curbuf);
bufref_T bufref;
+# ifdef FEAT_WINDOWS
+ tabpage_T *save_curtab = curtab;
+# endif
buf->b_closing = TRUE;
set_bufref(&bufref, buf);
@@ -686,7 +700,7 @@ buf_freeall(buf_T *buf, int flags)
FALSE, buf)
&& !bufref_valid(&bufref))
/* autocommands deleted the buffer */
- return;
+ return FAIL;
}
if ((flags & BFA_DEL) && buf->b_p_bl)
{
@@ -694,7 +708,7 @@ buf_freeall(buf_T *buf, int flags)
FALSE, buf)
&& !bufref_valid(&bufref))
/* autocommands deleted the buffer */
- return;
+ return FAIL;
}
if (flags & BFA_WIPE)
{
@@ -702,12 +716,16 @@ buf_freeall(buf_T *buf, int flags)
FALSE, buf)
&& !bufref_valid(&bufref))
/* autocommands deleted the buffer */
- return;
+ return FAIL;
}
buf->b_closing = FALSE;
# ifdef FEAT_EVAL
if (aborting()) /* autocmds may abort script processing */
- return;
+ return FAIL;
+# endif
+# ifdef FEAT_WINDOWS
+ if (save_curtab != curtab)
+ return FAIL;
# endif
/*
@@ -717,7 +735,7 @@ buf_freeall(buf_T *buf, int flags)
* Therefore only return if curbuf changed to the deleted buffer.
*/
if (buf == curbuf && !is_curbuf)
- return;
+ return FAIL;
#endif
#ifdef FEAT_DIFF
diff_buf_delete(buf); /* Can't use 'diff' for unloaded buffer. */
@@ -759,6 +777,7 @@ buf_freeall(buf_T *buf, int flags)
syntax_clear(&buf->b_s); /* reset syntax info */
#endif
buf->b_flags &= ~BF_READERR; /* a read error is no longer relevant */
+ return OK;
}
/*
@@ -1940,7 +1959,8 @@ buflist_new(
if (buf == curbuf)
{
/* free all things allocated for this buffer */
- buf_freeall(buf, 0);
+ if (buf_freeall(buf, 0) == FAIL)
+ return NULL;
if (buf != curbuf) /* autocommands deleted the buffer! */
return NULL;
#if defined(FEAT_AUTOCMD) && defined(FEAT_EVAL)
diff --git a/src/ex_cmds.c b/src/ex_cmds.c
index 9e6a72d..57ff429 100644
--- a/src/ex_cmds.c
+++ b/src/ex_cmds.c
@@ -4098,7 +4098,13 @@ do_ecmd(
goto theend;
}
u_unchanged(curbuf);
- buf_freeall(curbuf, BFA_KEEP_UNDO);
+ if (buf_freeall(curbuf, BFA_KEEP_UNDO) == FAIL)
+ {
+#ifdef FEAT_AUTOCMD
+ vim_free(new_name);
+#endif
+ goto theend;
+ }
/* tell readfile() not to clear or reload undo info */
readfile_flags = READ_KEEP_UNDO;
@@ -4394,6 +4400,11 @@ delbuf_msg(char_u *name)
vim_free(name);
au_new_curbuf.br_buf = NULL;
au_new_curbuf.br_buf_free_count = 0;
+
+# ifdef FEAT_WINDOWS
+ if (curwin->w_buffer == NULL)
+ enter_buffer(curbuf);
+# endif
}
#endif
diff --git a/src/proto/buffer.pro b/src/proto/buffer.pro
index 183f79a..dbe152b 100644
--- a/src/proto/buffer.pro
+++ b/src/proto/buffer.pro
@@ -5,7 +5,7 @@ int bufref_valid(bufref_T *bufref);
int buf_valid(buf_T *buf);
void close_buffer(win_T *win, buf_T *buf, int action, int abort_if_last);
void buf_clear_file(buf_T *buf);
-void buf_freeall(buf_T *buf, int flags);
+int buf_freeall(buf_T *buf, int flags);
void goto_buffer(exarg_T *eap, int start, int dir, int count);
void handle_swap_exists(bufref_T *old_curbuf);
char_u *do_bufdel(int command, char_u *arg, int addr_count, int start_bnr, int end_bnr, int forceit);
diff --git a/src/testdir/test_autocmd.vim b/src/testdir/test_autocmd.vim
index d856d32..7afa261 100644
--- a/src/testdir/test_autocmd.vim
+++ b/src/testdir/test_autocmd.vim
@@ -77,11 +77,48 @@ function Test_autocmd_bufunload_with_tabnext()
quit
call assert_equal(2, tabpagenr('$'))
+ autocmd! test_autocmd_bufunload_with_tabnext_group
augroup! test_autocmd_bufunload_with_tabnext_group
tablast
quit
endfunc
+" SEGV occurs in older version. (At least 7.4.2247 or older)
+function Test_autocmd_bufunload_avoiding_SEGV_01()
+ edit a.txt
+
+ augroup test_autocmd_bufunload
+ autocmd!
+ autocmd BufUnload <buffer> tabfirst | 2bwipeout!
+ augroup END
+
+ edit b.txt
+ call assert_equal(3, bufnr('$'))
+
+ autocmd! test_autocmd_bufunload
+ augroup! test_autocmd_bufunload
+ only!
+endfunc
+
+" SEGV occurs in older version. (At least 7.4.2247 or older)
+function Test_autocmd_bufunload_avoiding_SEGV_02()
+ setlocal buftype=nowrite
+
+ augroup test_autocmd_bufunload
+ autocmd!
+ autocmd BufUnload <buffer> tabfirst | 2bwipeout
+ augroup END
+
+ normal! i1
+ edit a.txt
+ call feedkeys("\<CR>", 'tx')
+ call assert_equal(1, bufnr('$'))
+
+ autocmd! test_autocmd_bufunload
+ augroup! test_autocmd_bufunload
+ new | only!
+endfunc
+
func Test_win_tab_autocmd()
let g:record = []
@@ -168,3 +205,5 @@ func Test_augroup_warning()
augroup END
call assert_true(match(execute('au VimEnter'), "-Deleted-.*VimEnter") >= 0)
endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/window.c b/src/window.c
index b015d1d..71202b8 100644
--- a/src/window.c
+++ b/src/window.c
@@ -2305,7 +2305,12 @@ win_close(win_T *win, int free_buf)
* and then close the window and the tab page to avoid that curwin and
* curtab are invalid while we are freeing memory. */
if (close_last_window_tabpage(win, free_buf, prev_curtab))
- return FAIL;
+ {
+ /* Autocommands have close curwin's buf. Restore curwin->w_buffer */
+ if (win_valid(curwin) && curwin->w_buffer == NULL)
+ curwin->w_buffer = curbuf;
+ return FAIL;
+ }
/* When closing the help window, try restoring a snapshot after closing
* the window. Otherwise clear the snapshot, it's now invalid. */
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment