diff src/undo.c @ 2189:cb94c42c0e1a v7.2.445

updated for version 7.2.445 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.
author Bram Moolenaar <bram@vim.org>
date Wed, 07 Jul 2010 18:20:28 +0200
parents f838615313cd
children 073ff46fe397
line wrap: on
line diff
--- 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