serialize_visualinfo(bufinfo_T *bi, visualinfo_T *info)
{
    serialize_pos(bi, info->vi_start);
    serialize_pos(bi, info->vi_end);
    undo_write_bytes(bi, (long_u)info->vi_mode, 4);
    undo_write_bytes(bi, (long_u)info->vi_curswant, 4);
}

typedef struct {
    buf_T	*bi_buf;
    FILE	*bi_fp;
#ifdef FEAT_CRYPT
    cryptstate_T *bi_state;
    char_u	*bi_buffer; /* CRYPT_BUF_SIZE, NULL when not buffering */
    size_t	bi_used;    /* bytes written to/read from bi_buffer */
    size_t	bi_avail;   /* bytes available in bi_buffer */
#endif
} bufinfo_T;

static int undo_write(bufinfo_T *bi, char_u *ptr, size_t len);

static int undo_flush(bufinfo_T *bi);

static int fwrite_crypt(bufinfo_T *bi, char_u *ptr, size_t len);

static int undo_write_bytes(bufinfo_T *bi, long_u nr, int len);

static void put_header_ptr(bufinfo_T *bi, u_header_T *uhp);

static int undo_read_4c(bufinfo_T *bi);

static int undo_read_2c(bufinfo_T *bi);

static int undo_read_byte(bufinfo_T *bi);

static time_t undo_read_time(bufinfo_T *bi);

static int undo_read(bufinfo_T *bi, char_u *buffer, size_t size);

static char_u *read_string_decrypt(bufinfo_T *bi, int len);

static int serialize_header(bufinfo_T *bi, char_u *hash);

static int serialize_uhp(bufinfo_T *bi, u_header_T *uhp);

static u_header_T *unserialize_uhp(bufinfo_T *bi, char_u *file_name);

static int serialize_uep(bufinfo_T *bi, u_entry_T *uep);

static u_entry_T *unserialize_uep(bufinfo_T *bi, int *error, char_u *file_name);

static void serialize_pos(bufinfo_T *bi, pos_T pos);

static void unserialize_pos(bufinfo_T *bi, pos_T *pos);

static void serialize_visualinfo(bufinfo_T *bi, visualinfo_T *info);

static void unserialize_visualinfo(bufinfo_T *bi, visualinfo_T *info);

#define U_ALLOC_LINE(size) lalloc((long_u)(size), FALSE)


u_undoredo(int undo)
{
    char_u	**newarray = NULL;
    linenr_T	oldsize;
    linenr_T	newsize;
    linenr_T	top, bot;
    linenr_T	lnum;
    linenr_T	newlnum = MAXLNUM;
    long	i;
    u_entry_T	*uep, *nuep;
    u_entry_T	*newlist = NULL;
    int		old_flags;
    int		new_flags;
    pos_T	namedm[NMARKS];
    visualinfo_T visualinfo;
    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
    old_flags = curhead->uh_flags;
    new_flags = (curbuf->b_changed ? UH_CHANGED : 0) +
	       ((curbuf->b_ml.ml_flags & ML_EMPTY) ? UH_EMPTYBUF : 0);
    setpcmark();

    /*
     * save marks before undo/redo
     */
    mch_memmove(namedm, curbuf->b_namedm, sizeof(pos_T) * NMARKS);
    visualinfo = curbuf->b_visual;
    curbuf->b_op_start.lnum = curbuf->b_ml.ml_line_count;
    curbuf->b_op_start.col = 0;
    curbuf->b_op_end.lnum = 0;
    curbuf->b_op_end.col = 0;

    for (uep = curhead->uh_entry; uep != NULL; uep = nuep)
    {
	top = uep->ue_top;
	bot = uep->ue_bot;
	if (bot == 0)
	    bot = curbuf->b_ml.ml_line_count + 1;
	if (top > curbuf->b_ml.ml_line_count || top >= bot
				      || bot > curbuf->b_ml.ml_line_count + 1)
	{
#ifdef FEAT_AUTOCMD
	    unblock_autocmds();
#endif
	    IEMSG(_("E438: u_undo: line numbers wrong"));
	    changed();		/* don't want UNCHANGED now */
	    return;
	}

	oldsize = bot - top - 1;    /* number of lines before undo */
	newsize = uep->ue_size;	    /* number of lines after undo */

	if (top < newlnum)
	{
	    /* If the saved cursor is somewhere in this undo block, move it to
	     * the remembered position.  Makes "gwap" put the cursor back
	     * where it was. */
	    lnum = curhead->uh_cursor.lnum;
	    if (lnum >= top && lnum <= top + newsize + 1)
	    {
		curwin->w_cursor = curhead->uh_cursor;
		newlnum = curwin->w_cursor.lnum - 1;
	    }
	    else
	    {
		/* Use the first line that actually changed.  Avoids that
		 * undoing auto-formatting puts the cursor in the previous
		 * line. */
		for (i = 0; i < newsize && i < oldsize; ++i)
		    if (STRCMP(uep->ue_array[i], ml_get(top + 1 + i)) != 0)
			break;
		if (i == newsize && newlnum == MAXLNUM && uep->ue_next == NULL)
		{
		    newlnum = top;
		    curwin->w_cursor.lnum = newlnum + 1;
		}
		else if (i < newsize)
		{
		    newlnum = top + i;
		    curwin->w_cursor.lnum = newlnum + 1;
		}
	    }
	}

	empty_buffer = FALSE;

	/* delete the lines between top and bot and save them in newarray */
	if (oldsize > 0)
	{
	    if ((newarray = (char_u **)U_ALLOC_LINE(
					 sizeof(char_u *) * oldsize)) == NULL)
	    {
		do_outofmem_msg((long_u)(sizeof(char_u *) * oldsize));
		/*
		 * We have messed up the entry list, repair is impossible.
		 * we have to free the rest of the list.
		 */
		while (uep != NULL)
		{
		    nuep = uep->ue_next;
		    u_freeentry(uep, uep->ue_size);
		    uep = nuep;
		}
		break;
	    }
	    /* delete backwards, it goes faster in most cases */
	    for (lnum = bot - 1, i = oldsize; --i >= 0; --lnum)
	    {
		/* what can we do when we run out of memory? */
		if ((newarray[i] = u_save_line(lnum)) == NULL)
		    do_outofmem_msg((long_u)0);
		/* remember we deleted the last line in the buffer, and a
		 * dummy empty line will be inserted */
		if (curbuf->b_ml.ml_line_count == 1)
		    empty_buffer = TRUE;
		ml_delete(lnum, FALSE);
	    }
	}
	else
	    newarray = NULL;

	/* insert the lines in u_array between top and bot */
	if (newsize)
	{
	    for (lnum = top, i = 0; i < newsize; ++i, ++lnum)
	    {
		/*
		 * If the file is empty, there is an empty line 1 that we
		 * should get rid of, by replacing it with the new line
		 */
		if (empty_buffer && lnum == 0)
		    ml_replace((linenr_T)1, uep->ue_array[i], TRUE);
		else
		    ml_append(lnum, uep->ue_array[i], (colnr_T)0, FALSE);
		vim_free(uep->ue_array[i]);
	    }
	    vim_free((char_u *)uep->ue_array);
	}

	/* adjust marks */
	if (oldsize != newsize)
	{
	    mark_adjust(top + 1, top + oldsize, (long)MAXLNUM,
					       (long)newsize - (long)oldsize);
	    if (curbuf->b_op_start.lnum > top + oldsize)
		curbuf->b_op_start.lnum += newsize - oldsize;
	    if (curbuf->b_op_end.lnum > top + oldsize)
		curbuf->b_op_end.lnum += newsize - oldsize;
	}

	changed_lines(top + 1, 0, bot, newsize - oldsize);

	/* set '[ and '] mark */
	if (top + 1 < curbuf->b_op_start.lnum)
	    curbuf->b_op_start.lnum = top + 1;
	if (newsize == 0 && top + 1 > curbuf->b_op_end.lnum)
	    curbuf->b_op_end.lnum = top + 1;
	else if (top + newsize > curbuf->b_op_end.lnum)
	    curbuf->b_op_end.lnum = top + newsize;

	u_newcount += newsize;
	u_oldcount += oldsize;
	uep->ue_size = oldsize;
	uep->ue_array = newarray;
	uep->ue_bot = top + newsize + 1;

	/*
	 * insert this entry in front of the new entry list
	 */
	nuep = uep->ue_next;
	uep->ue_next = newlist;
	newlist = uep;
    }

    curhead->uh_entry = newlist;
    curhead->uh_flags = new_flags;
    if ((old_flags & UH_EMPTYBUF) && bufempty())
	curbuf->b_ml.ml_flags |= ML_EMPTY;
    if (old_flags & UH_CHANGED)
	changed();
    else
#ifdef FEAT_NETBEANS_INTG
	/* per netbeans undo rules, keep it as modified */
	if (!isNetbeansModified(curbuf))
#endif
	unchanged(curbuf, FALSE);

    /*
     * restore marks from before undo/redo
     */
    for (i = 0; i < NMARKS; ++i)
    {
	if (curhead->uh_namedm[i].lnum != 0)
	    curbuf->b_namedm[i] = curhead->uh_namedm[i];
	if (namedm[i].lnum != 0)
	    curhead->uh_namedm[i] = namedm[i];
	else
	    curhead->uh_namedm[i].lnum = 0;
    }
    if (curhead->uh_visual.vi_start.lnum != 0)
    {
	curbuf->b_visual = curhead->uh_visual;
	curhead->uh_visual = visualinfo;
    }

    /*
     * If the cursor is only off by one line, put it at the same position as
     * before starting the change (for the "o" command).
     * Otherwise the cursor should go to the first undone line.
     */
    if (curhead->uh_cursor.lnum + 1 == curwin->w_cursor.lnum
						 && curwin->w_cursor.lnum > 1)
	--curwin->w_cursor.lnum;
    if (curwin->w_cursor.lnum <= curbuf->b_ml.ml_line_count)
    {
	if (curhead->uh_cursor.lnum == curwin->w_cursor.lnum)
	{
	    curwin->w_cursor.col = curhead->uh_cursor.col;
#ifdef FEAT_VIRTUALEDIT
	    if (virtual_active() && curhead->uh_cursor_vcol >= 0)
		coladvance((colnr_T)curhead->uh_cursor_vcol);
	    else
		curwin->w_cursor.coladd = 0;
#endif
	}
	else
	    beginline(BL_SOL | BL_FIX);
    }
    else
    {
	/* We get here with the current cursor line being past the end (eg
	 * after adding lines at the end of the file, and then undoing it).
	 * check_cursor() will move the cursor to the last line.  Move it to
	 * the first column here. */
	curwin->w_cursor.col = 0;
#ifdef FEAT_VIRTUALEDIT
	curwin->w_cursor.coladd = 0;
#endif
    }

    /* Make sure the cursor is on an existing line and column. */
    check_cursor();

    /* Remember where we are for "g-" and ":earlier 10s". */
    curbuf->b_u_seq_cur = curhead->uh_seq;
    if (undo)
	/* We are below the previous undo.  However, to make ":earlier 1s"
	 * work we compute this as being just above the just undone change. */
	--curbuf->b_u_seq_cur;

    /* Remember where we are for ":earlier 1f" and ":later 1f". */
    if (curhead->uh_save_nr != 0)
    {
	if (undo)
	    curbuf->b_u_save_nr_cur = curhead->uh_save_nr - 1;
	else
	    curbuf->b_u_save_nr_cur = curhead->uh_save_nr;
    }

    /* The timestamp can be the same for multiple changes, just use the one of
     * the undone/redone change. */
    curbuf->b_u_time_cur = curhead->uh_time;

#ifdef FEAT_AUTOCMD
    unblock_autocmds();
#endif
#ifdef U_DEBUG
    u_check(FALSE);
#endif
}

patch 8.0.0378: possible overflow when reading corrupted undo file

Problem:    Another possible overflow when reading corrupted undo file.
Solution:   Check if allocated size is not too big. (King)

#define UH_MAGIC 0x18dade	/* value for uh_magic when in use */

#define UE_MAGIC 0xabc123	/* value for ue_magic when in use */

typedef struct {
    buf_T	*bi_buf;
    FILE	*bi_fp;
#ifdef FEAT_CRYPT
    cryptstate_T *bi_state;
    char_u	*bi_buffer; /* CRYPT_BUF_SIZE, NULL when not buffering */
    size_t	bi_used;    /* bytes written to/read from bi_buffer */
    size_t	bi_avail;   /* bytes available in bi_buffer */
#endif
} bufinfo_T;

static long get_undolevel(void);

static void u_unch_branch(u_header_T *uhp);

static u_entry_T *u_get_headentry(void);

static void u_doit(int count);

static void u_undoredo(int undo);

static void u_undo_end(int did_undo, int absolute);

static void u_add_time(char_u *buf, size_t buflen, time_t tt);

static void u_freeheader(buf_T *buf, u_header_T *uhp, u_header_T **uhpp);

static void u_freebranch(buf_T *buf, u_header_T *uhp, u_header_T **uhpp);

static void u_freeentries(buf_T *buf, u_header_T *uhp, u_header_T **uhpp);

static void u_freeentry(u_entry_T *, long);

static void corruption_error(char *mesg, char_u *file_name);

static void u_free_uhp(u_header_T *uhp);

static int undo_write(bufinfo_T *bi, char_u *ptr, size_t len);

static int undo_flush(bufinfo_T *bi);

static int fwrite_crypt(bufinfo_T *bi, char_u *ptr, size_t len);

static int undo_write_bytes(bufinfo_T *bi, long_u nr, int len);

static void put_header_ptr(bufinfo_T *bi, u_header_T *uhp);

static int undo_read_4c(bufinfo_T *bi);

static int undo_read_2c(bufinfo_T *bi);

static int undo_read_byte(bufinfo_T *bi);

static int undo_read(bufinfo_T *bi, char_u *buffer, size_t size);

static char_u *read_string_decrypt(bufinfo_T *bi, int len);

static int serialize_header(bufinfo_T *bi, char_u *hash);

static int serialize_uhp(bufinfo_T *bi, u_header_T *uhp);

static u_header_T *unserialize_uhp(bufinfo_T *bi, char_u *file_name);

static int serialize_uep(bufinfo_T *bi, u_entry_T *uep);

static u_entry_T *unserialize_uep(bufinfo_T *bi, int *error, char_u *file_name);

static void serialize_pos(bufinfo_T *bi, pos_T pos);

static void unserialize_pos(bufinfo_T *bi, pos_T *pos);

static void serialize_visualinfo(bufinfo_T *bi, visualinfo_T *info);

static void unserialize_visualinfo(bufinfo_T *bi, visualinfo_T *info);

#define U_ALLOC_LINE(size) lalloc((long_u)(size), FALSE)

static char_u *u_save_line(linenr_T);

static long	u_newcount, u_oldcount;

static int	undo_undoes = FALSE;

static int	lastmark = 0;

static int seen_b_u_curhead;

static int seen_b_u_newhead;

static int header_count;

# 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		2	/* 2-byte undofile version number */


u_freebranch(
    buf_T	    *buf,
    u_header_T	    *uhp,
    u_header_T	    **uhpp)	/* if not NULL reset when freeing this header */
{
    u_header_T	    *tofree, *next;

    /* If this is the top branch we may need to use u_freeheader() to update
     * all the pointers. */
    if (uhp == buf->b_u_oldhead)
    {
	while (buf->b_u_oldhead != NULL)
	    u_freeheader(buf, buf->b_u_oldhead, uhpp);
	return;
    }

    if (uhp->uh_alt_prev.ptr != NULL)
	uhp->uh_alt_prev.ptr->uh_alt_next.ptr = NULL;

    next = uhp;
    while (next != NULL)
    {
	tofree = next;
	if (tofree->uh_alt_next.ptr != NULL)
	    u_freebranch(buf, tofree->uh_alt_next.ptr, uhpp);   /* recursive */
	next = tofree->uh_prev.ptr;
	u_freeentries(buf, tofree, uhpp);
    }
}

#define UH_MAGIC 0x18dade	/* value for uh_magic when in use */

#define UE_MAGIC 0xabc123	/* value for ue_magic when in use */

typedef struct {
    buf_T	*bi_buf;
    FILE	*bi_fp;
#ifdef FEAT_CRYPT
    cryptstate_T *bi_state;
    char_u	*bi_buffer; /* CRYPT_BUF_SIZE, NULL when not buffering */
    size_t	bi_used;    /* bytes written to/read from bi_buffer */
    size_t	bi_avail;   /* bytes available in bi_buffer */
#endif
} bufinfo_T;

static void u_unch_branch(u_header_T *uhp);

static void u_add_time(char_u *buf, size_t buflen, time_t tt);

static void u_freeheader(buf_T *buf, u_header_T *uhp, u_header_T **uhpp);

static void u_freebranch(buf_T *buf, u_header_T *uhp, u_header_T **uhpp);

static void u_freeentries(buf_T *buf, u_header_T *uhp, u_header_T **uhpp);

static void u_free_uhp(u_header_T *uhp);

static int undo_write(bufinfo_T *bi, char_u *ptr, size_t len);

static int fwrite_crypt(bufinfo_T *bi, char_u *ptr, size_t len);

static void put_header_ptr(bufinfo_T *bi, u_header_T *uhp);

static int serialize_uhp(bufinfo_T *bi, u_header_T *uhp);

static u_header_T *unserialize_uhp(bufinfo_T *bi, char_u *file_name);

# define UF_HEADER_MAGIC	0x5fd0	/* magic at start of header */

# define UF_HEADER_END_MAGIC	0xe7aa	/* magic after last header */


corruption_error(char *mesg, char_u *file_name)
{
    EMSG3(_("E825: Corrupted undo file (%s): %s"), mesg, file_name);
}

typedef struct {
    buf_T	*bi_buf;
    FILE	*bi_fp;
#ifdef FEAT_CRYPT
    cryptstate_T *bi_state;
    char_u	*bi_buffer; /* CRYPT_BUF_SIZE, NULL when not buffering */
    size_t	bi_used;    /* bytes written to/read from bi_buffer */
    size_t	bi_avail;   /* bytes available in bi_buffer */
#endif
} bufinfo_T;

static void u_undoredo(int undo);

static void u_add_time(char_u *buf, size_t buflen, time_t tt);

static void corruption_error(char *mesg, char_u *file_name);

static int undo_write(bufinfo_T *bi, char_u *ptr, size_t len);

static int fwrite_crypt(bufinfo_T *bi, char_u *ptr, size_t len);

static int undo_read(bufinfo_T *bi, char_u *buffer, size_t size);

static char_u *read_string_decrypt(bufinfo_T *bi, int len);

static int serialize_header(bufinfo_T *bi, char_u *hash);

static u_header_T *unserialize_uhp(bufinfo_T *bi, char_u *file_name);

static u_entry_T *unserialize_uep(bufinfo_T *bi, int *error, char_u *file_name);

static char_u *u_save_line(linenr_T);

static char_u e_not_open[] = N_("E828: Cannot open undo file for writing: %s");


u_save(linenr_T top, linenr_T bot)
{
    if (undo_off)
	return OK;

    if (top > curbuf->b_ml.ml_line_count
	    || top >= bot
	    || bot > curbuf->b_ml.ml_line_count + 1)
	return FALSE;	/* rely on caller to do error messages */

    if (top + 2 == bot)
	u_saveline((linenr_T)(top + 1));

    return (u_savecommon(top, bot, (linenr_T)0, FALSE));
}

static u_entry_T *unserialize_uep(bufinfo_T *bi, int *error, char_u *file_name);

#define U_ALLOC_LINE(size) lalloc((long_u)(size), FALSE)

static char_u *u_save_line(linenr_T);

static int	undo_undoes = FALSE;


u_savedel(linenr_T lnum, long nlines)
{
    if (undo_off)
	return OK;

    return (u_savecommon(lnum - 1, lnum + nlines,
		     nlines == curbuf->b_ml.ml_line_count ? 2 : lnum, FALSE));
}

static long get_undolevel(void);

static void u_freeentry(u_entry_T *, long);

#define U_ALLOC_LINE(size) lalloc((long_u)(size), FALSE)

static char_u *u_save_line(linenr_T);

static long	u_newcount, u_oldcount;

static int	undo_undoes = FALSE;


u_unch_branch(u_header_T *uhp)
{
    u_header_T	*uh;

    for (uh = uhp; uh != NULL; uh = uh->uh_prev.ptr)
    {
	uh->uh_flags |= UH_CHANGED;
	if (uh->uh_alt_next.ptr != NULL)
	    u_unch_branch(uh->uh_alt_next.ptr);	    /* recursive */
    }
}

#define UH_MAGIC 0x18dade	/* value for uh_magic when in use */

#define UE_MAGIC 0xabc123	/* value for ue_magic when in use */

typedef struct {
    buf_T	*bi_buf;
    FILE	*bi_fp;
#ifdef FEAT_CRYPT
    cryptstate_T *bi_state;
    char_u	*bi_buffer; /* CRYPT_BUF_SIZE, NULL when not buffering */
    size_t	bi_used;    /* bytes written to/read from bi_buffer */
    size_t	bi_avail;   /* bytes available in bi_buffer */
#endif
} bufinfo_T;

static void u_unch_branch(u_header_T *uhp);

static void u_freeheader(buf_T *buf, u_header_T *uhp, u_header_T **uhpp);

static void u_freebranch(buf_T *buf, u_header_T *uhp, u_header_T **uhpp);

static void u_freeentries(buf_T *buf, u_header_T *uhp, u_header_T **uhpp);

static void u_free_uhp(u_header_T *uhp);

static int undo_write(bufinfo_T *bi, char_u *ptr, size_t len);

static int fwrite_crypt(bufinfo_T *bi, char_u *ptr, size_t len);

static void put_header_ptr(bufinfo_T *bi, u_header_T *uhp);

static int serialize_uhp(bufinfo_T *bi, u_header_T *uhp);

static u_header_T *unserialize_uhp(bufinfo_T *bi, char_u *file_name);

static char_u e_not_open[] = N_("E828: Cannot open undo file for writing: %s");


u_savesub(linenr_T lnum)
{
    if (undo_off)
	return OK;

    return (u_savecommon(lnum - 1, lnum + 1, lnum + 1, FALSE));
}

#define U_ALLOC_LINE(size) lalloc((long_u)(size), FALSE)

static char_u *u_save_line(linenr_T);

static int	undo_undoes = FALSE;


u_eval_tree(u_header_T *first_uhp, list_T *list)
{
    u_header_T  *uhp = first_uhp;
    dict_T	*dict;

    while (uhp != NULL)
    {
	dict = dict_alloc();
	if (dict == NULL)
	    return;
	dict_add_nr_str(dict, "seq", uhp->uh_seq, NULL);
	dict_add_nr_str(dict, "time", (long)uhp->uh_time, NULL);
	if (uhp == curbuf->b_u_newhead)
	    dict_add_nr_str(dict, "newhead", 1, NULL);
	if (uhp == curbuf->b_u_curhead)
	    dict_add_nr_str(dict, "curhead", 1, NULL);
	if (uhp->uh_save_nr > 0)
	    dict_add_nr_str(dict, "save", uhp->uh_save_nr, NULL);

	if (uhp->uh_alt_next.ptr != NULL)
	{
	    list_T	*alt_list = list_alloc();

	    if (alt_list != NULL)
	    {
		/* Recursive call to add alternate undo tree. */
		u_eval_tree(uhp->uh_alt_next.ptr, alt_list);
		dict_add_list(dict, "alt", alt_list);
	    }
	}

	list_append_dict(list, dict);
	uhp = uhp->uh_prev.ptr;
    }
}

typedef struct {
    buf_T	*bi_buf;
    FILE	*bi_fp;
#ifdef FEAT_CRYPT
    cryptstate_T *bi_state;
    char_u	*bi_buffer; /* CRYPT_BUF_SIZE, NULL when not buffering */
    size_t	bi_used;    /* bytes written to/read from bi_buffer */
    size_t	bi_avail;   /* bytes available in bi_buffer */
#endif
} bufinfo_T;

static long get_undolevel(void);

static void u_unch_branch(u_header_T *uhp);

static void u_undoredo(int undo);

static void u_freeheader(buf_T *buf, u_header_T *uhp, u_header_T **uhpp);

static void u_freebranch(buf_T *buf, u_header_T *uhp, u_header_T **uhpp);

static void u_freeentries(buf_T *buf, u_header_T *uhp, u_header_T **uhpp);

static void u_freeentry(u_entry_T *, long);

static void u_free_uhp(u_header_T *uhp);

static int undo_write(bufinfo_T *bi, char_u *ptr, size_t len);

static int fwrite_crypt(bufinfo_T *bi, char_u *ptr, size_t len);

static void put_header_ptr(bufinfo_T *bi, u_header_T *uhp);

static int serialize_uhp(bufinfo_T *bi, u_header_T *uhp);

static u_header_T *unserialize_uhp(bufinfo_T *bi, char_u *file_name);

static long	u_newcount, u_oldcount;

static char_u e_not_open[] = N_("E828: Cannot open undo file for writing: %s");


u_write_undo(
    char_u	*name,
    int		forceit,
    buf_T	*buf,
    char_u	*hash)
{
    u_header_T	*uhp;
    char_u	*file_name;
    int		mark;
#ifdef U_DEBUG
    int		headers_written = 0;
#endif
    int		fd;
    FILE	*fp = NULL;
    int		perm;
    int		write_ok = FALSE;
#ifdef UNIX
    int		st_old_valid = FALSE;
    stat_T	st_old;
    stat_T	st_new;
#endif
    bufinfo_T	bi;

    vim_memset(&bi, 0, sizeof(bi));

    if (name == NULL)
    {
	file_name = u_get_undo_file_name(buf->b_ffname, FALSE);
	if (file_name == NULL)
	{
	    if (p_verbose > 0)
	    {
		verbose_enter();
		smsg((char_u *)
		   _("Cannot write undo file in any directory in 'undodir'"));
		verbose_leave();
	    }
	    return;
	}
    }
    else
	file_name = name;

    /*
     * 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)
	{
	    perm = st_old.st_mode;
	    st_old_valid = TRUE;
	}
#else
	perm = mch_getperm(buf->b_ffname);
	if (perm < 0)
	    perm = 0600;
#endif
    }

    /* strip any s-bit and executable bit */
    perm = perm & 0666;

    /* 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)
	{
	    /* Check we can read it and it's an undo file. */
	    fd = mch_open((char *)file_name, O_RDONLY|O_EXTRA, 0);
	    if (fd < 0)
	    {
		if (name != NULL || p_verbose > 0)
		{
		    if (name == NULL)
			verbose_enter();
		    smsg((char_u *)
		      _("Will not overwrite with undo file, cannot read: %s"),
								   file_name);
		    if (name == NULL)
			verbose_leave();
		}
		goto theend;
	    }
	    else
	    {
		char_u	mbuf[UF_START_MAGIC_LEN];
		int	len;

		len = read_eintr(fd, mbuf, UF_START_MAGIC_LEN);
		close(fd);
		if (len < UF_START_MAGIC_LEN
		      || memcmp(mbuf, UF_START_MAGIC, UF_START_MAGIC_LEN) != 0)
		{
		    if (name != NULL || p_verbose > 0)
		    {
			if (name == NULL)
			    verbose_enter();
			smsg((char_u *)
			_("Will not overwrite, this is not an undo file: %s"),
								   file_name);
			if (name == NULL)
			    verbose_leave();
		    }
		    goto theend;
		}
	    }
	}
	mch_remove(file_name);
    }

    /* If there is no undo information at all, quit here after deleting any
     * existing undo file. */
    if (buf->b_u_numhead == 0 && buf->b_u_line_ptr == NULL)
    {
	if (p_verbose > 0)
	    verb_msg((char_u *)_("Skipping undo file write, nothing to undo"));
	goto theend;
    }

    fd = mch_open((char *)file_name,
			    O_CREAT|O_EXTRA|O_WRONLY|O_EXCL|O_NOFOLLOW, perm);
    if (fd < 0)
    {
	EMSG2(_(e_not_open), file_name);
	goto theend;
    }
    (void)mch_setperm(file_name, perm);
    if (p_verbose > 0)
    {
	verbose_enter();
	smsg((char_u *)_("Writing undo file: %s"), file_name);
	verbose_leave();
    }

#ifdef U_DEBUG
    /* Check there is no problem in undo info before writing. */
    u_check(FALSE);
#endif

#ifdef UNIX
    /*
     * Try to set the group of the undo file same as the original file. If
     * this fails, set the protection bits for the group same as the
     * protection bits for others.
     */
    if (st_old_valid
	    && mch_stat((char *)file_name, &st_new) >= 0
	    && st_new.st_gid != st_old.st_gid
# ifdef HAVE_FCHOWN  /* sequent-ptx lacks fchown() */
	    && fchown(fd, (uid_t)-1, st_old.st_gid) != 0
# endif
       )
	mch_setperm(file_name, (perm & 0707) | ((perm & 07) << 3));
# if defined(HAVE_SELINUX) || defined(HAVE_SMACK)
    if (buf->b_ffname != NULL)
	mch_copy_sec(buf->b_ffname, file_name);
# endif
#endif

    fp = fdopen(fd, "w");
    if (fp == NULL)
    {
	EMSG2(_(e_not_open), file_name);
	close(fd);
	mch_remove(file_name);
	goto theend;
    }

    /* Undo must be synced. */
    u_sync(TRUE);

    /*
     * Write the header.  Initializes encryption, if enabled.
     */
    bi.bi_buf = buf;
    bi.bi_fp = fp;
    if (serialize_header(&bi, hash) == FAIL)
	goto write_error;

    /*
     * 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)
	{
	    uhp->uh_walk = mark;
#ifdef U_DEBUG
	    ++headers_written;
#endif
	    if (serialize_uhp(&bi, uhp) == FAIL)
		goto write_error;
	}

	/* Now walk through the tree - algorithm from undo_time(). */
	if (uhp->uh_prev.ptr != NULL && uhp->uh_prev.ptr->uh_walk != mark)
	    uhp = uhp->uh_prev.ptr;
	else if (uhp->uh_alt_next.ptr != NULL
				     && uhp->uh_alt_next.ptr->uh_walk != mark)
	    uhp = uhp->uh_alt_next.ptr;
	else if (uhp->uh_next.ptr != NULL && uhp->uh_alt_prev.ptr == NULL
					 && uhp->uh_next.ptr->uh_walk != mark)
	    uhp = uhp->uh_next.ptr;
	else if (uhp->uh_alt_prev.ptr != NULL)
	    uhp = uhp->uh_alt_prev.ptr;
	else
	    uhp = uhp->uh_next.ptr;
    }

    if (undo_write_bytes(&bi, (long_u)UF_HEADER_END_MAGIC, 2) == OK)
	write_ok = TRUE;
#ifdef U_DEBUG
    if (headers_written != buf->b_u_numhead)
    {
	EMSGN("Written %ld headers, ...", headers_written);
	EMSGN("... but numhead is %ld", buf->b_u_numhead);
    }
#endif

#ifdef FEAT_CRYPT
    if (bi.bi_state != NULL && undo_flush(&bi) == FAIL)
	write_ok = FALSE;
#endif

write_error:
    fclose(fp);
    if (!write_ok)
	EMSG2(_("E829: write error in undo file: %s"), file_name);

#if defined(MACOS_CLASSIC) || defined(WIN3264)
    /* Copy file attributes; for systems where this can only be done after
     * closing the file. */
    if (buf->b_ffname != NULL)
	(void)mch_copy_file_attribute(buf->b_ffname, file_name);
#endif
#ifdef HAVE_ACL
    if (buf->b_ffname != NULL)
    {
	vim_acl_T	    acl;

	/* For systems that support ACL: get the ACL from the original file. */
	acl = mch_get_acl(buf->b_ffname);
	mch_set_acl(file_name, acl);
	mch_free_acl(acl);
    }
#endif

theend:
#ifdef FEAT_CRYPT
    if (bi.bi_state != NULL)
	crypt_free_state(bi.bi_state);
    vim_free(bi.bi_buffer);
#endif
    if (file_name != name)
	vim_free(file_name);
}

#define UH_MAGIC 0x18dade	/* value for uh_magic when in use */

#define UE_MAGIC 0xabc123	/* value for ue_magic when in use */

typedef struct {
    buf_T	*bi_buf;
    FILE	*bi_fp;
#ifdef FEAT_CRYPT
    cryptstate_T *bi_state;
    char_u	*bi_buffer; /* CRYPT_BUF_SIZE, NULL when not buffering */
    size_t	bi_used;    /* bytes written to/read from bi_buffer */
    size_t	bi_avail;   /* bytes available in bi_buffer */
#endif
} bufinfo_T;

static long get_undolevel(void);

static void u_unch_branch(u_header_T *uhp);

static u_entry_T *u_get_headentry(void);

static void u_getbot(void);

static void u_doit(int count);

static void u_undoredo(int undo);

static void u_undo_end(int did_undo, int absolute);

static void u_add_time(char_u *buf, size_t buflen, time_t tt);

static void u_freeheader(buf_T *buf, u_header_T *uhp, u_header_T **uhpp);

static void u_freebranch(buf_T *buf, u_header_T *uhp, u_header_T **uhpp);

static void u_freeentries(buf_T *buf, u_header_T *uhp, u_header_T **uhpp);

static void u_freeentry(u_entry_T *, long);

static void corruption_error(char *mesg, char_u *file_name);

static void u_free_uhp(u_header_T *uhp);

static int undo_write(bufinfo_T *bi, char_u *ptr, size_t len);

static int undo_flush(bufinfo_T *bi);

static int fwrite_crypt(bufinfo_T *bi, char_u *ptr, size_t len);

static int undo_write_bytes(bufinfo_T *bi, long_u nr, int len);

static void put_header_ptr(bufinfo_T *bi, u_header_T *uhp);

static int undo_read_4c(bufinfo_T *bi);

static int undo_read_2c(bufinfo_T *bi);

static int undo_read_byte(bufinfo_T *bi);

static time_t undo_read_time(bufinfo_T *bi);

static int undo_read(bufinfo_T *bi, char_u *buffer, size_t size);

static char_u *read_string_decrypt(bufinfo_T *bi, int len);

static int serialize_header(bufinfo_T *bi, char_u *hash);

static int serialize_uhp(bufinfo_T *bi, u_header_T *uhp);

static u_header_T *unserialize_uhp(bufinfo_T *bi, char_u *file_name);

static int serialize_uep(bufinfo_T *bi, u_entry_T *uep);

static u_entry_T *unserialize_uep(bufinfo_T *bi, int *error, char_u *file_name);

static void serialize_pos(bufinfo_T *bi, pos_T pos);

static void unserialize_pos(bufinfo_T *bi, pos_T *pos);

static void serialize_visualinfo(bufinfo_T *bi, visualinfo_T *info);

static void unserialize_visualinfo(bufinfo_T *bi, visualinfo_T *info);

#define U_ALLOC_LINE(size) lalloc((long_u)(size), FALSE)

static char_u *u_save_line(linenr_T);

static int	undo_undoes = FALSE;

static int	lastmark = 0;

static int seen_b_u_curhead;

static int seen_b_u_newhead;

static int header_count;

# 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 */


u_read_undo(char_u *name, char_u *hash, char_u *orig_name)
{
    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;
    long	num_head = 0;
    long	old_header_seq, new_header_seq, cur_header_seq;
    long	seq_last, seq_cur;
    long	last_save_nr = 0;
    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_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
#ifdef UNIX
    stat_T	st_orig;
    stat_T	st_undo;
#endif
    bufinfo_T	bi;

    vim_memset(&bi, 0, sizeof(bi));
    if (name == NULL)
    {
	file_name = u_get_undo_file_name(curbuf->b_ffname, TRUE);
	if (file_name == NULL)
	    return;

#ifdef UNIX
	/* For safety we only read an undo file if the owner is equal to the
	 * owner of the text file or equal to the current user. */
	if (mch_stat((char *)orig_name, &st_orig) >= 0
		&& mch_stat((char *)file_name, &st_undo) >= 0
		&& st_orig.st_uid != st_undo.st_uid
		&& st_undo.st_uid != getuid())
	{
	    if (p_verbose > 0)
	    {
		verbose_enter();
		smsg((char_u *)_("Not reading undo file, owner differs: %s"),
								   file_name);
		verbose_leave();
	    }
	    return;
	}
#endif
    }
    else
	file_name = name;

    if (p_verbose > 0)
    {
	verbose_enter();
	smsg((char_u *)_("Reading undo file: %s"), file_name);
	verbose_leave();
    }

    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;
    }
    bi.bi_buf = curbuf;
    bi.bi_fp = fp;

    /*
     * 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_CRYPT)
    {
#ifdef FEAT_CRYPT
	if (*curbuf->b_p_key == NUL)
	{
	    EMSG2(_("E832: Non-encrypted file has encrypted undo file: %s"),
								   file_name);
	    goto error;
	}
	bi.bi_state = crypt_create_from_file(fp, curbuf->b_p_key);
	if (bi.bi_state == NULL)
	{
	    EMSG2(_("E826: Undo file decryption failed: %s"), file_name);
	    goto error;
	}
	if (crypt_whole_undofile(bi.bi_state->method_nr))
	{
	    bi.bi_buffer = alloc(CRYPT_BUF_SIZE);
	    if (bi.bi_buffer == NULL)
	    {
		crypt_free_state(bi.bi_state);
		bi.bi_state = NULL;
		goto error;
	    }
	    bi.bi_avail = 0;
	    bi.bi_used = 0;
	}
#else
	EMSG2(_("E827: Undo file is encrypted: %s"), file_name);
	goto error;
#endif
    }
    else if (version != UF_VERSION)
    {
	EMSG2(_("E824: Incompatible undo file: %s"), file_name);
	goto error;
    }

    if (undo_read(&bi, read_hash, (size_t)UNDO_HASH_SIZE) == FAIL)
    {
	corruption_error("hash", file_name);
	goto error;
    }
    line_count = (linenr_T)undo_read_4c(&bi);
    if (memcmp(hash, read_hash, UNDO_HASH_SIZE) != 0
				  || line_count != curbuf->b_ml.ml_line_count)
    {
	if (p_verbose > 0 || name != NULL)
	{
	    if (name == NULL)
		verbose_enter();
	    give_warning((char_u *)
		      _("File contents changed, cannot use undo info"), TRUE);
	    if (name == NULL)
		verbose_leave();
	}
	goto error;
    }

    /* Read undo data for "U" command. */
    str_len = undo_read_4c(&bi);
    if (str_len < 0)
	goto error;
    if (str_len > 0)
	line_ptr = read_string_decrypt(&bi, str_len);
    line_lnum = (linenr_T)undo_read_4c(&bi);
    line_colnr = (colnr_T)undo_read_4c(&bi);
    if (line_lnum < 0 || line_colnr < 0)
    {
	corruption_error("line lnum/col", file_name);
	goto error;
    }

    /* Begin general undo data */
    old_header_seq = undo_read_4c(&bi);
    new_header_seq = undo_read_4c(&bi);
    cur_header_seq = undo_read_4c(&bi);
    num_head = undo_read_4c(&bi);
    seq_last = undo_read_4c(&bi);
    seq_cur = undo_read_4c(&bi);
    seq_time = undo_read_time(&bi);

    /* Optional header fields. */
    for (;;)
    {
	int len = undo_read_byte(&bi);
	int what;

	if (len == 0 || len == EOF)
	    break;
	what = undo_read_byte(&bi);
	switch (what)
	{
	    case UF_LAST_SAVE_NR:
		last_save_nr = undo_read_4c(&bi);
		break;
	    default:
		/* field not supported, skip */
		while (--len >= 0)
		    (void)undo_read_byte(&bi);
	}
    }

    /* 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)
    {
	if (num_head < LONG_MAX / (long)sizeof(u_header_T *))
	    uhp_table = (u_header_T **)U_ALLOC_LINE(
					     num_head * sizeof(u_header_T *));
	if (uhp_table == NULL)
	    goto error;
    }

    while ((c = undo_read_2c(&bi)) == UF_HEADER_MAGIC)
    {
	if (num_read_uhps >= num_head)
	{
	    corruption_error("num_head too small", file_name);
	    goto error;
	}

	uhp = unserialize_uhp(&bi, file_name);
	if (uhp == NULL)
	    goto error;
	uhp_table[num_read_uhps++] = uhp;
    }

    if (num_read_uhps != num_head)
    {
	corruption_error("num_head", file_name);
	goto error;
    }
    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 have put all of the headers into a table. Now we iterate through the
     * table and swizzle each sequence number we have stored in uh_*_seq into
     * a pointer corresponding to the header with that sequence number. */
    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 && i != j
			      && uhp_table[i]->uh_seq == uhp_table[j]->uh_seq)
	    {
		corruption_error("duplicate uh_seq", file_name);
		goto error;
	    }
	for (j = 0; j < num_head; j++)
	    if (uhp_table[j] != NULL
				  && uhp_table[j]->uh_seq == uhp->uh_next.seq)
	    {
		uhp->uh_next.ptr = uhp_table[j];
		SET_FLAG(j);
		break;
	    }
	for (j = 0; j < num_head; j++)
	    if (uhp_table[j] != NULL
				  && uhp_table[j]->uh_seq == uhp->uh_prev.seq)
	    {
		uhp->uh_prev.ptr = uhp_table[j];
		SET_FLAG(j);
		break;
	    }
	for (j = 0; j < num_head; j++)
	    if (uhp_table[j] != NULL
			      && uhp_table[j]->uh_seq == uhp->uh_alt_next.seq)
	    {
		uhp->uh_alt_next.ptr = uhp_table[j];
		SET_FLAG(j);
		break;
	    }
	for (j = 0; j < num_head; j++)
	    if (uhp_table[j] != NULL
			      && uhp_table[j]->uh_seq == uhp->uh_alt_prev.seq)
	    {
		uhp->uh_alt_prev.ptr = uhp_table[j];
		SET_FLAG(j);
		break;
	    }
	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_time_cur = seq_time;
    curbuf->b_u_save_nr_last = last_save_nr;
    curbuf->b_u_save_nr_cur = last_save_nr;

    curbuf->b_u_synced = TRUE;
    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_read_uhps; i++)
	    if (uhp_table[i] != NULL)
		u_free_uhp(uhp_table[i]);
	vim_free(uhp_table);
    }

theend:
#ifdef FEAT_CRYPT
    if (bi.bi_state != NULL)
	crypt_free_state(bi.bi_state);
    vim_free(bi.bi_buffer);
#endif
    if (fp != NULL)
	fclose(fp);
    if (file_name != name)
	vim_free(file_name);
    return;
}

#define UH_MAGIC 0x18dade	/* value for uh_magic when in use */

#define UE_MAGIC 0xabc123	/* value for ue_magic when in use */

#define CRYPT_BUF_SIZE 8192

typedef struct {
    buf_T	*bi_buf;
    FILE	*bi_fp;
#ifdef FEAT_CRYPT
    cryptstate_T *bi_state;
    char_u	*bi_buffer; /* CRYPT_BUF_SIZE, NULL when not buffering */
    size_t	bi_used;    /* bytes written to/read from bi_buffer */
    size_t	bi_avail;   /* bytes available in bi_buffer */
#endif
} bufinfo_T;

static long get_undolevel(void);

static void u_unch_branch(u_header_T *uhp);

static u_entry_T *u_get_headentry(void);

static void u_getbot(void);

static void u_doit(int count);

static void u_undoredo(int undo);

static void u_undo_end(int did_undo, int absolute);

static void u_add_time(char_u *buf, size_t buflen, time_t tt);

static void u_freeheader(buf_T *buf, u_header_T *uhp, u_header_T **uhpp);

static void u_freebranch(buf_T *buf, u_header_T *uhp, u_header_T **uhpp);

static void u_freeentries(buf_T *buf, u_header_T *uhp, u_header_T **uhpp);

static void u_freeentry(u_entry_T *, long);

static void corruption_error(char *mesg, char_u *file_name);

static void u_free_uhp(u_header_T *uhp);

static int undo_write(bufinfo_T *bi, char_u *ptr, size_t len);

static int undo_flush(bufinfo_T *bi);

static int fwrite_crypt(bufinfo_T *bi, char_u *ptr, size_t len);

static int undo_write_bytes(bufinfo_T *bi, long_u nr, int len);

static void put_header_ptr(bufinfo_T *bi, u_header_T *uhp);

static int undo_read_4c(bufinfo_T *bi);

static int undo_read_2c(bufinfo_T *bi);

static int undo_read_byte(bufinfo_T *bi);

static time_t undo_read_time(bufinfo_T *bi);

static int undo_read(bufinfo_T *bi, char_u *buffer, size_t size);

static char_u *read_string_decrypt(bufinfo_T *bi, int len);

static int serialize_header(bufinfo_T *bi, char_u *hash);

static int serialize_uhp(bufinfo_T *bi, u_header_T *uhp);

static u_header_T *unserialize_uhp(bufinfo_T *bi, char_u *file_name);

static int serialize_uep(bufinfo_T *bi, u_entry_T *uep);

static u_entry_T *unserialize_uep(bufinfo_T *bi, int *error, char_u *file_name);

static void serialize_pos(bufinfo_T *bi, pos_T pos);

static void unserialize_pos(bufinfo_T *bi, pos_T *pos);

static void serialize_visualinfo(bufinfo_T *bi, visualinfo_T *info);

static void unserialize_visualinfo(bufinfo_T *bi, visualinfo_T *info);

#define U_ALLOC_LINE(size) lalloc((long_u)(size), FALSE)

static char_u *u_save_line(linenr_T);

static long	u_newcount, u_oldcount;

static int	undo_undoes = FALSE;

static int	lastmark = 0;

static int seen_b_u_curhead;

static int seen_b_u_newhead;

static int header_count;

# define UF_START_MAGIC	    "Vim\237UnDo\345"  /* magic at start of undofile */

# define UF_START_MAGIC_LEN	9


put_header_ptr(bufinfo_T *bi, u_header_T *uhp)
{
    undo_write_bytes(bi, (long_u)(uhp != NULL ? uhp->uh_seq : 0), 4);
}

typedef struct {
    buf_T	*bi_buf;
    FILE	*bi_fp;
#ifdef FEAT_CRYPT
    cryptstate_T *bi_state;
    char_u	*bi_buffer; /* CRYPT_BUF_SIZE, NULL when not buffering */
    size_t	bi_used;    /* bytes written to/read from bi_buffer */
    size_t	bi_avail;   /* bytes available in bi_buffer */
#endif
} bufinfo_T;

static void u_unch_branch(u_header_T *uhp);

static void u_freeheader(buf_T *buf, u_header_T *uhp, u_header_T **uhpp);

static void u_freebranch(buf_T *buf, u_header_T *uhp, u_header_T **uhpp);

static void u_freeentries(buf_T *buf, u_header_T *uhp, u_header_T **uhpp);

static void u_free_uhp(u_header_T *uhp);

static int undo_write(bufinfo_T *bi, char_u *ptr, size_t len);

static int undo_flush(bufinfo_T *bi);

static int fwrite_crypt(bufinfo_T *bi, char_u *ptr, size_t len);

static int undo_write_bytes(bufinfo_T *bi, long_u nr, int len);

static void put_header_ptr(bufinfo_T *bi, u_header_T *uhp);

static int undo_read_4c(bufinfo_T *bi);

static int undo_read_2c(bufinfo_T *bi);

static int undo_read_byte(bufinfo_T *bi);

static time_t undo_read_time(bufinfo_T *bi);

static int undo_read(bufinfo_T *bi, char_u *buffer, size_t size);

static char_u *read_string_decrypt(bufinfo_T *bi, int len);

static int serialize_header(bufinfo_T *bi, char_u *hash);

static int serialize_uhp(bufinfo_T *bi, u_header_T *uhp);

static u_header_T *unserialize_uhp(bufinfo_T *bi, char_u *file_name);

static int serialize_uep(bufinfo_T *bi, u_entry_T *uep);

static u_entry_T *unserialize_uep(bufinfo_T *bi, int *error, char_u *file_name);

static void serialize_pos(bufinfo_T *bi, pos_T pos);

static void unserialize_pos(bufinfo_T *bi, pos_T *pos);

static void serialize_visualinfo(bufinfo_T *bi, visualinfo_T *info);

static void unserialize_visualinfo(bufinfo_T *bi, visualinfo_T *info);

#define U_ALLOC_LINE(size) lalloc((long_u)(size), FALSE)


u_freeentries(
    buf_T	    *buf,
    u_header_T	    *uhp,
    u_header_T	    **uhpp)	/* if not NULL reset when freeing this header */
{
    u_entry_T	    *uep, *nuep;

    /* Check for pointers to the header that become invalid now. */
    if (buf->b_u_curhead == uhp)
	buf->b_u_curhead = NULL;
    if (buf->b_u_newhead == uhp)
	buf->b_u_newhead = NULL;  /* freeing the newest entry */
    if (uhpp != NULL && uhp == *uhpp)
	*uhpp = NULL;

    for (uep = uhp->uh_entry; uep != NULL; uep = nuep)
    {
	nuep = uep->ue_next;
	u_freeentry(uep, uep->ue_size);
    }

#ifdef U_DEBUG
    uhp->uh_magic = 0;
#endif
    vim_free((char_u *)uhp);
    --buf->b_u_numhead;
}

#define UH_MAGIC 0x18dade	/* value for uh_magic when in use */

#define UE_MAGIC 0xabc123	/* value for ue_magic when in use */

typedef struct {
    buf_T	*bi_buf;
    FILE	*bi_fp;
#ifdef FEAT_CRYPT
    cryptstate_T *bi_state;
    char_u	*bi_buffer; /* CRYPT_BUF_SIZE, NULL when not buffering */
    size_t	bi_used;    /* bytes written to/read from bi_buffer */
    size_t	bi_avail;   /* bytes available in bi_buffer */
#endif
} bufinfo_T;

static void u_unch_branch(u_header_T *uhp);

static u_entry_T *u_get_headentry(void);

static void u_add_time(char_u *buf, size_t buflen, time_t tt);

static void u_freeheader(buf_T *buf, u_header_T *uhp, u_header_T **uhpp);

static void u_freebranch(buf_T *buf, u_header_T *uhp, u_header_T **uhpp);

static void u_freeentries(buf_T *buf, u_header_T *uhp, u_header_T **uhpp);

static void u_freeentry(u_entry_T *, long);

static void corruption_error(char *mesg, char_u *file_name);

static void u_free_uhp(u_header_T *uhp);

static int undo_write(bufinfo_T *bi, char_u *ptr, size_t len);

static int fwrite_crypt(bufinfo_T *bi, char_u *ptr, size_t len);

static void put_header_ptr(bufinfo_T *bi, u_header_T *uhp);

static int undo_read(bufinfo_T *bi, char_u *buffer, size_t size);

static char_u *read_string_decrypt(bufinfo_T *bi, int len);

static int serialize_header(bufinfo_T *bi, char_u *hash);

static int serialize_uhp(bufinfo_T *bi, u_header_T *uhp);

static u_header_T *unserialize_uhp(bufinfo_T *bi, char_u *file_name);

static int serialize_uep(bufinfo_T *bi, u_entry_T *uep);

static u_entry_T *unserialize_uep(bufinfo_T *bi, int *error, char_u *file_name);

static char_u *u_save_line(linenr_T);

# 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 */

static char_u e_not_open[] = N_("E828: Cannot open undo file for writing: %s");


undo_time(
    long	step,
    int		sec,
    int		file,
    int		absolute)
{
    long	    target;
    long	    closest;
    long	    closest_start;
    long	    closest_seq = 0;
    long	    val;
    u_header_T	    *uhp;
    u_header_T	    *last;
    int		    mark;
    int		    nomark;
    int		    round;
    int		    dosec = sec;
    int		    dofile = file;
    int		    above = FALSE;
    int		    did_undo = TRUE;

    /* First make sure the current undoable change is synced. */
    if (curbuf->b_u_synced == FALSE)
	u_sync(TRUE);

    u_newcount = 0;
    u_oldcount = 0;
    if (curbuf->b_ml.ml_flags & ML_EMPTY)
	u_oldcount = -1;

    /* "target" is the node below which we want to be.
     * Init "closest" to a value we can't reach. */
    if (absolute)
    {
	if (step == 0)
	{
	    /* target 0 does not exist, got to 1 and above it. */
	    target = 1;
	    above = TRUE;
	}
	else
	    target = step;
	closest = -1;
    }
    else
    {
	if (dosec)
	    target = (long)(curbuf->b_u_time_cur) + step;
	else if (dofile)
	{
	    if (step < 0)
	    {
		/* Going back to a previous write. If there were changes after
		 * the last write, count that as moving one file-write, so
		 * that ":earlier 1f" undoes all changes since the last save. */
		uhp = curbuf->b_u_curhead;
		if (uhp != NULL)
		    uhp = uhp->uh_next.ptr;
		else
		    uhp = curbuf->b_u_newhead;
		if (uhp != NULL && uhp->uh_save_nr != 0)
		    /* "uh_save_nr" was set in the last block, that means
		     * there were no changes since the last write */
		    target = curbuf->b_u_save_nr_cur + step;
		else
		    /* count the changes since the last write as one step */
		    target = curbuf->b_u_save_nr_cur + step + 1;
		if (target <= 0)
		    /* Go to before first write: before the oldest change. Use
		     * the sequence number for that. */
		    dofile = FALSE;
	    }
	    else
	    {
		/* Moving forward to a newer write. */
		target = curbuf->b_u_save_nr_cur + step;
		if (target > curbuf->b_u_save_nr_last)
		{
		    /* Go to after last write: after the latest change. Use
		     * the sequence number for that. */
		    target = curbuf->b_u_seq_last + 1;
		    dofile = FALSE;
		}
	    }
	}
	else
	    target = curbuf->b_u_seq_cur + step;
	if (step < 0)
	{
	    if (target < 0)
		target = 0;
	    closest = -1;
	}
	else
	{
	    if (dosec)
		closest = (long)(vim_time() + 1);
	    else if (dofile)
		closest = curbuf->b_u_save_nr_last + 2;
	    else
		closest = curbuf->b_u_seq_last + 2;
	    if (target >= closest)
		target = closest - 1;
	}
    }
    closest_start = closest;
    closest_seq = curbuf->b_u_seq_cur;

    /*
     * May do this twice:
     * 1. Search for "target", update "closest" to the best match found.
     * 2. If "target" not found search for "closest".
     *
     * When using the closest time we use the sequence number in the second
     * round, because there may be several entries with the same time.
     */
    for (round = 1; round <= 2; ++round)
    {
	/* Find the path from the current state to where we want to go.  The
	 * desired state can be anywhere in the undo tree, need to go all over
	 * it.  We put "nomark" in uh_walk where we have been without success,
	 * "mark" where it could possibly be. */
	mark = ++lastmark;
	nomark = ++lastmark;

	if (curbuf->b_u_curhead == NULL)	/* at leaf of the tree */
	    uhp = curbuf->b_u_newhead;
	else
	    uhp = curbuf->b_u_curhead;

	while (uhp != NULL)
	{
	    uhp->uh_walk = mark;
	    if (dosec)
		val = (long)(uhp->uh_time);
	    else if (dofile)
		val = uhp->uh_save_nr;
	    else
		val = uhp->uh_seq;

	    if (round == 1 && !(dofile && val == 0))
	    {
		/* Remember the header that is closest to the target.
		 * It must be at least in the right direction (checked with
		 * "b_u_seq_cur").  When the timestamp is equal find the
		 * highest/lowest sequence number. */
		if ((step < 0 ? uhp->uh_seq <= curbuf->b_u_seq_cur
			      : uhp->uh_seq > curbuf->b_u_seq_cur)
			&& ((dosec && val == closest)
			    ? (step < 0
				? uhp->uh_seq < closest_seq
				: uhp->uh_seq > closest_seq)
			    : closest == closest_start
				|| (val > target
				    ? (closest > target
					? val - target <= closest - target
					: val - target <= target - closest)
				    : (closest > target
					? target - val <= closest - target
					: target - val <= target - closest))))
		{
		    closest = val;
		    closest_seq = uhp->uh_seq;
		}
	    }

	    /* Quit searching when we found a match.  But when searching for a
	     * time we need to continue looking for the best uh_seq. */
	    if (target == val && !dosec)
	    {
		target = uhp->uh_seq;
		break;
	    }

	    /* go down in the tree if we haven't been there */
	    if (uhp->uh_prev.ptr != NULL && uhp->uh_prev.ptr->uh_walk != nomark
					 && uhp->uh_prev.ptr->uh_walk != mark)
		uhp = uhp->uh_prev.ptr;

	    /* go to alternate branch if we haven't been there */
	    else if (uhp->uh_alt_next.ptr != NULL
		    && uhp->uh_alt_next.ptr->uh_walk != nomark
		    && uhp->uh_alt_next.ptr->uh_walk != mark)
		uhp = uhp->uh_alt_next.ptr;

	    /* go up in the tree if we haven't been there and we are at the
	     * start of alternate branches */
	    else if (uhp->uh_next.ptr != NULL && uhp->uh_alt_prev.ptr == NULL
		    && uhp->uh_next.ptr->uh_walk != nomark
		    && uhp->uh_next.ptr->uh_walk != mark)
	    {
		/* If still at the start we don't go through this change. */
		if (uhp == curbuf->b_u_curhead)
		    uhp->uh_walk = nomark;
		uhp = uhp->uh_next.ptr;
	    }

	    else
	    {
		/* need to backtrack; mark this node as useless */
		uhp->uh_walk = nomark;
		if (uhp->uh_alt_prev.ptr != NULL)
		    uhp = uhp->uh_alt_prev.ptr;
		else
		    uhp = uhp->uh_next.ptr;
	    }
	}

	if (uhp != NULL)    /* found it */
	    break;

	if (absolute)
	{
	    EMSGN(_("E830: Undo number %ld not found"), step);
	    return;
	}

	if (closest == closest_start)
	{
	    if (step < 0)
		MSG(_("Already at oldest change"));
	    else
		MSG(_("Already at newest change"));
	    return;
	}

	target = closest_seq;
	dosec = FALSE;
	dofile = FALSE;
	if (step < 0)
	    above = TRUE;	/* stop above the header */
    }

    /* If we found it: Follow the path to go to where we want to be. */
    if (uhp != NULL)
    {
	/*
	 * First go up the tree as much as needed.
	 */
	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;
	    else
		uhp = uhp->uh_next.ptr;
	    if (uhp == NULL || uhp->uh_walk != mark
					 || (uhp->uh_seq == target && !above))
		break;
	    curbuf->b_u_curhead = uhp;
	    u_undoredo(TRUE);
	    uhp->uh_walk = nomark;	/* don't go back down here */
	}

	/*
	 * And now go down the tree (redo), branching off where needed.
	 */
	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.ptr != NULL
				     && uhp->uh_alt_prev.ptr->uh_walk == mark)
		uhp = uhp->uh_alt_prev.ptr;

	    /* Find the last branch with a mark, that's the one. */
	    last = uhp;
	    while (last->uh_alt_next.ptr != NULL
				    && last->uh_alt_next.ptr->uh_walk == mark)
		last = last->uh_alt_next.ptr;
	    if (last != uhp)
	    {
		/* Make the used branch the first entry in the list of
		 * alternatives to make "u" and CTRL-R take this branch. */
		while (uhp->uh_alt_prev.ptr != NULL)
		    uhp = uhp->uh_alt_prev.ptr;
		if (last->uh_alt_next.ptr != NULL)
		    last->uh_alt_next.ptr->uh_alt_prev.ptr =
							last->uh_alt_prev.ptr;
		last->uh_alt_prev.ptr->uh_alt_next.ptr = last->uh_alt_next.ptr;
		last->uh_alt_prev.ptr = NULL;
		last->uh_alt_next.ptr = uhp;
		uhp->uh_alt_prev.ptr = last;

		if (curbuf->b_u_oldhead == uhp)
		    curbuf->b_u_oldhead = last;
		uhp = last;
		if (uhp->uh_next.ptr != NULL)
		    uhp->uh_next.ptr->uh_prev.ptr = uhp;
	    }
	    curbuf->b_u_curhead = uhp;

	    if (uhp->uh_walk != mark)
		break;	    /* must have reached the target */

	    /* Stop when going backwards in time and didn't find the exact
	     * header we were looking for. */
	    if (uhp->uh_seq == target && above)
	    {
		curbuf->b_u_seq_cur = target - 1;
		break;
	    }

	    u_undoredo(FALSE);

	    /* Advance "curhead" to below the header we last used.  If it
	     * becomes NULL then we need to set "newhead" to this leaf. */
	    if (uhp->uh_prev.ptr == NULL)
		curbuf->b_u_newhead = uhp;
	    curbuf->b_u_curhead = uhp->uh_prev.ptr;
	    did_undo = FALSE;

	    if (uhp->uh_seq == target)	/* found it! */
		break;

	    uhp = uhp->uh_prev.ptr;
	    if (uhp == NULL || uhp->uh_walk != mark)
	    {
		/* Need to redo more but can't find it... */
		internal_error("undo_time()");
		break;
	    }
	}
    }
    u_undo_end(did_undo, absolute);
}

#define UH_MAGIC 0x18dade	/* value for uh_magic when in use */

#define UE_MAGIC 0xabc123	/* value for ue_magic when in use */

typedef struct {
    buf_T	*bi_buf;
    FILE	*bi_fp;
#ifdef FEAT_CRYPT
    cryptstate_T *bi_state;
    char_u	*bi_buffer; /* CRYPT_BUF_SIZE, NULL when not buffering */
    size_t	bi_used;    /* bytes written to/read from bi_buffer */
    size_t	bi_avail;   /* bytes available in bi_buffer */
#endif
} bufinfo_T;

static long get_undolevel(void);

static void u_unch_branch(u_header_T *uhp);

static void u_doit(int count);

static void u_undoredo(int undo);

static void u_undo_end(int did_undo, int absolute);

static void u_freeheader(buf_T *buf, u_header_T *uhp, u_header_T **uhpp);

static void u_freebranch(buf_T *buf, u_header_T *uhp, u_header_T **uhpp);

static void u_freeentries(buf_T *buf, u_header_T *uhp, u_header_T **uhpp);

static void u_freeentry(u_entry_T *, long);

static void u_free_uhp(u_header_T *uhp);

static int undo_write(bufinfo_T *bi, char_u *ptr, size_t len);

static int undo_flush(bufinfo_T *bi);

static int fwrite_crypt(bufinfo_T *bi, char_u *ptr, size_t len);

static int undo_write_bytes(bufinfo_T *bi, long_u nr, int len);

static void put_header_ptr(bufinfo_T *bi, u_header_T *uhp);

static int undo_read_4c(bufinfo_T *bi);

static int undo_read_2c(bufinfo_T *bi);

static int undo_read_byte(bufinfo_T *bi);

static int undo_read(bufinfo_T *bi, char_u *buffer, size_t size);

static char_u *read_string_decrypt(bufinfo_T *bi, int len);

static int serialize_header(bufinfo_T *bi, char_u *hash);

static int serialize_uhp(bufinfo_T *bi, u_header_T *uhp);

static u_header_T *unserialize_uhp(bufinfo_T *bi, char_u *file_name);

static int serialize_uep(bufinfo_T *bi, u_entry_T *uep);

static u_entry_T *unserialize_uep(bufinfo_T *bi, int *error, char_u *file_name);

#define U_ALLOC_LINE(size) lalloc((long_u)(size), FALSE)

static long	u_newcount, u_oldcount;

static int	undo_undoes = FALSE;

static int	lastmark = 0;

static int seen_b_u_curhead;

static int seen_b_u_newhead;

static int header_count;

# define UF_START_MAGIC	    "Vim\237UnDo\345"  /* magic at start of undofile */

# 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		2	/* 2-byte undofile version number */

static char_u e_not_open[] = N_("E828: Cannot open undo file for writing: %s");


get_undolevel(void)
{
    if (curbuf->b_p_ul == NO_LOCAL_UNDOLEVEL)
	return p_ul;
    return curbuf->b_p_ul;
}

static long get_undolevel(void);

static void u_unch_branch(u_header_T *uhp);

static u_entry_T *u_get_headentry(void);

static void u_getbot(void);

static void u_doit(int count);

static void u_undoredo(int undo);

static void u_undo_end(int did_undo, int absolute);

static void u_add_time(char_u *buf, size_t buflen, time_t tt);

static void u_freeheader(buf_T *buf, u_header_T *uhp, u_header_T **uhpp);

static void u_freebranch(buf_T *buf, u_header_T *uhp, u_header_T **uhpp);

static void u_freeentries(buf_T *buf, u_header_T *uhp, u_header_T **uhpp);

static void u_freeentry(u_entry_T *, long);

static void corruption_error(char *mesg, char_u *file_name);

static void u_free_uhp(u_header_T *uhp);

static void put_header_ptr(bufinfo_T *bi, u_header_T *uhp);

static void serialize_pos(bufinfo_T *bi, pos_T pos);

static void unserialize_pos(bufinfo_T *bi, pos_T *pos);

static void serialize_visualinfo(bufinfo_T *bi, visualinfo_T *info);

static void unserialize_visualinfo(bufinfo_T *bi, visualinfo_T *info);


u_sync(
    int	    force)	/* Also sync when no_u_sync is set. */
{
    /* Skip it when already synced or syncing is disabled. */
    if (curbuf->b_u_synced || (!force && no_u_sync > 0))
	return;
#if defined(FEAT_XIM) && defined(FEAT_GUI_GTK)
    if (im_is_preediting())
	return;		    /* XIM is busy, don't break an undo sequence */
#endif
    if (get_undolevel() < 0)
	curbuf->b_u_synced = TRUE;  /* no entries, nothing to do */
    else
    {
	u_getbot();		    /* compute ue_bot of previous u_save */
	curbuf->b_u_curhead = NULL;
    }
}

#define UH_MAGIC 0x18dade	/* value for uh_magic when in use */

#define UE_MAGIC 0xabc123	/* value for ue_magic when in use */

typedef struct {
    buf_T	*bi_buf;
    FILE	*bi_fp;
#ifdef FEAT_CRYPT
    cryptstate_T *bi_state;
    char_u	*bi_buffer; /* CRYPT_BUF_SIZE, NULL when not buffering */
    size_t	bi_used;    /* bytes written to/read from bi_buffer */
    size_t	bi_avail;   /* bytes available in bi_buffer */
#endif
} bufinfo_T;

static long get_undolevel(void);

static void u_getbot(void);

static void u_doit(int count);

static void u_undoredo(int undo);

static void u_undo_end(int did_undo, int absolute);

static int undo_write(bufinfo_T *bi, char_u *ptr, size_t len);

static int undo_flush(bufinfo_T *bi);

static int fwrite_crypt(bufinfo_T *bi, char_u *ptr, size_t len);

static int undo_write_bytes(bufinfo_T *bi, long_u nr, int len);

static int undo_read_4c(bufinfo_T *bi);

static int undo_read_2c(bufinfo_T *bi);

static int undo_read_byte(bufinfo_T *bi);

static int undo_read(bufinfo_T *bi, char_u *buffer, size_t size);

static char_u *read_string_decrypt(bufinfo_T *bi, int len);

static int serialize_header(bufinfo_T *bi, char_u *hash);

static int serialize_uhp(bufinfo_T *bi, u_header_T *uhp);

static int serialize_uep(bufinfo_T *bi, u_entry_T *uep);

static u_entry_T *unserialize_uep(bufinfo_T *bi, int *error, char_u *file_name);

static int	undo_undoes = FALSE;

static int	lastmark = 0;

static int seen_b_u_curhead;

static int seen_b_u_newhead;

static int header_count;

static char_u e_not_open[] = N_("E828: Cannot open undo file for writing: %s");


undo_flush(bufinfo_T *bi)
{
    if (bi->bi_buffer != NULL && bi->bi_used > 0)
    {
	crypt_encode_inplace(bi->bi_state, bi->bi_buffer, bi->bi_used);
	if (fwrite(bi->bi_buffer, bi->bi_used, (size_t)1, bi->bi_fp) != 1)
	    return FAIL;
	bi->bi_used = 0;
    }
    return OK;
}

typedef struct {
    buf_T	*bi_buf;
    FILE	*bi_fp;
#ifdef FEAT_CRYPT
    cryptstate_T *bi_state;
    char_u	*bi_buffer; /* CRYPT_BUF_SIZE, NULL when not buffering */
    size_t	bi_used;    /* bytes written to/read from bi_buffer */
    size_t	bi_avail;   /* bytes available in bi_buffer */
#endif
} bufinfo_T;

static void u_add_time(char_u *buf, size_t buflen, time_t tt);

static int undo_write(bufinfo_T *bi, char_u *ptr, size_t len);

static int undo_flush(bufinfo_T *bi);

static int fwrite_crypt(bufinfo_T *bi, char_u *ptr, size_t len);

static int undo_write_bytes(bufinfo_T *bi, long_u nr, int len);

static void put_header_ptr(bufinfo_T *bi, u_header_T *uhp);

static int undo_read_4c(bufinfo_T *bi);

static int undo_read_2c(bufinfo_T *bi);

static int undo_read_byte(bufinfo_T *bi);

static time_t undo_read_time(bufinfo_T *bi);

static int undo_read(bufinfo_T *bi, char_u *buffer, size_t size);

static char_u *read_string_decrypt(bufinfo_T *bi, int len);

static int serialize_header(bufinfo_T *bi, char_u *hash);

static int serialize_uhp(bufinfo_T *bi, u_header_T *uhp);

static u_header_T *unserialize_uhp(bufinfo_T *bi, char_u *file_name);

static int serialize_uep(bufinfo_T *bi, u_entry_T *uep);

static u_entry_T *unserialize_uep(bufinfo_T *bi, int *error, char_u *file_name);

static void serialize_pos(bufinfo_T *bi, pos_T pos);

static void unserialize_pos(bufinfo_T *bi, pos_T *pos);

static void serialize_visualinfo(bufinfo_T *bi, visualinfo_T *info);

static void unserialize_visualinfo(bufinfo_T *bi, visualinfo_T *info);


undo_read_2c(bufinfo_T *bi)
{
#ifdef FEAT_CRYPT
    if (bi->bi_buffer != NULL)
    {
	char_u  buf[2];
	int	n;

	undo_read(bi, buf, (size_t)2);
	n = (buf[0] << 8) + buf[1];
	return n;
    }
#endif
    return get2c(bi->bi_fp);
}

typedef struct {
    buf_T	*bi_buf;
    FILE	*bi_fp;
#ifdef FEAT_CRYPT
    cryptstate_T *bi_state;
    char_u	*bi_buffer; /* CRYPT_BUF_SIZE, NULL when not buffering */
    size_t	bi_used;    /* bytes written to/read from bi_buffer */
    size_t	bi_avail;   /* bytes available in bi_buffer */
#endif
} bufinfo_T;

static void u_doit(int count);

static void u_undoredo(int undo);

static void u_undo_end(int did_undo, int absolute);

static void u_add_time(char_u *buf, size_t buflen, time_t tt);

static void u_freeheader(buf_T *buf, u_header_T *uhp, u_header_T **uhpp);

static void u_freebranch(buf_T *buf, u_header_T *uhp, u_header_T **uhpp);

static void u_freeentries(buf_T *buf, u_header_T *uhp, u_header_T **uhpp);

static void corruption_error(char *mesg, char_u *file_name);

static int undo_write(bufinfo_T *bi, char_u *ptr, size_t len);

static int undo_flush(bufinfo_T *bi);

static int fwrite_crypt(bufinfo_T *bi, char_u *ptr, size_t len);

static int undo_write_bytes(bufinfo_T *bi, long_u nr, int len);

static void put_header_ptr(bufinfo_T *bi, u_header_T *uhp);

static int undo_read_4c(bufinfo_T *bi);

static int undo_read_2c(bufinfo_T *bi);

static int undo_read_byte(bufinfo_T *bi);

static time_t undo_read_time(bufinfo_T *bi);

static int undo_read(bufinfo_T *bi, char_u *buffer, size_t size);

static char_u *read_string_decrypt(bufinfo_T *bi, int len);

static int serialize_header(bufinfo_T *bi, char_u *hash);

static int serialize_uhp(bufinfo_T *bi, u_header_T *uhp);

static u_header_T *unserialize_uhp(bufinfo_T *bi, char_u *file_name);

static int serialize_uep(bufinfo_T *bi, u_entry_T *uep);

static u_entry_T *unserialize_uep(bufinfo_T *bi, int *error, char_u *file_name);

static void serialize_pos(bufinfo_T *bi, pos_T pos);

static void unserialize_pos(bufinfo_T *bi, pos_T *pos);

static void serialize_visualinfo(bufinfo_T *bi, visualinfo_T *info);

static void unserialize_visualinfo(bufinfo_T *bi, visualinfo_T *info);

static char_u *u_save_line(linenr_T);

static int	undo_undoes = FALSE;

static int	lastmark = 0;

static int seen_b_u_curhead;

static int seen_b_u_newhead;

static int header_count;

static char_u e_not_open[] = N_("E828: Cannot open undo file for writing: %s");


u_get_headentry(void)
{
    if (curbuf->b_u_newhead == NULL || curbuf->b_u_newhead->uh_entry == NULL)
    {
	IEMSG(_("E439: undo list corrupt"));
	return NULL;
    }
    return curbuf->b_u_newhead->uh_entry;
}

typedef struct {
    buf_T	*bi_buf;
    FILE	*bi_fp;
#ifdef FEAT_CRYPT
    cryptstate_T *bi_state;
    char_u	*bi_buffer; /* CRYPT_BUF_SIZE, NULL when not buffering */
    size_t	bi_used;    /* bytes written to/read from bi_buffer */
    size_t	bi_avail;   /* bytes available in bi_buffer */
#endif
} bufinfo_T;

static long get_undolevel(void);

static void u_unch_branch(u_header_T *uhp);

static u_entry_T *u_get_headentry(void);

static void u_getbot(void);

static void u_doit(int count);

static void u_undoredo(int undo);

static void u_undo_end(int did_undo, int absolute);

static void u_add_time(char_u *buf, size_t buflen, time_t tt);

static void u_freeheader(buf_T *buf, u_header_T *uhp, u_header_T **uhpp);

static void u_freebranch(buf_T *buf, u_header_T *uhp, u_header_T **uhpp);

static void u_freeentries(buf_T *buf, u_header_T *uhp, u_header_T **uhpp);

static void u_freeentry(u_entry_T *, long);

static void corruption_error(char *mesg, char_u *file_name);

static void u_free_uhp(u_header_T *uhp);

static void put_header_ptr(bufinfo_T *bi, u_header_T *uhp);

static void serialize_pos(bufinfo_T *bi, pos_T pos);

static void unserialize_pos(bufinfo_T *bi, pos_T *pos);

static void serialize_visualinfo(bufinfo_T *bi, visualinfo_T *info);

static void unserialize_visualinfo(bufinfo_T *bi, visualinfo_T *info);

static char_u e_not_open[] = N_("E828: Cannot open undo file for writing: %s");


undo_read_time(bufinfo_T *bi)
{
#ifdef FEAT_CRYPT
    if (bi->bi_buffer != NULL)
    {
	char_u  buf[8];
	time_t	n = 0;
	int	i;

	undo_read(bi, buf, (size_t)8);
	for (i = 0; i < 8; ++i)
	    n = (n << 8) + buf[i];
	return n;
    }
#endif
    return get8ctime(bi->bi_fp);
}

#define UH_MAGIC 0x18dade	/* value for uh_magic when in use */

#define UE_MAGIC 0xabc123	/* value for ue_magic when in use */

typedef struct {
    buf_T	*bi_buf;
    FILE	*bi_fp;
#ifdef FEAT_CRYPT
    cryptstate_T *bi_state;
    char_u	*bi_buffer; /* CRYPT_BUF_SIZE, NULL when not buffering */
    size_t	bi_used;    /* bytes written to/read from bi_buffer */
    size_t	bi_avail;   /* bytes available in bi_buffer */
#endif
} bufinfo_T;

static void u_doit(int count);

static void u_undoredo(int undo);

static void u_undo_end(int did_undo, int absolute);

static void u_add_time(char_u *buf, size_t buflen, time_t tt);

static void u_freeheader(buf_T *buf, u_header_T *uhp, u_header_T **uhpp);

static void u_freebranch(buf_T *buf, u_header_T *uhp, u_header_T **uhpp);

static void u_freeentries(buf_T *buf, u_header_T *uhp, u_header_T **uhpp);

static void corruption_error(char *mesg, char_u *file_name);

static int undo_write(bufinfo_T *bi, char_u *ptr, size_t len);

static int undo_flush(bufinfo_T *bi);

static int fwrite_crypt(bufinfo_T *bi, char_u *ptr, size_t len);

static int undo_write_bytes(bufinfo_T *bi, long_u nr, int len);

static void put_header_ptr(bufinfo_T *bi, u_header_T *uhp);

static int undo_read_4c(bufinfo_T *bi);

static int undo_read_2c(bufinfo_T *bi);

static int undo_read_byte(bufinfo_T *bi);

static time_t undo_read_time(bufinfo_T *bi);

static int undo_read(bufinfo_T *bi, char_u *buffer, size_t size);

static char_u *read_string_decrypt(bufinfo_T *bi, int len);

static int serialize_header(bufinfo_T *bi, char_u *hash);

static int serialize_uhp(bufinfo_T *bi, u_header_T *uhp);

static u_header_T *unserialize_uhp(bufinfo_T *bi, char_u *file_name);

static int serialize_uep(bufinfo_T *bi, u_entry_T *uep);

static u_entry_T *unserialize_uep(bufinfo_T *bi, int *error, char_u *file_name);

static void serialize_pos(bufinfo_T *bi, pos_T pos);

static void unserialize_pos(bufinfo_T *bi, pos_T *pos);

static void serialize_visualinfo(bufinfo_T *bi, visualinfo_T *info);

static void unserialize_visualinfo(bufinfo_T *bi, visualinfo_T *info);

static char_u *u_save_line(linenr_T);

static int	undo_undoes = FALSE;

static int	lastmark = 0;

static int seen_b_u_curhead;

static int seen_b_u_newhead;

static int header_count;

static char_u e_not_open[] = N_("E828: Cannot open undo file for writing: %s");


unserialize_uhp(bufinfo_T *bi, char_u *file_name)
{
    u_header_T	*uhp;
    int		i;
    u_entry_T	*uep, *last_uep;
    int		c;
    int		error;

    uhp = (u_header_T *)U_ALLOC_LINE(sizeof(u_header_T));
    if (uhp == NULL)
	return NULL;
    vim_memset(uhp, 0, sizeof(u_header_T));
#ifdef U_DEBUG
    uhp->uh_magic = UH_MAGIC;
#endif
    uhp->uh_next.seq = undo_read_4c(bi);
    uhp->uh_prev.seq = undo_read_4c(bi);
    uhp->uh_alt_next.seq = undo_read_4c(bi);
    uhp->uh_alt_prev.seq = undo_read_4c(bi);
    uhp->uh_seq = undo_read_4c(bi);
    if (uhp->uh_seq <= 0)
    {
	corruption_error("uh_seq", file_name);
	vim_free(uhp);
	return NULL;
    }
    unserialize_pos(bi, &uhp->uh_cursor);
#ifdef FEAT_VIRTUALEDIT
    uhp->uh_cursor_vcol = undo_read_4c(bi);
#else
    (void)undo_read_4c(bi);
#endif
    uhp->uh_flags = undo_read_2c(bi);
    for (i = 0; i < NMARKS; ++i)
	unserialize_pos(bi, &uhp->uh_namedm[i]);
    unserialize_visualinfo(bi, &uhp->uh_visual);
    uhp->uh_time = undo_read_time(bi);

    /* Optional fields. */
    for (;;)
    {
	int len = undo_read_byte(bi);
	int what;

	if (len == 0)
	    break;
	what = undo_read_byte(bi);
	switch (what)
	{
	    case UHP_SAVE_NR:
		uhp->uh_save_nr = undo_read_4c(bi);
		break;
	    default:
		/* field not supported, skip */
		while (--len >= 0)
		    (void)undo_read_byte(bi);
	}
    }

    /* Unserialize the uep list. */
    last_uep = NULL;
    while ((c = undo_read_2c(bi)) == UF_ENTRY_MAGIC)
    {
	error = FALSE;
	uep = unserialize_uep(bi, &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);
	    return NULL;
	}
    }
    if (c != UF_ENTRY_END_MAGIC)
    {
	corruption_error("entry end", file_name);
	u_free_uhp(uhp);
	return NULL;
    }

    return uhp;
}

#define UH_MAGIC 0x18dade	/* value for uh_magic when in use */

#define UE_MAGIC 0xabc123	/* value for ue_magic when in use */

typedef struct {
    buf_T	*bi_buf;
    FILE	*bi_fp;
#ifdef FEAT_CRYPT
    cryptstate_T *bi_state;
    char_u	*bi_buffer; /* CRYPT_BUF_SIZE, NULL when not buffering */
    size_t	bi_used;    /* bytes written to/read from bi_buffer */
    size_t	bi_avail;   /* bytes available in bi_buffer */
#endif
} bufinfo_T;

static long get_undolevel(void);

static void u_unch_branch(u_header_T *uhp);

static u_entry_T *u_get_headentry(void);

static void u_getbot(void);

static void u_doit(int count);

static void u_undoredo(int undo);

static void u_undo_end(int did_undo, int absolute);

static void u_add_time(char_u *buf, size_t buflen, time_t tt);

static void u_freeheader(buf_T *buf, u_header_T *uhp, u_header_T **uhpp);

static void u_freebranch(buf_T *buf, u_header_T *uhp, u_header_T **uhpp);

static void u_freeentries(buf_T *buf, u_header_T *uhp, u_header_T **uhpp);

static void u_freeentry(u_entry_T *, long);

static void corruption_error(char *mesg, char_u *file_name);

static void u_free_uhp(u_header_T *uhp);

static int undo_write(bufinfo_T *bi, char_u *ptr, size_t len);

static int undo_flush(bufinfo_T *bi);

static int fwrite_crypt(bufinfo_T *bi, char_u *ptr, size_t len);

static int undo_write_bytes(bufinfo_T *bi, long_u nr, int len);

static void put_header_ptr(bufinfo_T *bi, u_header_T *uhp);

static int undo_read_4c(bufinfo_T *bi);

static int undo_read_2c(bufinfo_T *bi);

static int undo_read_byte(bufinfo_T *bi);

static time_t undo_read_time(bufinfo_T *bi);

static int undo_read(bufinfo_T *bi, char_u *buffer, size_t size);

static char_u *read_string_decrypt(bufinfo_T *bi, int len);

static int serialize_header(bufinfo_T *bi, char_u *hash);

static int serialize_uhp(bufinfo_T *bi, u_header_T *uhp);

static u_header_T *unserialize_uhp(bufinfo_T *bi, char_u *file_name);

static int serialize_uep(bufinfo_T *bi, u_entry_T *uep);

static u_entry_T *unserialize_uep(bufinfo_T *bi, int *error, char_u *file_name);

static void serialize_pos(bufinfo_T *bi, pos_T pos);

static void unserialize_pos(bufinfo_T *bi, pos_T *pos);

static void serialize_visualinfo(bufinfo_T *bi, visualinfo_T *info);

static void unserialize_visualinfo(bufinfo_T *bi, visualinfo_T *info);

#define U_ALLOC_LINE(size) lalloc((long_u)(size), FALSE)

static char_u *u_save_line(linenr_T);

static int	undo_undoes = FALSE;

static int	lastmark = 0;

static int seen_b_u_curhead;

static int seen_b_u_newhead;

static int header_count;

# define UF_ENTRY_MAGIC		0xf518	/* magic at start of entry */

# define UF_ENTRY_END_MAGIC	0x3581	/* magic after last entry */

# define UHP_SAVE_NR		1

static char_u e_not_open[] = N_("E828: Cannot open undo file for writing: %s");


u_save_line(linenr_T lnum)
{
    return vim_strsave(ml_get(lnum));
}


bufIsChanged(buf_T *buf)
{
    return
#ifdef FEAT_QUICKFIX
	    !bt_dontwrite(buf) &&
#endif
	    (buf->b_changed || file_ff_differs(buf, TRUE));
}

typedef struct {
    buf_T	*bi_buf;
    FILE	*bi_fp;
#ifdef FEAT_CRYPT
    cryptstate_T *bi_state;
    char_u	*bi_buffer; /* CRYPT_BUF_SIZE, NULL when not buffering */
    size_t	bi_used;    /* bytes written to/read from bi_buffer */
    size_t	bi_avail;   /* bytes available in bi_buffer */
#endif
} bufinfo_T;

static void u_add_time(char_u *buf, size_t buflen, time_t tt);

static void u_freeheader(buf_T *buf, u_header_T *uhp, u_header_T **uhpp);

static void u_freebranch(buf_T *buf, u_header_T *uhp, u_header_T **uhpp);

static void u_freeentries(buf_T *buf, u_header_T *uhp, u_header_T **uhpp);


u_getbot(void)
{
    u_entry_T	*uep;
    linenr_T	extra;

    uep = u_get_headentry();	/* check for corrupt undo list */
    if (uep == NULL)
	return;

    uep = curbuf->b_u_newhead->uh_getbot_entry;
    if (uep != NULL)
    {
	/*
	 * the new ue_bot is computed from the number of lines that has been
	 * inserted (0 - deleted) since calling u_save. This is equal to the
	 * old line count subtracted from the current line count.
	 */
	extra = curbuf->b_ml.ml_line_count - uep->ue_lcount;
	uep->ue_bot = uep->ue_top + uep->ue_size + 1 + extra;
	if (uep->ue_bot < 1 || uep->ue_bot > curbuf->b_ml.ml_line_count)
	{
	    IEMSG(_("E440: undo line missing"));
	    uep->ue_bot = uep->ue_top + 1;  /* assume all lines deleted, will
					     * get all the old lines back
					     * without deleting the current
					     * ones */
	}

	curbuf->b_u_newhead->uh_getbot_entry = NULL;
    }

    curbuf->b_u_synced = TRUE;
}

#define UH_MAGIC 0x18dade	/* value for uh_magic when in use */

#define UE_MAGIC 0xabc123	/* value for ue_magic when in use */

typedef struct {
    buf_T	*bi_buf;
    FILE	*bi_fp;
#ifdef FEAT_CRYPT
    cryptstate_T *bi_state;
    char_u	*bi_buffer; /* CRYPT_BUF_SIZE, NULL when not buffering */
    size_t	bi_used;    /* bytes written to/read from bi_buffer */
    size_t	bi_avail;   /* bytes available in bi_buffer */
#endif
} bufinfo_T;

static long get_undolevel(void);

static void u_unch_branch(u_header_T *uhp);

static u_entry_T *u_get_headentry(void);

static void u_getbot(void);

static void u_doit(int count);

static void u_undoredo(int undo);

static void u_undo_end(int did_undo, int absolute);

static void u_add_time(char_u *buf, size_t buflen, time_t tt);

static void u_freeheader(buf_T *buf, u_header_T *uhp, u_header_T **uhpp);

static void u_freebranch(buf_T *buf, u_header_T *uhp, u_header_T **uhpp);

static void u_freeentries(buf_T *buf, u_header_T *uhp, u_header_T **uhpp);

static void u_freeentry(u_entry_T *, long);

static void corruption_error(char *mesg, char_u *file_name);

static void u_free_uhp(u_header_T *uhp);

static void put_header_ptr(bufinfo_T *bi, u_header_T *uhp);

static int serialize_uep(bufinfo_T *bi, u_entry_T *uep);

static u_entry_T *unserialize_uep(bufinfo_T *bi, int *error, char_u *file_name);

static void serialize_pos(bufinfo_T *bi, pos_T pos);

static void unserialize_pos(bufinfo_T *bi, pos_T *pos);

static void serialize_visualinfo(bufinfo_T *bi, visualinfo_T *info);

static void unserialize_visualinfo(bufinfo_T *bi, visualinfo_T *info);

static char_u *u_save_line(linenr_T);

# define UF_VERSION		2	/* 2-byte undofile version number */

static char_u e_not_open[] = N_("E828: Cannot open undo file for writing: %s");


undo_write_bytes(bufinfo_T *bi, long_u nr, int len)
{
    char_u  buf[8];
    int	    i;
    int	    bufi = 0;

    for (i = len - 1; i >= 0; --i)
	buf[bufi++] = (char_u)(nr >> (i * 8));
    return undo_write(bi, buf, (size_t)len);
}

#define UH_MAGIC 0x18dade	/* value for uh_magic when in use */

#define UE_MAGIC 0xabc123	/* value for ue_magic when in use */

typedef struct {
    buf_T	*bi_buf;
    FILE	*bi_fp;
#ifdef FEAT_CRYPT
    cryptstate_T *bi_state;
    char_u	*bi_buffer; /* CRYPT_BUF_SIZE, NULL when not buffering */
    size_t	bi_used;    /* bytes written to/read from bi_buffer */
    size_t	bi_avail;   /* bytes available in bi_buffer */
#endif
} bufinfo_T;

static void u_doit(int count);

static void u_undoredo(int undo);

static void u_undo_end(int did_undo, int absolute);

static void u_add_time(char_u *buf, size_t buflen, time_t tt);

static void u_freeheader(buf_T *buf, u_header_T *uhp, u_header_T **uhpp);

static void u_freebranch(buf_T *buf, u_header_T *uhp, u_header_T **uhpp);

static void u_freeentries(buf_T *buf, u_header_T *uhp, u_header_T **uhpp);

static void corruption_error(char *mesg, char_u *file_name);

static int undo_write(bufinfo_T *bi, char_u *ptr, size_t len);

static int undo_flush(bufinfo_T *bi);

static int fwrite_crypt(bufinfo_T *bi, char_u *ptr, size_t len);

static int undo_write_bytes(bufinfo_T *bi, long_u nr, int len);

static void put_header_ptr(bufinfo_T *bi, u_header_T *uhp);

static int undo_read_4c(bufinfo_T *bi);

static int undo_read_2c(bufinfo_T *bi);

static int undo_read_byte(bufinfo_T *bi);

static time_t undo_read_time(bufinfo_T *bi);

static int undo_read(bufinfo_T *bi, char_u *buffer, size_t size);

static char_u *read_string_decrypt(bufinfo_T *bi, int len);

static int serialize_header(bufinfo_T *bi, char_u *hash);

static int serialize_uhp(bufinfo_T *bi, u_header_T *uhp);

static u_header_T *unserialize_uhp(bufinfo_T *bi, char_u *file_name);

static int serialize_uep(bufinfo_T *bi, u_entry_T *uep);

static u_entry_T *unserialize_uep(bufinfo_T *bi, int *error, char_u *file_name);

static void serialize_pos(bufinfo_T *bi, pos_T pos);

static void unserialize_pos(bufinfo_T *bi, pos_T *pos);

static void serialize_visualinfo(bufinfo_T *bi, visualinfo_T *info);

static void unserialize_visualinfo(bufinfo_T *bi, visualinfo_T *info);

#define U_ALLOC_LINE(size) lalloc((long_u)(size), FALSE)

static char_u *u_save_line(linenr_T);

static int	undo_undoes = FALSE;

static int	lastmark = 0;

static int seen_b_u_curhead;

static int seen_b_u_newhead;

static int header_count;

static char_u e_not_open[] = N_("E828: Cannot open undo file for writing: %s");


u_clearall(buf_T *buf)
{
    buf->b_u_newhead = buf->b_u_oldhead = buf->b_u_curhead = NULL;
    buf->b_u_synced = TRUE;
    buf->b_u_numhead = 0;
    buf->b_u_line_ptr = NULL;
    buf->b_u_line_lnum = 0;
}


u_undo_end(
    int		did_undo,	/* just did an undo */
    int		absolute)	/* used ":undo N" */
{
    char	*msgstr;
    u_header_T	*uhp;
    char_u	msgbuf[80];

#ifdef FEAT_FOLDING
    if ((fdo_flags & FDO_UNDO) && KeyTyped)
	foldOpenCursor();
#endif

    if (global_busy	    /* no messages now, wait until global is finished */
	    || !messaging())  /* 'lazyredraw' set, don't do messages now */
	return;

    if (curbuf->b_ml.ml_flags & ML_EMPTY)
	--u_newcount;

    u_oldcount -= u_newcount;
    if (u_oldcount == -1)
	msgstr = N_("more line");
    else if (u_oldcount < 0)
	msgstr = N_("more lines");
    else if (u_oldcount == 1)
	msgstr = N_("line less");
    else if (u_oldcount > 1)
	msgstr = N_("fewer lines");
    else
    {
	u_oldcount = u_newcount;
	if (u_newcount == 1)
	    msgstr = N_("change");
	else
	    msgstr = N_("changes");
    }

    if (curbuf->b_u_curhead != NULL)
    {
	/* For ":undo N" we prefer a "after #N" message. */
	if (absolute && curbuf->b_u_curhead->uh_next.ptr != NULL)
	{
	    uhp = curbuf->b_u_curhead->uh_next.ptr;
	    did_undo = FALSE;
	}
	else if (did_undo)
	    uhp = curbuf->b_u_curhead;
	else
	    uhp = curbuf->b_u_curhead->uh_next.ptr;
    }
    else
	uhp = curbuf->b_u_newhead;

    if (uhp == NULL)
	*msgbuf = NUL;
    else
	u_add_time(msgbuf, sizeof(msgbuf), uhp->uh_time);

#ifdef FEAT_CONCEAL
    {
	win_T	*wp;

	FOR_ALL_WINDOWS(wp)
	{
	    if (wp->w_buffer == curbuf && wp->w_p_cole > 0)
		redraw_win_later(wp, NOT_VALID);
	}
    }
#endif

    smsg((char_u *)_("%ld %s; %s #%ld  %s"),
	    u_oldcount < 0 ? -u_oldcount : u_oldcount,
	    _(msgstr),
	    did_undo ? _("before") : _("after"),
	    uhp == NULL ? 0L : uhp->uh_seq,
	    msgbuf);
}

typedef struct {
    buf_T	*bi_buf;
    FILE	*bi_fp;
#ifdef FEAT_CRYPT
    cryptstate_T *bi_state;
    char_u	*bi_buffer; /* CRYPT_BUF_SIZE, NULL when not buffering */
    size_t	bi_used;    /* bytes written to/read from bi_buffer */
    size_t	bi_avail;   /* bytes available in bi_buffer */
#endif
} bufinfo_T;

static void u_unch_branch(u_header_T *uhp);

static void u_doit(int count);

static void u_undoredo(int undo);

static void u_undo_end(int did_undo, int absolute);

static void u_add_time(char_u *buf, size_t buflen, time_t tt);

static void u_freeheader(buf_T *buf, u_header_T *uhp, u_header_T **uhpp);

static void u_freebranch(buf_T *buf, u_header_T *uhp, u_header_T **uhpp);

static void u_freeentries(buf_T *buf, u_header_T *uhp, u_header_T **uhpp);

static void corruption_error(char *mesg, char_u *file_name);

static void u_free_uhp(u_header_T *uhp);

static int undo_write(bufinfo_T *bi, char_u *ptr, size_t len);

static int undo_flush(bufinfo_T *bi);

static int fwrite_crypt(bufinfo_T *bi, char_u *ptr, size_t len);

static int undo_write_bytes(bufinfo_T *bi, long_u nr, int len);

static void put_header_ptr(bufinfo_T *bi, u_header_T *uhp);

static int undo_read_4c(bufinfo_T *bi);

static int undo_read_2c(bufinfo_T *bi);

static int undo_read_byte(bufinfo_T *bi);

static int undo_read(bufinfo_T *bi, char_u *buffer, size_t size);

static char_u *read_string_decrypt(bufinfo_T *bi, int len);

static int serialize_header(bufinfo_T *bi, char_u *hash);

static int serialize_uhp(bufinfo_T *bi, u_header_T *uhp);

static u_header_T *unserialize_uhp(bufinfo_T *bi, char_u *file_name);

static int serialize_uep(bufinfo_T *bi, u_entry_T *uep);

static u_entry_T *unserialize_uep(bufinfo_T *bi, int *error, char_u *file_name);

#define U_ALLOC_LINE(size) lalloc((long_u)(size), FALSE)

static char_u *u_save_line(linenr_T);

static long	u_newcount, u_oldcount;

static int	undo_undoes = FALSE;

static int	lastmark = 0;

static int seen_b_u_curhead;

static int seen_b_u_newhead;

static int header_count;

# define UF_HEADER_END_MAGIC	0xe7aa	/* magic after last header */

# define UF_ENTRY_END_MAGIC	0x3581	/* magic after last entry */

static char_u e_not_open[] = N_("E828: Cannot open undo file for writing: %s");


u_add_time(char_u *buf, size_t buflen, time_t tt)
{
#ifdef HAVE_STRFTIME
    struct tm	*curtime;

    if (vim_time() - tt >= 100)
    {
	curtime = localtime(&tt);
	if (vim_time() - tt < (60L * 60L * 12L))
	    /* within 12 hours */
	    (void)strftime((char *)buf, buflen, "%H:%M:%S", curtime);
	else
	    /* longer ago */
	    (void)strftime((char *)buf, buflen, "%Y/%m/%d %H:%M:%S", curtime);
    }
    else
#endif
	vim_snprintf((char *)buf, buflen, _("%ld seconds ago"),
						      (long)(vim_time() - tt));
}

typedef struct {
    buf_T	*bi_buf;
    FILE	*bi_fp;
#ifdef FEAT_CRYPT
    cryptstate_T *bi_state;
    char_u	*bi_buffer; /* CRYPT_BUF_SIZE, NULL when not buffering */
    size_t	bi_used;    /* bytes written to/read from bi_buffer */
    size_t	bi_avail;   /* bytes available in bi_buffer */
#endif
} bufinfo_T;

static long get_undolevel(void);

static void u_unch_branch(u_header_T *uhp);

static u_entry_T *u_get_headentry(void);

static void u_getbot(void);

static void u_doit(int count);

static void u_undoredo(int undo);

static void u_undo_end(int did_undo, int absolute);

static void u_add_time(char_u *buf, size_t buflen, time_t tt);

static void u_freeheader(buf_T *buf, u_header_T *uhp, u_header_T **uhpp);

static void u_freebranch(buf_T *buf, u_header_T *uhp, u_header_T **uhpp);

static void u_freeentries(buf_T *buf, u_header_T *uhp, u_header_T **uhpp);

static void u_freeentry(u_entry_T *, long);

static void corruption_error(char *mesg, char_u *file_name);

static void u_free_uhp(u_header_T *uhp);

static int undo_write(bufinfo_T *bi, char_u *ptr, size_t len);

static int fwrite_crypt(bufinfo_T *bi, char_u *ptr, size_t len);

static void put_header_ptr(bufinfo_T *bi, u_header_T *uhp);

static time_t undo_read_time(bufinfo_T *bi);

static int undo_read(bufinfo_T *bi, char_u *buffer, size_t size);

static char_u *read_string_decrypt(bufinfo_T *bi, int len);

static int serialize_header(bufinfo_T *bi, char_u *hash);

static u_header_T *unserialize_uhp(bufinfo_T *bi, char_u *file_name);

static u_entry_T *unserialize_uep(bufinfo_T *bi, int *error, char_u *file_name);

static void serialize_pos(bufinfo_T *bi, pos_T pos);

static void unserialize_pos(bufinfo_T *bi, pos_T *pos);

static void serialize_visualinfo(bufinfo_T *bi, visualinfo_T *info);

static void unserialize_visualinfo(bufinfo_T *bi, visualinfo_T *info);

static char_u *u_save_line(linenr_T);

static long	u_newcount, u_oldcount;

static char_u e_not_open[] = N_("E828: Cannot open undo file for writing: %s");


u_saveline(linenr_T lnum)
{
    if (lnum == curbuf->b_u_line_lnum)	    /* line is already saved */
	return;
    if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count) /* should never happen */
	return;
    u_clearline();
    curbuf->b_u_line_lnum = lnum;
    if (curwin->w_cursor.lnum == lnum)
	curbuf->b_u_line_colnr = curwin->w_cursor.col;
    else
	curbuf->b_u_line_colnr = 0;
    if ((curbuf->b_u_line_ptr = u_save_line(lnum)) == NULL)
	do_outofmem_msg((long_u)0);
}

typedef struct {
    buf_T	*bi_buf;
    FILE	*bi_fp;
#ifdef FEAT_CRYPT
    cryptstate_T *bi_state;
    char_u	*bi_buffer; /* CRYPT_BUF_SIZE, NULL when not buffering */
    size_t	bi_used;    /* bytes written to/read from bi_buffer */
    size_t	bi_avail;   /* bytes available in bi_buffer */
#endif
} bufinfo_T;

static int undo_write_bytes(bufinfo_T *bi, long_u nr, int len);

#define U_ALLOC_LINE(size) lalloc((long_u)(size), FALSE)

static char_u *u_save_line(linenr_T);


u_inssub(linenr_T lnum)
{
    if (undo_off)
	return OK;

    return (u_savecommon(lnum - 1, lnum, lnum + 1, FALSE));
}

#define U_ALLOC_LINE(size) lalloc((long_u)(size), FALSE)

static char_u *u_save_line(linenr_T);

static int	undo_undoes = FALSE;


curbufIsChanged(void)
{
    return
#ifdef FEAT_QUICKFIX
	!bt_dontwrite(curbuf) &&
#endif
	(curbuf->b_changed || file_ff_differs(curbuf, TRUE));
}

typedef struct {
    buf_T	*bi_buf;
    FILE	*bi_fp;
#ifdef FEAT_CRYPT
    cryptstate_T *bi_state;
    char_u	*bi_buffer; /* CRYPT_BUF_SIZE, NULL when not buffering */
    size_t	bi_used;    /* bytes written to/read from bi_buffer */
    size_t	bi_avail;   /* bytes available in bi_buffer */
#endif
} bufinfo_T;

static long get_undolevel(void);

static void u_unch_branch(u_header_T *uhp);

static u_entry_T *u_get_headentry(void);

static void u_getbot(void);

static void u_doit(int count);

static void u_undoredo(int undo);

static void u_undo_end(int did_undo, int absolute);

static void u_add_time(char_u *buf, size_t buflen, time_t tt);

static void u_freeheader(buf_T *buf, u_header_T *uhp, u_header_T **uhpp);

static void u_freebranch(buf_T *buf, u_header_T *uhp, u_header_T **uhpp);

static void u_freeentries(buf_T *buf, u_header_T *uhp, u_header_T **uhpp);

static void u_freeentry(u_entry_T *, long);

static void corruption_error(char *mesg, char_u *file_name);

static void u_free_uhp(u_header_T *uhp);

static void put_header_ptr(bufinfo_T *bi, u_header_T *uhp);

static void serialize_pos(bufinfo_T *bi, pos_T pos);

static void unserialize_pos(bufinfo_T *bi, pos_T *pos);

static void serialize_visualinfo(bufinfo_T *bi, visualinfo_T *info);

static void unserialize_visualinfo(bufinfo_T *bi, visualinfo_T *info);


u_undo(int count)
{
    /*
     * If we get an undo command while executing a macro, we behave like the
     * original vi. If this happens twice in one macro the result will not
     * be compatible.
     */
    if (curbuf->b_u_synced == FALSE)
    {
	u_sync(TRUE);
	count = 1;
    }

    if (vim_strchr(p_cpo, CPO_UNDO) == NULL)
	undo_undoes = TRUE;
    else
	undo_undoes = !undo_undoes;
    u_doit(count);
}

typedef struct {
    buf_T	*bi_buf;
    FILE	*bi_fp;
#ifdef FEAT_CRYPT
    cryptstate_T *bi_state;
    char_u	*bi_buffer; /* CRYPT_BUF_SIZE, NULL when not buffering */
    size_t	bi_used;    /* bytes written to/read from bi_buffer */
    size_t	bi_avail;   /* bytes available in bi_buffer */
#endif
} bufinfo_T;

static void u_doit(int count);

static void u_undoredo(int undo);

static void u_undo_end(int did_undo, int absolute);

static int undo_write(bufinfo_T *bi, char_u *ptr, size_t len);

static int undo_flush(bufinfo_T *bi);

static int fwrite_crypt(bufinfo_T *bi, char_u *ptr, size_t len);

static int undo_write_bytes(bufinfo_T *bi, long_u nr, int len);

static int undo_read_4c(bufinfo_T *bi);

static int undo_read_2c(bufinfo_T *bi);

static int undo_read_byte(bufinfo_T *bi);

static int undo_read(bufinfo_T *bi, char_u *buffer, size_t size);

static char_u *read_string_decrypt(bufinfo_T *bi, int len);

static int serialize_header(bufinfo_T *bi, char_u *hash);

static int serialize_uhp(bufinfo_T *bi, u_header_T *uhp);

static int serialize_uep(bufinfo_T *bi, u_entry_T *uep);

static u_entry_T *unserialize_uep(bufinfo_T *bi, int *error, char_u *file_name);

#define U_ALLOC_LINE(size) lalloc((long_u)(size), FALSE)

static int	undo_undoes = FALSE;

static int	lastmark = 0;

static int seen_b_u_curhead;

static int seen_b_u_newhead;

static int header_count;

static char_u e_not_open[] = N_("E828: Cannot open undo file for writing: %s");


ares_parse_naptr_reply (const unsigned char *abuf, int alen,
                        struct ares_naptr_reply **naptr_out)
{
  unsigned int qdcount, ancount, i;
  const unsigned char *aptr, *vptr;
  int status, rr_type, rr_class, rr_len;
  long len;
  char *hostname = NULL, *rr_name = NULL;
  struct ares_naptr_reply *naptr_head = NULL;
  struct ares_naptr_reply *naptr_last = NULL;
  struct ares_naptr_reply *naptr_curr;

  /* Set *naptr_out to NULL for all failure cases. */
  *naptr_out = NULL;

  /* Give up if abuf doesn't have room for a header. */
  if (alen < HFIXEDSZ)
    return ARES_EBADRESP;

  /* Fetch the question and answer count from the header. */
  qdcount = DNS_HEADER_QDCOUNT (abuf);
  ancount = DNS_HEADER_ANCOUNT (abuf);
  if (qdcount != 1)
    return ARES_EBADRESP;
  if (ancount == 0)
    return ARES_ENODATA;

  /* Expand the name from the question, and skip past the question. */
  aptr = abuf + HFIXEDSZ;
  status = ares_expand_name (aptr, abuf, alen, &hostname, &len);
  if (status != ARES_SUCCESS)
    return status;

  if (aptr + len + QFIXEDSZ > abuf + alen)
    {
      ares_free (hostname);
      return ARES_EBADRESP;
    }
  aptr += len + QFIXEDSZ;

  /* Examine each answer resource record (RR) in turn. */
  for (i = 0; i < ancount; i++)
    {
      /* Decode the RR up to the data field. */
      status = ares_expand_name (aptr, abuf, alen, &rr_name, &len);
      if (status != ARES_SUCCESS)
        {
          break;
        }
      aptr += len;
      if (aptr + RRFIXEDSZ > abuf + alen)
        {
          status = ARES_EBADRESP;
          break;
        }
      rr_type = DNS_RR_TYPE (aptr);
      rr_class = DNS_RR_CLASS (aptr);
      rr_len = DNS_RR_LEN (aptr);
      aptr += RRFIXEDSZ;
      if (aptr + rr_len > abuf + alen)
        {
          status = ARES_EBADRESP;
          break;
        }

      /* Check if we are really looking at a NAPTR record */
      if (rr_class == C_IN && rr_type == T_NAPTR)
        {
          /* parse the NAPTR record itself */

          /* RR must contain at least 7 bytes = 2 x int16 + 3 x name */
          if (rr_len < 7)
            {
              status = ARES_EBADRESP;
              break;
            }

          /* Allocate storage for this NAPTR answer appending it to the list */
          naptr_curr = ares_malloc_data(ARES_DATATYPE_NAPTR_REPLY);
          if (!naptr_curr)
            {
              status = ARES_ENOMEM;
              break;
            }
          if (naptr_last)
            {
              naptr_last->next = naptr_curr;
            }
          else
            {
              naptr_head = naptr_curr;
            }
          naptr_last = naptr_curr;

          vptr = aptr;
          naptr_curr->order = DNS__16BIT(vptr);
          vptr += sizeof(unsigned short);
          naptr_curr->preference = DNS__16BIT(vptr);
          vptr += sizeof(unsigned short);

          status = ares_expand_string(vptr, abuf, alen, &naptr_curr->flags, &len);
          if (status != ARES_SUCCESS)
            break;
          vptr += len;

          status = ares_expand_string(vptr, abuf, alen, &naptr_curr->service, &len);
          if (status != ARES_SUCCESS)
            break;
          vptr += len;

          status = ares_expand_string(vptr, abuf, alen, &naptr_curr->regexp, &len);
          if (status != ARES_SUCCESS)
            break;
          vptr += len;

          status = ares_expand_name(vptr, abuf, alen, &naptr_curr->replacement, &len);
          if (status != ARES_SUCCESS)
            break;
        }

      /* Don't lose memory in the next iteration */
      ares_free (rr_name);
      rr_name = NULL;

      /* Move on to the next record */
      aptr += rr_len;
    }

  if (hostname)
    ares_free (hostname);
  if (rr_name)
    ares_free (rr_name);

  /* clean up on error */
  if (status != ARES_SUCCESS)
    {
      if (naptr_head)
        ares_free_data (naptr_head);
      return status;
    }

  /* everything looks fine, return the data */
  *naptr_out = naptr_head;

  return ARES_SUCCESS;
}

ares_parse_naptr_reply: make buffer length check more accurate

9478908a490a6bf009ba58d81de8c1d06d50a117 introduced a length check
for records parsed by `ares_parse_naptr_reply()`. However, that
function is designed to parse replies which also contain non-NAPTR
records; for A records, the `rr_len > 7` check will fail as there
are only 4 bytes of payload.
In particular, parsing ANY replies for NAPTR records was broken
by that patch.

Fix that by moving the check into the case in which it is already
known that the record is a NAPTR record.

#define T_NAPTR 35 /* naming authority pointer */