From 4441b94a76aea705a589151e30cec4e6228f2ec9 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Wed, 7 Jul 2010 18:18:27 +0000 Subject: [PATCH] [7.2.445] Crash when using undo/redo and a FileChangedRO autocmd event that Problem: Crash when using undo/redo and a FileChangedRO autocmd event that reloads the buffer. (Dominique Pelle) Solution: Do not allow autocommands while performing and undo or redo. Patch 7.2.445 --- src/misc1.c | 12 +++++++++++- src/undo.c | 41 ++++++++++++++++++++++++++++++++++++----- src/version.c | 2 ++ 3 files changed, 49 insertions(+), 6 deletions(-) diff --git a/src/misc1.c b/src/misc1.c index ffd0b83..5482e45 100644 --- a/src/misc1.c +++ b/src/misc1.c @@ -2467,10 +2467,12 @@ skip_to_option_part(p) } /* - * changed() is called when something in the current buffer is changed. + * Call this function when something in the current buffer is changed. * * Most often called through changed_bytes() and changed_lines(), which also * mark the area of the display to be redrawn. + * + * Careful: may trigger autocommands that reload the buffer. */ void changed() @@ -2536,6 +2538,7 @@ static void changed_common __ARGS((linenr_T lnum, colnr_T col, linenr_T lnume, l * - marks the windows on this buffer to be redisplayed * - marks the buffer changed by calling changed() * - invalidates cached values + * Careful: may trigger autocommands that reload the buffer. */ void changed_bytes(lnum, col) @@ -2649,6 +2652,7 @@ deleted_lines_mark(lnum, count) * below the changed lines (BEFORE the change). * When only inserting lines, "lnum" and "lnume" are equal. * Takes care of calling changed() and updating b_mod_*. + * Careful: may trigger autocommands that reload the buffer. */ void changed_lines(lnum, col, lnume, xtra) @@ -2716,6 +2720,11 @@ changed_lines_buf(buf, lnum, lnume, xtra) } } +/* + * Common code for when a change is was made. + * See changed_lines() for the arguments. + * Careful: may trigger autocommands that reload the buffer. + */ static void changed_common(lnum, col, lnume, xtra) linenr_T lnum; @@ -2966,6 +2975,7 @@ check_status(buf) * Don't use emsg(), because it flushes the macro buffer. * If we have undone all changes b_changed will be FALSE, but "b_did_warn" * will be TRUE. + * Careful: may trigger autocommands that reload the buffer. */ void change_warning(col) diff --git a/src/undo.c b/src/undo.c index 39af55b..0f34af8 100644 --- a/src/undo.c +++ b/src/undo.c @@ -185,7 +185,7 @@ u_check_tree(u_header_T *uhp, } } - void + static void u_check(int newhead_may_be_NULL) { seen_b_u_newhead = 0; @@ -320,6 +320,9 @@ undo_allowed() return TRUE; } +/* + * Common code for various ways to save text before a change. + */ static int u_savecommon(top, bot, newbot) linenr_T top, bot; @@ -374,7 +377,7 @@ u_savecommon(top, bot, newbot) size = bot - top - 1; /* - * if curbuf->b_u_synced == TRUE make a new header + * If curbuf->b_u_synced == TRUE make a new header. */ if (curbuf->b_u_synced) { @@ -709,6 +712,12 @@ u_doit(startcount) u_oldcount = -1; while (count--) { + /* Do the change warning now, so that it triggers FileChangedRO when + * needed. This may cause the file to be reloaded, that must happen + * before we do anything, because it may change curbuf->b_u_curhead + * and more. */ + change_warning(0); + if (undo_undoes) { if (curbuf->b_u_curhead == NULL) /* first undo */ @@ -952,8 +961,11 @@ undo_time(step, sec, absolute) /* * First go up the tree as much as needed. */ - for (;;) + while (!got_int) { + /* Do the change warning now, for the same reason as above. */ + change_warning(0); + uhp = curbuf->b_u_curhead; if (uhp == NULL) uhp = curbuf->b_u_newhead; @@ -970,9 +982,15 @@ undo_time(step, sec, absolute) /* * And now go down the tree (redo), branching off where needed. */ - uhp = curbuf->b_u_curhead; - while (uhp != NULL) + while (!got_int) { + /* Do the change warning now, for the same reason as above. */ + change_warning(0); + + uhp = curbuf->b_u_curhead; + if (uhp == NULL) + break; + /* Go back to the first branch with a mark. */ while (uhp->uh_alt_prev != NULL && uhp->uh_alt_prev->uh_walk == mark) @@ -1070,6 +1088,12 @@ u_undoredo(undo) int empty_buffer; /* buffer became empty */ u_header_T *curhead = curbuf->b_u_curhead; +#ifdef FEAT_AUTOCMD + /* Don't want autocommands using the undo structures here, they are + * invalid till the end. */ + block_autocmds(); +#endif + #ifdef U_DEBUG u_check(FALSE); #endif @@ -1099,6 +1123,9 @@ u_undoredo(undo) if (top > curbuf->b_ml.ml_line_count || top >= bot || bot > curbuf->b_ml.ml_line_count + 1) { +#ifdef FEAT_AUTOCMD + unblock_autocmds(); +#endif EMSG(_("E438: u_undo: line numbers wrong")); changed(); /* don't want UNCHANGED now */ return; @@ -1304,6 +1331,10 @@ u_undoredo(undo) /* The timestamp can be the same for multiple changes, just use the one of * the undone/redone change. */ curbuf->b_u_seq_time = curhead->uh_time; + +#ifdef FEAT_AUTOCMD + unblock_autocmds(); +#endif #ifdef U_DEBUG u_check(FALSE); #endif diff --git a/src/version.c b/src/version.c index c5a029b..c009697 100644 --- a/src/version.c +++ b/src/version.c @@ -682,6 +682,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 445, +/**/ 444, /**/ 443, -- 2.11.4.GIT