To: vim_dev@googlegroups.com Subject: Patch 7.4.2324 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ 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. Files: src/structs.h, src/buffer.c, src/ex_cmds.c, src/ex_docmd.c, src/window.c, src/testdir/test13.in, src/testdir/test13.ok, src/testdir/test_autocmd.vim, src/testdir/Make_all.mak, src/Makefile *** ../vim-7.4.2323/src/structs.h 2016-08-29 22:48:12.169106012 +0200 --- src/structs.h 2016-09-04 17:20:33.023755549 +0200 *************** *** 1845,1852 **** int b_flags; /* various BF_ flags */ #ifdef FEAT_AUTOCMD ! int b_closing; /* buffer is being closed, don't let ! autocommands close it too. */ #endif /* --- 1845,1852 ---- int b_flags; /* various BF_ flags */ #ifdef FEAT_AUTOCMD ! int b_locked; /* Buffer is being closed or referenced, don't ! let autocommands wipe it out. */ #endif /* *** ../vim-7.4.2323/src/buffer.c 2016-09-03 16:29:01.442841209 +0200 --- src/buffer.c 2016-09-04 18:21:06.192908890 +0200 *************** *** 476,481 **** --- 476,489 ---- 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,505 **** /* When the buffer is no longer in a window, trigger BufWinLeave */ if (buf->b_nwindows == 1) { ! buf->b_closing = TRUE; if (apply_autocmds(EVENT_BUFWINLEAVE, buf->b_fname, buf->b_fname, FALSE, buf) && !bufref_valid(&bufref)) --- 507,513 ---- /* When the buffer is no longer in a window, trigger BufWinLeave */ if (buf->b_nwindows == 1) { ! ++buf->b_locked; if (apply_autocmds(EVENT_BUFWINLEAVE, buf->b_fname, buf->b_fname, FALSE, buf) && !bufref_valid(&bufref)) *************** *** 509,515 **** EMSG(_(e_auabort)); return; } ! buf->b_closing = FALSE; if (abort_if_last && one_window()) /* Autocommands made this the only window. */ goto aucmd_abort; --- 517,523 ---- EMSG(_(e_auabort)); return; } ! --buf->b_locked; if (abort_if_last && one_window()) /* Autocommands made this the only window. */ goto aucmd_abort; *************** *** 518,530 **** * BufHidden */ if (!unload_buf) { ! buf->b_closing = TRUE; 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; if (abort_if_last && one_window()) /* Autocommands made this the only window. */ goto aucmd_abort; --- 526,538 ---- * BufHidden */ if (!unload_buf) { ! ++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_locked; if (abort_if_last && one_window()) /* Autocommands made this the only window. */ goto aucmd_abort; *************** *** 685,691 **** # endif /* Make sure the buffer isn't closed by autocommands. */ ! buf->b_closing = TRUE; set_bufref(&bufref, buf); if (buf->b_ml.ml_mfp != NULL) { --- 693,699 ---- # endif /* Make sure the buffer isn't closed by autocommands. */ ! ++buf->b_locked; set_bufref(&bufref, buf); if (buf->b_ml.ml_mfp != NULL) { *************** *** 711,717 **** /* autocommands deleted the buffer */ return; } ! buf->b_closing = FALSE; # ifdef FEAT_WINDOWS /* If the buffer was in curwin and the window has changed, go back to that --- 719,725 ---- /* autocommands deleted the buffer */ return; } ! --buf->b_locked; # ifdef FEAT_WINDOWS /* If the buffer was in curwin and the window has changed, go back to that *************** *** 1369,1375 **** */ while (buf == curbuf # ifdef FEAT_AUTOCMD ! && !(curwin->w_closing || curwin->w_buffer->b_closing) # endif && (firstwin != lastwin || first_tabpage->tp_next != NULL)) { --- 1377,1383 ---- */ while (buf == curbuf # ifdef FEAT_AUTOCMD ! && !(curwin->w_closing || curwin->w_buffer->b_locked > 0) # endif && (firstwin != lastwin || first_tabpage->tp_next != NULL)) { *************** *** 5100,5106 **** #endif ) && firstwin != lastwin #ifdef FEAT_AUTOCMD ! && !(wp->w_closing || wp->w_buffer->b_closing) #endif ) { --- 5108,5114 ---- #endif ) && firstwin != lastwin #ifdef FEAT_AUTOCMD ! && !(wp->w_closing || wp->w_buffer->b_locked > 0) #endif ) { *** ../vim-7.4.2323/src/ex_cmds.c 2016-09-03 16:29:01.446841174 +0200 --- src/ex_cmds.c 2016-09-04 19:44:37.748006216 +0200 *************** *** 3872,3879 **** oldbuf = TRUE; set_bufref(&bufref, buf); (void)buf_check_timestamp(buf, FALSE); ! /* Check if autocommands made buffer invalid or changed the current ! * buffer. */ if (!bufref_valid(&bufref) #ifdef FEAT_AUTOCMD || curbuf != old_curbuf.br_buf --- 3872,3879 ---- oldbuf = TRUE; set_bufref(&bufref, buf); (void)buf_check_timestamp(buf, FALSE); ! /* 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,3945 **** win_T *the_curwin = curwin; /* Set the w_closing flag to avoid that autocommands close the ! * window. */ the_curwin->w_closing = TRUE; if (curbuf == old_curbuf.br_buf) #endif --- 3938,3946 ---- win_T *the_curwin = curwin; /* Set the w_closing flag to avoid that autocommands close the ! * 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,3958 **** --- 3954,3960 ---- #ifdef FEAT_AUTOCMD the_curwin->w_closing = FALSE; + --buf->b_locked; # ifdef FEAT_EVAL /* autocmds may abort script processing */ *************** *** 4140,4150 **** 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); --- 4142,4147 ---- *** ../vim-7.4.2323/src/ex_docmd.c 2016-09-03 16:29:01.450841139 +0200 --- src/ex_docmd.c 2016-09-04 17:23:25.074265604 +0200 *************** *** 7201,7207 **** /* 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)) return; #endif --- 7201,7207 ---- /* 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_locked > 0)) return; #endif *************** *** 7283,7289 **** 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)) return; #endif --- 7283,7289 ---- 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_locked > 0)) return; #endif *************** *** 7665,7671 **** 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)) return; #endif --- 7665,7671 ---- 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_locked > 0)) return; #endif *** ../vim-7.4.2323/src/window.c 2016-09-03 16:29:01.450841139 +0200 --- src/window.c 2016-09-04 17:24:16.305822424 +0200 *************** *** 2127,2133 **** { if (wp->w_buffer == buf && (!keep_curwin || wp != curwin) #ifdef FEAT_AUTOCMD ! && !(wp->w_closing || wp->w_buffer->b_closing) #endif ) { --- 2127,2133 ---- { if (wp->w_buffer == buf && (!keep_curwin || wp != curwin) #ifdef FEAT_AUTOCMD ! && !(wp->w_closing || wp->w_buffer->b_locked > 0) #endif ) { *************** *** 2148,2154 **** 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) #endif ) { --- 2148,2154 ---- 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_locked > 0) #endif ) { *************** *** 2287,2293 **** } #ifdef FEAT_AUTOCMD ! if (win->w_closing || (win->w_buffer != NULL && win->w_buffer->b_closing)) return FAIL; /* window is already being closed */ if (win == aucmd_win) { --- 2287,2294 ---- } #ifdef FEAT_AUTOCMD ! 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,2509 **** #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)) return; /* window is already being closed */ #endif --- 2504,2511 ---- #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_locked > 0)) return; /* window is already being closed */ #endif *** ../vim-7.4.2323/src/testdir/test13.in 2015-06-19 12:43:02.380196210 +0200 --- src/testdir/test13.in 1970-01-01 01:00:00.000000000 +0100 *************** *** 1,64 **** - 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 --- 0 ---- *** ../vim-7.4.2323/src/testdir/test13.ok 2015-06-19 12:43:02.380196210 +0200 --- src/testdir/test13.ok 1970-01-01 01:00:00.000000000 +0100 *************** *** 1,31 **** - 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 --- 0 ---- *** ../vim-7.4.2323/src/testdir/test_autocmd.vim 2016-09-03 16:59:03.043059395 +0200 --- src/testdir/test_autocmd.vim 2016-09-04 18:25:24.522751653 +0200 *************** *** 77,87 **** --- 77,125 ---- 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 ' . (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 ' . (lastbuf + 1) . 'bwipeout!' + augroup END + + normal! i1 + call assert_fails('edit a.txt', 'E517:') + call feedkeys("\") + + autocmd! test_autocmd_bufunload + augroup! test_autocmd_bufunload + bwipe! a.txt + endfunc + func Test_win_tab_autocmd() let g:record = [] *************** *** 196,198 **** --- 234,296 ---- 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 *** ../vim-7.4.2323/src/testdir/Make_all.mak 2016-09-03 17:33:39.752215409 +0200 --- src/testdir/Make_all.mak 2016-09-04 18:02:13.950370486 +0200 *************** *** 111,117 **** SCRIPTS_MORE2 = \ test2.out \ test12.out \ - test13.out \ test25.out \ test49.out \ test97.out \ --- 111,116 ---- *** ../vim-7.4.2323/src/Makefile 2016-09-03 17:33:39.752215409 +0200 --- src/Makefile 2016-09-04 18:02:42.130134856 +0200 *************** *** 2042,2048 **** test_utf8 \ test_wordcount \ test2 test3 test4 test5 test6 test7 test8 test9 \ ! test11 test12 test13 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 \ --- 2042,2048 ---- test_utf8 \ test_wordcount \ test2 test3 test4 test5 test6 test7 test8 test9 \ ! 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 \ *** ../vim-7.4.2323/src/version.c 2016-09-04 15:13:36.621508727 +0200 --- src/version.c 2016-09-04 19:45:34.727544841 +0200 *************** *** 765,766 **** --- 765,768 ---- { /* Add new patch number below this line */ + /**/ + 2324, /**/ -- hundred-and-one symptoms of being an internet addict: 165. You have a web page burned into your glasses /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\ \\\ an exciting new programming language -- http://www.Zimbu.org /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///