summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBram Moolenaar <Bram@vim.org>2016-09-04 19:50:54 +0200
committerBram Moolenaar <Bram@vim.org>2016-09-04 19:50:54 +0200
commite0ab94e7123ca7855f45919114d948ef2bc1e8c3 (patch)
tree868d44c51ca94d1aade6e80cf07617acc1448f80
parentd77f9d595eb5f301b39b4373f2900a13c0ca30e2 (diff)
downloadvim-git-e0ab94e7123ca7855f45919114d948ef2bc1e8c3.tar.gz
patch 7.4.2324v7.4.2324
Problem: Crash when editing a new buffer and BufUnload autocommand wipes out the new buffer. (Norio Takagi) Solution: Don't allow wiping out this buffer. (partly by Hirohito Higashi) Move old style test13 into test_autocmd. Avoid ml_get error when editing a file.
-rw-r--r--src/Makefile2
-rw-r--r--src/buffer.c24
-rw-r--r--src/ex_cmds.c13
-rw-r--r--src/ex_docmd.c6
-rw-r--r--src/structs.h4
-rw-r--r--src/testdir/Make_all.mak1
-rw-r--r--src/testdir/test13.in64
-rw-r--r--src/testdir/test13.ok31
-rw-r--r--src/testdir/test_autocmd.vim98
-rw-r--r--src/version.c2
-rw-r--r--src/window.c10
11 files changed, 133 insertions, 122 deletions
diff --git a/src/Makefile b/src/Makefile
index b201ed494..a56aa04e6 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -2042,7 +2042,7 @@ test1 \
test_utf8 \
test_wordcount \
test2 test3 test4 test5 test6 test7 test8 test9 \
- test11 test12 test13 test14 test15 test17 test18 test19 \
+ test11 test12 test14 test15 test17 test18 test19 \
test20 test21 test22 test23 test24 test25 test26 test27 test28 test29 \
test30 test31 test32 test33 test34 test36 test37 test38 test39 \
test40 test41 test42 test43 test44 test45 test48 test49 \
diff --git a/src/buffer.c b/src/buffer.c
index 9270c39fc..f375c1f64 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -476,6 +476,14 @@ close_buffer(
unload_buf = TRUE;
#endif
+ /* Disallow deleting the buffer when it is locked (already being closed or
+ * halfway a command that relies on it). Unloading is allowed. */
+ if (buf->b_locked > 0 && (del_buf || wipe_buf))
+ {
+ EMSG(_("E937: Attempt to delete a buffer that is in use"));
+ return;
+ }
+
if (win != NULL
#ifdef FEAT_WINDOWS
&& win_valid_any_tab(win) /* in case autocommands closed the window */
@@ -499,7 +507,7 @@ close_buffer(
/* When the buffer is no longer in a window, trigger BufWinLeave */
if (buf->b_nwindows == 1)
{
- buf->b_closing = TRUE;
+ ++buf->b_locked;
if (apply_autocmds(EVENT_BUFWINLEAVE, buf->b_fname, buf->b_fname,
FALSE, buf)
&& !bufref_valid(&bufref))
@@ -509,7 +517,7 @@ aucmd_abort:
EMSG(_(e_auabort));
return;
}
- buf->b_closing = FALSE;
+ --buf->b_locked;
if (abort_if_last && one_window())
/* Autocommands made this the only window. */
goto aucmd_abort;
@@ -518,13 +526,13 @@ aucmd_abort:
* BufHidden */
if (!unload_buf)
{
- buf->b_closing = TRUE;
+ ++buf->b_locked;
if (apply_autocmds(EVENT_BUFHIDDEN, buf->b_fname, buf->b_fname,
FALSE, buf)
&& !bufref_valid(&bufref))
/* Autocommands deleted the buffer. */
goto aucmd_abort;
- buf->b_closing = FALSE;
+ --buf->b_locked;
if (abort_if_last && one_window())
/* Autocommands made this the only window. */
goto aucmd_abort;
@@ -685,7 +693,7 @@ buf_freeall(buf_T *buf, int flags)
# endif
/* Make sure the buffer isn't closed by autocommands. */
- buf->b_closing = TRUE;
+ ++buf->b_locked;
set_bufref(&bufref, buf);
if (buf->b_ml.ml_mfp != NULL)
{
@@ -711,7 +719,7 @@ buf_freeall(buf_T *buf, int flags)
/* autocommands deleted the buffer */
return;
}
- buf->b_closing = FALSE;
+ --buf->b_locked;
# ifdef FEAT_WINDOWS
/* If the buffer was in curwin and the window has changed, go back to that
@@ -1369,7 +1377,7 @@ do_buffer(
*/
while (buf == curbuf
# ifdef FEAT_AUTOCMD
- && !(curwin->w_closing || curwin->w_buffer->b_closing)
+ && !(curwin->w_closing || curwin->w_buffer->b_locked > 0)
# endif
&& (firstwin != lastwin || first_tabpage->tp_next != NULL))
{
@@ -5100,7 +5108,7 @@ ex_buffer_all(exarg_T *eap)
#endif
) && firstwin != lastwin
#ifdef FEAT_AUTOCMD
- && !(wp->w_closing || wp->w_buffer->b_closing)
+ && !(wp->w_closing || wp->w_buffer->b_locked > 0)
#endif
)
{
diff --git a/src/ex_cmds.c b/src/ex_cmds.c
index 61ab2ab12..daae0dd6f 100644
--- a/src/ex_cmds.c
+++ b/src/ex_cmds.c
@@ -3872,8 +3872,8 @@ do_ecmd(
oldbuf = TRUE;
set_bufref(&bufref, buf);
(void)buf_check_timestamp(buf, FALSE);
- /* Check if autocommands made buffer invalid or changed the current
- * buffer. */
+ /* Check if autocommands made the buffer invalid or changed the
+ * current buffer. */
if (!bufref_valid(&bufref)
#ifdef FEAT_AUTOCMD
|| curbuf != old_curbuf.br_buf
@@ -3938,8 +3938,9 @@ do_ecmd(
win_T *the_curwin = curwin;
/* Set the w_closing flag to avoid that autocommands close the
- * window. */
+ * window. And set b_locked for the same reason. */
the_curwin->w_closing = TRUE;
+ ++buf->b_locked;
if (curbuf == old_curbuf.br_buf)
#endif
@@ -3953,6 +3954,7 @@ do_ecmd(
#ifdef FEAT_AUTOCMD
the_curwin->w_closing = FALSE;
+ --buf->b_locked;
# ifdef FEAT_EVAL
/* autocmds may abort script processing */
@@ -4140,11 +4142,6 @@ do_ecmd(
retval = OK;
/*
- * Reset cursor position, could be used by autocommands.
- */
- check_cursor();
-
- /*
* Check if we are editing the w_arg_idx file in the argument list.
*/
check_arg_idx(curwin);
diff --git a/src/ex_docmd.c b/src/ex_docmd.c
index b568e46c3..dc0a71e68 100644
--- a/src/ex_docmd.c
+++ b/src/ex_docmd.c
@@ -7201,7 +7201,7 @@ ex_quit(exarg_T *eap)
/* Refuse to quit when locked or when the buffer in the last window is
* being closed (can only happen in autocommands). */
if (curbuf_locked() || (wp->w_buffer->b_nwindows == 1
- && wp->w_buffer->b_closing))
+ && wp->w_buffer->b_locked > 0))
return;
#endif
@@ -7283,7 +7283,7 @@ ex_quit_all(exarg_T *eap)
apply_autocmds(EVENT_QUITPRE, NULL, NULL, FALSE, curbuf);
/* Refuse to quit when locked or when the buffer in the last window is
* being closed (can only happen in autocommands). */
- if (curbuf_locked() || (curbuf->b_nwindows == 1 && curbuf->b_closing))
+ if (curbuf_locked() || (curbuf->b_nwindows == 1 && curbuf->b_locked > 0))
return;
#endif
@@ -7665,7 +7665,7 @@ ex_exit(exarg_T *eap)
apply_autocmds(EVENT_QUITPRE, NULL, NULL, FALSE, curbuf);
/* Refuse to quit when locked or when the buffer in the last window is
* being closed (can only happen in autocommands). */
- if (curbuf_locked() || (curbuf->b_nwindows == 1 && curbuf->b_closing))
+ if (curbuf_locked() || (curbuf->b_nwindows == 1 && curbuf->b_locked > 0))
return;
#endif
diff --git a/src/structs.h b/src/structs.h
index ebeefd091..a44bcec4c 100644
--- a/src/structs.h
+++ b/src/structs.h
@@ -1845,8 +1845,8 @@ struct file_buffer
int b_flags; /* various BF_ flags */
#ifdef FEAT_AUTOCMD
- int b_closing; /* buffer is being closed, don't let
- autocommands close it too. */
+ int b_locked; /* Buffer is being closed or referenced, don't
+ let autocommands wipe it out. */
#endif
/*
diff --git a/src/testdir/Make_all.mak b/src/testdir/Make_all.mak
index b038605fb..5a7099337 100644
--- a/src/testdir/Make_all.mak
+++ b/src/testdir/Make_all.mak
@@ -111,7 +111,6 @@ SCRIPTS_MORE1 = \
SCRIPTS_MORE2 = \
test2.out \
test12.out \
- test13.out \
test25.out \
test49.out \
test97.out \
diff --git a/src/testdir/test13.in b/src/testdir/test13.in
deleted file mode 100644
index cbf78c736..000000000
--- a/src/testdir/test13.in
+++ /dev/null
@@ -1,64 +0,0 @@
-Tests for autocommands on :close command
-
-Write three files and open them, each in a window.
-Then go to next window, with autocommand that deletes the previous one.
-Do this twice, writing the file.
-
-Also test deleting the buffer on a Unload event. If this goes wrong there
-will be the ATTENTION prompt.
-
-Also test changing buffers in a BufDel autocommand. If this goes wrong there
-are ml_line errors and/or a Crash.
-
-STARTTEST
-:so small.vim
-:/^start of testfile/,/^end of testfile/w! Xtestje1
-:/^start of testfile/,/^end of testfile/w! Xtestje2
-:/^start of testfile/,/^end of testfile/w! Xtestje3
-:e Xtestje1
-otestje1
-:w
-:sp Xtestje2
-otestje2
-:w
-:sp Xtestje3
-otestje3
-:w
-
-:au WinLeave Xtestje2 bwipe
-
-:w! test.out
-:au WinLeave Xtestje1 bwipe Xtestje3
-:close
-:w >>test.out
-:e Xtestje1
-:bwipe Xtestje2 Xtestje3 test.out
-:au!
-:au! BufUnload Xtestje1 bwipe
-:e Xtestje3
-:w >>test.out
-:e Xtestje2
-:sp Xtestje1
-:e
-:w >>test.out
-:au!
-:only
-:e Xtestje1
-:bwipe Xtestje2 Xtestje3 test.out test13.in
-:au BufWipeout Xtestje1 buf Xtestje1
-:bwipe
-:w >>test.out
-:only
-:help
-:wincmd w
-:1quit
-:$put ='Final line'
-:$w >>test.out
-:qa!
-ENDTEST
-
-start of testfile
- contents
- contents
- contents
-end of testfile
diff --git a/src/testdir/test13.ok b/src/testdir/test13.ok
deleted file mode 100644
index 66ebce63f..000000000
--- a/src/testdir/test13.ok
+++ /dev/null
@@ -1,31 +0,0 @@
-start of testfile
-testje1
- contents
- contents
- contents
-end of testfile
-start of testfile
-testje1
- contents
- contents
- contents
-end of testfile
-start of testfile
-testje3
- contents
- contents
- contents
-end of testfile
-start of testfile
-testje2
- contents
- contents
- contents
-end of testfile
-start of testfile
-testje1
- contents
- contents
- contents
-end of testfile
-Final line
diff --git a/src/testdir/test_autocmd.vim b/src/testdir/test_autocmd.vim
index f05a55f1a..43aa8d48d 100644
--- a/src/testdir/test_autocmd.vim
+++ b/src/testdir/test_autocmd.vim
@@ -77,11 +77,49 @@ 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 versions. (At least 7.4.2321 or older)
+function Test_autocmd_bufunload_avoiding_SEGV_01()
+ split aa.txt
+ let lastbuf = bufnr('$')
+
+ augroup test_autocmd_bufunload
+ autocmd!
+ exe 'autocmd BufUnload <buffer> ' . (lastbuf + 1) . 'bwipeout!'
+ augroup END
+
+ call assert_fails('edit bb.txt', 'E937:')
+
+ autocmd! test_autocmd_bufunload
+ augroup! test_autocmd_bufunload
+ bwipe! aa.txt
+ bwipe! bb.txt
+endfunc
+
+" SEGV occurs in older versions. (At least 7.4.2321 or older)
+function Test_autocmd_bufunload_avoiding_SEGV_02()
+ setlocal buftype=nowrite
+ let lastbuf = bufnr('$')
+
+ augroup test_autocmd_bufunload
+ autocmd!
+ exe 'autocmd BufUnload <buffer> ' . (lastbuf + 1) . 'bwipeout!'
+ augroup END
+
+ normal! i1
+ call assert_fails('edit a.txt', 'E517:')
+ call feedkeys("\<CR>")
+
+ autocmd! test_autocmd_bufunload
+ augroup! test_autocmd_bufunload
+ bwipe! a.txt
+endfunc
+
func Test_win_tab_autocmd()
let g:record = []
@@ -196,3 +234,63 @@ func Test_augroup_deleted()
au! VimEnter
endfunc
+" Tests for autocommands on :close command.
+" This used to be in test13.
+func Test_three_windows()
+ " Write three files and open them, each in a window.
+ " Then go to next window, with autocommand that deletes the previous one.
+ " Do this twice, writing the file.
+ e! Xtestje1
+ call setline(1, 'testje1')
+ w
+ sp Xtestje2
+ call setline(1, 'testje2')
+ w
+ sp Xtestje3
+ call setline(1, 'testje3')
+ w
+ wincmd w
+ au WinLeave Xtestje2 bwipe
+ wincmd w
+ call assert_equal('Xtestje1', expand('%'))
+
+ au WinLeave Xtestje1 bwipe Xtestje3
+ close
+ call assert_equal('Xtestje1', expand('%'))
+
+ " Test deleting the buffer on a Unload event. If this goes wrong there
+ " will be the ATTENTION prompt.
+ e Xtestje1
+ au!
+ au! BufUnload Xtestje1 bwipe
+ call assert_fails('e Xtestje3', 'E937:')
+ call assert_equal('Xtestje3', expand('%'))
+
+ e Xtestje2
+ sp Xtestje1
+ call assert_fails('e', 'E937:')
+ call assert_equal('Xtestje2', expand('%'))
+
+ " Test changing buffers in a BufWipeout autocommand. If this goes wrong
+ " there are ml_line errors and/or a Crash.
+ au!
+ only
+ e Xanother
+ e Xtestje1
+ bwipe Xtestje2
+ bwipe Xtestje3
+ au BufWipeout Xtestje1 buf Xtestje1
+ bwipe
+ call assert_equal('Xanother', expand('%'))
+
+ only
+ help
+ wincmd w
+ 1quit
+ call assert_equal('Xanother', expand('%'))
+
+ au!
+ call delete('Xtestje1')
+ call delete('Xtestje2')
+ call delete('Xtestje3')
+endfunc
diff --git a/src/version.c b/src/version.c
index 85121d6e3..f5843dd50 100644
--- a/src/version.c
+++ b/src/version.c
@@ -764,6 +764,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
+ 2324,
+/**/
2323,
/**/
2322,
diff --git a/src/window.c b/src/window.c
index 4f992711f..f61a83bb2 100644
--- a/src/window.c
+++ b/src/window.c
@@ -2127,7 +2127,7 @@ close_windows(
{
if (wp->w_buffer == buf && (!keep_curwin || wp != curwin)
#ifdef FEAT_AUTOCMD
- && !(wp->w_closing || wp->w_buffer->b_closing)
+ && !(wp->w_closing || wp->w_buffer->b_locked > 0)
#endif
)
{
@@ -2148,7 +2148,7 @@ close_windows(
for (wp = tp->tp_firstwin; wp != NULL; wp = wp->w_next)
if (wp->w_buffer == buf
#ifdef FEAT_AUTOCMD
- && !(wp->w_closing || wp->w_buffer->b_closing)
+ && !(wp->w_closing || wp->w_buffer->b_locked > 0)
#endif
)
{
@@ -2287,7 +2287,8 @@ win_close(win_T *win, int free_buf)
}
#ifdef FEAT_AUTOCMD
- if (win->w_closing || (win->w_buffer != NULL && win->w_buffer->b_closing))
+ if (win->w_closing || (win->w_buffer != NULL
+ && win->w_buffer->b_locked > 0))
return FAIL; /* window is already being closed */
if (win == aucmd_win)
{
@@ -2503,7 +2504,8 @@ win_close_othertab(win_T *win, int free_buf, tabpage_T *tp)
#ifdef FEAT_AUTOCMD
/* Get here with win->w_buffer == NULL when win_close() detects the tab
* page changed. */
- if (win->w_closing || (win->w_buffer != NULL && win->w_buffer->b_closing))
+ if (win->w_closing || (win->w_buffer != NULL
+ && win->w_buffer->b_locked > 0))
return; /* window is already being closed */
#endif