| To: vim_dev@googlegroups.com |
| Subject: Patch 7.3.449 |
| Fcc: outbox |
| From: Bram Moolenaar <Bram@moolenaar.net> |
| Mime-Version: 1.0 |
| Content-Type: text/plain; charset=UTF-8 |
| Content-Transfer-Encoding: 8bit |
| |
| |
| Patch 7.3.449 |
| Problem: Crash when a BufWinLeave autocommand closes the only other window. |
| (Daniel Hunt) |
| Solution: Abort closing a buffer when it becomes the only one. |
| Files: src/buffer.c, src/proto/buffer.pro, src/ex_cmds.c, src/ex_getln.c, |
| src/misc2.c, src/quickfix.c, src/window.c, src/proto/window.pro |
| |
| |
| |
| |
| |
| *** 64,69 **** |
| --- 64,72 ---- |
| static char *msg_loclist = N_("[Location List]"); |
| static char *msg_qflist = N_("[Quickfix List]"); |
| #endif |
| + #ifdef FEAT_AUTOCMD |
| + static char *e_auabort = N_("E855: Autocommands caused command to abort"); |
| + #endif |
| |
| /* |
| * Open current buffer, that is: open the memfile and read the file into |
| |
| *** 96,102 **** |
| * There MUST be a memfile, otherwise we can't do anything |
| * If we can't create one for the current buffer, take another buffer |
| */ |
| ! close_buffer(NULL, curbuf, 0); |
| for (curbuf = firstbuf; curbuf != NULL; curbuf = curbuf->b_next) |
| if (curbuf->b_ml.ml_mfp != NULL) |
| break; |
| --- 99,105 ---- |
| * There MUST be a memfile, otherwise we can't do anything |
| * If we can't create one for the current buffer, take another buffer |
| */ |
| ! close_buffer(NULL, curbuf, 0, FALSE); |
| for (curbuf = firstbuf; curbuf != NULL; curbuf = curbuf->b_next) |
| if (curbuf->b_ml.ml_mfp != NULL) |
| break; |
| |
| *** 316,327 **** |
| * get a new buffer very soon! |
| * |
| * The 'bufhidden' option can force freeing and deleting. |
| */ |
| void |
| ! close_buffer(win, buf, action) |
| win_T *win; /* if not NULL, set b_last_cursor */ |
| buf_T *buf; |
| int action; |
| { |
| #ifdef FEAT_AUTOCMD |
| int is_curbuf; |
| --- 319,335 ---- |
| * get a new buffer very soon! |
| * |
| * The 'bufhidden' option can force freeing and deleting. |
| + * |
| + * When "abort_if_last" is TRUE then do not close the buffer if autocommands |
| + * cause there to be only one window with this buffer. e.g. when ":quit" is |
| + * supposed to close the window but autocommands close all other windows. |
| */ |
| void |
| ! close_buffer(win, buf, action, abort_if_last) |
| win_T *win; /* if not NULL, set b_last_cursor */ |
| buf_T *buf; |
| int action; |
| + int abort_if_last; |
| { |
| #ifdef FEAT_AUTOCMD |
| int is_curbuf; |
| |
| *** 371,378 **** |
| { |
| apply_autocmds(EVENT_BUFWINLEAVE, buf->b_fname, buf->b_fname, |
| FALSE, buf); |
| ! if (!buf_valid(buf)) /* autocommands may delete the buffer */ |
| return; |
| |
| /* When the buffer becomes hidden, but is not unloaded, trigger |
| * BufHidden */ |
| --- 379,390 ---- |
| { |
| apply_autocmds(EVENT_BUFWINLEAVE, buf->b_fname, buf->b_fname, |
| FALSE, buf); |
| ! /* Return if autocommands deleted the buffer or made it the only one. */ |
| ! if (!buf_valid(buf) || (abort_if_last && one_window())) |
| ! { |
| ! EMSG(_(e_auabort)); |
| return; |
| + } |
| |
| /* When the buffer becomes hidden, but is not unloaded, trigger |
| * BufHidden */ |
| |
| *** 380,387 **** |
| { |
| apply_autocmds(EVENT_BUFHIDDEN, buf->b_fname, buf->b_fname, |
| FALSE, buf); |
| ! if (!buf_valid(buf)) /* autocmds may delete the buffer */ |
| return; |
| } |
| # ifdef FEAT_EVAL |
| if (aborting()) /* autocmds may abort script processing */ |
| --- 392,404 ---- |
| { |
| apply_autocmds(EVENT_BUFHIDDEN, buf->b_fname, buf->b_fname, |
| FALSE, buf); |
| ! /* Return if autocommands deleted the buffer or made it the only |
| ! * one. */ |
| ! if (!buf_valid(buf) || (abort_if_last && one_window())) |
| ! { |
| ! EMSG(_(e_auabort)); |
| return; |
| + } |
| } |
| # ifdef FEAT_EVAL |
| if (aborting()) /* autocmds may abort script processing */ |
| |
| *** 775,781 **** |
| * open a new, empty buffer. */ |
| swap_exists_action = SEA_NONE; /* don't want it again */ |
| swap_exists_did_quit = TRUE; |
| ! close_buffer(curwin, curbuf, DOBUF_UNLOAD); |
| if (!buf_valid(old_curbuf) || old_curbuf == curbuf) |
| old_curbuf = buflist_new(NULL, NULL, 1L, BLN_CURBUF | BLN_LISTED); |
| if (old_curbuf != NULL) |
| --- 792,798 ---- |
| * open a new, empty buffer. */ |
| swap_exists_action = SEA_NONE; /* don't want it again */ |
| swap_exists_did_quit = TRUE; |
| ! close_buffer(curwin, curbuf, DOBUF_UNLOAD, FALSE); |
| if (!buf_valid(old_curbuf) || old_curbuf == curbuf) |
| old_curbuf = buflist_new(NULL, NULL, 1L, BLN_CURBUF | BLN_LISTED); |
| if (old_curbuf != NULL) |
| |
| *** 1122,1128 **** |
| * if the buffer still exists. |
| */ |
| if (buf != curbuf && buf_valid(buf) && buf->b_nwindows == 0) |
| ! close_buffer(NULL, buf, action); |
| return retval; |
| } |
| |
| --- 1139,1145 ---- |
| * if the buffer still exists. |
| */ |
| if (buf != curbuf && buf_valid(buf) && buf->b_nwindows == 0) |
| ! close_buffer(NULL, buf, action, FALSE); |
| return retval; |
| } |
| |
| |
| *** 1146,1152 **** |
| close_windows(buf, FALSE); |
| #endif |
| if (buf != curbuf && buf_valid(buf) && buf->b_nwindows <= 0) |
| ! close_buffer(NULL, buf, action); |
| return OK; |
| } |
| |
| --- 1163,1169 ---- |
| close_windows(buf, FALSE); |
| #endif |
| if (buf != curbuf && buf_valid(buf) && buf->b_nwindows <= 0) |
| ! close_buffer(NULL, buf, action, FALSE); |
| return OK; |
| } |
| |
| |
| *** 1378,1384 **** |
| close_buffer(prevbuf == curwin->w_buffer ? curwin : NULL, prevbuf, |
| unload ? action : (action == DOBUF_GOTO |
| && !P_HID(prevbuf) |
| ! && !bufIsChanged(prevbuf)) ? DOBUF_UNLOAD : 0); |
| } |
| } |
| #ifdef FEAT_AUTOCMD |
| --- 1395,1401 ---- |
| close_buffer(prevbuf == curwin->w_buffer ? curwin : NULL, prevbuf, |
| unload ? action : (action == DOBUF_GOTO |
| && !P_HID(prevbuf) |
| ! && !bufIsChanged(prevbuf)) ? DOBUF_UNLOAD : 0, FALSE); |
| } |
| } |
| #ifdef FEAT_AUTOCMD |
| |
| *** 2708,2714 **** |
| vim_free(ffname); |
| return FAIL; |
| } |
| ! close_buffer(NULL, obuf, DOBUF_WIPE); /* delete from the list */ |
| } |
| sfname = vim_strsave(sfname); |
| if (ffname == NULL || sfname == NULL) |
| --- 2725,2732 ---- |
| vim_free(ffname); |
| return FAIL; |
| } |
| ! /* delete from the list */ |
| ! close_buffer(NULL, obuf, DOBUF_WIPE, FALSE); |
| } |
| sfname = vim_strsave(sfname); |
| if (ffname == NULL || sfname == NULL) |
| |
| *** 5638,5644 **** |
| if (!aucmd) /* Don't trigger BufDelete autocommands here. */ |
| block_autocmds(); |
| #endif |
| ! close_buffer(NULL, buf, DOBUF_WIPE); |
| #ifdef FEAT_AUTOCMD |
| if (!aucmd) |
| unblock_autocmds(); |
| --- 5656,5662 ---- |
| if (!aucmd) /* Don't trigger BufDelete autocommands here. */ |
| block_autocmds(); |
| #endif |
| ! close_buffer(NULL, buf, DOBUF_WIPE, FALSE); |
| #ifdef FEAT_AUTOCMD |
| if (!aucmd) |
| unblock_autocmds(); |
| |
| |
| |
| *** 1,7 **** |
| /* buffer.c */ |
| int open_buffer __ARGS((int read_stdin, exarg_T *eap, int flags)); |
| int buf_valid __ARGS((buf_T *buf)); |
| ! void close_buffer __ARGS((win_T *win, buf_T *buf, int action)); |
| void buf_clear_file __ARGS((buf_T *buf)); |
| void buf_freeall __ARGS((buf_T *buf, int flags)); |
| void goto_buffer __ARGS((exarg_T *eap, int start, int dir, int count)); |
| --- 1,7 ---- |
| /* buffer.c */ |
| int open_buffer __ARGS((int read_stdin, exarg_T *eap, int flags)); |
| int buf_valid __ARGS((buf_T *buf)); |
| ! void close_buffer __ARGS((win_T *win, buf_T *buf, int action, int abort_if_last)); |
| void buf_clear_file __ARGS((buf_T *buf)); |
| void buf_freeall __ARGS((buf_T *buf, int flags)); |
| void goto_buffer __ARGS((exarg_T *eap, int start, int dir, int count)); |
| |
| |
| |
| *** 3387,3393 **** |
| /* close the link to the current buffer */ |
| u_sync(FALSE); |
| close_buffer(oldwin, curbuf, |
| ! (flags & ECMD_HIDE) ? 0 : DOBUF_UNLOAD); |
| |
| #ifdef FEAT_AUTOCMD |
| /* Autocommands may open a new window and leave oldwin open |
| --- 3387,3393 ---- |
| /* close the link to the current buffer */ |
| u_sync(FALSE); |
| close_buffer(oldwin, curbuf, |
| ! (flags & ECMD_HIDE) ? 0 : DOBUF_UNLOAD, FALSE); |
| |
| #ifdef FEAT_AUTOCMD |
| /* Autocommands may open a new window and leave oldwin open |
| |
| |
| |
| *** 6443,6449 **** |
| /* win_close() may have already wiped the buffer when 'bh' is |
| * set to 'wipe' */ |
| if (buf_valid(bp)) |
| ! close_buffer(NULL, bp, DOBUF_WIPE); |
| |
| /* Restore window sizes. */ |
| win_size_restore(&winsizes); |
| --- 6443,6449 ---- |
| /* win_close() may have already wiped the buffer when 'bh' is |
| * set to 'wipe' */ |
| if (buf_valid(bp)) |
| ! close_buffer(NULL, bp, DOBUF_WIPE, FALSE); |
| |
| /* Restore window sizes. */ |
| win_size_restore(&winsizes); |
| |
| |
| |
| *** 1173,1179 **** |
| for (buf = firstbuf; buf != NULL; ) |
| { |
| nextbuf = buf->b_next; |
| ! close_buffer(NULL, buf, DOBUF_WIPE); |
| if (buf_valid(buf)) |
| buf = nextbuf; /* didn't work, try next one */ |
| else |
| --- 1173,1179 ---- |
| for (buf = firstbuf; buf != NULL; ) |
| { |
| nextbuf = buf->b_next; |
| ! close_buffer(NULL, buf, DOBUF_WIPE, FALSE); |
| if (buf_valid(buf)) |
| buf = nextbuf; /* didn't work, try next one */ |
| else |
| |
| |
| |
| *** 3565,3571 **** |
| buf_T *buf; |
| { |
| if (curbuf != buf) /* safety check */ |
| ! close_buffer(NULL, buf, DOBUF_UNLOAD); |
| } |
| |
| #if defined(FEAT_EVAL) || defined(PROTO) |
| --- 3565,3571 ---- |
| buf_T *buf; |
| { |
| if (curbuf != buf) /* safety check */ |
| ! close_buffer(NULL, buf, DOBUF_UNLOAD, FALSE); |
| } |
| |
| #if defined(FEAT_EVAL) || defined(PROTO) |
| |
| |
| |
| *** 23,29 **** |
| static void win_totop __ARGS((int size, int flags)); |
| static void win_equal_rec __ARGS((win_T *next_curwin, int current, frame_T *topfr, int dir, int col, int row, int width, int height)); |
| static int last_window __ARGS((void)); |
| - static int one_window __ARGS((void)); |
| static win_T *win_free_mem __ARGS((win_T *win, int *dirp, tabpage_T *tp)); |
| static frame_T *win_altframe __ARGS((win_T *win, tabpage_T *tp)); |
| static tabpage_T *alt_tabpage __ARGS((void)); |
| --- 23,28 ---- |
| |
| *** 2083,2089 **** |
| * Return TRUE if there is only one window other than "aucmd_win" in the |
| * current tab page. |
| */ |
| ! static int |
| one_window() |
| { |
| #ifdef FEAT_AUTOCMD |
| --- 2082,2088 ---- |
| * Return TRUE if there is only one window other than "aucmd_win" in the |
| * current tab page. |
| */ |
| ! int |
| one_window() |
| { |
| #ifdef FEAT_AUTOCMD |
| |
| *** 2109,2115 **** |
| * Close window "win". Only works for the current tab page. |
| * If "free_buf" is TRUE related buffer may be unloaded. |
| * |
| ! * called by :quit, :close, :xit, :wq and findtag() |
| */ |
| void |
| win_close(win, free_buf) |
| --- 2108,2114 ---- |
| * Close window "win". Only works for the current tab page. |
| * If "free_buf" is TRUE related buffer may be unloaded. |
| * |
| ! * Called by :quit, :close, :xit, :wq and findtag(). |
| */ |
| void |
| win_close(win, free_buf) |
| |
| *** 2222,2228 **** |
| * Close the link to the buffer. |
| */ |
| if (win->w_buffer != NULL) |
| ! close_buffer(win, win->w_buffer, free_buf ? DOBUF_UNLOAD : 0); |
| |
| /* Autocommands may have closed the window already, or closed the only |
| * other window or moved to another tab page. */ |
| --- 2221,2227 ---- |
| * Close the link to the buffer. |
| */ |
| if (win->w_buffer != NULL) |
| ! close_buffer(win, win->w_buffer, free_buf ? DOBUF_UNLOAD : 0, TRUE); |
| |
| /* Autocommands may have closed the window already, or closed the only |
| * other window or moved to another tab page. */ |
| |
| *** 2328,2334 **** |
| int free_tp = FALSE; |
| |
| /* Close the link to the buffer. */ |
| ! close_buffer(win, win->w_buffer, free_buf ? DOBUF_UNLOAD : 0); |
| |
| /* Careful: Autocommands may have closed the tab page or made it the |
| * current tab page. */ |
| --- 2327,2333 ---- |
| int free_tp = FALSE; |
| |
| /* Close the link to the buffer. */ |
| ! close_buffer(win, win->w_buffer, free_buf ? DOBUF_UNLOAD : 0, FALSE); |
| |
| /* Careful: Autocommands may have closed the tab page or made it the |
| * current tab page. */ |
| |
| |
| |
| *** 1,13 **** |
| /* window.c */ |
| void do_window __ARGS((int nchar, long Prenum, int xchar)); |
| int win_split __ARGS((int size, int flags)); |
| ! int win_split_ins __ARGS((int size, int flags, win_T *newwin, int dir)); |
| int win_valid __ARGS((win_T *win)); |
| int win_count __ARGS((void)); |
| int make_windows __ARGS((int count, int vertical)); |
| void win_move_after __ARGS((win_T *win1, win_T *win2)); |
| void win_equal __ARGS((win_T *next_curwin, int current, int dir)); |
| void close_windows __ARGS((buf_T *buf, int keep_curwin)); |
| void win_close __ARGS((win_T *win, int free_buf)); |
| void win_close_othertab __ARGS((win_T *win, int free_buf, tabpage_T *tp)); |
| void win_free_all __ARGS((void)); |
| --- 1,14 ---- |
| /* window.c */ |
| void do_window __ARGS((int nchar, long Prenum, int xchar)); |
| int win_split __ARGS((int size, int flags)); |
| ! int win_split_ins __ARGS((int size, int flags, win_T *new_wp, int dir)); |
| int win_valid __ARGS((win_T *win)); |
| int win_count __ARGS((void)); |
| int make_windows __ARGS((int count, int vertical)); |
| void win_move_after __ARGS((win_T *win1, win_T *win2)); |
| void win_equal __ARGS((win_T *next_curwin, int current, int dir)); |
| void close_windows __ARGS((buf_T *buf, int keep_curwin)); |
| + int one_window __ARGS((void)); |
| void win_close __ARGS((win_T *win, int free_buf)); |
| void win_close_othertab __ARGS((win_T *win, int free_buf, tabpage_T *tp)); |
| void win_free_all __ARGS((void)); |
| |
| |
| |
| *** 716,717 **** |
| --- 716,719 ---- |
| { /* Add new patch number below this line */ |
| + /**/ |
| + 449, |
| /**/ |
| |
| -- |
| From "know your smileys": |
| :-)-O Smiling doctor with stethoscope |
| |
| /// 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 /// |