]> granicus.if.org Git - vim/commitdiff
patch 7.4.2324 v7.4.2324
authorBram Moolenaar <Bram@vim.org>
Sun, 4 Sep 2016 17:50:54 +0000 (19:50 +0200)
committerBram Moolenaar <Bram@vim.org>
Sun, 4 Sep 2016 17:50:54 +0000 (19:50 +0200)
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.

src/Makefile
src/buffer.c
src/ex_cmds.c
src/ex_docmd.c
src/structs.h
src/testdir/Make_all.mak
src/testdir/test13.in [deleted file]
src/testdir/test13.ok [deleted file]
src/testdir/test_autocmd.vim
src/version.c
src/window.c

index b201ed494c49f9d1bc4ab06919e857458e08b229..a56aa04e6c4061276afacefac775d713b9750f4a 100644 (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 \
index 9270c39fc9f038fa36b13e8bcdc69283b2226980..f375c1f64947d762b333c8ef8abb6b737de43a3f 100644 (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
                    )
            {
index 61ab2ab125e744524a16a26e7a2292eb557e1fd9..daae0dd6fd9cc56e1a000dce6779f5153b2f6adb 100644 (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.
      */
index b568e46c3b5c5831215dc2282a1f9e5dd4b8223b..dc0a71e682a8a6d117a2e1f6fa3d496bd3c72df0 100644 (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
 
index ebeefd091f4623efb03cd6f349fb7ed249c33fd6..a44bcec4cbc79b27039ab4c279706413b3f96ac6 100644 (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
 
     /*
index b038605fbceefd7b2ad92353f09f3cd07d24feb4..5a7099337d6aef267d49aa298333e35ac99eadaf 100644 (file)
@@ -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 (file)
index cbf78c7..0000000
+++ /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\e
-:w
-:sp Xtestje2
-otestje2\e
-:w
-:sp Xtestje3
-otestje3\e
-:w
-\17\17
-:au WinLeave Xtestje2 bwipe
-\17\17
-: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 (file)
index 66ebce6..0000000
+++ /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
index f05a55f1aaa6e2659275e551335b71a593530abf..43aa8d48dd8d8189755f7112a0d2f1eec83e276d 100644 (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
index 85121d6e32e01db0d3ea63be5de023a87c15efa7..f5843dd502369638354ba4bfffc42a64c857e82d 100644 (file)
@@ -763,6 +763,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    2324,
 /**/
     2323,
 /**/
index 4f992711fe9dc9467fe9f16456a0fba5deea032f..f61a83bb25bb764bf0f6545f2df90bf6e8a20e85 100644 (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