# HG changeset patch # User Bram Moolenaar # Date 1329919117 -3600 # Node ID 9ccdc4a69d8ff7aab8abf89f9a3cc9dad12249dd # Parent e8d5db3a6d4fe650aec969f1829ca131823eb444 updated for version 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. diff --git a/src/buffer.c b/src/buffer.c --- a/src/buffer.c +++ b/src/buffer.c @@ -64,6 +64,9 @@ static void buf_delete_signs __ARGS((buf 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,7 +99,7 @@ open_buffer(read_stdin, eap, flags) * 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); + close_buffer(NULL, curbuf, 0, FALSE); for (curbuf = firstbuf; curbuf != NULL; curbuf = curbuf->b_next) if (curbuf->b_ml.ml_mfp != NULL) break; @@ -316,12 +319,17 @@ buf_valid(buf) * 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) +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,8 +379,12 @@ close_buffer(win, buf, action) { apply_autocmds(EVENT_BUFWINLEAVE, buf->b_fname, buf->b_fname, FALSE, buf); - if (!buf_valid(buf)) /* autocommands may delete the buffer */ + /* 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,8 +392,13 @@ close_buffer(win, buf, action) { apply_autocmds(EVENT_BUFHIDDEN, buf->b_fname, buf->b_fname, FALSE, buf); - if (!buf_valid(buf)) /* autocmds may delete the buffer */ + /* 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,7 +792,7 @@ handle_swap_exists(old_curbuf) * 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); + 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,7 +1139,7 @@ do_buffer(action, start, dir, count, for * if the buffer still exists. */ if (buf != curbuf && buf_valid(buf) && buf->b_nwindows == 0) - close_buffer(NULL, buf, action); + close_buffer(NULL, buf, action, FALSE); return retval; } @@ -1146,7 +1163,7 @@ do_buffer(action, start, dir, count, for close_windows(buf, FALSE); #endif if (buf != curbuf && buf_valid(buf) && buf->b_nwindows <= 0) - close_buffer(NULL, buf, action); + close_buffer(NULL, buf, action, FALSE); return OK; } @@ -1378,7 +1395,7 @@ set_curbuf(buf, action) close_buffer(prevbuf == curwin->w_buffer ? curwin : NULL, prevbuf, unload ? action : (action == DOBUF_GOTO && !P_HID(prevbuf) - && !bufIsChanged(prevbuf)) ? DOBUF_UNLOAD : 0); + && !bufIsChanged(prevbuf)) ? DOBUF_UNLOAD : 0, FALSE); } } #ifdef FEAT_AUTOCMD @@ -2708,7 +2725,8 @@ setfname(buf, ffname, sfname, message) vim_free(ffname); return FAIL; } - close_buffer(NULL, obuf, DOBUF_WIPE); /* delete from the list */ + /* delete from the list */ + close_buffer(NULL, obuf, DOBUF_WIPE, FALSE); } sfname = vim_strsave(sfname); if (ffname == NULL || sfname == NULL) @@ -5638,7 +5656,7 @@ wipe_buffer(buf, aucmd) if (!aucmd) /* Don't trigger BufDelete autocommands here. */ block_autocmds(); #endif - close_buffer(NULL, buf, DOBUF_WIPE); + close_buffer(NULL, buf, DOBUF_WIPE, FALSE); #ifdef FEAT_AUTOCMD if (!aucmd) unblock_autocmds(); diff --git a/src/ex_cmds.c b/src/ex_cmds.c --- a/src/ex_cmds.c +++ b/src/ex_cmds.c @@ -3387,7 +3387,7 @@ do_ecmd(fnum, ffname, sfname, eap, newln /* close the link to the current buffer */ u_sync(FALSE); close_buffer(oldwin, curbuf, - (flags & ECMD_HIDE) ? 0 : DOBUF_UNLOAD); + (flags & ECMD_HIDE) ? 0 : DOBUF_UNLOAD, FALSE); #ifdef FEAT_AUTOCMD /* Autocommands may open a new window and leave oldwin open diff --git a/src/ex_getln.c b/src/ex_getln.c --- a/src/ex_getln.c +++ b/src/ex_getln.c @@ -6443,7 +6443,7 @@ ex_window() /* win_close() may have already wiped the buffer when 'bh' is * set to 'wipe' */ if (buf_valid(bp)) - close_buffer(NULL, bp, DOBUF_WIPE); + close_buffer(NULL, bp, DOBUF_WIPE, FALSE); /* Restore window sizes. */ win_size_restore(&winsizes); diff --git a/src/misc2.c b/src/misc2.c --- a/src/misc2.c +++ b/src/misc2.c @@ -1173,7 +1173,7 @@ free_all_mem() for (buf = firstbuf; buf != NULL; ) { nextbuf = buf->b_next; - close_buffer(NULL, buf, DOBUF_WIPE); + close_buffer(NULL, buf, DOBUF_WIPE, FALSE); if (buf_valid(buf)) buf = nextbuf; /* didn't work, try next one */ else diff --git a/src/proto/buffer.pro b/src/proto/buffer.pro --- a/src/proto/buffer.pro +++ b/src/proto/buffer.pro @@ -1,7 +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 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)); diff --git a/src/proto/window.pro b/src/proto/window.pro --- a/src/proto/window.pro +++ b/src/proto/window.pro @@ -1,13 +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 *newwin, int dir)); +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)); diff --git a/src/quickfix.c b/src/quickfix.c --- a/src/quickfix.c +++ b/src/quickfix.c @@ -3565,7 +3565,7 @@ unload_dummy_buffer(buf) buf_T *buf; { if (curbuf != buf) /* safety check */ - close_buffer(NULL, buf, DOBUF_UNLOAD); + close_buffer(NULL, buf, DOBUF_UNLOAD, FALSE); } #if defined(FEAT_EVAL) || defined(PROTO) diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -715,6 +715,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 449, +/**/ 448, /**/ 447, diff --git a/src/window.c b/src/window.c --- a/src/window.c +++ b/src/window.c @@ -23,7 +23,6 @@ static void win_rotate __ARGS((int, int) 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)); @@ -2083,7 +2082,7 @@ last_window() * Return TRUE if there is only one window other than "aucmd_win" in the * current tab page. */ - static int + int one_window() { #ifdef FEAT_AUTOCMD @@ -2109,7 +2108,7 @@ one_window() * 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() + * Called by :quit, :close, :xit, :wq and findtag(). */ void win_close(win, free_buf) @@ -2222,7 +2221,7 @@ win_close(win, free_buf) * Close the link to the buffer. */ if (win->w_buffer != NULL) - close_buffer(win, win->w_buffer, free_buf ? DOBUF_UNLOAD : 0); + 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,7 +2327,7 @@ win_close_othertab(win, free_buf, tp) int free_tp = FALSE; /* Close the link to the buffer. */ - close_buffer(win, win->w_buffer, free_buf ? DOBUF_UNLOAD : 0); + 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. */