]> granicus.if.org Git - vim/commitdiff
Various improvements to undo file code to make it more robust.
authorBram Moolenaar <Bram@vim.org>
Sat, 29 May 2010 18:33:07 +0000 (20:33 +0200)
committerBram Moolenaar <Bram@vim.org>
Sat, 29 May 2010 18:33:07 +0000 (20:33 +0200)
runtime/doc/tags
runtime/doc/todo.txt
runtime/doc/undo.txt
runtime/syntax/sed.vim
src/misc2.c
src/proto/undo.pro
src/undo.c

index bc573b7d686147be0dfe49c609e59e5b227b0ed5..66cc0332dd0cd1a133f2583fa1d0bbe3bdd942ab 100644 (file)
@@ -4170,13 +4170,10 @@ E822    undo.txt        /*E822*
 E823   undo.txt        /*E823*
 E824   undo.txt        /*E824*
 E825   undo.txt        /*E825*
-E826   undo.txt        /*E826*
-E827   undo.txt        /*E827*
 E828   undo.txt        /*E828*
 E829   undo.txt        /*E829*
 E83    message.txt     /*E83*
 E830   undo.txt        /*E830*
-E831   undo.txt        /*E831*
 E84    windows.txt     /*E84*
 E85    options.txt     /*E85*
 E86    windows.txt     /*E86*
index a53eb533ee297abf23726c9833ee9779c6ff45dc..bde8146be0b4d937debc6e1e38343dff07cf5fc6 100644 (file)
@@ -1097,6 +1097,7 @@ Vim 7.3:
     - When there is no undo info (undolevels negative), delete the undo file.
     - Need to check all values for evil manipulation.
     - Add undofile(name): get undo file name for buffer "name".
+- patch for unused functions. (Dominique Pelle, 2010 May 29)
 - Also crypt the undo file.
 - Also crypt the swap file, each block separately.  Change mf_write() and
     mf_read().  How to get b_p_key to these functions?
index c0b9d12511bed95afd80874b29f69b644a3cd938..a7ae1736c328b0002bf5c163e0d614d12cf9a15a 100644 (file)
@@ -267,10 +267,7 @@ Reading an existing undo file may fail for several reasons:
        The file text differs from when the undo file was written.  This means
        the undo file cannot be used, it would corrupt the text.  This also
        happens when 'encoding' differs from when the undo file was written.
-*E825* *E826* *E831*
-       The undo file does not contain valid contents and cannot be used.
-*E827* The magic number at the end of the file was not found.  This usually
-       means the file was truncated.
+*E825*  The undo file does not contain valid contents and cannot be used.
 
 Writing an undo file may fail for these reasons:
 *E828* The file to be written cannot be created.  Perhaps you do not have
index 0383b6fe4fccda2be8dd929d206e1781e48a3f85..42ad6d8e629161919ff8ab117e3eff99d7353ebb 100644 (file)
@@ -2,7 +2,7 @@
 " Language:    sed
 " Maintainer:  Haakon Riiser <hakonrk@fys.uio.no>
 " URL:         http://folk.uio.no/hakonrk/vim/syntax/sed.vim
-" Last Change: 2005 Dec 15
+" Last Change: 2010 May 29
 
 " For version 5.x: Clear all syntax items
 " For version 6.x: Quit when a syntax file was already loaded
@@ -49,7 +49,7 @@ syn match sedReplaceMeta    "&\|\\\($\|.\)" contains=sedTab contained
 " Metacharacters: $ * . \ ^ [ ~
 " @ is used as delimiter and treated on its own below
 let __at = char2nr("@")
-let __sed_i = char2nr(" ")
+let __sed_i = char2nr(" ") " ASCII: 32, EBCDIC: 64
 if has("ebcdic")
     let __sed_last = 255
 else
@@ -105,8 +105,8 @@ if version >= 508 || !exists("did_sed_syntax_inits")
     if exists("highlight_sedtabs")
        HiLink sedTab           Todo
     endif
-    let __sed_i = 32
-    while __sed_i <= 126
+    let __sed_i = char2nr(" ") " ASCII: 32, EBCDIC: 64
+    while __sed_i <= __sed_last
        exe "HiLink sedRegexp".__sed_i          "Macro"
        exe "HiLink sedReplacement".__sed_i     "NONE"
        let __sed_i = __sed_i + 1
@@ -115,7 +115,7 @@ if version >= 508 || !exists("did_sed_syntax_inits")
     delcommand HiLink
 endif
 
-unlet __sed_i __sed_delimiter __sed_metacharacters
+unlet __sed_i __sed_last __sed_delimiter __sed_metacharacters
 
 let b:current_syntax = "sed"
 
index 470bc2f4ce80797afbc8aae7fd051c447ee063a1..f5bad9c8712c181def2c9e25eb2237da03bb7e4b 100644 (file)
@@ -6134,7 +6134,7 @@ emsgn(s, n)
 get2c(fd)
     FILE       *fd;
 {
-    long       n;
+    int                n;
 
     n = getc(fd);
     n = (n << 8) + getc(fd);
@@ -6148,7 +6148,7 @@ get2c(fd)
 get3c(fd)
     FILE       *fd;
 {
-    long       n;
+    int                n;
 
     n = getc(fd);
     n = (n << 8) + getc(fd);
@@ -6163,7 +6163,7 @@ get3c(fd)
 get4c(fd)
     FILE       *fd;
 {
-    long       n;
+    int                n;
 
     n = getc(fd);
     n = (n << 8) + getc(fd);
index e5f28bec418e2080e69eb3c35219395a0638096e..90e11d2bb14f5f8cb23c799099672feedd87d0df 100644 (file)
@@ -1,4 +1,5 @@
 /* undo.c */
+void u_check __ARGS((int newhead_may_be_NULL));
 int u_save_cursor __ARGS((void));
 int u_save __ARGS((linenr_T top, linenr_T bot));
 int u_savesub __ARGS((linenr_T lnum));
index 52d6ae0fcbc6ef54ec040ffe17e5bb5850d569dc..958c6d2dea33dad533f1872d2d2cf9fc9441a5a0 100644 (file)
@@ -100,13 +100,16 @@ static void u_freebranch __ARGS((buf_T *buf, u_header_T *uhp, u_header_T **uhpp)
 static void u_freeentries __ARGS((buf_T *buf, u_header_T *uhp, u_header_T **uhpp));
 static void u_freeentry __ARGS((u_entry_T *, long));
 #ifdef FEAT_PERSISTENT_UNDO
-static void unserialize_pos __ARGS((pos_T *pos, FILE *fp));
-static void unserialize_visualinfo __ARGS((visualinfo_T *info, FILE *fp));
 static char_u *u_get_undo_file_name __ARGS((char_u *, int reading));
+static void corruption_error __ARGS((char *msg, char_u *file_name));
 static void u_free_uhp __ARGS((u_header_T *uhp));
 static int serialize_uep __ARGS((u_entry_T *uep, FILE *fp));
+static u_entry_T *unserialize_uep __ARGS((FILE *fp, int *error, char_u *file_name));
 static void serialize_pos __ARGS((pos_T pos, FILE *fp));
+static void unserialize_pos __ARGS((pos_T *pos, FILE *fp));
 static void serialize_visualinfo __ARGS((visualinfo_T *info, FILE *fp));
+static void unserialize_visualinfo __ARGS((visualinfo_T *info, FILE *fp));
+static void put_header_ptr __ARGS((FILE        *fp, u_header_T *uhp));
 #endif
 
 #define U_ALLOC_LINE(size) lalloc((long_u)(size), FALSE)
@@ -122,7 +125,7 @@ static int  undo_undoes = FALSE;
 
 static int     lastmark = 0;
 
-#ifdef U_DEBUG
+#if defined(U_DEBUG) || defined(PROTO)
 /*
  * Check the undo structures for being valid.  Print a warning when something
  * looks wrong.
@@ -658,13 +661,15 @@ nomem:
 
 #ifdef FEAT_PERSISTENT_UNDO
 
-# define UF_START_MAGIC            0xfeac      /* magic at start of undofile */
-# define UF_HEADER_MAGIC    0x5fd0     /* magic at start of header */
-# define UF_END_MAGIC      0xe7aa      /* magic after last header */
-# define UF_VERSION        1           /* 2-byte undofile version number */
+# define UF_START_MAGIC            "Vim\237UnDo\345"  /* magic at start of undofile */
+# define UF_START_MAGIC_LEN    9
+# define UF_HEADER_MAGIC       0x5fd0  /* magic at start of header */
+# define UF_HEADER_END_MAGIC   0xe7aa  /* magic after last header */
+# define UF_ENTRY_MAGIC                0xf518  /* magic at start of entry */
+# define UF_ENTRY_END_MAGIC    0x3581  /* magic after last entry */
+# define UF_VERSION            1       /* 2-byte undofile version number */
 
 static char_u e_not_open[] = N_("E828: Cannot open undo file for writing: %s");
-static char_u e_corrupted[] = N_("E823: Corrupted undo file: %s");
 
 /*
  * Compute the hash for the current buffer text into hash[UNDO_HASH_SIZE].
@@ -686,42 +691,12 @@ u_compute_hash(hash)
     sha256_finish(&ctx, hash);
 }
 
-/*
- * Unserialize the pos_T at the current position in fp.
- */
-    static void
-unserialize_pos(pos, fp)
-    pos_T *pos;
-    FILE  *fp;
-{
-    pos->lnum = get4c(fp);
-    pos->col = get4c(fp);
-#ifdef FEAT_VIRTUALEDIT
-    pos->coladd = get4c(fp);
-#else
-    (void)get4c(fp);
-#endif
-}
-
-/*
- * Unserialize the visualinfo_T at the current position in fp.
- */
-    static void
-unserialize_visualinfo(info, fp)
-    visualinfo_T    *info;
-    FILE           *fp;
-{
-    unserialize_pos(&info->vi_start, fp);
-    unserialize_pos(&info->vi_end, fp);
-    info->vi_mode = get4c(fp);
-    info->vi_curswant = get4c(fp);
-}
-
 /*
  * Return an allocated string of the full path of the target undofile.
  * When "reading" is TRUE find the file to read, go over all directories in
  * 'undodir'.
  * When "reading" is FALSE use the first name where the directory exists.
+ * Returns NULL when there is no place to write or no file to read.
  */
     static char_u *
 u_get_undo_file_name(buf_ffname, reading)
@@ -798,435 +773,188 @@ u_get_undo_file_name(buf_ffname, reading)
     return undo_file_name;
 }
 
-/*
- * Load the undo tree from an undo file.
- * If "name" is not NULL use it as the undo file name.  This also means being
- * a bit more verbose.
- * Otherwise use curbuf->b_ffname to generate the undo file name.
- * "hash[UNDO_HASH_SIZE]" must be the hash value of the buffer text.
- */
-    void
-u_read_undo(name, hash)
-    char_u *name;
-    char_u *hash;
+    static void
+corruption_error(msg, file_name)
+    char *msg;
+    char_u *file_name;
 {
-    char_u     *file_name;
-    FILE       *fp;
-    long       magic, version, str_len;
-    char_u     *line_ptr = NULL;
-    linenr_T   line_lnum;
-    colnr_T    line_colnr;
-    linenr_T   line_count;
-    int                uep_len;
-    int                line_len;
-    int                num_head = 0;
-    long       old_header_seq, new_header_seq, cur_header_seq;
-    long       seq_last, seq_cur;
-    short      old_idx = -1, new_idx = -1, cur_idx = -1;
-    long       num_read_uhps = 0;
-    time_t     seq_time;
-    int                i, j;
-    int                c;
-    char_u     **array;
-    char_u     *line;
-    u_entry_T  *uep, *last_uep;
-    u_header_T *uhp;
-    u_header_T **uhp_table = NULL;
-    char_u     read_hash[UNDO_HASH_SIZE];
-
-    if (name == NULL)
-    {
-        file_name = u_get_undo_file_name(curbuf->b_ffname, TRUE);
-       if (file_name == NULL)
-           return;
-    }
-    else
-        file_name = name;
+    EMSG3(_("E825: Corrupted undo file (%s): %s"), msg, file_name);
+}
 
-    if (p_verbose > 0)
-       smsg((char_u *)_("Reading undo file: %s"), file_name);
-    fp = mch_fopen((char *)file_name, "r");
-    if (fp == NULL)
-    {
-        if (name != NULL || p_verbose > 0)
-            EMSG2(_("E822: Cannot open undo file for reading: %s"), file_name);
-       goto error;
-    }
+    static void
+u_free_uhp(uhp)
+    u_header_T *uhp;
+{
+    u_entry_T  *nuep;
+    u_entry_T  *uep;
 
-    /* Begin overall file information */
-    magic = get2c(fp);
-    if (magic != UF_START_MAGIC)
-    {
-        EMSG2(_(e_corrupted), file_name);
-        goto error;
-    }
-    version = get2c(fp);
-    if (version != UF_VERSION)
+    uep = uhp->uh_entry;
+    while (uep != NULL)
     {
-        EMSG2(_("E824: Incompatible undo file: %s"), file_name);
-        goto error;
+       nuep = uep->ue_next;
+       u_freeentry(uep, uep->ue_size);
+       uep = nuep;
     }
+    vim_free(uhp);
+}
 
-    if (fread(read_hash, UNDO_HASH_SIZE, 1, fp) != 1)
-    {
-        EMSG2(_(e_corrupted), file_name);
-        goto error;
-    }
-    line_count = (linenr_T)get4c(fp);
-    if (memcmp(hash, read_hash, UNDO_HASH_SIZE) != 0
-                                 || line_count != curbuf->b_ml.ml_line_count)
-    {
-        if (p_verbose > 0 || name != NULL)
-        {
-            verbose_enter();
-            give_warning((char_u *)_("File contents changed, cannot use undo info"), TRUE);
-            verbose_leave();
-        }
-        goto error;
-    }
+/*
+ * Serialize "uep" to "fp".
+ */
+    static int
+serialize_uep(uep, fp)
+    u_entry_T  *uep;
+    FILE       *fp;
+{
+    int                i;
+    size_t     len;
 
-    /* Begin undo data for U */
-    str_len = get4c(fp);
-    if (str_len < 0)
-        goto error;
-    else if (str_len > 0)
+    put_bytes(fp, (long_u)uep->ue_top, 4);
+    put_bytes(fp, (long_u)uep->ue_bot, 4);
+    put_bytes(fp, (long_u)uep->ue_lcount, 4);
+    put_bytes(fp, (long_u)uep->ue_size, 4);
+    for (i = 0; i < uep->ue_size; ++i)
     {
-        if ((line_ptr = U_ALLOC_LINE(str_len + 1)) == NULL)
-            goto error;
-        for (i = 0; i < str_len; i++)
-            line_ptr[i] = (char_u)getc(fp);
-        line_ptr[i] = NUL;
+       len = STRLEN(uep->ue_array[i]);
+        if (put_bytes(fp, (long_u)len, 4) == FAIL)
+           return FAIL;
+        if (len > 0 && fwrite(uep->ue_array[i], len, (size_t)1, fp) != 1)
+           return FAIL;
     }
-    line_lnum = (linenr_T)get4c(fp);
-    line_colnr = (colnr_T)get4c(fp);
+    return OK;
+}
 
-    /* Begin general undo data */
-    old_header_seq = get4c(fp);
-    new_header_seq = get4c(fp);
-    cur_header_seq = get4c(fp);
-    num_head = get4c(fp);
-    seq_last = get4c(fp);
-    seq_cur = get4c(fp);
-    seq_time = get8ctime(fp);
+    static u_entry_T *
+unserialize_uep(fp, error, file_name)
+    FILE       *fp;
+    int                *error;
+    char_u     *file_name;
+{
+    int                i;
+    int                j;
+    u_entry_T  *uep;
+    char_u     **array;
+    char_u     *line;
+    int                line_len;
 
-    /* uhp_table will store the freshly created undo headers we allocate
-     * until we insert them into curbuf. The table remains sorted by the
-     * sequence numbers of the headers.
-     * When there are no headers uhp_table is NULL. */
-    if (num_head > 0)
+    uep = (u_entry_T *)U_ALLOC_LINE(sizeof(u_entry_T));
+    if (uep == NULL)
+       return NULL;
+    vim_memset(uep, 0, sizeof(u_entry_T));
+#ifdef U_DEBUG
+    uep->ue_magic = UE_MAGIC;
+#endif
+    uep->ue_top = get4c(fp);
+    uep->ue_bot = get4c(fp);
+    uep->ue_lcount = get4c(fp);
+    uep->ue_size = get4c(fp);
+    if (uep->ue_size > 0)
     {
-       uhp_table = (u_header_T **)U_ALLOC_LINE(
-                                            num_head * sizeof(u_header_T *));
-       if (uhp_table == NULL)
-           goto error;
-       vim_memset(uhp_table, 0, num_head * sizeof(u_header_T *));
+       array = (char_u **)U_ALLOC_LINE(sizeof(char_u *) * uep->ue_size);
+       if (array == NULL)
+       {
+           *error = TRUE;
+           return uep;
+       }
+       vim_memset(array, 0, sizeof(char_u *) * uep->ue_size);
     }
+    uep->ue_array = array;
 
-    c = get2c(fp);
-    while (c == UF_HEADER_MAGIC)
+    for (i = 0; i < uep->ue_size; ++i)
     {
-       if (num_read_uhps >= num_head)
+       line_len = get4c(fp);
+       if (line_len >= 0)
+           line = (char_u *)U_ALLOC_LINE(line_len + 1);
+       else
        {
-           EMSG2(_("E831 Undo file corruption: num_head: %s"), file_name);
-           u_free_uhp(uhp);
-           goto error;
+           line = NULL;
+           corruption_error("line length", file_name);
+       }
+       if (line == NULL)
+       {
+           *error = TRUE;
+           return uep;
        }
+       for (j = 0; j < line_len; j++)
+           line[j] = getc(fp);
+       line[j] = NUL;
+       array[i] = line;
+    }
+    return uep;
+}
 
-        uhp = (u_header_T *)U_ALLOC_LINE(sizeof(u_header_T));
-        if (uhp == NULL)
-            goto error;
-        vim_memset(uhp, 0, sizeof(u_header_T));
-        /* We're not actually trying to store pointers here. We're just storing
-         * IDs so we can swizzle them into pointers later - hence the type
-        * cast. */
-        uhp->uh_next = (u_header_T *)(long_u)get4c(fp);
-        uhp->uh_prev = (u_header_T *)(long_u)get4c(fp);
-        uhp->uh_alt_next = (u_header_T *)(long_u)get4c(fp);
-        uhp->uh_alt_prev = (u_header_T *)(long_u)get4c(fp);
-        uhp->uh_seq = get4c(fp);
-        if (uhp->uh_seq <= 0)
-        {
-            EMSG2(_("E825: Undo file corruption: invalid uh_seq.: %s"),
-                                                                  file_name);
-            vim_free(uhp);
-            goto error;
-        }
-        uhp->uh_walk = 0;
-        unserialize_pos(&uhp->uh_cursor, fp);
+/*
+ * Serialize "pos" to "fp".
+ */
+    static void
+serialize_pos(pos, fp)
+    pos_T pos;
+    FILE  *fp;
+{
+    put_bytes(fp, (long_u)pos.lnum, 4);
+    put_bytes(fp, (long_u)pos.col, 4);
 #ifdef FEAT_VIRTUALEDIT
-        uhp->uh_cursor_vcol = get4c(fp);
+    put_bytes(fp, (long_u)pos.coladd, 4);
 #else
-        (void)get4c(fp);
+    put_bytes(fp, (long_u)0, 4);
 #endif
-        uhp->uh_flags = get2c(fp);
-        for (i = 0; i < NMARKS; ++i)
-            unserialize_pos(&uhp->uh_namedm[i], fp);
-#ifdef FEAT_VISUAL
-        unserialize_visualinfo(&uhp->uh_visual, fp);
+}
+
+/*
+ * Unserialize the pos_T at the current position in fp.
+ */
+    static void
+unserialize_pos(pos, fp)
+    pos_T *pos;
+    FILE  *fp;
+{
+    pos->lnum = get4c(fp);
+    pos->col = get4c(fp);
+#ifdef FEAT_VIRTUALEDIT
+    pos->coladd = get4c(fp);
 #else
-       {
-           visualinfo_T info;
-           unserialize_visualinfo(&info, fp);
-       }
+    (void)get4c(fp);
 #endif
-        uhp->uh_time = get8ctime(fp);
-
-        /* Unserialize uep list. The first 4 bytes is the length of the
-         * entire uep in bytes minus the length of the strings within.
-         * -1 is a sentinel value meaning no more ueps.*/
-        last_uep = NULL;
-        while ((uep_len = get4c(fp)) != -1)
-        {
-            uep = (u_entry_T *)U_ALLOC_LINE(sizeof(u_entry_T));
-            if (uep == NULL)
-           {
-               u_free_uhp(uhp);
-                goto error;
-           }
-            vim_memset(uep, 0, sizeof(u_entry_T));
-            if (last_uep == NULL)
-                uhp->uh_entry = uep;
-           else
-                last_uep->ue_next = uep;
-            last_uep = uep;
-
-            uep->ue_top = get4c(fp);
-            uep->ue_bot = get4c(fp);
-            uep->ue_lcount = get4c(fp);
-            uep->ue_size = get4c(fp);
-            uep->ue_next = NULL;
-           if (uep->ue_size > 0)
-           {
-               array = (char_u **)U_ALLOC_LINE(
-                                            sizeof(char_u *) * uep->ue_size);
-               if (array == NULL)
-               {
-                   u_free_uhp(uhp);
-                   goto error;
-               }
-               vim_memset(array, 0, sizeof(char_u *) * uep->ue_size);
-           }
-            uep->ue_array = array;
-
-            for (i = 0; i < uep->ue_size; i++)
-            {
-                line_len = get4c(fp);
-               if (line_len >= 0)
-                   line = (char_u *)U_ALLOC_LINE(line_len + 1);
-               else
-                   line = NULL;
-                if (line == NULL)
-               {
-                   u_free_uhp(uhp);
-                    goto error;
-               }
-                for (j = 0; j < line_len; j++)
-                    line[j] = getc(fp);
-                line[j] = '\0';
-                array[i] = line;
-            }
-        }
-
-        /* Insertion sort the uhp into the table by its uh_seq. This is
-         * required because, while the number of uhps is limited to
-         * num_head, and the uh_seq order is monotonic with respect to
-         * creation time, the starting uh_seq can be > 0 if any undolevel
-         * culling was done at undofile write time, and there can be uh_seq
-         * gaps in the uhps.
-         */
-        for (i = num_read_uhps - 1; i >= -1; i--)
-        {
-            /* if i == -1, we've hit the leftmost side of the table, so insert
-             * at uhp_table[0]. */
-            if (i == -1 || uhp->uh_seq > uhp_table[i]->uh_seq)
-            {
-                /* If we've had to move from the rightmost side of the table,
-                 * we have to shift everything to the right by one spot. */
-                if (num_read_uhps - i - 1 > 0)
-                {
-                    memmove(uhp_table + i + 2, uhp_table + i + 1,
-                             (num_read_uhps - i - 1) * sizeof(u_header_T *));
-                }
-                uhp_table[i + 1] = uhp;
-                break;
-            }
-            else if (uhp->uh_seq == uhp_table[i]->uh_seq)
-            {
-                EMSG2(_("E826 Undo file corruption: duplicate uh_seq: %s"),
-                                                                  file_name);
-               u_free_uhp(uhp);
-                goto error;
-            }
-        }
-        num_read_uhps++;
-       c = get2c(fp);
-    }
-
-    if (c != UF_END_MAGIC)
-    {
-       EMSG2(_("E827: Undo file corruption; no end marker: %s"), file_name);
-       goto error;
-    }
-
-    /* We've organized all of the uhps into a table sorted by uh_seq. Now we
-     * iterate through the table and swizzle each sequence number we've
-     * stored in uh_foo into a pointer corresponding to the header with that
-     * sequence number. Then free curbuf's old undo structure, give curbuf
-     * the updated {old,new,cur}head pointers, and then free the table. */
-    for (i = 0; i < num_head; i++)
-    {
-        uhp = uhp_table[i];
-        if (uhp == NULL)
-            continue;
-        for (j = 0; j < num_head; j++)
-        {
-            if (uhp_table[j] == NULL)
-                continue;
-            if (uhp_table[j]->uh_seq == (long)uhp->uh_next)
-                uhp->uh_next = uhp_table[j];
-            if (uhp_table[j]->uh_seq == (long)uhp->uh_prev)
-                uhp->uh_prev = uhp_table[j];
-            if (uhp_table[j]->uh_seq == (long)uhp->uh_alt_next)
-                uhp->uh_alt_next = uhp_table[j];
-            if (uhp_table[j]->uh_seq == (long)uhp->uh_alt_prev)
-                uhp->uh_alt_prev = uhp_table[j];
-        }
-        if (old_header_seq > 0 && old_idx < 0 && uhp->uh_seq == old_header_seq)
-            old_idx = i;
-        if (new_header_seq > 0 && new_idx < 0 && uhp->uh_seq == new_header_seq)
-            new_idx = i;
-        if (cur_header_seq > 0 && cur_idx < 0 && uhp->uh_seq == cur_header_seq)
-            cur_idx = i;
-    }
-    u_blockfree(curbuf);
-    curbuf->b_u_oldhead = old_idx < 0 ? 0 : uhp_table[old_idx];
-    curbuf->b_u_newhead = new_idx < 0 ? 0 : uhp_table[new_idx];
-    curbuf->b_u_curhead = cur_idx < 0 ? 0 : uhp_table[cur_idx];
-    curbuf->b_u_line_ptr = line_ptr;
-    curbuf->b_u_line_lnum = line_lnum;
-    curbuf->b_u_line_colnr = line_colnr;
-    curbuf->b_u_numhead = num_head;
-    curbuf->b_u_seq_last = seq_last;
-    curbuf->b_u_seq_cur = seq_cur;
-    curbuf->b_u_seq_time = seq_time;
-    vim_free(uhp_table);
-#ifdef U_DEBUG
-    u_check(TRUE);
-#endif
-    if (name != NULL)
-       smsg((char_u *)_("Finished reading undo file %s"), file_name);
-    goto theend;
-
-error:
-    vim_free(line_ptr);
-    if (uhp_table != NULL)
-    {
-        for (i = 0; i < num_head; i++)
-            if (uhp_table[i] != NULL)
-               u_free_uhp(uhp_table[i]);
-        vim_free(uhp_table);
-    }
-
-theend:
-    if (fp != NULL)
-        fclose(fp);
-    if (file_name != name)
-       vim_free(file_name);
-    return;
-}
-
-    static void
-u_free_uhp(uhp)
-    u_header_T *uhp;
-{
-    u_entry_T  *nuep;
-    u_entry_T  *uep;
-
-    uep = uhp->uh_entry;
-    while (uep != NULL)
-    {
-       nuep = uep->ue_next;
-       u_freeentry(uep, uep->ue_size);
-       uep = nuep;
-    }
-    vim_free(uhp);
 }
 
 /*
- * Serialize "uep" to "fp".
+ * Serialize "info" to "fp".
  */
-    static int
-serialize_uep(uep, fp)
-    u_entry_T  *uep;
-    FILE       *fp;
+    static void
+serialize_visualinfo(info, fp)
+    visualinfo_T    *info;
+    FILE           *fp;
 {
-    int i;
-    int uep_len;
-    int *entry_lens;
-
-    if (uep->ue_size > 0)
-        entry_lens = (int *)alloc(uep->ue_size * sizeof(int));
-    else
-       entry_lens = NULL;
-
-    /* Define uep_len to be the size of the entire uep minus the size of its
-     * component strings, in bytes. The sizes of the component strings
-     * are written before each individual string.
-     * We have 4 entries each of 4 bytes, plus ue_size * 4 bytes
-     * of string size information. */
-
-    uep_len = uep->ue_size * 4;
-    /* Collect sizing information for later serialization. */
-    for (i = 0; i < uep->ue_size; i++)
-    {
-        entry_lens[i] = (int)STRLEN(uep->ue_array[i]);
-        uep_len += entry_lens[i];
-    }
-    put_bytes(fp, (long_u)uep_len, 4);
-    put_bytes(fp, (long_u)uep->ue_top, 4);
-    put_bytes(fp, (long_u)uep->ue_bot, 4);
-    put_bytes(fp, (long_u)uep->ue_lcount, 4);
-    put_bytes(fp, (long_u)uep->ue_size, 4);
-    for (i = 0; i < uep->ue_size; i++)
-    {
-        if (put_bytes(fp, (long_u)entry_lens[i], 4) == FAIL)
-           return FAIL;
-        fprintf(fp, "%s", uep->ue_array[i]);
-    }
-    if (uep->ue_size > 0)
-        vim_free(entry_lens);
-    return OK;
+    serialize_pos(info->vi_start, fp);
+    serialize_pos(info->vi_end, fp);
+    put_bytes(fp, (long_u)info->vi_mode, 4);
+    put_bytes(fp, (long_u)info->vi_curswant, 4);
 }
 
 /*
- * Serialize "pos" to "fp".
+ * Unserialize the visualinfo_T at the current position in fp.
  */
     static void
-serialize_pos(pos, fp)
-    pos_T pos;
-    FILE  *fp;
+unserialize_visualinfo(info, fp)
+    visualinfo_T    *info;
+    FILE           *fp;
 {
-    put_bytes(fp, (long_u)pos.lnum, 4);
-    put_bytes(fp, (long_u)pos.col, 4);
-#ifdef FEAT_VIRTUALEDIT
-    put_bytes(fp, (long_u)pos.coladd, 4);
-#else
-    put_bytes(fp, (long_u)0, 4);
-#endif
+    unserialize_pos(&info->vi_start, fp);
+    unserialize_pos(&info->vi_end, fp);
+    info->vi_mode = get4c(fp);
+    info->vi_curswant = get4c(fp);
 }
 
 /*
- * Serialize "info" to "fp".
- */
+ * Write the pointer to an undo header.  Instead of writing the pointer itself
+ * we use the sequence number of the header.  This is converted back to
+ * pointers when reading. */
     static void
-serialize_visualinfo(info, fp)
-    visualinfo_T    *info;
-    FILE           *fp;
+put_header_ptr(fp, uhp)
+    FILE       *fp;
+    u_header_T *uhp;
 {
-    serialize_pos(info->vi_start, fp);
-    serialize_pos(info->vi_end, fp);
-    put_bytes(fp, (long_u)info->vi_mode, 4);
-    put_bytes(fp, (long_u)info->vi_curswant, 4);
+    put_bytes(fp, (long_u)(uhp != NULL ? uhp->uh_seq : 0), 4);
 }
 
 /*
@@ -1248,7 +976,10 @@ u_write_undo(name, forceit, buf, hash)
     u_header_T *uhp;
     u_entry_T  *uep;
     char_u     *file_name;
-    int                str_len, i, uep_len, mark;
+    int                str_len, i, mark;
+#ifdef U_DEBUG
+    int                headers_written = 0;
+#endif
     int                fd;
     FILE       *fp = NULL;
     int                perm;
@@ -1263,14 +994,22 @@ u_write_undo(name, forceit, buf, hash)
     {
         file_name = u_get_undo_file_name(buf->b_ffname, FALSE);
        if (file_name == NULL)
+       {
+           if (p_verbose > 0)
+               smsg((char_u *)_("Cannot write undo file in any directory in 'undodir'"));
            return;
+       }
     }
     else
         file_name = name;
 
-    if (buf->b_ffname == NULL)
-       perm = 0600;
-    else
+    /*
+     * Decide about the permission to use for the undo file.  If the buffer
+     * has a name use the permission of the original file.  Otherwise only
+     * allow the user to access the undo file.
+     */
+    perm = 0600;
+    if (buf->b_ffname != NULL)
     {
 #ifdef UNIX
        if (mch_stat((char *)buf->b_ffname, &st_old) >= 0)
@@ -1278,8 +1017,6 @@ u_write_undo(name, forceit, buf, hash)
            perm = st_old.st_mode;
            st_old_valid = TRUE;
        }
-       else
-           perm = 0600;
 #else
        perm = mch_getperm(buf->b_ffname);
        if (perm < 0)
@@ -1287,11 +1024,11 @@ u_write_undo(name, forceit, buf, hash)
 #endif
     }
 
-    /* set file protection same as original file, but strip s-bit */
+    /* strip any s-bit */
     perm = perm & 0777;
 
-    /* If the undo file exists, verify that it actually is an undo file, and
-     * delete it. */
+    /* If the undo file already exists, verify that it actually is an undo
+     * file, and delete it. */
     if (mch_getperm(file_name) >= 0)
     {
        if (name == NULL || !forceit)
@@ -1307,12 +1044,13 @@ u_write_undo(name, forceit, buf, hash)
            }
            else
            {
-               char_u  buf[2];
+               char_u  buf[UF_START_MAGIC_LEN];
                int     len;
 
-               len = vim_read(fd, buf, 2);
+               len = vim_read(fd, buf, UF_START_MAGIC_LEN);
                close(fd);
-               if (len < 2 || (buf[0] << 8) + buf[1] != UF_START_MAGIC)
+               if (len < UF_START_MAGIC_LEN
+                     || memcmp(buf, UF_START_MAGIC, UF_START_MAGIC_LEN) != 0)
                {
                    if (name != NULL || p_verbose > 0)
                        smsg((char_u *)_("Will not overwrite, this is not an undo file: %s"),
@@ -1326,12 +1064,12 @@ u_write_undo(name, forceit, buf, hash)
 
     fd = mch_open((char *)file_name,
                            O_CREAT|O_EXTRA|O_WRONLY|O_EXCL|O_NOFOLLOW, perm);
-    (void)mch_setperm(file_name, perm);
     if (fd < 0)
     {
         EMSG2(_(e_not_open), file_name);
        goto theend;
     }
+    (void)mch_setperm(file_name, perm);
     if (p_verbose > 0)
        smsg((char_u *)_("Writing undo file: %s"), file_name);
 
@@ -1363,8 +1101,9 @@ u_write_undo(name, forceit, buf, hash)
        goto theend;
     }
 
-    /* Start writing, first overall file information */
-    put_bytes(fp, (long_u)UF_START_MAGIC, 2);
+    /* Start writing, first the undo file header. */
+    if (fwrite(UF_START_MAGIC, (size_t)UF_START_MAGIC_LEN, (size_t)1, fp) != 1)
+       goto write_error;
     put_bytes(fp, (long_u)UF_VERSION, 2);
 
     /* Write a hash of the buffer text, so that we can verify it is still the
@@ -1384,21 +1123,18 @@ u_write_undo(name, forceit, buf, hash)
     put_bytes(fp, (long_u)buf->b_u_line_colnr, 4);
 
     /* Begin general undo data */
-    uhp = buf->b_u_oldhead;
-    put_bytes(fp, (long_u)(uhp != NULL ? uhp->uh_seq : 0), 4);
-
-    uhp = buf->b_u_newhead;
-    put_bytes(fp, (long_u)(uhp != NULL ? uhp->uh_seq : 0), 4);
-
-    uhp = buf->b_u_curhead;
-    put_bytes(fp, (long_u)(uhp != NULL ? uhp->uh_seq : 0), 4);
+    put_header_ptr(fp, buf->b_u_oldhead);
+    put_header_ptr(fp, buf->b_u_newhead);
+    put_header_ptr(fp, buf->b_u_curhead);
 
     put_bytes(fp, (long_u)buf->b_u_numhead, 4);
     put_bytes(fp, (long_u)buf->b_u_seq_last, 4);
     put_bytes(fp, (long_u)buf->b_u_seq_cur, 4);
     put_time(fp, buf->b_u_seq_time);
 
-    /* Iteratively serialize UHPs and their UEPs from the top down.  */
+    /*
+     * Iteratively serialize UHPs and their UEPs from the top down.
+     */
     mark = ++lastmark;
     uhp = buf->b_u_oldhead;
     while (uhp != NULL)
@@ -1406,17 +1142,18 @@ u_write_undo(name, forceit, buf, hash)
         /* Serialize current UHP if we haven't seen it */
         if (uhp->uh_walk != mark)
         {
+            uhp->uh_walk = mark;
+#ifdef U_DEBUG
+           ++headers_written;
+#endif
+
            if (put_bytes(fp, (long_u)UF_HEADER_MAGIC, 2) == FAIL)
                goto write_error;
 
-            put_bytes(fp, (long_u)((uhp->uh_next != NULL)
-                                             ? uhp->uh_next->uh_seq : 0), 4);
-            put_bytes(fp, (long_u)((uhp->uh_prev != NULL)
-                                             ? uhp->uh_prev->uh_seq : 0), 4);
-            put_bytes(fp, (long_u)((uhp->uh_alt_next != NULL)
-                                         ? uhp->uh_alt_next->uh_seq : 0), 4);
-            put_bytes(fp, (long_u)((uhp->uh_alt_prev != NULL)
-                                         ? uhp->uh_alt_prev->uh_seq : 0), 4);
+           put_header_ptr(fp, uhp->uh_next);
+           put_header_ptr(fp, uhp->uh_prev);
+           put_header_ptr(fp, uhp->uh_alt_next);
+           put_header_ptr(fp, uhp->uh_alt_prev);
             put_bytes(fp, uhp->uh_seq, 4);
             serialize_pos(uhp->uh_cursor, fp);
 #ifdef FEAT_VIRTUALEDIT
@@ -1440,17 +1177,14 @@ u_write_undo(name, forceit, buf, hash)
 #endif
             put_time(fp, uhp->uh_time);
 
-            uep = uhp->uh_entry;
-            while (uep != NULL)
-            {
+           /* Write all the entries. */
+           for (uep = uhp->uh_entry; uep != NULL; uep = uep->ue_next)
+           {
+               put_bytes(fp, (long_u)UF_ENTRY_MAGIC, 2);
                 if (serialize_uep(uep, fp) == FAIL)
                    goto write_error;
-                uep = uep->ue_next;
-            }
-            /* Sentinel value: no more ueps */
-            uep_len = -1;
-            put_bytes(fp, (long_u)uep_len, 4);
-            uhp->uh_walk = mark;
+           }
+           put_bytes(fp, (long_u)UF_ENTRY_END_MAGIC, 2);
         }
 
         /* Now walk through the tree - algorithm from undo_time */
@@ -1467,8 +1201,13 @@ u_write_undo(name, forceit, buf, hash)
             uhp = uhp->uh_next;
     }
 
-    if (put_bytes(fp, (long_u)UF_END_MAGIC, 2) == OK)
+    if (put_bytes(fp, (long_u)UF_HEADER_END_MAGIC, 2) == OK)
        write_ok = TRUE;
+#ifdef U_DEBUG
+    if (headers_written != buf->b_u_numhead)
+       EMSG3("Written %ld headers, but numhead is %ld",
+                                          headers_written, buf->b_u_numhead);
+#endif
 
 write_error:
     fclose(fp);
@@ -1495,6 +1234,351 @@ theend:
        vim_free(file_name);
 }
 
+/*
+ * Load the undo tree from an undo file.
+ * If "name" is not NULL use it as the undo file name.  This also means being
+ * a bit more verbose.
+ * Otherwise use curbuf->b_ffname to generate the undo file name.
+ * "hash[UNDO_HASH_SIZE]" must be the hash value of the buffer text.
+ */
+    void
+u_read_undo(name, hash)
+    char_u *name;
+    char_u *hash;
+{
+    char_u     *file_name;
+    FILE       *fp;
+    long       version, str_len;
+    char_u     *line_ptr = NULL;
+    linenr_T   line_lnum;
+    colnr_T    line_colnr;
+    linenr_T   line_count;
+    int                num_head = 0;
+    long       old_header_seq, new_header_seq, cur_header_seq;
+    long       seq_last, seq_cur;
+    short      old_idx = -1, new_idx = -1, cur_idx = -1;
+    long       num_read_uhps = 0;
+    time_t     seq_time;
+    int                i, j;
+    int                c;
+    u_entry_T  *uep, *last_uep;
+    u_header_T *uhp;
+    u_header_T **uhp_table = NULL;
+    char_u     read_hash[UNDO_HASH_SIZE];
+    char_u     magic_buf[UF_START_MAGIC_LEN];
+#ifdef U_DEBUG
+    int                *uhp_table_used;
+#endif
+
+    if (name == NULL)
+    {
+        file_name = u_get_undo_file_name(curbuf->b_ffname, TRUE);
+       if (file_name == NULL)
+           return;
+    }
+    else
+        file_name = name;
+
+    if (p_verbose > 0)
+       smsg((char_u *)_("Reading undo file: %s"), file_name);
+    fp = mch_fopen((char *)file_name, "r");
+    if (fp == NULL)
+    {
+        if (name != NULL || p_verbose > 0)
+            EMSG2(_("E822: Cannot open undo file for reading: %s"), file_name);
+       goto error;
+    }
+
+    /*
+     * Read the undo file header.
+     */
+    if (fread(magic_buf, UF_START_MAGIC_LEN, 1, fp) != 1
+               || memcmp(magic_buf, UF_START_MAGIC, UF_START_MAGIC_LEN) != 0)
+    {
+       EMSG2(_("E823: Not an undo file: %s"), file_name);
+        goto error;
+    }
+    version = get2c(fp);
+    if (version != UF_VERSION)
+    {
+        EMSG2(_("E824: Incompatible undo file: %s"), file_name);
+        goto error;
+    }
+
+    if (fread(read_hash, UNDO_HASH_SIZE, 1, fp) != 1)
+    {
+       corruption_error("hash", file_name);
+        goto error;
+    }
+    line_count = (linenr_T)get4c(fp);
+    if (memcmp(hash, read_hash, UNDO_HASH_SIZE) != 0
+                                 || line_count != curbuf->b_ml.ml_line_count)
+    {
+        if (p_verbose > 0 || name != NULL)
+        {
+            verbose_enter();
+            give_warning((char_u *)_("File contents changed, cannot use undo info"), TRUE);
+            verbose_leave();
+        }
+        goto error;
+    }
+
+    /* Begin undo data for U */
+    str_len = get4c(fp);
+    if (str_len < 0)
+        goto error;
+    else if (str_len > 0)
+    {
+        if ((line_ptr = U_ALLOC_LINE(str_len + 1)) == NULL)
+            goto error;
+        for (i = 0; i < str_len; i++)
+            line_ptr[i] = (char_u)getc(fp);
+        line_ptr[i] = NUL;
+    }
+    line_lnum = (linenr_T)get4c(fp);
+    line_colnr = (colnr_T)get4c(fp);
+
+    /* Begin general undo data */
+    old_header_seq = get4c(fp);
+    new_header_seq = get4c(fp);
+    cur_header_seq = get4c(fp);
+    num_head = get4c(fp);
+    seq_last = get4c(fp);
+    seq_cur = get4c(fp);
+    seq_time = get8ctime(fp);
+
+    /* uhp_table will store the freshly created undo headers we allocate
+     * until we insert them into curbuf. The table remains sorted by the
+     * sequence numbers of the headers.
+     * When there are no headers uhp_table is NULL. */
+    if (num_head > 0)
+    {
+       uhp_table = (u_header_T **)U_ALLOC_LINE(
+                                            num_head * sizeof(u_header_T *));
+       if (uhp_table == NULL)
+           goto error;
+       vim_memset(uhp_table, 0, num_head * sizeof(u_header_T *));
+    }
+
+    while ((c = get2c(fp)) == UF_HEADER_MAGIC)
+    {
+       if (num_read_uhps >= num_head)
+       {
+           corruption_error("num_head", file_name);
+           u_free_uhp(uhp);
+           goto error;
+       }
+
+        uhp = (u_header_T *)U_ALLOC_LINE(sizeof(u_header_T));
+        if (uhp == NULL)
+            goto error;
+        vim_memset(uhp, 0, sizeof(u_header_T));
+#ifdef U_DEBUG
+       uhp->uh_magic = UH_MAGIC;
+#endif
+        /* We're not actually trying to store pointers here. We're just storing
+         * IDs so we can swizzle them into pointers later - hence the type
+        * cast. */
+        uhp->uh_next = (u_header_T *)(long_u)get4c(fp);
+        uhp->uh_prev = (u_header_T *)(long_u)get4c(fp);
+        uhp->uh_alt_next = (u_header_T *)(long_u)get4c(fp);
+        uhp->uh_alt_prev = (u_header_T *)(long_u)get4c(fp);
+        uhp->uh_seq = get4c(fp);
+        if (uhp->uh_seq <= 0)
+        {
+           corruption_error("uh_seq", file_name);
+            vim_free(uhp);
+            goto error;
+        }
+        uhp->uh_walk = 0;
+        unserialize_pos(&uhp->uh_cursor, fp);
+#ifdef FEAT_VIRTUALEDIT
+        uhp->uh_cursor_vcol = get4c(fp);
+#else
+        (void)get4c(fp);
+#endif
+        uhp->uh_flags = get2c(fp);
+        for (i = 0; i < NMARKS; ++i)
+            unserialize_pos(&uhp->uh_namedm[i], fp);
+#ifdef FEAT_VISUAL
+        unserialize_visualinfo(&uhp->uh_visual, fp);
+#else
+       {
+           visualinfo_T info;
+           unserialize_visualinfo(&info, fp);
+       }
+#endif
+        uhp->uh_time = get8ctime(fp);
+
+        /* Unserialize the uep list. */
+        last_uep = NULL;
+        while ((c = get2c(fp)) == UF_ENTRY_MAGIC)
+        {
+           int error = FALSE;
+
+           uep = unserialize_uep(fp, &error, file_name);
+            if (last_uep == NULL)
+                uhp->uh_entry = uep;
+           else
+                last_uep->ue_next = uep;
+            last_uep = uep;
+           if (uep == NULL || error)
+           {
+               u_free_uhp(uhp);
+                goto error;
+           }
+        }
+       if (c != UF_ENTRY_END_MAGIC)
+        {
+           corruption_error("entry end", file_name);
+            u_free_uhp(uhp);
+            goto error;
+        }
+
+        /* Insertion sort the uhp into the table by its uh_seq. This is
+         * required because, while the number of uhps is limited to
+         * num_head, and the uh_seq order is monotonic with respect to
+         * creation time, the starting uh_seq can be > 0 if any undolevel
+         * culling was done at undofile write time, and there can be uh_seq
+         * gaps in the uhps.
+         */
+        for (i = num_read_uhps - 1; i >= -1; i--)
+        {
+            /* if i == -1, we've hit the leftmost side of the table, so insert
+             * at uhp_table[0]. */
+            if (i == -1 || uhp->uh_seq > uhp_table[i]->uh_seq)
+            {
+                /* If we've had to move from the rightmost side of the table,
+                 * we have to shift everything to the right by one spot. */
+                if (num_read_uhps - i - 1 > 0)
+                {
+                    memmove(uhp_table + i + 2, uhp_table + i + 1,
+                             (num_read_uhps - i - 1) * sizeof(u_header_T *));
+                }
+                uhp_table[i + 1] = uhp;
+                break;
+            }
+            else if (uhp->uh_seq == uhp_table[i]->uh_seq)
+            {
+               corruption_error("duplicate uh_seq", file_name);
+               u_free_uhp(uhp);
+                goto error;
+            }
+        }
+        num_read_uhps++;
+    }
+
+    if (c != UF_HEADER_END_MAGIC)
+    {
+       corruption_error("end marker", file_name);
+       goto error;
+    }
+
+#ifdef U_DEBUG
+    uhp_table_used = (int *)alloc_clear(
+                                    (unsigned)(sizeof(int) * num_head + 1));
+# define SET_FLAG(j) ++uhp_table_used[j]
+#else
+# define SET_FLAG(j)
+#endif
+
+    /* We've organized all of the uhps into a table sorted by uh_seq. Now we
+     * iterate through the table and swizzle each sequence number we've
+     * stored in uh_* into a pointer corresponding to the header with that
+     * sequence number. Then free curbuf's old undo structure, give curbuf
+     * the updated {old,new,cur}head pointers, and then free the table. */
+    for (i = 0; i < num_head; i++)
+    {
+        uhp = uhp_table[i];
+        if (uhp == NULL)
+            continue;
+        for (j = 0; j < num_head; j++)
+        {
+            if (uhp_table[j] == NULL)
+                continue;
+            if (uhp_table[j]->uh_seq == (long)uhp->uh_next)
+           {
+                uhp->uh_next = uhp_table[j];
+               SET_FLAG(j);
+           }
+            if (uhp_table[j]->uh_seq == (long)uhp->uh_prev)
+           {
+                uhp->uh_prev = uhp_table[j];
+               SET_FLAG(j);
+           }
+            if (uhp_table[j]->uh_seq == (long)uhp->uh_alt_next)
+           {
+                uhp->uh_alt_next = uhp_table[j];
+               SET_FLAG(j);
+           }
+            if (uhp_table[j]->uh_seq == (long)uhp->uh_alt_prev)
+           {
+                uhp->uh_alt_prev = uhp_table[j];
+               SET_FLAG(j);
+           }
+        }
+        if (old_header_seq > 0 && old_idx < 0 && uhp->uh_seq == old_header_seq)
+       {
+            old_idx = i;
+           SET_FLAG(i);
+       }
+        if (new_header_seq > 0 && new_idx < 0 && uhp->uh_seq == new_header_seq)
+       {
+            new_idx = i;
+           SET_FLAG(i);
+       }
+        if (cur_header_seq > 0 && cur_idx < 0 && uhp->uh_seq == cur_header_seq)
+       {
+            cur_idx = i;
+           SET_FLAG(i);
+       }
+    }
+
+    /* Now that we have read the undo info successfully, free the current undo
+     * info and use the info from the file. */
+    u_blockfree(curbuf);
+    curbuf->b_u_oldhead = old_idx < 0 ? NULL : uhp_table[old_idx];
+    curbuf->b_u_newhead = new_idx < 0 ? NULL : uhp_table[new_idx];
+    curbuf->b_u_curhead = cur_idx < 0 ? NULL : uhp_table[cur_idx];
+    curbuf->b_u_line_ptr = line_ptr;
+    curbuf->b_u_line_lnum = line_lnum;
+    curbuf->b_u_line_colnr = line_colnr;
+    curbuf->b_u_numhead = num_head;
+    curbuf->b_u_seq_last = seq_last;
+    curbuf->b_u_seq_cur = seq_cur;
+    curbuf->b_u_seq_time = seq_time;
+    vim_free(uhp_table);
+
+#ifdef U_DEBUG
+    for (i = 0; i < num_head; ++i)
+       if (uhp_table_used[i] == 0)
+           EMSGN("uhp_table entry %ld not used, leaking memory", i);
+    vim_free(uhp_table_used);
+    u_check(TRUE);
+#endif
+
+    if (name != NULL)
+       smsg((char_u *)_("Finished reading undo file %s"), file_name);
+    goto theend;
+
+error:
+    vim_free(line_ptr);
+    if (uhp_table != NULL)
+    {
+        for (i = 0; i < num_head; i++)
+            if (uhp_table[i] != NULL)
+               u_free_uhp(uhp_table[i]);
+        vim_free(uhp_table);
+    }
+
+theend:
+    if (fp != NULL)
+        fclose(fp);
+    if (file_name != name)
+       vim_free(file_name);
+    return;
+}
+
 #endif /* FEAT_PERSISTENT_UNDO */