aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJustin M. Keyes <justinkz@gmail.com>2017-03-20 14:01:22 +0100
committerJustin M. Keyes <justinkz@gmail.com>2017-03-22 18:42:16 +0100
commit165ba3e636769c38d67285e1b8ea2966ccb00b30 (patch)
tree06dddf294d07fbb93aea6711dea72e014eba08b7 /src
parentca853edb6f9ffe1d2e5d4a63bf88e4c3a059f5fb (diff)
downloadrneovim-165ba3e636769c38d67285e1b8ea2966ccb00b30.tar.gz
rneovim-165ba3e636769c38d67285e1b8ea2966ccb00b30.tar.bz2
rneovim-165ba3e636769c38d67285e1b8ea2966ccb00b30.zip
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. https://github.com/vim/vim/commit/e0ab94e7123ca7855f45919114d948ef2bc1e8c3
Diffstat (limited to 'src')
-rw-r--r--src/nvim/buffer.c23
-rw-r--r--src/nvim/buffer_defs.h6
-rw-r--r--src/nvim/ex_cmds.c11
-rw-r--r--src/nvim/ex_docmd.c6
-rw-r--r--src/nvim/testdir/test13.in63
-rw-r--r--src/nvim/testdir/test13.ok31
-rw-r--r--src/nvim/testdir/test_autocmd.vim99
-rw-r--r--src/nvim/version.c2
-rw-r--r--src/nvim/window.c13
9 files changed, 133 insertions, 121 deletions
diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c
index 9e781f5dff..5edc87eab1 100644
--- a/src/nvim/buffer.c
+++ b/src/nvim/buffer.c
@@ -360,6 +360,13 @@ void close_buffer(win_T *win, buf_T *buf, int action, int abort_if_last)
wipe_buf = true;
}
+ // 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_valid_any_tab(win)) {
// Set b_last_cursor when closing the last window for the buffer.
// Remember the last cursor position and window options of the buffer.
@@ -378,14 +385,14 @@ void close_buffer(win_T *win, buf_T *buf, int action, int abort_if_last)
/* 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)) {
// Autocommands deleted the buffer.
EMSG(_(e_auabort));
return;
}
- buf->b_closing = false;
+ buf->b_locked--;
if (abort_if_last && one_window()) {
/* Autocommands made this the only window. */
EMSG(_(e_auabort));
@@ -395,14 +402,14 @@ void close_buffer(win_T *win, buf_T *buf, int action, int abort_if_last)
/* When the buffer becomes hidden, but is not unloaded, trigger
* 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.
EMSG(_(e_auabort));
return;
}
- buf->b_closing = false;
+ buf->b_locked--;
if (abort_if_last && one_window()) {
/* Autocommands made this the only window. */
EMSG(_(e_auabort));
@@ -559,7 +566,7 @@ void buf_freeall(buf_T *buf, int flags)
tabpage_T *the_curtab = curtab;
// Make sure the buffer isn't closed by autocommands.
- buf->b_closing = true;
+ buf->b_locked++;
bufref_T bufref;
set_bufref(&bufref, buf);
@@ -584,7 +591,7 @@ void buf_freeall(buf_T *buf, int flags)
// Autocommands may delete the buffer.
return;
}
- buf->b_closing = false;
+ buf->b_locked--;
// If the buffer was in curwin and the window has changed, go back to that
// window, if it still exists. This avoids that ":edit x" triggering a
@@ -1111,7 +1118,7 @@ do_buffer (
* a window with this buffer.
*/
while (buf == curbuf
- && !(curwin->w_closing || curwin->w_buffer->b_closing)
+ && !(curwin->w_closing || curwin->w_buffer->b_locked > 0)
&& (firstwin != lastwin || first_tabpage->tp_next != NULL)) {
if (win_close(curwin, FALSE) == FAIL)
break;
@@ -4499,7 +4506,7 @@ void ex_buffer_all(exarg_T *eap)
: wp->w_width != Columns)
|| (had_tab > 0 && wp != firstwin)
) && firstwin != lastwin
- && !(wp->w_closing || wp->w_buffer->b_closing)
+ && !(wp->w_closing || wp->w_buffer->b_locked > 0)
) {
win_close(wp, FALSE);
wpnext = firstwin; /* just in case an autocommand does
diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h
index fdd7d945c9..9d350c763e 100644
--- a/src/nvim/buffer_defs.h
+++ b/src/nvim/buffer_defs.h
@@ -470,9 +470,9 @@ struct file_buffer {
int b_nwindows; /* nr of windows open on this buffer */
- int b_flags; /* various BF_ flags */
- bool b_closing; /* buffer is being closed, don't let
- autocommands close it too. */
+ int b_flags; // various BF_ flags
+ int b_locked; // Buffer is being closed or referenced, don't
+ // let autocommands wipe it out.
/*
* b_ffname has the full path of the file (NULL for no name).
diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c
index 1b83677807..41f97b0cef 100644
--- a/src/nvim/ex_cmds.c
+++ b/src/nvim/ex_cmds.c
@@ -2284,8 +2284,11 @@ int do_ecmd(
} else {
win_T *the_curwin = curwin;
- // Set the w_closing flag to avoid that autocommands close the window.
+ // Set w_closing to avoid that autocommands close the window.
+ // Set b_locked for the same reason.
the_curwin->w_closing = true;
+ buf->b_locked++;
+
if (curbuf == old_curbuf.br_buf) {
buf_copy_options(buf, BCO_ENTER);
}
@@ -2298,6 +2301,7 @@ int do_ecmd(
false);
the_curwin->w_closing = false;
+ buf->b_locked--;
// autocmds may abort script processing
if (aborting() && curwin->w_buffer != NULL) {
@@ -2445,11 +2449,6 @@ int 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/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
index bd3b8c204a..ebfbf2bf09 100644
--- a/src/nvim/ex_docmd.c
+++ b/src/nvim/ex_docmd.c
@@ -5775,7 +5775,7 @@ static void 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_nwindows == 1 && wp->w_buffer->b_locked > 0)) {
return;
}
@@ -5836,7 +5836,7 @@ static void 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;
exiting = true;
@@ -6131,7 +6131,7 @@ static void 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;
// if more files or windows we won't exit
diff --git a/src/nvim/testdir/test13.in b/src/nvim/testdir/test13.in
deleted file mode 100644
index fa9ba312b7..0000000000
--- a/src/nvim/testdir/test13.in
+++ /dev/null
@@ -1,63 +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
-:/^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
-:new|set buftype=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/nvim/testdir/test13.ok b/src/nvim/testdir/test13.ok
deleted file mode 100644
index 66ebce63f7..0000000000
--- a/src/nvim/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/nvim/testdir/test_autocmd.vim b/src/nvim/testdir/test_autocmd.vim
index 395c4a8596..39cd92440e 100644
--- a/src/nvim/testdir/test_autocmd.vim
+++ b/src/nvim/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,6 +234,67 @@ 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
+
func Test_BufEnter()
au! BufEnter
au Bufenter * let val = val . '+'
diff --git a/src/nvim/version.c b/src/nvim/version.c
index 32ef2c2fdb..8ae5e4057e 100644
--- a/src/nvim/version.c
+++ b/src/nvim/version.c
@@ -117,7 +117,7 @@ static int included_patches[] = {
// 2327 NA
2326,
// 2325 NA
- // 2324,
+ 2324,
2323,
2322,
2321,
diff --git a/src/nvim/window.c b/src/nvim/window.c
index eda3cd7810..a737ffb33c 100644
--- a/src/nvim/window.c
+++ b/src/nvim/window.c
@@ -1727,7 +1727,7 @@ void close_windows(buf_T *buf, int keep_curwin)
for (win_T *wp = firstwin; wp != NULL && lastwin != firstwin; ) {
if (wp->w_buffer == buf && (!keep_curwin || wp != curwin)
- && !(wp->w_closing || wp->w_buffer->b_closing)) {
+ && !(wp->w_closing || wp->w_buffer->b_locked > 0)) {
if (win_close(wp, false) == FAIL) {
// If closing the window fails give up, to avoid looping forever.
break;
@@ -1745,8 +1745,7 @@ void close_windows(buf_T *buf, int keep_curwin)
if (tp != curtab) {
FOR_ALL_WINDOWS_IN_TAB(wp, tp) {
if (wp->w_buffer == buf
- && !(wp->w_closing || wp->w_buffer->b_closing)
- ) {
+ && !(wp->w_closing || wp->w_buffer->b_locked > 0)) {
win_close_othertab(wp, FALSE, tp);
/* Start all over, the tab page may be closed and
@@ -1882,8 +1881,9 @@ int win_close(win_T *win, int free_buf)
return FAIL;
}
- if (win->w_closing || (win->w_buffer != NULL && win->w_buffer->b_closing))
- return FAIL; /* window is already being closed */
+ 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) {
EMSG(_("E813: Cannot close autocmd window"));
return FAIL;
@@ -2064,7 +2064,8 @@ void win_close_othertab(win_T *win, int free_buf, tabpage_T *tp)
// 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
}