1
0
forked from aniani/vim

patch 7.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.
This commit is contained in:
Bram Moolenaar 2016-09-04 19:50:54 +02:00
parent d77f9d595e
commit e0ab94e712
11 changed files with 133 additions and 122 deletions

View File

@ -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 \

View File

@ -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
)
{

View File

@ -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 */
@ -4139,11 +4141,6 @@ do_ecmd(
/* Assume success now */
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.
*/

View File

@ -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

View File

@ -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
/*

View File

@ -111,7 +111,6 @@ SCRIPTS_MORE1 = \
SCRIPTS_MORE2 = \
test2.out \
test12.out \
test13.out \
test25.out \
test49.out \
test97.out \

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -763,6 +763,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
2324,
/**/
2323,
/**/

View File

@ -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