diff options
author | Justin M. Keyes <justinkz@gmail.com> | 2017-03-20 14:01:22 +0100 |
---|---|---|
committer | Justin M. Keyes <justinkz@gmail.com> | 2017-03-22 18:42:16 +0100 |
commit | 165ba3e636769c38d67285e1b8ea2966ccb00b30 (patch) | |
tree | 06dddf294d07fbb93aea6711dea72e014eba08b7 /src | |
parent | ca853edb6f9ffe1d2e5d4a63bf88e4c3a059f5fb (diff) | |
download | rneovim-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.c | 23 | ||||
-rw-r--r-- | src/nvim/buffer_defs.h | 6 | ||||
-rw-r--r-- | src/nvim/ex_cmds.c | 11 | ||||
-rw-r--r-- | src/nvim/ex_docmd.c | 6 | ||||
-rw-r--r-- | src/nvim/testdir/test13.in | 63 | ||||
-rw-r--r-- | src/nvim/testdir/test13.ok | 31 | ||||
-rw-r--r-- | src/nvim/testdir/test_autocmd.vim | 99 | ||||
-rw-r--r-- | src/nvim/version.c | 2 | ||||
-rw-r--r-- | src/nvim/window.c | 13 |
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 } |