To: vim-dev@vim.org Subject: patch 5.4.47 Fcc: outbox From: Bram Moolenaar ------------ Patch 5.4.47 Problem: When executing BufWritePre or BufWritePost autocommands for a hidden buffer, the cursor could be moved to a non-existing position. (Vince Negri) Solution: Save and restore the cursor and topline for the current window when it is going to be used to execute autocommands for a hidden buffer. Use an existing window for the buffer when it's not hidden. Files: src/fileio.c *** ../vim-5.4.46/src/fileio.c Wed Aug 18 12:53:26 1999 --- src/fileio.c Thu Aug 26 17:54:08 1999 *************** *** 36,41 **** --- 36,55 ---- # define CRYPT_MAGIC_LEN 12 #endif + #ifdef AUTOCMD + struct aco_save + { + WIN *save_curwin; + BUF *save_buf; + FPOS save_cursor; + linenr_t save_topline; + }; + + static void aucmd_prepbuf __ARGS((struct aco_save *aco)); + static void aucmd_restbuf __ARGS((struct aco_save *aco)); + + #endif + #ifdef VIMINFO static void check_marks_read __ARGS((void)); #endif *************** *** 1062,1074 **** int fd; char_u *backup = NULL; char_u *ffname; - #ifdef AUTOCMD - BUF *save_buf; - int buf_ffname = FALSE; - int buf_sfname = FALSE; - int buf_fname_f = FALSE; - int buf_fname_s = FALSE; - #endif char_u *s; char_u *ptr; char_u c; --- 1076,1081 ---- *************** *** 1181,1269 **** buf->b_op_end.col = 0; #ifdef AUTOCMD - /* - * Apply PRE aucocommands. - * Set curbuf to the buffer to be written. - * Careful: The autocommands may call buf_write() recursively! - */ - save_buf = curbuf; - curbuf = buf; - curwin->w_buffer = buf; - if (ffname == buf->b_ffname) - buf_ffname = TRUE; - if (sfname == buf->b_sfname) - buf_sfname = TRUE; - if (fname == buf->b_ffname) - buf_fname_f = TRUE; - if (fname == buf->b_sfname) - buf_fname_s = TRUE; - - if (append) - apply_autocmds(EVENT_FILEAPPENDPRE, fname, fname, FALSE, curbuf); - else if (filtering) - apply_autocmds(EVENT_FILTERWRITEPRE, NULL, fname, FALSE, curbuf); - else if (reset_changed && whole) - apply_autocmds(EVENT_BUFWRITEPRE, fname, fname, FALSE, curbuf); - else - apply_autocmds(EVENT_FILEWRITEPRE, fname, fname, FALSE, curbuf); - /* - * If the autocommands deleted or unloaded the buffer, give an error - * message. - */ - if (!buf_valid(buf) || buf->b_ml.ml_mfp == NULL) { ! --no_wait_return; ! msg_scroll = msg_save; ! EMSG("Autocommands deleted or unloaded buffer to be written"); ! return FAIL; ! } ! /* ! * If the autocommands didn't change the current buffer, go back to the ! * original current buffer, if it still exists. ! */ ! if (curbuf == buf && buf_valid(save_buf)) ! { ! curbuf = save_buf; ! curwin->w_buffer = save_buf; ! } ! /* ! * The autocommands may have changed the number of lines in the file. ! * When writing the whole file, adjust the end. ! * When writing part of the file, assume that the autocommands only ! * changed the number of lines that are to be written (tricky!). ! */ ! if (buf->b_ml.ml_line_count != old_line_count) ! { ! if (whole) /* writing all */ ! end = buf->b_ml.ml_line_count; ! else if (buf->b_ml.ml_line_count > old_line_count) /* more lines */ ! end += buf->b_ml.ml_line_count - old_line_count; ! else /* less lines */ { ! end -= old_line_count - buf->b_ml.ml_line_count; ! if (end < start) { ! --no_wait_return; ! msg_scroll = msg_save; ! EMSG("Autocommand changed number of lines in unexpected way"); ! return FAIL; } } - } ! /* ! * The autocommands may have changed the name of the buffer, which may be ! * kept in fname, ffname and sfname. ! */ ! if (buf_ffname) ! ffname = buf->b_ffname; ! if (buf_sfname) ! sfname = buf->b_sfname; ! if (buf_fname_f) ! fname = buf->b_ffname; ! if (buf_fname_s) ! fname = buf->b_sfname; #endif if (shortmess(SHM_OVER)) --- 1188,1294 ---- buf->b_op_end.col = 0; #ifdef AUTOCMD { ! struct aco_save aco; ! BUF *save_buf; ! int buf_ffname = FALSE; ! int buf_sfname = FALSE; ! int buf_fname_f = FALSE; ! int buf_fname_s = FALSE; ! ! /* ! * Apply PRE aucocommands. ! * Set curbuf to the buffer to be written. ! * Careful: The autocommands may call buf_write() recursively! ! */ ! if (ffname == buf->b_ffname) ! buf_ffname = TRUE; ! if (sfname == buf->b_sfname) ! buf_sfname = TRUE; ! if (fname == buf->b_ffname) ! buf_fname_f = TRUE; ! if (fname == buf->b_sfname) ! buf_fname_s = TRUE; ! ! save_buf = curbuf; ! curbuf = buf; ! aucmd_prepbuf(&aco); ! ! if (append) ! apply_autocmds(EVENT_FILEAPPENDPRE, fname, fname, FALSE, curbuf); ! else if (filtering) ! apply_autocmds(EVENT_FILTERWRITEPRE, NULL, fname, FALSE, curbuf); ! else if (reset_changed && whole) ! apply_autocmds(EVENT_BUFWRITEPRE, fname, fname, FALSE, curbuf); ! else ! apply_autocmds(EVENT_FILEWRITEPRE, fname, fname, FALSE, curbuf); ! aucmd_restbuf(&aco); ! ! /* ! * If the autocommands deleted or unloaded the buffer, give an error ! * message. ! */ ! if (!buf_valid(buf) || buf->b_ml.ml_mfp == NULL) ! { ! curbuf = curwin->w_buffer; ! --no_wait_return; ! msg_scroll = msg_save; ! EMSG("Autocommands deleted or unloaded buffer to be written"); ! return FAIL; ! } ! /* ! * If the autocommands didn't change the current buffer, go back to ! * the original current buffer, if it still exists. ! */ ! if (curbuf == buf && buf_valid(save_buf)) { ! --curwin->w_buffer->b_nwindows; ! curbuf = save_buf; ! curwin->w_buffer = save_buf; ! ++curbuf->b_nwindows; ! } ! else ! curbuf = curwin->w_buffer; ! ! /* ! * The autocommands may have changed the number of lines in the file. ! * When writing the whole file, adjust the end. ! * When writing part of the file, assume that the autocommands only ! * changed the number of lines that are to be written (tricky!). ! */ ! if (buf->b_ml.ml_line_count != old_line_count) ! { ! if (whole) /* write all */ ! end = buf->b_ml.ml_line_count; ! else if (buf->b_ml.ml_line_count > old_line_count) /* more lines */ ! end += buf->b_ml.ml_line_count - old_line_count; ! else /* less lines */ { ! end -= old_line_count - buf->b_ml.ml_line_count; ! if (end < start) ! { ! --no_wait_return; ! msg_scroll = msg_save; ! EMSG("Autocommand changed number of lines in unexpected way"); ! return FAIL; ! } } } ! /* ! * The autocommands may have changed the name of the buffer, which may ! * be kept in fname, ffname and sfname. ! */ ! if (buf_ffname) ! ffname = buf->b_ffname; ! if (buf_sfname) ! sfname = buf->b_sfname; ! if (buf_fname_f) ! fname = buf->b_ffname; ! if (buf_fname_s) ! fname = buf->b_sfname; ! } #endif if (shortmess(SHM_OVER)) *************** *** 2112,2142 **** msg_scroll = msg_save; #ifdef AUTOCMD - write_no_eol_lnum = 0; /* in case it was set by the previous read */ - - /* - * Apply POST autocommands. - * Careful: The autocommands may call buf_write() recursively! - */ - save_buf = curbuf; - curbuf = buf; - curwin->w_buffer = buf; - if (append) - apply_autocmds(EVENT_FILEAPPENDPOST, fname, fname, FALSE, curbuf); - else if (filtering) - apply_autocmds(EVENT_FILTERWRITEPOST, NULL, fname, FALSE, curbuf); - else if (reset_changed && whole) - apply_autocmds(EVENT_BUFWRITEPOST, fname, fname, FALSE, curbuf); - else - apply_autocmds(EVENT_FILEWRITEPOST, fname, fname, FALSE, curbuf); - /* - * If the autocommands didn't change the current buffer, go back to the - * original current buffer, if it still exists. - */ - if (curbuf == buf && buf_valid(save_buf)) { ! curbuf = save_buf; ! curwin->w_buffer = save_buf; } #endif --- 2137,2180 ---- msg_scroll = msg_save; #ifdef AUTOCMD { ! struct aco_save aco; ! BUF *save_buf; ! ! write_no_eol_lnum = 0; /* in case it was set by the previous read */ ! ! /* ! * Apply POST autocommands. ! * Careful: The autocommands may call buf_write() recursively! ! */ ! save_buf = curbuf; ! curbuf = buf; ! aucmd_prepbuf(&aco); ! ! if (append) ! apply_autocmds(EVENT_FILEAPPENDPOST, fname, fname, FALSE, curbuf); ! else if (filtering) ! apply_autocmds(EVENT_FILTERWRITEPOST, NULL, fname, FALSE, curbuf); ! else if (reset_changed && whole) ! apply_autocmds(EVENT_BUFWRITEPOST, fname, fname, FALSE, curbuf); ! else ! apply_autocmds(EVENT_FILEWRITEPOST, fname, fname, FALSE, curbuf); ! ! aucmd_restbuf(&aco); ! ! /* ! * If the autocommands didn't change the current buffer, go back to ! * the original current buffer, if it still exists. ! */ ! if (curbuf == buf && buf_valid(save_buf)) ! { ! --curwin->w_buffer->b_nwindows; ! curbuf = save_buf; ! curwin->w_buffer = save_buf; ! ++curbuf->b_nwindows; ! } ! else ! curbuf = curwin->w_buffer; } #endif *************** *** 3942,3957 **** do_autoall(arg) char_u *arg; { ! BUF *save_buf = NULL; ! WIN *win; ! WIN *save_curwin = NULL; ! int retval; ! FPOS save_cursor; ! linenr_t save_topline = 0; ! int len; ! ! save_cursor.lnum = 0; /* init for gcc */ ! save_cursor.col = 0; /* * This is a bit tricky: For some commands curwin->w_buffer needs to be --- 3980,3987 ---- do_autoall(arg) char_u *arg; { ! int retval; ! struct aco_save aco; /* * This is a bit tricky: For some commands curwin->w_buffer needs to be *************** *** 3964,4029 **** { if (curbuf->b_ml.ml_mfp != NULL) { ! for (win = firstwin; win != NULL; win = win->w_next) ! if (win->w_buffer == curbuf) ! break; ! /* if there is a window for this buffer, make it the curwin */ ! if (win != NULL) ! { ! save_curwin = curwin; ! curwin = win; ! } ! else ! { ! /* if there is no window for this buffer, use curwin */ ! --curwin->w_buffer->b_nwindows; ! save_buf = curwin->w_buffer; ! curwin->w_buffer = curbuf; ! ++curwin->w_buffer->b_nwindows; ! ! /* set cursor and topline to safe values */ ! save_cursor = curwin->w_cursor; ! curwin->w_cursor.lnum = 1; ! curwin->w_cursor.col = 0; ! save_topline = curwin->w_topline; ! curwin->w_topline = 1; ! } retval = do_doautocmd(arg, FALSE); do_modelines(); ! if (win != NULL) /* restore curwin */ ! { ! if (win_valid(save_curwin)) ! curwin = save_curwin; ! } ! else /* restore buffer for curwin */ ! { ! if (buf_valid(save_buf)) ! { ! --curwin->w_buffer->b_nwindows; ! curwin->w_buffer = save_buf; ! ++save_buf->b_nwindows; ! if (save_cursor.lnum <= save_buf->b_ml.ml_line_count) ! { ! curwin->w_cursor = save_cursor; ! len = STRLEN(ml_get_buf(save_buf, ! curwin->w_cursor.lnum, FALSE)); ! if (len == 0) ! curwin->w_cursor.col = 0; ! else if ((int)curwin->w_cursor.col >= len) ! curwin->w_cursor.col = len - 1; ! } ! else ! { ! curwin->w_cursor.lnum = save_buf->b_ml.ml_line_count; ! curwin->w_cursor.col = 0; ! } ! /* check topline < line_count, in case lines got deleted */ ! if (save_topline <= save_buf->b_ml.ml_line_count) ! curwin->w_topline = save_topline; ! else ! curwin->w_topline = save_buf->b_ml.ml_line_count; ! } ! } if (retval == FAIL) break; } --- 3994,4009 ---- { if (curbuf->b_ml.ml_mfp != NULL) { ! /* find a window for this buffer and save some values */ ! aucmd_prepbuf(&aco); ! ! /* execute the autocommands for this buffer */ retval = do_doautocmd(arg, FALSE); do_modelines(); ! ! /* restore the current window */ ! aucmd_restbuf(&aco); ! if (retval == FAIL) break; } *************** *** 4031,4036 **** --- 4011,4105 ---- curbuf = curwin->w_buffer; adjust_cursor(); /* just in case lines got deleted */ + } + + /* + * Prepare for executing autocommands for a (hidden) buffer. + * Search a window for the current buffer. Save the cursor position and + * screen offset. + */ + static void + aucmd_prepbuf(aco) + struct aco_save *aco; /* structure to save values in */ + { + WIN *win; + + if (curwin->w_buffer == curbuf) + win = curwin; + else + for (win = firstwin; win != NULL; win = win->w_next) + if (win->w_buffer == curbuf) + break; + + /* if there is a window for the current buffer, make it the curwin */ + if (win != NULL) + { + aco->save_curwin = curwin; + curwin = win; + } + else + { + /* if there is no window for the current buffer, use curwin */ + aco->save_curwin = NULL; + + aco->save_buf = curwin->w_buffer; + --aco->save_buf->b_nwindows; + curwin->w_buffer = curbuf; + ++curbuf->b_nwindows; + + /* set cursor and topline to safe values */ + aco->save_cursor = curwin->w_cursor; + curwin->w_cursor.lnum = 1; + curwin->w_cursor.col = 0; + aco->save_topline = curwin->w_topline; + curwin->w_topline = 1; + } + } + + /* + * Cleanup after executing autocommands for a (hidden) buffer. + * Restore the window as it was. + */ + static void + aucmd_restbuf(aco) + struct aco_save *aco; /* structure hoding saved values */ + { + int len; + + if (aco->save_curwin != NULL) /* restore curwin */ + { + if (win_valid(aco->save_curwin)) + curwin = aco->save_curwin; + } + else /* restore buffer for curwin */ + { + if (buf_valid(aco->save_buf)) + { + --curwin->w_buffer->b_nwindows; + curwin->w_buffer = aco->save_buf; + ++aco->save_buf->b_nwindows; + if (aco->save_cursor.lnum <= aco->save_buf->b_ml.ml_line_count) + { + curwin->w_cursor = aco->save_cursor; + len = STRLEN(ml_get_buf(aco->save_buf, + curwin->w_cursor.lnum, FALSE)); + if (len == 0) + curwin->w_cursor.col = 0; + else if ((int)curwin->w_cursor.col >= len) + curwin->w_cursor.col = len - 1; + } + else + { + curwin->w_cursor.lnum = aco->save_buf->b_ml.ml_line_count; + curwin->w_cursor.col = 0; + } + /* check topline < line_count, in case lines got deleted */ + if (aco->save_topline <= aco->save_buf->b_ml.ml_line_count) + curwin->w_topline = aco->save_topline; + else + curwin->w_topline = aco->save_buf->b_ml.ml_line_count; + } + } } static int autocmd_nested = FALSE; *** ../vim-5.4.46/src/version.h Sun Aug 22 23:08:15 1999 --- src/version.h Thu Aug 26 18:19:56 1999 *************** *** 19,26 **** #define VIM_VERSION_MINOR_STR "4" #define VIM_VERSION_BUILD 57 #define VIM_VERSION_BUILD_STR "57" ! #define VIM_VERSION_PATCHLEVEL 46 ! #define VIM_VERSION_PATCHLEVEL_STR "46" /* * VIM_VERSION_NODOT is used for the runtime directory name. --- 19,26 ---- #define VIM_VERSION_MINOR_STR "4" #define VIM_VERSION_BUILD 57 #define VIM_VERSION_BUILD_STR "57" ! #define VIM_VERSION_PATCHLEVEL 47 ! #define VIM_VERSION_PATCHLEVEL_STR "47" /* * VIM_VERSION_NODOT is used for the runtime directory name. *************** *** 30,35 **** */ #define VIM_VERSION_NODOT "vim54" #define VIM_VERSION_SHORT "5.4" ! #define VIM_VERSION_MEDIUM "5.4.46" ! #define VIM_VERSION_LONG "VIM - Vi IMproved 5.4.46 (1999 Aug 22)" ! #define VIM_VERSION_LONG_DATE "VIM - Vi IMproved 5.4.46 (1999 Aug 22, compiled " --- 30,35 ---- */ #define VIM_VERSION_NODOT "vim54" #define VIM_VERSION_SHORT "5.4" ! #define VIM_VERSION_MEDIUM "5.4.47" ! #define VIM_VERSION_LONG "VIM - Vi IMproved 5.4.47 (1999 Aug 26)" ! #define VIM_VERSION_LONG_DATE "VIM - Vi IMproved 5.4.47 (1999 Aug 26, compiled " -- hundred-and-one symptoms of being an internet addict: 246. You use up your free 100 hours in less than a week. --/-/---- Bram Moolenaar ---- Bram@moolenaar.net ---- Bram@vim.org ---\-\-- \ \ www.vim.org/iccf www.moolenaar.net www.vim.org / /