To: vim-dev@vim.org Subject: Patch 6.1.462 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=ISO-8859-1 Content-Transfer-Encoding: 8bit ------------ Patch 6.1.462 Problem: When autocommands wipe out a buffer, a crash may happen. (Hari Krishna Dara) Solution: Don't decrement the window count of a buffer before calling the autocommands for it. When re-using the current buffer, watch out for autocommands changing the current buffer. Files: src/buffer.c, src/ex_cmds.c, src/proto/buffer.pro *** ../vim61.461/src/buffer.c Sun Apr 6 15:22:34 2003 --- src/buffer.c Thu Apr 10 21:48:43 2003 *************** *** 308,324 **** unload_buf = TRUE; #endif - /* decrease the link count from windows (unless not in any window) */ - if (buf->b_nwindows > 0) - --buf->b_nwindows; - if (win != NULL) { /* Set b_last_cursor when closing the last window for the buffer. * Remember the last cursor position and window options of the buffer. * This used to be only for the current window, but then options like * 'foldmethod' may be lost with a ":only" command. */ ! if (buf->b_nwindows == 0) set_last_cursor(win); buflist_setfpos(buf, win, win->w_cursor.lnum == 1 ? 0 : win->w_cursor.lnum, --- 308,320 ---- unload_buf = TRUE; #endif if (win != NULL) { /* Set b_last_cursor when closing the last window for the buffer. * Remember the last cursor position and window options of the buffer. * This used to be only for the current window, but then options like * 'foldmethod' may be lost with a ":only" command. */ ! if (buf->b_nwindows == 1) set_last_cursor(win); buflist_setfpos(buf, win, win->w_cursor.lnum == 1 ? 0 : win->w_cursor.lnum, *************** *** 327,333 **** #ifdef FEAT_AUTOCMD /* When the buffer is no longer in a window, trigger BufWinLeave */ ! if (buf->b_nwindows == 0 && nwindows > 0) { apply_autocmds(EVENT_BUFWINLEAVE, buf->b_fname, buf->b_fname, FALSE, buf); --- 323,329 ---- #ifdef FEAT_AUTOCMD /* When the buffer is no longer in a window, trigger BufWinLeave */ ! if (buf->b_nwindows == 1) { apply_autocmds(EVENT_BUFWINLEAVE, buf->b_fname, buf->b_fname, FALSE, buf); *************** *** 344,351 **** --- 340,352 ---- return; } } + nwindows = buf->b_nwindows; #endif + /* decrease the link count from windows (unless not in any window) */ + if (buf->b_nwindows > 0) + --buf->b_nwindows; + /* Return when a window is displaying the buffer or when it's not * unloaded. */ if (buf->b_nwindows > 0 || !unload_buf) *************** *** 364,385 **** * Also calls the "BufDelete" autocommands when del_buf is TRUE. */ #ifdef FEAT_AUTOCMD is_curbuf = (buf == curbuf); #endif ! buf_freeall(buf, del_buf); #ifdef FEAT_AUTOCMD ! if (wipe_buf && buf_valid(buf)) ! apply_autocmds(EVENT_BUFWIPEOUT, buf->b_fname, buf->b_fname, ! FALSE, buf); /* - * Autocommands may have deleted the buffer. * It's possible that autocommands change curbuf to the one being deleted. * This might cause the previous curbuf to be deleted unexpectedly. But * in some cases it's OK to delete the curbuf, because a new one is * obtained anyway. Therefore only return if curbuf changed to the * deleted buffer. */ ! if (!buf_valid(buf) || (buf == curbuf && !is_curbuf)) return; #endif --- 365,396 ---- * Also calls the "BufDelete" autocommands when del_buf is TRUE. */ #ifdef FEAT_AUTOCMD + /* Remember if we are closing the current buffer. Restore the number of + * windows, so that autocommands in buf_freeall() don't get confused. */ is_curbuf = (buf == curbuf); + buf->b_nwindows = nwindows; #endif ! ! buf_freeall(buf, del_buf, wipe_buf); ! #ifdef FEAT_AUTOCMD ! /* Autocommands may have deleted the buffer. */ ! if (!buf_valid(buf)) ! return; ! ! /* Autocommands may have opened or closed windows for this buffer. ! * Decrement the count for the close we do here. */ ! if (buf->b_nwindows > 0) ! --buf->b_nwindows; ! /* * It's possible that autocommands change curbuf to the one being deleted. * This might cause the previous curbuf to be deleted unexpectedly. But * in some cases it's OK to delete the curbuf, because a new one is * obtained anyway. Therefore only return if curbuf changed to the * deleted buffer. */ ! if (buf == curbuf && !is_curbuf) return; #endif *************** *** 467,475 **** */ /*ARGSUSED*/ void ! buf_freeall(buf, del_buf) buf_T *buf; int del_buf; /* buffer is going to be deleted */ { #ifdef FEAT_AUTOCMD int is_curbuf = (buf == curbuf); --- 478,487 ---- */ /*ARGSUSED*/ void ! buf_freeall(buf, del_buf, wipe_buf) buf_T *buf; int del_buf; /* buffer is going to be deleted */ + int wipe_buf; /* buffer is going to be wiped out */ { #ifdef FEAT_AUTOCMD int is_curbuf = (buf == curbuf); *************** *** 483,488 **** --- 495,508 ---- if (!buf_valid(buf)) /* autocommands may delete the buffer */ return; } + if (wipe_buf) + { + apply_autocmds(EVENT_BUFWIPEOUT, buf->b_fname, buf->b_fname, + FALSE, buf); + if (!buf_valid(buf)) /* autocommands may delete the buffer */ + return; + } + /* * It's possible that autocommands change curbuf to the one being deleted. * This might cause curbuf to be deleted unexpectedly. But in some cases *************** *** 1320,1325 **** --- 1340,1346 ---- * * This is the ONLY place where a new buffer structure is allocated! */ + buf = NULL; if ((flags & BLN_CURBUF) && curbuf != NULL && curbuf->b_ffname == NULL *************** *** 1328,1345 **** { buf = curbuf; #ifdef FEAT_AUTOCMD ! /* It's like this buffer is deleted. */ if (curbuf->b_p_bl) apply_autocmds(EVENT_BUFDELETE, NULL, NULL, FALSE, curbuf); ! apply_autocmds(EVENT_BUFWIPEOUT, NULL, NULL, FALSE, curbuf); #endif #ifdef FEAT_QUICKFIX ! /* Make sure 'bufhidden' and 'buftype' are empty */ ! clear_string_option(&buf->b_p_bh); ! clear_string_option(&buf->b_p_bt); #endif } ! else { buf = (buf_T *)alloc_clear((unsigned)sizeof(buf_T)); if (buf == NULL) --- 1349,1373 ---- { buf = curbuf; #ifdef FEAT_AUTOCMD ! /* It's like this buffer is deleted. Watch out for autocommands that ! * change curbuf! If that happens, allocate a new buffer anyway. */ if (curbuf->b_p_bl) apply_autocmds(EVENT_BUFDELETE, NULL, NULL, FALSE, curbuf); ! if (buf == curbuf) ! apply_autocmds(EVENT_BUFWIPEOUT, NULL, NULL, FALSE, curbuf); #endif #ifdef FEAT_QUICKFIX ! # ifdef FEAT_AUTOCMD ! if (buf == curbuf) ! # endif ! { ! /* Make sure 'bufhidden' and 'buftype' are empty */ ! clear_string_option(&buf->b_p_bh); ! clear_string_option(&buf->b_p_bt); ! } #endif } ! if (buf != curbuf || curbuf == NULL) { buf = (buf_T *)alloc_clear((unsigned)sizeof(buf_T)); if (buf == NULL) *************** *** 1372,1378 **** if (buf == curbuf) { ! buf_freeall(buf, FALSE); /* free all things allocated for this buffer */ if (buf != curbuf) /* autocommands deleted the buffer! */ return NULL; /* buf->b_nwindows = 0; why was this here? */ --- 1400,1407 ---- if (buf == curbuf) { ! /* free all things allocated for this buffer */ ! buf_freeall(buf, FALSE, FALSE); if (buf != curbuf) /* autocommands deleted the buffer! */ return NULL; /* buf->b_nwindows = 0; why was this here? */ *** ../vim61.461/src/ex_cmds.c Sun Apr 13 20:05:37 2003 --- src/ex_cmds.c Fri Apr 11 10:50:00 2003 *************** *** 2833,2839 **** else new_name = NULL; #endif ! buf_freeall(curbuf, FALSE); /* free all things for buffer */ #ifdef FEAT_AUTOCMD /* If autocommands deleted the buffer we were going to re-edit, give * up and jump to the end. */ --- 2833,2839 ---- else new_name = NULL; #endif ! buf_freeall(curbuf, FALSE, FALSE); /* free all things for buffer */ #ifdef FEAT_AUTOCMD /* If autocommands deleted the buffer we were going to re-edit, give * up and jump to the end. */ *** ../vim61.461/src/proto/buffer.pro Sun Mar 9 17:49:38 2003 --- src/proto/buffer.pro Thu Apr 10 21:19:22 2003 *************** *** 3,9 **** 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 del_buf)); void goto_buffer __ARGS((exarg_T *eap, int start, int dir, int count)); void handle_swap_exists __ARGS((buf_T *old_curbuf)); char_u *do_bufdel __ARGS((int command, char_u *arg, int addr_count, int start_bnr, int end_bnr, int forceit)); --- 3,9 ---- 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 del_buf, int wipe_buf)); void goto_buffer __ARGS((exarg_T *eap, int start, int dir, int count)); void handle_swap_exists __ARGS((buf_T *old_curbuf)); char_u *do_bufdel __ARGS((int command, char_u *arg, int addr_count, int start_bnr, int end_bnr, int forceit)); *** ../vim61.461/src/version.c Sun Apr 13 20:37:44 2003 --- src/version.c Sun Apr 13 20:39:40 2003 *************** *** 613,614 **** --- 613,616 ---- { /* Add new patch number below this line */ + /**/ + 462, /**/ -- OLD WOMAN: King of the WHO? ARTHUR: The Britons. OLD WOMAN: Who are the Britons? "Monty Python and the Holy Grail" PYTHON (MONTY) PICTURES LTD /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// Creator of Vim - Vi IMproved -- http://www.Vim.org \\\ \\\ Project leader for A-A-P -- http://www.A-A-P.org /// \\\ Help AIDS victims, buy at Amazon -- http://ICCF.nl/click1.html ///