Index: ex_docmd.c
===================================================================
--- ex_docmd.c	(revision 1365)
+++ ex_docmd.c	(working copy)
@@ -296,6 +296,8 @@
 static void	ex_at __ARGS((exarg_T *eap));
 static void	ex_bang __ARGS((exarg_T *eap));
 static void	ex_undo __ARGS((exarg_T *eap));
+static void	ex_wundo __ARGS((exarg_T *eap));
+static void	ex_rundo __ARGS((exarg_T *eap));
 static void	ex_redo __ARGS((exarg_T *eap));
 static void	ex_later __ARGS((exarg_T *eap));
 static void	ex_redir __ARGS((exarg_T *eap));
@@ -8394,6 +8396,22 @@
 	u_undo(1);
 }
 
+    void
+ex_wundo(eap)
+    exarg_T *eap;
+{
+    char_u	*name = eap->arg;
+    u_wundo(name, curbuf);
+}
+
+    void
+ex_rundo(eap)
+    exarg_T *eap;
+{
+    char_u	*name = eap->arg;
+    u_rundo(name, 0);
+}
+
 /*
  * ":redo".
  */
Index: ex_cmds.c
===================================================================
--- ex_cmds.c	(revision 1365)
+++ ex_cmds.c	(working copy)
@@ -2717,6 +2717,8 @@
 	    DO_AUTOCHDIR
 	}
     }
+    if (curbuf->b_p_udf)
+	u_wundo(ffname, curbuf);
 
 theend:
 #ifdef FEAT_BROWSE
@@ -3663,6 +3665,9 @@
 		retval = FAIL;
 	    handle_swap_exists(old_curbuf);
 #endif
+            if (curbuf->b_p_udf && !curbuf->b_p_ro) {
+                u_rundo(NULL, 1);
+            }
 	}
 #ifdef FEAT_AUTOCMD
 	else
Index: ex_cmds.h
===================================================================
--- ex_cmds.h	(revision 1365)
+++ ex_cmds.h	(working copy)
@@ -769,6 +769,8 @@
 			RANGE|DFLALL|EXTRA|NEEDARG|CMDWIN),
 EX(CMD_rubyfile,	"rubyfile",	ex_rubyfile,
 			RANGE|FILE1|NEEDARG|CMDWIN),
+EX(CMD_rundo,		"rundo",	ex_rundo,
+			EXTRA),
 EX(CMD_rviminfo,	"rviminfo",	ex_viminfo,
 			BANG|FILE1|TRLBAR|CMDWIN),
 EX(CMD_substitute,	"substitute",	do_sub,
@@ -1055,6 +1057,8 @@
 			BANG|FILE1|ARGOPT|DFLALL|TRLBAR),
 EX(CMD_wsverb,		"wsverb",	ex_wsverb,
 			EXTRA|NOTADR|NEEDARG),
+EX(CMD_wundo,		"wundo",	ex_wundo,
+			EXTRA),
 EX(CMD_wviminfo,	"wviminfo",	ex_viminfo,
 			BANG|FILE1|TRLBAR|CMDWIN),
 EX(CMD_xit,		"xit",		ex_exit,
Index: ex_cmds2.c
===================================================================
--- ex_cmds2.c	(revision 1365)
+++ ex_cmds2.c	(working copy)
@@ -1628,6 +1628,10 @@
     retval = (buf_write(buf, buf->b_ffname, buf->b_fname,
 				   (linenr_T)1, buf->b_ml.ml_line_count, NULL,
 						  FALSE, forceit, TRUE, FALSE));
+    if (retval == OK && buf->b_p_udf)
+    {
+	u_wundo(buf->b_ffname, buf);
+    }
 #ifdef FEAT_AUTOCMD
     if (curbuf != old_curbuf)
     {
Index: os_mac.h
===================================================================
--- os_mac.h	(revision 1365)
+++ os_mac.h	(working copy)
@@ -203,6 +203,10 @@
 # define DFLT_DIR	"."	/* default for 'directory' */
 #endif
 
+#ifndef DFLT_UDIR
+# define DFLT_UDIR	"."	/* default for 'undodir' */
+#endif
+
 #ifndef DFLT_VDIR
 #  define DFLT_VDIR	"$VIM/vimfiles/view"	/* default for 'viewdir' */
 #endif
Index: option.c
===================================================================
--- option.c	(revision 1365)
+++ option.c	(working copy)
@@ -176,6 +176,7 @@
 #define PV_TS		OPT_BUF(BV_TS)
 #define PV_TW		OPT_BUF(BV_TW)
 #define PV_TX		OPT_BUF(BV_TX)
+#define PV_UDF		OPT_BUF(BV_UDF)
 #define PV_WM		OPT_BUF(BV_WM)
 
 /*
@@ -357,6 +358,7 @@
 static long	p_ts;
 static long	p_tw;
 static int	p_tx;
+static int	p_udf;
 static long	p_wm;
 #ifdef FEAT_KEYMAP
 static char_u	*p_keymap;
@@ -2550,6 +2552,12 @@
     {"ttytype",	    "tty",  P_STRING|P_EXPAND|P_NODEFAULT|P_NO_MKRC|P_VI_DEF|P_RALL,
 			    (char_u *)&T_NAME, PV_NONE,
 			    {(char_u *)"", (char_u *)0L}},
+    {"undodir",     "udir", P_STRING|P_EXPAND|P_COMMA|P_NODUP|P_SECURE|P_VI_DEF,
+			    (char_u *)&p_udir, PV_NONE,
+			    {(char_u *)DFLT_UDIR, (char_u *)0L}},
+    {"undofile",    "udf",  P_BOOL|P_VIM,
+			    (char_u *)&p_udf, PV_UDF,
+			    {(char_u *)TRUE, (char_u *)0L}},
     {"undolevels",  "ul",   P_NUM|P_VI_DEF,
 			    (char_u *)&p_ul, PV_NONE,
 			    {
@@ -9215,6 +9223,7 @@
 	case PV_TS:	return (char_u *)&(curbuf->b_p_ts);
 	case PV_TW:	return (char_u *)&(curbuf->b_p_tw);
 	case PV_TX:	return (char_u *)&(curbuf->b_p_tx);
+	case PV_UDF:	return (char_u *)&(curbuf->b_p_udf);
 	case PV_WM:	return (char_u *)&(curbuf->b_p_wm);
 #ifdef FEAT_KEYMAP
 	case PV_KMAP:	return (char_u *)&(curbuf->b_p_keymap);
@@ -9586,6 +9595,7 @@
 #if defined(FEAT_BEVAL) && defined(FEAT_EVAL)
 	    buf->b_p_bexpr = empty_option;
 #endif
+	    buf->b_p_udf = p_udf;
 
 	    /*
 	     * Don't copy the options set by ex_help(), use the saved values,
Index: option.h
===================================================================
--- option.h	(revision 1365)
+++ option.h	(working copy)
@@ -815,6 +815,7 @@
 # define TTYM_JSBTERM		0x10
 # define TTYM_PTERM		0x20
 #endif
+EXTERN char_u	*p_udir;	/* 'undodir' */
 EXTERN long	p_ul;		/* 'undolevels' */
 EXTERN long	p_uc;		/* 'updatecount' */
 EXTERN long	p_ut;		/* 'updatetime' */
@@ -1003,6 +1004,7 @@
     , BV_TS
     , BV_TW
     , BV_TX
+    , BV_UDF
     , BV_WM
     , BV_COUNT	    /* must be the last one */
 };
Index: proto/undo.pro
===================================================================
--- proto/undo.pro	(revision 1365)
+++ proto/undo.pro	(working copy)
@@ -7,6 +7,8 @@
 int undo_allowed __ARGS((void));
 void u_undo __ARGS((int count));
 void u_redo __ARGS((int count));
+void u_wundo __ARGS((char_u *name, buf_T *buf));
+void u_rundo __ARGS((char_u *name, int quiet));
 void undo_time __ARGS((long step, int sec, int absolute));
 void u_sync __ARGS((int force));
 void ex_undolist __ARGS((exarg_T *eap));
Index: proto/misc2.pro
===================================================================
--- proto/misc2.pro	(revision 1365)
+++ proto/misc2.pro	(working copy)
@@ -84,6 +84,8 @@
 void crypt_init_keys __ARGS((char_u *passwd));
 char_u *get_crypt_key __ARGS((int store, int twice));
 void *vim_findfile_init __ARGS((char_u *path, char_u *filename, char_u *stopdirs, int level, int free_visited, int find_what, void *search_ctx_arg, int tagfile, char_u *rel_fname));
+int cksum_crc32 __ARGS((int crc, char_u *buf, int len));
+int buf_cksum __ARGS((buf_T *buf));
 char_u *vim_findfile_stopdir __ARGS((char_u *buf));
 void vim_findfile_cleanup __ARGS((void *ctx));
 char_u *vim_findfile __ARGS((void *search_ctx_arg));
Index: proto/spell.pro
===================================================================
--- proto/spell.pro	(revision 1365)
+++ proto/spell.pro	(working copy)
@@ -23,4 +23,7 @@
 int spell_word_start __ARGS((int startcol));
 void spell_expand_check_cap __ARGS((colnr_T col));
 int expand_spelling __ARGS((linenr_T lnum, int col, char_u *pat, char_u ***matchp));
+int get2c __ARGS((FILE *fd));
+int get3c __ARGS((FILE *fd));
+int get4c __ARGS((FILE *fd));
 /* vim: set ft=c : */
Index: structs.h
===================================================================
--- structs.h	(revision 1365)
+++ structs.h	(working copy)
@@ -1456,6 +1456,7 @@
     char_u	*b_p_dict;	/* 'dictionary' local value */
     char_u	*b_p_tsr;	/* 'thesaurus' local value */
 #endif
+    int		b_p_udf;	/* 'undofile' */
 
     /* end of buffer options */
 
Index: undo.c
===================================================================
--- undo.c	(revision 1365)
+++ undo.c	(working copy)
@@ -81,6 +81,8 @@
 /* #define U_DEBUG 1 */
 #define UH_MAGIC 0x18dade	/* value for uh_magic when in use */
 #define UE_MAGIC 0xabc123	/* value for ue_magic when in use */
+#define UF_MAGIC 0xfeac		/* Quick undofile corruption check */
+#define UF_VERSION 0		/* 2-byte undofile version number */
 
 #include "vim.h"
 
@@ -99,6 +101,12 @@
 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));
+static void serialize_uep __ARGS((FILE *fp, u_entry_T *uep));
+static void serialize_pos __ARGS((pos_T pos, FILE *fp));
+static void serialize_visualinfo __ARGS((visualinfo_T info, FILE *fp));
+static int unserialize_pos __ARGS((pos_T *, FILE *));
+static int unserialize_visualinfo __ARGS((visualinfo_T *, FILE *));
+static char_u *u_get_undofile __ARGS((char_u *));
 
 #ifdef U_USE_MALLOC
 # define U_FREE_LINE(ptr) vim_free(ptr)
@@ -652,6 +660,507 @@
     return FAIL;
 }
 
+    static int
+unserialize_pos(pos_T *pos, FILE *fp)
+{
+    pos->lnum = get4c(fp);
+    pos->col = get4c(fp);
+#ifdef FEAT_VIRTUALEDIT
+    pos->coladd = get4c(fp);
+#endif
+    return 0;
+}
+
+#ifdef FEAT_VISUAL
+    static int
+unserialize_visualinfo(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 0;
+}
+#endif
+
+    static char_u *
+u_get_undofile(char_u *ffname)
+{
+    /* Returns an allocated string of the full path of the target undofile */
+    char_u *undo_path = NULL;
+    char_u *dirp;
+    char_u dir_name[IOSIZE + 1];
+    char_u *munged_name;
+    int dir_len;
+    int undo_path_len, name_len;
+    int i;
+    if (ffname == NULL)
+        ffname = (char_u *) "";
+
+    name_len = STRLEN(ffname);
+    munged_name = alloc(name_len + 1);
+    mch_memmove(munged_name, ffname, name_len + 1);
+
+    for (i = 0; i < name_len; i++) {
+        if (munged_name[i] == '/')
+            munged_name[i] = '_';
+    }
+
+    dirp = p_udir;
+    while (*dirp)
+    {
+        dir_len = copy_option_part(&dirp, dir_name, IOSIZE, ",");
+        dir_name[dir_len] = '\0';
+        if (!mch_isdir(dir_name)) {
+            continue;
+        }
+        /* get_file_in_dir returns allocated memory */
+        undo_path = get_file_in_dir(munged_name, dir_name);
+        undo_path_len = STRLEN(undo_path);
+        undo_path = vim_realloc(undo_path, sizeof(char_u) * undo_path_len + 5);
+        vim_strncpy(undo_path + undo_path_len, (char_u *) ".und", 4);
+        return undo_path;
+    }
+    vim_free(undo_path);
+    return NULL;
+}
+
+    void
+u_rundo(char_u *name, int quiet)
+{
+    char_u *undo_file;
+    if (name == NULL || STRLEN(name) == 0) {
+        undo_file = u_get_undofile(curbuf->b_ffname);
+    }
+    else {
+        undo_file = u_get_undofile(name);
+    }
+    if (undo_file == NULL) {
+        if (!quiet)
+            EMSG(_("Error: Can't open undo dir for read"));
+        return;
+    }
+    FILE *fp = fopen((char *)undo_file, "r");
+    if (fp == NULL) {
+        if (!quiet)
+            EMSG2(_("Error: Can't open undo file %s for read"), undo_file);
+        vim_free(undo_file);
+        return;
+    }
+
+    long magic, version;
+    long str_len, file_cksum;
+    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;
+    long old_header_seq, new_header_seq, cur_header_seq, seq_last, seq_cur;
+    time_t seq_time;
+    int i, j;
+    int c;
+    int found_first_uep = 0;
+    char_u **array;
+    char_u *line;
+    u_entry_T *uep, *last_uep, *nuep;
+    u_header_T *uhp;
+    u_header_T **uhp_table = NULL;
+
+    /* Begin overall file information */
+    magic = get2c(fp);
+    if (magic != UF_MAGIC) {
+        EMSG2(_("Error: corrupted undo file %s"), undo_file);
+        vim_free(undo_file);
+        goto error;
+    }
+    version = get2c(fp);
+    if (version != UF_VERSION) {
+        EMSG2(_("Error: incompatible undo file %s"), undo_file);
+        vim_free(undo_file);
+        goto error;
+    }
+    vim_free(undo_file);
+
+#ifdef FEAT_CRYPT
+    file_cksum = (long_u) get4c(fp);
+    if (file_cksum != buf_cksum(curbuf)) {
+        if (p_verbose > 0)
+        {
+            verbose_enter();
+            give_warning((char_u *)_("Undo file checksum differs"), TRUE);
+            verbose_leave();
+        }
+        goto error;
+    }
+#endif
+
+    line_count = (linenr_T) get4c(fp);
+    if (line_count != curbuf->b_ml.ml_line_count) {
+        if (p_verbose > 0)
+        {
+            verbose_enter();
+            give_warning((char_u *)_("Undo file linecount differs"), 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)) == NULL)
+        {
+            EMSG("u_rundo: out of memory!");
+            goto error;
+        }
+        for (i = 0; i < str_len; i++) {
+            line_ptr[i] = (char_u) getc(fp);
+        }
+        line_ptr[i] = '\0';
+    }
+    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 = get4c(fp);
+    /* A table that will map undo sequence number to memory address by the
+     * end of the loop. Since undo sequences begin at 1, always subtract 1
+     * from the undo sequence number when indexing into this table. */
+    uhp_table = (u_header_T **)U_ALLOC_LINE(num_head * sizeof(u_header_T *));
+    if (uhp_table == NULL)
+    {
+        EMSG("u_rundo: out of memory!");
+        goto error;
+    }
+    vim_memset(uhp_table, 0, num_head * sizeof(u_header_T *));
+
+    c = getc(fp);
+    while (c != 0x0a && c != EOF) {
+        ungetc(c, fp);
+
+        found_first_uep = 0;
+        uhp = (u_header_T *)U_ALLOC_LINE((unsigned)sizeof(u_header_T));
+        if (uhp == NULL)
+        {
+            EMSG("u_rundo: out of memory!");
+            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 type hack */
+        uhp->uh_next = (u_header_T *)(long)get4c(fp);
+        uhp->uh_prev = (u_header_T *)(long)get4c(fp);
+        uhp->uh_alt_next = (u_header_T *)(long)get4c(fp);
+        uhp->uh_alt_prev = (u_header_T *)(long)get4c(fp);
+        uhp->uh_seq = get4c(fp);
+        if (uhp->uh_seq <= 0 || uhp->uh_seq > num_head)
+        {
+            EMSG("u_rundo: undo file corruption detected: invalid uh_seq.");
+            U_FREE_LINE(uhp);
+            goto error;
+        }
+        uhp->uh_walk = 0;
+        unserialize_pos(&uhp->uh_cursor, fp);
+#ifdef FEAT_VIRTUALEDIT
+        uhp->uh_cursor_vcol = 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);
+#endif
+        uhp->uh_time = get4c(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((unsigned)sizeof(u_entry_T));
+            vim_memset(uep, 0, sizeof(u_entry_T));
+            if (uep == NULL) {
+                EMSG("u_rundo: out of memory!");
+                goto error;
+            }
+            uep->ue_top = get4c(fp);
+            uep->ue_bot = get4c(fp);
+            uep->ue_lcount = get4c(fp);
+            uep->ue_size = get4c(fp);
+            uep->ue_next = NULL;
+            array = (char_u **)U_ALLOC_LINE((unsigned)(sizeof(char_u *) * uep->ue_size));
+            for (i = 0; i < uep->ue_size; i++) {
+                line_len = get4c(fp);
+                /* U_ALLOC_LINE provides an extra byte for the NUL terminator.*/
+                line = (char_u *)U_ALLOC_LINE((unsigned) (sizeof(char_u) * line_len));
+                if (line == NULL) {
+                    EMSG("u_rundo: out of memory!");
+                    goto error;
+                }
+                for (j = 0; j < line_len; j++) {
+                    line[j] = getc(fp);
+                }
+                line[j] = '\0';
+                array[i] = line;
+            }
+            uep->ue_array = array;
+            if (found_first_uep == 0) {
+                uhp->uh_entry = uep;
+                found_first_uep = 1;
+            }
+            else {
+                last_uep->ue_next = uep;
+            }
+            last_uep = uep;
+        }
+        uhp_table[uhp->uh_seq - 1] = uhp;
+        c = getc(fp);
+    }
+
+    /* We've organized all of the uhps into a table mapping sequence
+     * number to pointer. Now link all of them up, replace current undo
+     * strutures, free them, and free the table. */
+    for (i = 0; i < num_head; i++) {
+        uhp = uhp_table[i];
+        if (uhp == NULL) {
+            continue;
+        }
+        if (uhp->uh_next > 0) {
+            uhp->uh_next = uhp_table[(long)uhp->uh_next - 1];
+        }
+        if (uhp->uh_prev > 0) {
+            uhp->uh_prev = uhp_table[(long)uhp->uh_prev - 1];
+        }
+        if (uhp->uh_alt_next > 0) {
+            uhp->uh_alt_next = uhp_table[(long)uhp->uh_alt_next - 1];
+        }
+        if (uhp->uh_alt_prev > 0) {
+            uhp->uh_alt_prev = uhp_table[(long)uhp->uh_alt_prev - 1];
+        }
+    }
+    u_blockfree(curbuf);
+    curbuf->b_u_line_ptr = line_ptr;
+    curbuf->b_u_line_lnum = line_lnum;
+    curbuf->b_u_line_colnr = line_colnr;
+    curbuf->b_u_oldhead = old_header_seq >0 ? uhp_table[old_header_seq - 1] : 0;
+    curbuf->b_u_newhead = new_header_seq >0 ? uhp_table[new_header_seq - 1] : 0;
+    curbuf->b_u_curhead = cur_header_seq >0 ? uhp_table[cur_header_seq - 1] : 0;
+    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;
+    U_FREE_LINE(uhp_table);
+#ifdef U_DEBUG
+    u_check(TRUE);
+#endif
+    fclose(fp);
+    return;
+
+
+error:
+    if (fp != NULL)
+        fclose(fp);
+    if (line_ptr != NULL)
+        U_FREE_LINE(line_ptr);
+    if (uhp_table != NULL)
+    {
+        for (i = 0; i < num_head; i++)
+        {
+            if (uhp_table[i] != NULL)
+            {
+                uep = uhp_table[i]->uh_entry;
+                while (uep != NULL)
+                {
+                    nuep = uep->ue_next;
+                    u_freeentry(uep, uep->ue_size);
+                    uep = nuep;
+                }
+                U_FREE_LINE(uhp_table[i]);
+            }
+        }
+        U_FREE_LINE(uhp_table);
+    }
+    return;
+}
+
+    static void
+serialize_uep(FILE *fp, u_entry_T *uep)
+{
+    int i;
+    int uep_len;
+    int entry_lens[uep->ue_size];
+
+    /* 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++) {
+        put_bytes(fp, (long_u) entry_lens[i], 4);
+        fprintf(fp, "%s", uep->ue_array[i]);
+    }
+}
+
+    static void
+serialize_pos(pos_T pos, 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);
+#endif
+}
+
+    static void
+serialize_visualinfo(visualinfo_T info, FILE *fp)
+{
+    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);
+}
+
+static int lastmark = 0;
+
+    void
+u_wundo(char_u *name, buf_T *buf)
+{
+    u_header_T *uhp;
+    u_entry_T *uep;
+    char_u *undo_file;
+    int str_len, i, uep_len, mark;
+    long cur_seq;
+
+    if (name == NULL || STRLEN(name) == 0) {
+        undo_file = u_get_undofile(buf->b_ffname);
+    }
+    else {
+        undo_file = u_get_undofile(name);
+    }
+    if (undo_file == NULL) {
+        EMSG(_("Error: Can't open undo dir for write"));
+        return;
+    }
+    FILE *fp = fopen((char *)undo_file, "w");
+    if (fp == NULL) {
+        EMSG2(_("Error: Can't open undo file %s for write"), undo_file);
+        vim_free(undo_file);
+        return;
+    }
+    vim_free(undo_file);
+
+    /* Begin overall file information */
+    put_bytes(fp, (long_u) UF_MAGIC, 2);
+    put_bytes(fp, (long_u) UF_VERSION, 2);
+
+#ifdef FEAT_CRYPT
+    put_bytes(fp, (long_u) buf_cksum(buf), 4);
+#endif
+    put_bytes(fp, (long_u) buf->b_ml.ml_line_count, 4);
+
+    /* Begin undo data for U */
+    str_len = buf->b_u_line_ptr != NULL ? STRLEN(buf->b_u_line_ptr) : 0;
+    put_bytes(fp, (long_u) str_len, 4);
+    for (i = 0; i < str_len; i++) {
+        put_bytes(fp, (long_u) buf->b_u_line_ptr[i], 1);
+    }
+
+    put_bytes(fp, (long_u) buf->b_u_line_lnum, 4);
+    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_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_bytes(fp, (long_u) buf->b_u_seq_time, 4);
+
+    /* Iteratively serialize UHPs and their UEPs from the top down.  */
+    mark = ++lastmark;
+    uhp = buf->b_u_oldhead;
+    while (uhp != NULL)
+    {
+        /* Serialize current UHP if we haven't seen it */
+        if (uhp->uh_walk != mark)
+        {
+            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_bytes(fp, uhp->uh_seq, 4);
+            serialize_pos(uhp->uh_cursor, fp);
+#ifdef FEAT_VIRTUALEDIT
+            put_bytes(fp, (long_u) uhp->uh_cursor_vcol, 4);
+#endif
+            put_bytes(fp, (long_u) uhp->uh_flags, 2);
+            /* Assume NMARKS will stay the same. */
+            for (i = 0; i < NMARKS; ++i) {
+                serialize_pos(uhp->uh_namedm[i], fp);
+            }
+#ifdef FEAT_VISUAL
+            serialize_visualinfo(uhp->uh_visual, fp);
+#endif
+            put_bytes(fp, (long_u) uhp->uh_time, 4);
+
+            uep = uhp->uh_entry;
+            while (uep != NULL) {
+                serialize_uep(fp, uep);
+                uep = uep->ue_next;
+            }
+            /* Sentinel value: no more ueps */
+            uep_len = -1;
+            put_bytes(fp, (long_u) uep_len, 4);
+            uhp->uh_walk = mark;
+        }
+
+        /* Now walk through the tree - algorithm from undo_time*/
+        if (uhp->uh_prev != NULL && uhp->uh_prev->uh_walk != mark)
+            uhp = uhp->uh_prev;
+        else if (uhp->uh_alt_next != NULL && uhp->uh_alt_next->uh_walk != mark)
+            uhp = uhp->uh_alt_next;
+        else if (uhp->uh_next != NULL && uhp->uh_alt_prev == NULL
+                && uhp->uh_next->uh_walk != mark)
+            uhp = uhp->uh_next;
+        else if (uhp->uh_alt_prev != NULL)
+            uhp = uhp->uh_alt_prev;
+        else
+            uhp = uhp->uh_next;
+    }
+    fclose(fp);
+}
+
+
 /*
  * If 'cpoptions' contains 'u': Undo the previous undo or redo (vi compatible).
  * If 'cpoptions' does not contain 'u': Always undo.
@@ -757,7 +1266,6 @@
     u_undo_end(undo_undoes, FALSE);
 }
 
-static int lastmark = 0;
 
 /*
  * Undo or redo over the timeline.
@@ -1403,6 +1911,7 @@
     }
 }
 
+
 /*
  * ":undolist": List the leafs of the undo tree
  */
Index: buffer.c
===================================================================
--- buffer.c	(revision 1365)
+++ buffer.c	(working copy)
@@ -1411,6 +1411,8 @@
 #endif
 
 	open_buffer(FALSE, NULL);
+	if (curbuf->b_p_udf)
+	    u_rundo(NULL, 1);
     }
     else
     {
Index: main.c
===================================================================
--- main.c	(revision 1365)
+++ main.c	(working copy)
@@ -2481,6 +2481,11 @@
 #endif
 		set_buflisted(TRUE);
 		(void)open_buffer(FALSE, NULL); /* create memfile, read file */
+		if (curbuf->b_p_udf && !curbuf->b_p_ro)
+		{
+                    /* Try to restore the undofile, quietly. */
+		    u_rundo(NULL, 1);
+		}
 
 #if defined(HAS_SWAP_EXISTS_ACTION)
 		if (swap_exists_action == SEA_QUIT)
Index: os_riscos.h
===================================================================
--- os_riscos.h	(revision 1365)
+++ os_riscos.h	(working copy)
@@ -45,6 +45,10 @@
 # define DFLT_DIR	"<Wimp$ScrapDir>.,."	/* default for 'directory' */
 #endif
 
+#ifndef DFLT_UDIR
+# define DFLT_UDIR	".,<Wimp$ScrapDir>."	/* default for 'undodir' */
+#endif
+
 #ifndef DFLT_VDIR
 # define DFLT_VDIR	"Choices:Vim.view"	/* default for 'viewdir' */
 #endif
Index: misc2.c
===================================================================
--- misc2.c	(revision 1365)
+++ misc2.c	(working copy)
@@ -3753,6 +3753,31 @@
     return p1;
 }
 
+    int
+cksum_crc32 (int crc, char_u *buf, int len)
+{
+    int i;
+    make_crc_tab();
+    crc = ~crc;
+    for (i = 0; i < len; i++) 
+        crc = crc_32_tab[(crc ^ buf[i]) & 0xff] ^ (crc >> 8);
+    return ~crc;
+}
+
+    int
+buf_cksum (buf_T *buf)
+{
+    char_u *line;
+    int crc = 0;
+    int i = 0;
+    for (i = 0; i < buf->b_ml.ml_line_count; i++)
+    {
+        line = ml_get_buf(buf, i, FALSE);
+        crc = cksum_crc32(crc, line, STRLEN(line));
+    }
+    return crc;
+}
+
 #endif /* FEAT_CRYPT */
 
 /* TODO: make some #ifdef for this */
Index: spell.c
===================================================================
--- spell.c	(revision 1365)
+++ spell.c	(working copy)
@@ -854,9 +854,6 @@
 static void int_wordlist_spl __ARGS((char_u *fname));
 static void spell_load_cb __ARGS((char_u *fname, void *cookie));
 static slang_T *spell_load_file __ARGS((char_u *fname, char_u *lang, slang_T *old_lp, int silent));
-static int get2c __ARGS((FILE *fd));
-static int get3c __ARGS((FILE *fd));
-static int get4c __ARGS((FILE *fd));
 static time_t get8c __ARGS((FILE *fd));
 static char_u *read_cnt_string __ARGS((FILE *fd, int cnt_bytes, int *lenp));
 static char_u *read_string __ARGS((FILE *fd, int cnt));
@@ -2988,7 +2985,7 @@
 /*
  * Read 2 bytes from "fd" and turn them into an int, MSB first.
  */
-    static int
+    int
 get2c(fd)
     FILE	*fd;
 {
@@ -3002,7 +2999,7 @@
 /*
  * Read 3 bytes from "fd" and turn them into an int, MSB first.
  */
-    static int
+    int
 get3c(fd)
     FILE	*fd;
 {
@@ -3017,7 +3014,7 @@
 /*
  * Read 4 bytes from "fd" and turn them into an int, MSB first.
  */
-    static int
+    int
 get4c(fd)
     FILE	*fd;
 {
Index: os_unix.h
===================================================================
--- os_unix.h	(revision 1365)
+++ os_unix.h	(working copy)
@@ -362,6 +362,18 @@
 # endif
 #endif
 
+#ifndef DFLT_UDIR
+# ifdef OS2
+#  define DFLT_UDIR     ".,c:/tmp,~/tmp,~/"
+# else
+#  ifdef VMS
+#   define DFLT_UDIR    "./,sys$login:,tmp:"
+#  else
+#   define DFLT_UDIR    "$HOME/.vim/undo"    /* default for 'undodir' */
+#  endif
+# endif
+#endif
+
 #ifndef DFLT_DIR
 # ifdef OS2
 #  define DFLT_DIR      ".,~/tmp,c:/tmp,/tmp"
Index: os_dos.h
===================================================================
--- os_dos.h	(revision 1365)
+++ os_dos.h	(working copy)
@@ -106,6 +106,10 @@
 # define DFLT_BDIR	".,c:\\tmp,c:\\temp"	/* default for 'backupdir' */
 #endif
 
+#ifndef DFLT_UDIR
+# define DFLT_UDIR	".,c:\\tmp,c:\\temp"	/* default for 'undodir' */
+#endif
+
 #ifndef DFLT_VDIR
 # define DFLT_VDIR	"$VIM/vimfiles/view"	/* default for 'viewdir' */
 #endif
