xfs_dinode_verify(
	struct xfs_mount	*mp,
	xfs_ino_t		ino,
	struct xfs_dinode	*dip)
{
	uint16_t		mode;
	uint16_t		flags;
	uint64_t		flags2;
	uint64_t		di_size;

	if (dip->di_magic != cpu_to_be16(XFS_DINODE_MAGIC))
		return __this_address;

	/* Verify v3 integrity information first */
	if (dip->di_version >= 3) {
		if (!xfs_sb_version_hascrc(&mp->m_sb))
			return __this_address;
		if (!xfs_verify_cksum((char *)dip, mp->m_sb.sb_inodesize,
				      XFS_DINODE_CRC_OFF))
			return __this_address;
		if (be64_to_cpu(dip->di_ino) != ino)
			return __this_address;
		if (!uuid_equal(&dip->di_uuid, &mp->m_sb.sb_meta_uuid))
			return __this_address;
	}

	/* don't allow invalid i_size */
	di_size = be64_to_cpu(dip->di_size);
	if (di_size & (1ULL << 63))
		return __this_address;

	mode = be16_to_cpu(dip->di_mode);
	if (mode && xfs_mode_to_ftype(mode) == XFS_DIR3_FT_UNKNOWN)
		return __this_address;

	/* No zero-length symlinks/dirs. */
	if ((S_ISLNK(mode) || S_ISDIR(mode)) && di_size == 0)
		return __this_address;

	/* Fork checks carried over from xfs_iformat_fork */
	if (mode &&
	    be32_to_cpu(dip->di_nextents) + be16_to_cpu(dip->di_anextents) >
			be64_to_cpu(dip->di_nblocks))
		return __this_address;

	if (mode && XFS_DFORK_BOFF(dip) > mp->m_sb.sb_inodesize)
		return __this_address;

	flags = be16_to_cpu(dip->di_flags);

	if (mode && (flags & XFS_DIFLAG_REALTIME) && !mp->m_rtdev_targp)
		return __this_address;

	/* Do we have appropriate data fork formats for the mode? */
	switch (mode & S_IFMT) {
	case S_IFIFO:
	case S_IFCHR:
	case S_IFBLK:
	case S_IFSOCK:
		if (dip->di_format != XFS_DINODE_FMT_DEV)
			return __this_address;
		break;
	case S_IFREG:
	case S_IFLNK:
	case S_IFDIR:
		switch (dip->di_format) {
		case XFS_DINODE_FMT_LOCAL:
			/*
			 * no local regular files yet
			 */
			if (S_ISREG(mode))
				return __this_address;
			if (di_size > XFS_DFORK_DSIZE(dip, mp))
				return __this_address;
			if (dip->di_nextents)
				return __this_address;
			/* fall through */
		case XFS_DINODE_FMT_EXTENTS:
		case XFS_DINODE_FMT_BTREE:
			break;
		default:
			return __this_address;
		}
		break;
	case 0:
		/* Uninitialized inode ok. */
		break;
	default:
		return __this_address;
	}

	if (XFS_DFORK_Q(dip)) {
		switch (dip->di_aformat) {
		case XFS_DINODE_FMT_LOCAL:
			if (dip->di_anextents)
				return __this_address;
		/* fall through */
		case XFS_DINODE_FMT_EXTENTS:
		case XFS_DINODE_FMT_BTREE:
			break;
		default:
			return __this_address;
		}
	} else {
		/*
		 * If there is no fork offset, this may be a freshly-made inode
		 * in a new disk cluster, in which case di_aformat is zeroed.
		 * Otherwise, such an inode must be in EXTENTS format; this goes
		 * for freed inodes as well.
		 */
		switch (dip->di_aformat) {
		case 0:
		case XFS_DINODE_FMT_EXTENTS:
			break;
		default:
			return __this_address;
		}
		if (dip->di_anextents)
			return __this_address;
	}

	/* only version 3 or greater inodes are extensively verified here */
	if (dip->di_version < 3)
		return NULL;

	flags2 = be64_to_cpu(dip->di_flags2);

	/* don't allow reflink/cowextsize if we don't have reflink */
	if ((flags2 & (XFS_DIFLAG2_REFLINK | XFS_DIFLAG2_COWEXTSIZE)) &&
            !xfs_sb_version_hasreflink(&mp->m_sb))
		return __this_address;

	/* only regular files get reflink */
	if ((flags2 & XFS_DIFLAG2_REFLINK) && (mode & S_IFMT) != S_IFREG)
		return __this_address;

	/* don't let reflink and realtime mix */
	if ((flags2 & XFS_DIFLAG2_REFLINK) && (flags & XFS_DIFLAG_REALTIME))
		return __this_address;

	/* don't let reflink and dax mix */
	if ((flags2 & XFS_DIFLAG2_REFLINK) && (flags2 & XFS_DIFLAG2_DAX))
		return __this_address;

	return NULL;
}


xfs_iread(
	xfs_mount_t	*mp,
	xfs_trans_t	*tp,
	xfs_inode_t	*ip,
	uint		iget_flags)
{
	xfs_buf_t	*bp;
	xfs_dinode_t	*dip;
	xfs_failaddr_t	fa;
	int		error;

	/*
	 * Fill in the location information in the in-core inode.
	 */
	error = xfs_imap(mp, tp, ip->i_ino, &ip->i_imap, iget_flags);
	if (error)
		return error;

	/* shortcut IO on inode allocation if possible */
	if ((iget_flags & XFS_IGET_CREATE) &&
	    xfs_sb_version_hascrc(&mp->m_sb) &&
	    !(mp->m_flags & XFS_MOUNT_IKEEP)) {
		/* initialise the on-disk inode core */
		memset(&ip->i_d, 0, sizeof(ip->i_d));
		VFS_I(ip)->i_generation = prandom_u32();
		ip->i_d.di_version = 3;
		return 0;
	}

	/*
	 * Get pointers to the on-disk inode and the buffer containing it.
	 */
	error = xfs_imap_to_bp(mp, tp, &ip->i_imap, &dip, &bp, 0, iget_flags);
	if (error)
		return error;

	/* even unallocated inodes are verified */
	fa = xfs_dinode_verify(mp, ip->i_ino, dip);
	if (fa) {
		xfs_inode_verifier_error(ip, -EFSCORRUPTED, "dinode", dip,
				sizeof(*dip), fa);
		error = -EFSCORRUPTED;
		goto out_brelse;
	}

	/*
	 * If the on-disk inode is already linked to a directory
	 * entry, copy all of the inode into the in-core inode.
	 * xfs_iformat_fork() handles copying in the inode format
	 * specific information.
	 * Otherwise, just get the truly permanent information.
	 */
	if (dip->di_mode) {
		xfs_inode_from_disk(ip, dip);
		error = xfs_iformat_fork(ip, dip);
		if (error)  {
#ifdef DEBUG
			xfs_alert(mp, "%s: xfs_iformat() returned error %d",
				__func__, error);
#endif /* DEBUG */
			goto out_brelse;
		}
	} else {
		/*
		 * Partial initialisation of the in-core inode. Just the bits
		 * that xfs_ialloc won't overwrite or relies on being correct.
		 */
		ip->i_d.di_version = dip->di_version;
		VFS_I(ip)->i_generation = be32_to_cpu(dip->di_gen);
		ip->i_d.di_flushiter = be16_to_cpu(dip->di_flushiter);

		/*
		 * Make sure to pull in the mode here as well in
		 * case the inode is released without being used.
		 * This ensures that xfs_inactive() will see that
		 * the inode is already free and not try to mess
		 * with the uninitialized part of it.
		 */
		VFS_I(ip)->i_mode = 0;
	}

	ASSERT(ip->i_d.di_version >= 2);
	ip->i_delayed_blks = 0;

	/*
	 * Mark the buffer containing the inode as something to keep
	 * around for a while.  This helps to keep recently accessed
	 * meta-data in-core longer.
	 */
	xfs_buf_set_ref(bp, XFS_INO_REF);

	/*
	 * Use xfs_trans_brelse() to release the buffer containing the on-disk
	 * inode, because it was acquired with xfs_trans_read_buf() in
	 * xfs_imap_to_bp() above.  If tp is NULL, this is just a normal
	 * brelse().  If we're within a transaction, then xfs_trans_brelse()
	 * will only release the buffer if it is not dirty within the
	 * transaction.  It will be OK to release the buffer in this case,
	 * because inodes on disk are never destroyed and we will be locking the
	 * new in-core inode before putting it in the cache where other
	 * processes can find it.  Thus we don't have to worry about the inode
	 * being changed just because we released the buffer.
	 */
 out_brelse:
	xfs_trans_brelse(tp, bp);
	return error;
}

xfs: enhance dinode verifier

Add several more validations to xfs_dinode_verify:

- For LOCAL data fork formats, di_nextents must be 0.
- For LOCAL attr fork formats, di_anextents must be 0.
- For inodes with no attr fork offset,
  - format must be XFS_DINODE_FMT_EXTENTS if set at all
  - di_anextents must be 0.

Thanks to dchinner for pointing out a couple related checks I had
forgotten to add.

Signed-off-by: Eric Sandeen <sandeen@redhat.com>
Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=199377
Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com>
Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>


xfs_inobp_check(
	xfs_mount_t	*mp,
	xfs_buf_t	*bp)
{
	int		i;
	int		j;
	xfs_dinode_t	*dip;

	j = mp->m_inode_cluster_size >> mp->m_sb.sb_inodelog;

	for (i = 0; i < j; i++) {
		dip = xfs_buf_offset(bp, i * mp->m_sb.sb_inodesize);
		if (!dip->di_next_unlinked)  {
			xfs_alert(mp,
	"Detected bogus zero next_unlinked field in inode %d buffer 0x%llx.",
				i, (long long)bp->b_bn);
		}
	}
}


xfs_dinode_good_version(
	struct xfs_mount *mp,
	__u8		version)
{
	if (xfs_sb_version_hascrc(&mp->m_sb))
		return version == 3;

	return version == 1 || version == 2;
}


xfs_inode_buf_read_verify(
	struct xfs_buf	*bp)
{
	xfs_inode_buf_verify(bp, false);
}


xfs_inode_buf_readahead_verify(
	struct xfs_buf	*bp)
{
	xfs_inode_buf_verify(bp, true);
}


xfs_inode_from_disk(
	struct xfs_inode	*ip,
	struct xfs_dinode	*from)
{
	struct xfs_icdinode	*to = &ip->i_d;
	struct inode		*inode = VFS_I(ip);

	/*
	 * Convert v1 inodes immediately to v2 inode format as this is the
	 * minimum inode version format we support in the rest of the code.
	 */
	to->di_version = from->di_version;
	if (to->di_version == 1) {
		set_nlink(inode, be16_to_cpu(from->di_onlink));
		to->di_projid_lo = 0;
		to->di_projid_hi = 0;
		to->di_version = 2;
	} else {
		set_nlink(inode, be32_to_cpu(from->di_nlink));
		to->di_projid_lo = be16_to_cpu(from->di_projid_lo);
		to->di_projid_hi = be16_to_cpu(from->di_projid_hi);
	}

	to->di_format = from->di_format;
	to->di_uid = be32_to_cpu(from->di_uid);
	to->di_gid = be32_to_cpu(from->di_gid);
	to->di_flushiter = be16_to_cpu(from->di_flushiter);

	/*
	 * Time is signed, so need to convert to signed 32 bit before
	 * storing in inode timestamp which may be 64 bit. Otherwise
	 * a time before epoch is converted to a time long after epoch
	 * on 64 bit systems.
	 */
	inode->i_atime.tv_sec = (int)be32_to_cpu(from->di_atime.t_sec);
	inode->i_atime.tv_nsec = (int)be32_to_cpu(from->di_atime.t_nsec);
	inode->i_mtime.tv_sec = (int)be32_to_cpu(from->di_mtime.t_sec);
	inode->i_mtime.tv_nsec = (int)be32_to_cpu(from->di_mtime.t_nsec);
	inode->i_ctime.tv_sec = (int)be32_to_cpu(from->di_ctime.t_sec);
	inode->i_ctime.tv_nsec = (int)be32_to_cpu(from->di_ctime.t_nsec);
	inode->i_generation = be32_to_cpu(from->di_gen);
	inode->i_mode = be16_to_cpu(from->di_mode);

	to->di_size = be64_to_cpu(from->di_size);
	to->di_nblocks = be64_to_cpu(from->di_nblocks);
	to->di_extsize = be32_to_cpu(from->di_extsize);
	to->di_nextents = be32_to_cpu(from->di_nextents);
	to->di_anextents = be16_to_cpu(from->di_anextents);
	to->di_forkoff = from->di_forkoff;
	to->di_aformat	= from->di_aformat;
	to->di_dmevmask	= be32_to_cpu(from->di_dmevmask);
	to->di_dmstate	= be16_to_cpu(from->di_dmstate);
	to->di_flags	= be16_to_cpu(from->di_flags);

	if (to->di_version == 3) {
		inode_set_iversion_queried(inode,
					   be64_to_cpu(from->di_changecount));
		to->di_crtime.t_sec = be32_to_cpu(from->di_crtime.t_sec);
		to->di_crtime.t_nsec = be32_to_cpu(from->di_crtime.t_nsec);
		to->di_flags2 = be64_to_cpu(from->di_flags2);
		to->di_cowextsize = be32_to_cpu(from->di_cowextsize);
	}
}


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

	undo_read(bi, buf, (size_t)4);
	n = ((unsigned)buf[0] << 24) + (buf[1] << 16) + (buf[2] << 8) + buf[3];
	return n;
    }
#endif
    return get4c(bi->bi_fp);
}

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)

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_undo_file_name(char_u *buf_ffname, int reading)
{
    char_u	*dirp;
    char_u	dir_name[IOSIZE + 1];
    char_u	*munged_name = NULL;
    char_u	*undo_file_name = NULL;
    int		dir_len;
    char_u	*p;
    stat_T	st;
    char_u	*ffname = buf_ffname;
#ifdef HAVE_READLINK
    char_u	fname_buf[MAXPATHL];
#endif

    if (ffname == NULL)
	return NULL;

#ifdef HAVE_READLINK
    /* Expand symlink in the file name, so that we put the undo file with the
     * actual file instead of with the symlink. */
    if (resolve_symlink(ffname, fname_buf) == OK)
	ffname = fname_buf;
#endif

    /* Loop over 'undodir'.  When reading find the first file that exists.
     * When not reading use the first directory that exists or ".". */
    dirp = p_udir;
    while (*dirp != NUL)
    {
	dir_len = copy_option_part(&dirp, dir_name, IOSIZE, ",");
	if (dir_len == 1 && dir_name[0] == '.')
	{
	    /* Use same directory as the ffname,
	     * "dir/name" -> "dir/.name.un~" */
	    undo_file_name = vim_strnsave(ffname, (int)(STRLEN(ffname) + 5));
	    if (undo_file_name == NULL)
		break;
	    p = gettail(undo_file_name);
#ifdef VMS
	    /* VMS can not handle more than one dot in the filenames
	     * use "dir/name" -> "dir/_un_name" - add _un_
	     * at the beginning to keep the extension */
	    mch_memmove(p + 4,  p, STRLEN(p) + 1);
	    mch_memmove(p, "_un_", 4);

#else
	    /* Use same directory as the ffname,
	     * "dir/name" -> "dir/.name.un~" */
	    mch_memmove(p + 1, p, STRLEN(p) + 1);
	    *p = '.';
	    STRCAT(p, ".un~");
#endif
	}
	else
	{
	    dir_name[dir_len] = NUL;
	    if (mch_isdir(dir_name))
	    {
		if (munged_name == NULL)
		{
		    munged_name = vim_strsave(ffname);
		    if (munged_name == NULL)
			return NULL;
		    for (p = munged_name; *p != NUL; mb_ptr_adv(p))
			if (vim_ispathsep(*p))
			    *p = '%';
		}
		undo_file_name = concat_fnames(dir_name, munged_name, TRUE);
	    }
	}

	/* When reading check if the file exists. */
	if (undo_file_name != NULL && (!reading
			       || mch_stat((char *)undo_file_name, &st) >= 0))
	    break;
	vim_free(undo_file_name);
	undo_file_name = NULL;
    }

    vim_free(munged_name);
    return undo_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 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 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 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 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");


ex_undolist(exarg_T *eap UNUSED)
{
    garray_T	ga;
    u_header_T	*uhp;
    int		mark;
    int		nomark;
    int		changes = 1;
    int		i;

    /*
     * 1: walk the tree to find all leafs, put the info in "ga".
     * 2: sort the lines
     * 3: display the list
     */
    mark = ++lastmark;
    nomark = ++lastmark;
    ga_init2(&ga, (int)sizeof(char *), 20);

    uhp = curbuf->b_u_oldhead;
    while (uhp != NULL)
    {
	if (uhp->uh_prev.ptr == NULL && uhp->uh_walk != nomark
						      && uhp->uh_walk != mark)
	{
	    if (ga_grow(&ga, 1) == FAIL)
		break;
	    vim_snprintf((char *)IObuff, IOSIZE, "%6ld %7ld  ",
							uhp->uh_seq, changes);
	    u_add_time(IObuff + STRLEN(IObuff), IOSIZE - STRLEN(IObuff),
								uhp->uh_time);
	    if (uhp->uh_save_nr > 0)
	    {
		while (STRLEN(IObuff) < 33)
		    STRCAT(IObuff, " ");
		vim_snprintf_add((char *)IObuff, IOSIZE,
						   "  %3ld", uhp->uh_save_nr);
	    }
	    ((char_u **)(ga.ga_data))[ga.ga_len++] = vim_strsave(IObuff);
	}

	uhp->uh_walk = mark;

	/* 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;
	    ++changes;
	}

	/* 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)
	{
	    uhp = uhp->uh_next.ptr;
	    --changes;
	}

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

    if (ga.ga_len == 0)
	MSG(_("Nothing to undo"));
    else
    {
	sort_strings((char_u **)ga.ga_data, ga.ga_len);

	msg_start();
	msg_puts_attr((char_u *)_("number changes  when               saved"),
							      hl_attr(HLF_T));
	for (i = 0; i < ga.ga_len && !got_int; ++i)
	{
	    msg_putchar('\n');
	    if (got_int)
		break;
	    msg_puts(((char_u **)ga.ga_data)[i]);
	}
	msg_end();

	ga_clear_strings(&ga);
    }
}

#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_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);

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;

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

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

# define UF_ENTRY_MAGIC		0xf518	/* magic at start of 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");


serialize_header(bufinfo_T *bi, char_u *hash)
{
    int		len;
    buf_T	*buf = bi->bi_buf;
    FILE	*fp = bi->bi_fp;
    char_u	time_buf[8];

    /* Start writing, first the magic marker and undo info version. */
    if (fwrite(UF_START_MAGIC, (size_t)UF_START_MAGIC_LEN, (size_t)1, fp) != 1)
	return FAIL;

    /* If the buffer is encrypted then all text bytes following will be
     * encrypted.  Numbers and other info is not crypted. */
#ifdef FEAT_CRYPT
    if (*buf->b_p_key != NUL)
    {
	char_u *header;
	int    header_len;

	undo_write_bytes(bi, (long_u)UF_VERSION_CRYPT, 2);
	bi->bi_state = crypt_create_for_writing(crypt_get_method_nr(buf),
					  buf->b_p_key, &header, &header_len);
	if (bi->bi_state == NULL)
	    return FAIL;
	len = (int)fwrite(header, (size_t)header_len, (size_t)1, fp);
	vim_free(header);
	if (len != 1)
	{
	    crypt_free_state(bi->bi_state);
	    bi->bi_state = NULL;
	    return FAIL;
	}

	if (crypt_whole_undofile(crypt_get_method_nr(buf)))
	{
	    bi->bi_buffer = alloc(CRYPT_BUF_SIZE);
	    if (bi->bi_buffer == NULL)
	    {
		crypt_free_state(bi->bi_state);
		bi->bi_state = NULL;
		return FAIL;
	    }
	    bi->bi_used = 0;
	}
    }
    else
#endif
	undo_write_bytes(bi, (long_u)UF_VERSION, 2);

    /* Write a hash of the buffer text, so that we can verify it is still the
     * same when reading the buffer text. */
    if (undo_write(bi, hash, (size_t)UNDO_HASH_SIZE) == FAIL)
	return FAIL;

    /* buffer-specific data */
    undo_write_bytes(bi, (long_u)buf->b_ml.ml_line_count, 4);
    len = buf->b_u_line_ptr != NULL ? (int)STRLEN(buf->b_u_line_ptr) : 0;
    undo_write_bytes(bi, (long_u)len, 4);
    if (len > 0 && fwrite_crypt(bi, buf->b_u_line_ptr, (size_t)len) == FAIL)
	return FAIL;
    undo_write_bytes(bi, (long_u)buf->b_u_line_lnum, 4);
    undo_write_bytes(bi, (long_u)buf->b_u_line_colnr, 4);

    /* Undo structures header data */
    put_header_ptr(bi, buf->b_u_oldhead);
    put_header_ptr(bi, buf->b_u_newhead);
    put_header_ptr(bi, buf->b_u_curhead);

    undo_write_bytes(bi, (long_u)buf->b_u_numhead, 4);
    undo_write_bytes(bi, (long_u)buf->b_u_seq_last, 4);
    undo_write_bytes(bi, (long_u)buf->b_u_seq_cur, 4);
    time_to_bytes(buf->b_u_time_cur, time_buf);
    undo_write(bi, time_buf, 8);

    /* Optional fields. */
    undo_write_bytes(bi, 4, 1);
    undo_write_bytes(bi, UF_LAST_SAVE_NR, 1);
    undo_write_bytes(bi, (long_u)buf->b_u_save_nr_last, 4);

    undo_write_bytes(bi, 0, 1);  /* end marker */

    return OK;
}

#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 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;

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

# define UF_START_MAGIC_LEN	9

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

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

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

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


serialize_pos(bufinfo_T *bi, pos_T pos)
{
    undo_write_bytes(bi, (long_u)pos.lnum, 4);
    undo_write_bytes(bi, (long_u)pos.col, 4);
#ifdef FEAT_VIRTUALEDIT
    undo_write_bytes(bi, (long_u)pos.coladd, 4);
#else
    undo_write_bytes(bi, (long_u)0, 4);
#endif
}

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_blockfree(buf_T *buf)
{
    while (buf->b_u_oldhead != NULL)
	u_freeheader(buf, buf->b_u_oldhead, NULL);
    vim_free(buf->b_u_line_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 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);


fwrite_crypt(bufinfo_T *bi, char_u *ptr, size_t len)
{
#ifdef FEAT_CRYPT
    char_u  *copy;
    char_u  small_buf[100];
    size_t  i;

    if (bi->bi_state != NULL && bi->bi_buffer == NULL)
    {
	/* crypting every piece of text separately */
	if (len < 100)
	    copy = small_buf;  /* no malloc()/free() for short strings */
	else
	{
	    copy = lalloc(len, FALSE);
	    if (copy == NULL)
		return 0;
	}
	crypt_encode(bi->bi_state, ptr, len, copy);
	i = fwrite(copy, len, (size_t)1, bi->bi_fp);
	if (copy != small_buf)
	    vim_free(copy);
	return i == 1 ? OK : FAIL;
    }
#endif
    return undo_write(bi, ptr, 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_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 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 char_u e_not_open[] = N_("E828: Cannot open undo file for writing: %s");


undo_read_byte(bufinfo_T *bi)
{
#ifdef FEAT_CRYPT
    if (bi->bi_buffer != NULL)
    {
	char_u  buf[1];

	undo_read(bi, buf, (size_t)1);
	return buf[0];
    }
#endif
    return getc(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_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 char_u e_not_open[] = N_("E828: Cannot open undo file for writing: %s");


ex_undojoin(exarg_T *eap UNUSED)
{
    if (curbuf->b_u_newhead == NULL)
	return;		    /* nothing changed before */
    if (curbuf->b_u_curhead != NULL)
    {
	EMSG(_("E790: undojoin is not allowed after undo"));
	return;
    }
    if (!curbuf->b_u_synced)
	return;		    /* already unsynced */
    if (get_undolevel() < 0)
	return;		    /* no entries, nothing to do */
    else
	/* Append next change to the last entry */
	curbuf->b_u_synced = FALSE;
}

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_undoredo(int undo);

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

static int	undo_undoes = FALSE;

# 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_allowed(void)
{
    /* Don't allow changes when 'modifiable' is off.  */
    if (!curbuf->b_p_ma)
    {
	EMSG(_(e_modifiable));
	return FALSE;
    }

#ifdef HAVE_SANDBOX
    /* In the sandbox it's not allowed to change the text. */
    if (sandbox != 0)
    {
	EMSG(_(e_sandbox));
	return FALSE;
    }
#endif

    /* Don't allow changes in the buffer while editing the cmdline.  The
     * caller of getcmdline() may get confused. */
    if (textlock != 0)
    {
	EMSG(_(e_secure));
	return FALSE;
    }

    return 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 undo_read(bufinfo_T *bi, char_u *buffer, size_t size);

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 int	undo_undoes = FALSE;


u_free_uhp(u_header_T *uhp)
{
    u_entry_T	*nuep;
    u_entry_T	*uep;

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

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_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 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 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);


serialize_uhp(bufinfo_T *bi, u_header_T *uhp)
{
    int		i;
    u_entry_T	*uep;
    char_u	time_buf[8];

    if (undo_write_bytes(bi, (long_u)UF_HEADER_MAGIC, 2) == FAIL)
	return FAIL;

    put_header_ptr(bi, uhp->uh_next.ptr);
    put_header_ptr(bi, uhp->uh_prev.ptr);
    put_header_ptr(bi, uhp->uh_alt_next.ptr);
    put_header_ptr(bi, uhp->uh_alt_prev.ptr);
    undo_write_bytes(bi, uhp->uh_seq, 4);
    serialize_pos(bi, uhp->uh_cursor);
#ifdef FEAT_VIRTUALEDIT
    undo_write_bytes(bi, (long_u)uhp->uh_cursor_vcol, 4);
#else
    undo_write_bytes(bi, (long_u)0, 4);
#endif
    undo_write_bytes(bi, (long_u)uhp->uh_flags, 2);
    /* Assume NMARKS will stay the same. */
    for (i = 0; i < NMARKS; ++i)
	serialize_pos(bi, uhp->uh_namedm[i]);
    serialize_visualinfo(bi, &uhp->uh_visual);
    time_to_bytes(uhp->uh_time, time_buf);
    undo_write(bi, time_buf, 8);

    /* Optional fields. */
    undo_write_bytes(bi, 4, 1);
    undo_write_bytes(bi, UHP_SAVE_NR, 1);
    undo_write_bytes(bi, (long_u)uhp->uh_save_nr, 4);

    undo_write_bytes(bi, 0, 1);  /* end marker */

    /* Write all the entries. */
    for (uep = uhp->uh_entry; uep != NULL; uep = uep->ue_next)
    {
	undo_write_bytes(bi, (long_u)UF_ENTRY_MAGIC, 2);
	if (serialize_uep(bi, uep) == FAIL)
	    return FAIL;
    }
    undo_write_bytes(bi, (long_u)UF_ENTRY_END_MAGIC, 2);
    return OK;
}

#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_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_HEADER_MAGIC	0x5fd0	/* magic at start of header */

# 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


unserialize_visualinfo(bufinfo_T *bi, visualinfo_T *info)
{
    unserialize_pos(bi, &info->vi_start);
    unserialize_pos(bi, &info->vi_end);
    info->vi_mode = undo_read_4c(bi);
    info->vi_curswant = undo_read_4c(bi);
}

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);


u_redo(int count)
{
    if (vim_strchr(p_cpo, CPO_UNDO) == NULL)
	undo_undoes = FALSE;
    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;


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

    /* When there is an alternate redo list free that branch completely,
     * because we can never go there. */
    if (uhp->uh_alt_next.ptr != NULL)
	u_freebranch(buf, uhp->uh_alt_next.ptr, uhpp);

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

    /* Update the links in the list to remove the header. */
    if (uhp->uh_next.ptr == NULL)
	buf->b_u_oldhead = uhp->uh_prev.ptr;
    else
	uhp->uh_next.ptr->uh_prev.ptr = uhp->uh_prev.ptr;

    if (uhp->uh_prev.ptr == NULL)
	buf->b_u_newhead = uhp->uh_next.ptr;
    else
	for (uhap = uhp->uh_prev.ptr; uhap != NULL;
						 uhap = uhap->uh_alt_next.ptr)
	    uhap->uh_next.ptr = uhp->uh_next.ptr;

    u_freeentries(buf, uhp, 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 */

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


u_clearline(void)
{
    if (curbuf->b_u_line_ptr != NULL)
    {
	vim_free(curbuf->b_u_line_ptr);
	curbuf->b_u_line_ptr = NULL;
	curbuf->b_u_line_lnum = 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 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_undoline(void)
{
    colnr_T t;
    char_u  *oldp;

    if (undo_off)
	return;

    if (curbuf->b_u_line_ptr == NULL
			|| curbuf->b_u_line_lnum > curbuf->b_ml.ml_line_count)
    {
	beep_flush();
	return;
    }

    /* first save the line for the 'u' command */
    if (u_savecommon(curbuf->b_u_line_lnum - 1,
		       curbuf->b_u_line_lnum + 1, (linenr_T)0, FALSE) == FAIL)
	return;
    oldp = u_save_line(curbuf->b_u_line_lnum);
    if (oldp == NULL)
    {
	do_outofmem_msg((long_u)0);
	return;
    }
    ml_replace(curbuf->b_u_line_lnum, curbuf->b_u_line_ptr, TRUE);
    changed_bytes(curbuf->b_u_line_lnum, 0);
    vim_free(curbuf->b_u_line_ptr);
    curbuf->b_u_line_ptr = oldp;

    t = curbuf->b_u_line_colnr;
    if (curwin->w_cursor.lnum == curbuf->b_u_line_lnum)
	curbuf->b_u_line_colnr = curwin->w_cursor.col;
    curwin->w_cursor.col = t;
    curwin->w_cursor.lnum = curbuf->b_u_line_lnum;
    check_cursor_col();
}

#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 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(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);

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

static char_u *u_save_line(linenr_T);

static int	undo_undoes = FALSE;

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


u_compute_hash(char_u *hash)
{
    context_sha256_T	ctx;
    linenr_T		lnum;
    char_u		*p;

    sha256_start(&ctx);
    for (lnum = 1; lnum <= curbuf->b_ml.ml_line_count; ++lnum)
    {
	p = ml_get(lnum);
	sha256_update(&ctx, p, (UINT32_T)(STRLEN(p) + 1));
    }
    sha256_finish(&ctx, hash);
}

#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_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");


read_string_decrypt(bufinfo_T *bi, int len)
{
    char_u  *ptr = alloc((unsigned)len + 1);

    if (ptr != NULL)
    {
	if (len > 0 && undo_read(bi, ptr, len) == FAIL)
	{
	    vim_free(ptr);
	    return NULL;
	}
	ptr[len] = NUL;
#ifdef FEAT_CRYPT
	if (bi->bi_state != NULL && bi->bi_buffer == NULL)
	    crypt_decode_inplace(bi->bi_state, ptr, len);
#endif
    }
    return 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 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 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_pos(bufinfo_T *bi, pos_T *pos)
{
    pos->lnum = undo_read_4c(bi);
    if (pos->lnum < 0)
	pos->lnum = 0;
    pos->col = undo_read_4c(bi);
    if (pos->col < 0)
	pos->col = 0;
#ifdef FEAT_VIRTUALEDIT
    pos->coladd = undo_read_4c(bi);
    if (pos->coladd < 0)
	pos->coladd = 0;
#else
    (void)undo_read_4c(bi);
#endif
}

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);


u_freeentry(u_entry_T *uep, long n)
{
    while (n > 0)
	vim_free(uep->ue_array[--n]);
    vim_free((char_u *)uep->ue_array);
#ifdef U_DEBUG
    uep->ue_magic = 0;
#endif
    vim_free((char_u *)uep);
}

#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 u_entry_T *u_get_headentry(void);

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

static void u_freeentry(u_entry_T *, long);

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 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);

static long	u_newcount, u_oldcount;

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


undo_write(bufinfo_T *bi, char_u *ptr, size_t len)
{
#ifdef FEAT_CRYPT
    if (bi->bi_buffer != NULL)
    {
	size_t	len_todo = len;
	char_u  *p = ptr;

	while (bi->bi_used + len_todo >= CRYPT_BUF_SIZE)
	{
	    size_t	n = CRYPT_BUF_SIZE - bi->bi_used;

	    mch_memmove(bi->bi_buffer + bi->bi_used, p, n);
	    len_todo -= n;
	    p += n;
	    bi->bi_used = CRYPT_BUF_SIZE;
	    if (undo_flush(bi) == FAIL)
		return FAIL;
	}
	if (len_todo > 0)
	{
	    mch_memmove(bi->bi_buffer + bi->bi_used, p, len_todo);
	    bi->bi_used += len_todo;
	}
	return OK;
    }
#endif
    if (fwrite(ptr, len, (size_t)1, bi->bi_fp) != 1)
	return FAIL;
    return OK;
}

#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 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 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 char_u e_not_open[] = N_("E828: Cannot open undo file for writing: %s");


serialize_uep(
    bufinfo_T	*bi,
    u_entry_T	*uep)
{
    int		i;
    size_t	len;

    undo_write_bytes(bi, (long_u)uep->ue_top, 4);
    undo_write_bytes(bi, (long_u)uep->ue_bot, 4);
    undo_write_bytes(bi, (long_u)uep->ue_lcount, 4);
    undo_write_bytes(bi, (long_u)uep->ue_size, 4);
    for (i = 0; i < uep->ue_size; ++i)
    {
	len = STRLEN(uep->ue_array[i]);
	if (undo_write_bytes(bi, (long_u)len, 4) == FAIL)
	    return FAIL;
	if (len > 0 && fwrite_crypt(bi, uep->ue_array[i], len) == FAIL)
	    return FAIL;
    }
    return OK;
}

#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 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_freeentry(u_entry_T *, long);

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 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_savecommon(
    linenr_T	top,
    linenr_T	bot,
    linenr_T	newbot,
    int		reload)
{
    linenr_T	lnum;
    long	i;
    u_header_T	*uhp;
    u_header_T	*old_curhead;
    u_entry_T	*uep;
    u_entry_T	*prev_uep;
    long	size;

    if (!reload)
    {
	/* When making changes is not allowed return FAIL.  It's a crude way
	 * to make all change commands fail. */
	if (!undo_allowed())
	    return FAIL;

#ifdef FEAT_NETBEANS_INTG
	/*
	 * Netbeans defines areas that cannot be modified.  Bail out here when
	 * trying to change text in a guarded area.
	 */
	if (netbeans_active())
	{
	    if (netbeans_is_guarded(top, bot))
	    {
		EMSG(_(e_guarded));
		return FAIL;
	    }
	    if (curbuf->b_p_ro)
	    {
		EMSG(_(e_nbreadonly));
		return FAIL;
	    }
	}
#endif

#ifdef FEAT_AUTOCMD
	/*
	 * Saving text for undo means we are going to make a change.  Give a
	 * warning for a read-only file before making the change, so that the
	 * FileChangedRO event can replace the buffer with a read-write version
	 * (e.g., obtained from a source control system).
	 */
	change_warning(0);
	if (bot > curbuf->b_ml.ml_line_count + 1)
	{
	    /* This happens when the FileChangedRO autocommand changes the
	     * file in a way it becomes shorter. */
	    EMSG(_("E881: Line count changed unexpectedly"));
	    return FAIL;
	}
#endif
    }

#ifdef U_DEBUG
    u_check(FALSE);
#endif

    size = bot - top - 1;

    /*
     * If curbuf->b_u_synced == TRUE make a new header.
     */
    if (curbuf->b_u_synced)
    {
#ifdef FEAT_JUMPLIST
	/* Need to create new entry in b_changelist. */
	curbuf->b_new_change = TRUE;
#endif

	if (get_undolevel() >= 0)
	{
	    /*
	     * Make a new header entry.  Do this first so that we don't mess
	     * up the undo info when out of memory.
	     */
	    uhp = (u_header_T *)U_ALLOC_LINE(sizeof(u_header_T));
	    if (uhp == NULL)
		goto nomem;
#ifdef U_DEBUG
	    uhp->uh_magic = UH_MAGIC;
#endif
	}
	else
	    uhp = NULL;

	/*
	 * If we undid more than we redid, move the entry lists before and
	 * including curbuf->b_u_curhead to an alternate branch.
	 */
	old_curhead = curbuf->b_u_curhead;
	if (old_curhead != NULL)
	{
	    curbuf->b_u_newhead = old_curhead->uh_next.ptr;
	    curbuf->b_u_curhead = NULL;
	}

	/*
	 * free headers to keep the size right
	 */
	while (curbuf->b_u_numhead > get_undolevel()
					       && curbuf->b_u_oldhead != NULL)
	{
	    u_header_T	    *uhfree = curbuf->b_u_oldhead;

	    if (uhfree == old_curhead)
		/* Can't reconnect the branch, delete all of it. */
		u_freebranch(curbuf, uhfree, &old_curhead);
	    else if (uhfree->uh_alt_next.ptr == NULL)
		/* There is no branch, only free one header. */
		u_freeheader(curbuf, uhfree, &old_curhead);
	    else
	    {
		/* Free the oldest alternate branch as a whole. */
		while (uhfree->uh_alt_next.ptr != NULL)
		    uhfree = uhfree->uh_alt_next.ptr;
		u_freebranch(curbuf, uhfree, &old_curhead);
	    }
#ifdef U_DEBUG
	    u_check(TRUE);
#endif
	}

	if (uhp == NULL)		/* no undo at all */
	{
	    if (old_curhead != NULL)
		u_freebranch(curbuf, old_curhead, NULL);
	    curbuf->b_u_synced = FALSE;
	    return OK;
	}

	uhp->uh_prev.ptr = NULL;
	uhp->uh_next.ptr = curbuf->b_u_newhead;
	uhp->uh_alt_next.ptr = old_curhead;
	if (old_curhead != NULL)
	{
	    uhp->uh_alt_prev.ptr = old_curhead->uh_alt_prev.ptr;
	    if (uhp->uh_alt_prev.ptr != NULL)
		uhp->uh_alt_prev.ptr->uh_alt_next.ptr = uhp;
	    old_curhead->uh_alt_prev.ptr = uhp;
	    if (curbuf->b_u_oldhead == old_curhead)
		curbuf->b_u_oldhead = uhp;
	}
	else
	    uhp->uh_alt_prev.ptr = NULL;
	if (curbuf->b_u_newhead != NULL)
	    curbuf->b_u_newhead->uh_prev.ptr = uhp;

	uhp->uh_seq = ++curbuf->b_u_seq_last;
	curbuf->b_u_seq_cur = uhp->uh_seq;
	uhp->uh_time = vim_time();
	uhp->uh_save_nr = 0;
	curbuf->b_u_time_cur = uhp->uh_time + 1;

	uhp->uh_walk = 0;
	uhp->uh_entry = NULL;
	uhp->uh_getbot_entry = NULL;
	uhp->uh_cursor = curwin->w_cursor;	/* save cursor pos. for undo */
#ifdef FEAT_VIRTUALEDIT
	if (virtual_active() && curwin->w_cursor.coladd > 0)
	    uhp->uh_cursor_vcol = getviscol();
	else
	    uhp->uh_cursor_vcol = -1;
#endif

	/* save changed and buffer empty flag for undo */
	uhp->uh_flags = (curbuf->b_changed ? UH_CHANGED : 0) +
		       ((curbuf->b_ml.ml_flags & ML_EMPTY) ? UH_EMPTYBUF : 0);

	/* save named marks and Visual marks for undo */
	mch_memmove(uhp->uh_namedm, curbuf->b_namedm, sizeof(pos_T) * NMARKS);
	uhp->uh_visual = curbuf->b_visual;

	curbuf->b_u_newhead = uhp;
	if (curbuf->b_u_oldhead == NULL)
	    curbuf->b_u_oldhead = uhp;
	++curbuf->b_u_numhead;
    }
    else
    {
	if (get_undolevel() < 0)	/* no undo at all */
	    return OK;

	/*
	 * When saving a single line, and it has been saved just before, it
	 * doesn't make sense saving it again.  Saves a lot of memory when
	 * making lots of changes inside the same line.
	 * This is only possible if the previous change didn't increase or
	 * decrease the number of lines.
	 * Check the ten last changes.  More doesn't make sense and takes too
	 * long.
	 */
	if (size == 1)
	{
	    uep = u_get_headentry();
	    prev_uep = NULL;
	    for (i = 0; i < 10; ++i)
	    {
		if (uep == NULL)
		    break;

		/* If lines have been inserted/deleted we give up.
		 * Also when the line was included in a multi-line save. */
		if ((curbuf->b_u_newhead->uh_getbot_entry != uep
			    ? (uep->ue_top + uep->ue_size + 1
				!= (uep->ue_bot == 0
				    ? curbuf->b_ml.ml_line_count + 1
				    : uep->ue_bot))
			    : uep->ue_lcount != curbuf->b_ml.ml_line_count)
			|| (uep->ue_size > 1
			    && top >= uep->ue_top
			    && top + 2 <= uep->ue_top + uep->ue_size + 1))
		    break;

		/* If it's the same line we can skip saving it again. */
		if (uep->ue_size == 1 && uep->ue_top == top)
		{
		    if (i > 0)
		    {
			/* It's not the last entry: get ue_bot for the last
			 * entry now.  Following deleted/inserted lines go to
			 * the re-used entry. */
			u_getbot();
			curbuf->b_u_synced = FALSE;

			/* Move the found entry to become the last entry.  The
			 * order of undo/redo doesn't matter for the entries
			 * we move it over, since they don't change the line
			 * count and don't include this line.  It does matter
			 * for the found entry if the line count is changed by
			 * the executed command. */
			prev_uep->ue_next = uep->ue_next;
			uep->ue_next = curbuf->b_u_newhead->uh_entry;
			curbuf->b_u_newhead->uh_entry = uep;
		    }

		    /* The executed command may change the line count. */
		    if (newbot != 0)
			uep->ue_bot = newbot;
		    else if (bot > curbuf->b_ml.ml_line_count)
			uep->ue_bot = 0;
		    else
		    {
			uep->ue_lcount = curbuf->b_ml.ml_line_count;
			curbuf->b_u_newhead->uh_getbot_entry = uep;
		    }
		    return OK;
		}
		prev_uep = uep;
		uep = uep->ue_next;
	    }
	}

	/* find line number for ue_bot for previous u_save() */
	u_getbot();
    }

#if !defined(UNIX) && !defined(WIN32)
	/*
	 * With Amiga we can't handle big undo's, because
	 * then u_alloc_line would have to allocate a block larger than 32K
	 */
    if (size >= 8000)
	goto nomem;
#endif

    /*
     * add lines in front of entry list
     */
    uep = (u_entry_T *)U_ALLOC_LINE(sizeof(u_entry_T));
    if (uep == NULL)
	goto nomem;
    vim_memset(uep, 0, sizeof(u_entry_T));
#ifdef U_DEBUG
    uep->ue_magic = UE_MAGIC;
#endif

    uep->ue_size = size;
    uep->ue_top = top;
    if (newbot != 0)
	uep->ue_bot = newbot;
    /*
     * Use 0 for ue_bot if bot is below last line.
     * Otherwise we have to compute ue_bot later.
     */
    else if (bot > curbuf->b_ml.ml_line_count)
	uep->ue_bot = 0;
    else
    {
	uep->ue_lcount = curbuf->b_ml.ml_line_count;
	curbuf->b_u_newhead->uh_getbot_entry = uep;
    }

    if (size > 0)
    {
	if ((uep->ue_array = (char_u **)U_ALLOC_LINE(
					    sizeof(char_u *) * size)) == NULL)
	{
	    u_freeentry(uep, 0L);
	    goto nomem;
	}
	for (i = 0, lnum = top + 1; i < size; ++i)
	{
	    fast_breakcheck();
	    if (got_int)
	    {
		u_freeentry(uep, i);
		return FAIL;
	    }
	    if ((uep->ue_array[i] = u_save_line(lnum++)) == NULL)
	    {
		u_freeentry(uep, i);
		goto nomem;
	    }
	}
    }
    else
	uep->ue_array = NULL;
    uep->ue_next = curbuf->b_u_newhead->uh_entry;
    curbuf->b_u_newhead->uh_entry = uep;
    curbuf->b_u_synced = FALSE;
    undo_undoes = FALSE;

#ifdef U_DEBUG
    u_check(FALSE);
#endif
    return OK;

nomem:
    msg_silent = 0;	/* must display the prompt */
    if (ask_yesno((char_u *)_("No undo possible; continue anyway"), TRUE)
								       == 'y')
    {
	undo_off = TRUE;	    /* will be reset when character typed */
	return OK;
    }
    do_outofmem_msg((long_u)0);
    return FAIL;
}

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


u_find_first_changed(void)
{
    u_header_T	*uhp = curbuf->b_u_newhead;
    u_entry_T   *uep;
    linenr_T	lnum;

    if (curbuf->b_u_curhead != NULL || uhp == NULL)
	return;  /* undid something in an autocmd? */

    /* Check that the last undo block was for the whole file. */
    uep = uhp->uh_entry;
    if (uep->ue_top != 0 || uep->ue_bot != 0)
	return;

    for (lnum = 1; lnum < curbuf->b_ml.ml_line_count
					      && lnum <= uep->ue_size; ++lnum)
	if (STRCMP(ml_get_buf(curbuf, lnum, FALSE),
						uep->ue_array[lnum - 1]) != 0)
	{
	    clearpos(&(uhp->uh_cursor));
	    uhp->uh_cursor.lnum = lnum;
	    return;
	}
    if (curbuf->b_ml.ml_line_count != uep->ue_size)
    {
	/* lines added or deleted at the end, put the cursor there */
	clearpos(&(uhp->uh_cursor));
	uhp->uh_cursor.lnum = lnum;
    }
}

#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_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;

# 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_check(int newhead_may_be_NULL)
{
    seen_b_u_newhead = 0;
    seen_b_u_curhead = 0;
    header_count = 0;

    u_check_tree(curbuf->b_u_oldhead, NULL, NULL);

    if (seen_b_u_newhead == 0 && curbuf->b_u_oldhead != NULL
	    && !(newhead_may_be_NULL && curbuf->b_u_newhead == NULL))
	EMSGN("b_u_newhead invalid: 0x%x", curbuf->b_u_newhead);
    if (curbuf->b_u_curhead != NULL && seen_b_u_curhead == 0)
	EMSGN("b_u_curhead invalid: 0x%x", curbuf->b_u_curhead);
    if (header_count != curbuf->b_u_numhead)
    {
	EMSG("b_u_numhead invalid");
	smsg((char_u *)"expected: %ld, actual: %ld",
			       (long)header_count, (long)curbuf->b_u_numhead);
    }
}

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_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_freeentry(u_entry_T *, long);

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 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 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;

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


u_doit(int startcount)
{
    int count = startcount;

    if (!undo_allowed())
	return;

    u_newcount = 0;
    u_oldcount = 0;
    if (curbuf->b_ml.ml_flags & ML_EMPTY)
	u_oldcount = -1;
    while (count--)
    {
	/* Do the change warning now, so that it triggers FileChangedRO when
	 * needed.  This may cause the file to be reloaded, that must happen
	 * before we do anything, because it may change curbuf->b_u_curhead
	 * and more. */
	change_warning(0);

	if (undo_undoes)
	{
	    if (curbuf->b_u_curhead == NULL)		/* first undo */
		curbuf->b_u_curhead = curbuf->b_u_newhead;
	    else if (get_undolevel() > 0)		/* multi level undo */
		/* get next undo */
		curbuf->b_u_curhead = curbuf->b_u_curhead->uh_next.ptr;
	    /* nothing to undo */
	    if (curbuf->b_u_numhead == 0 || curbuf->b_u_curhead == NULL)
	    {
		/* stick curbuf->b_u_curhead at end */
		curbuf->b_u_curhead = curbuf->b_u_oldhead;
		beep_flush();
		if (count == startcount - 1)
		{
		    MSG(_("Already at oldest change"));
		    return;
		}
		break;
	    }

	    u_undoredo(TRUE);
	}
	else
	{
	    if (curbuf->b_u_curhead == NULL || get_undolevel() <= 0)
	    {
		beep_flush();	/* nothing to redo */
		if (count == startcount - 1)
		{
		    MSG(_("Already at newest change"));
		    return;
		}
		break;
	    }

	    u_undoredo(FALSE);

	    /* Advance for next redo.  Set "newhead" when at the end of the
	     * redoable changes. */
	    if (curbuf->b_u_curhead->uh_prev.ptr == NULL)
		curbuf->b_u_newhead = curbuf->b_u_curhead;
	    curbuf->b_u_curhead = curbuf->b_u_curhead->uh_prev.ptr;
	}
    }
    u_undo_end(undo_undoes, FALSE);
}

#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_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 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;

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


undo_read(bufinfo_T *bi, char_u *buffer, size_t size)
{
#ifdef FEAT_CRYPT
    if (bi->bi_buffer != NULL)
    {
	int	size_todo = (int)size;
	char_u	*p = buffer;

	while (size_todo > 0)
	{
	    size_t n;

	    if (bi->bi_used >= bi->bi_avail)
	    {
		n = fread(bi->bi_buffer, 1, (size_t)CRYPT_BUF_SIZE, bi->bi_fp);
		if (n == 0)
		{
		    /* Error may be checked for only later.  Fill with zeros,
		     * so that the reader won't use garbage. */
		    vim_memset(p, 0, size_todo);
		    return FAIL;
		}
		bi->bi_avail = n;
		bi->bi_used = 0;
		crypt_decode_inplace(bi->bi_state, bi->bi_buffer, bi->bi_avail);
	    }
	    n = size_todo;
	    if (n > bi->bi_avail - bi->bi_used)
		n = bi->bi_avail - bi->bi_used;
	    mch_memmove(p, bi->bi_buffer + bi->bi_used, n);
	    bi->bi_used += n;
	    size_todo -= (int)n;
	    p += n;
	}
	return OK;
    }
#endif
    if (fread(buffer, (size_t)size, 1, bi->bi_fp) != 1)
	return FAIL;
    return OK;
}

#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 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 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_unchanged(buf_T *buf)
{
    u_unch_branch(buf->b_u_oldhead);
    buf->b_did_warn = FALSE;
}

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);

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

static int	undo_undoes = FALSE;


unserialize_uep(bufinfo_T *bi, int *error, char_u *file_name)
{
    int		i;
    u_entry_T	*uep;
    char_u	**array = NULL;
    char_u	*line;
    int		line_len;

    uep = (u_entry_T *)U_ALLOC_LINE(sizeof(u_entry_T));
    if (uep == NULL)
	return NULL;
    vim_memset(uep, 0, sizeof(u_entry_T));
#ifdef U_DEBUG
    uep->ue_magic = UE_MAGIC;
#endif
    uep->ue_top = undo_read_4c(bi);
    uep->ue_bot = undo_read_4c(bi);
    uep->ue_lcount = undo_read_4c(bi);
    uep->ue_size = undo_read_4c(bi);
    if (uep->ue_size > 0)
    {
	if (uep->ue_size < LONG_MAX / (int)sizeof(char_u *))
	    array = (char_u **)U_ALLOC_LINE(sizeof(char_u *) * uep->ue_size);
	if (array == NULL)
	{
	    *error = TRUE;
	    return uep;
	}
	vim_memset(array, 0, sizeof(char_u *) * uep->ue_size);
    }
    uep->ue_array = array;

    for (i = 0; i < uep->ue_size; ++i)
    {
	line_len = undo_read_4c(bi);
	if (line_len >= 0)
	    line = read_string_decrypt(bi, line_len);
	else
	{
	    line = NULL;
	    corruption_error("line length", file_name);
	}
	if (line == NULL)
	{
	    *error = TRUE;
	    return uep;
	}
	array[i] = line;
    }
    return uep;
}

#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 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_freeentry(u_entry_T *, long);

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_check_tree(u_header_T *uhp,
	u_header_T *exp_uh_next,
	u_header_T *exp_uh_alt_prev)
{
    u_entry_T *uep;

    if (uhp == NULL)
	return;
    ++header_count;
    if (uhp == curbuf->b_u_curhead && ++seen_b_u_curhead > 1)
    {
	EMSG("b_u_curhead found twice (looping?)");
	return;
    }
    if (uhp == curbuf->b_u_newhead && ++seen_b_u_newhead > 1)
    {
	EMSG("b_u_newhead found twice (looping?)");
	return;
    }

    if (uhp->uh_magic != UH_MAGIC)
	EMSG("uh_magic wrong (may be using freed memory)");
    else
    {
	/* Check pointers back are correct. */
	if (uhp->uh_next.ptr != exp_uh_next)
	{
	    EMSG("uh_next wrong");
	    smsg((char_u *)"expected: 0x%x, actual: 0x%x",
					       exp_uh_next, uhp->uh_next.ptr);
	}
	if (uhp->uh_alt_prev.ptr != exp_uh_alt_prev)
	{
	    EMSG("uh_alt_prev wrong");
	    smsg((char_u *)"expected: 0x%x, actual: 0x%x",
				       exp_uh_alt_prev, uhp->uh_alt_prev.ptr);
	}

	/* Check the undo tree at this header. */
	for (uep = uhp->uh_entry; uep != NULL; uep = uep->ue_next)
	{
	    if (uep->ue_magic != UE_MAGIC)
	    {
		EMSG("ue_magic wrong (may be using freed memory)");
		break;
	    }
	}

	/* Check the next alt tree. */
	u_check_tree(uhp->uh_alt_next.ptr, uhp->uh_next.ptr, uhp);

	/* Check the next header in this branch. */
	u_check_tree(uhp->uh_prev.ptr, uhp, 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 void u_unch_branch(u_header_T *uhp);

static u_entry_T *u_get_headentry(void);

static void u_undoredo(int undo);

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);

static int seen_b_u_curhead;

static int seen_b_u_newhead;

static int header_count;

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

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

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


u_save_cursor(void)
{
    return (u_save((linenr_T)(curwin->w_cursor.lnum - 1),
				      (linenr_T)(curwin->w_cursor.lnum + 1)));
}

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 *u_save_line(linenr_T);


u_update_save_nr(buf_T *buf)
{
    u_header_T	*uhp;

    ++buf->b_u_save_nr_last;
    buf->b_u_save_nr_cur = buf->b_u_save_nr_last;
    uhp = buf->b_u_curhead;
    if (uhp != NULL)
	uhp = uhp->uh_next.ptr;
    else
	uhp = buf->b_u_newhead;
    if (uhp != NULL)
	uhp->uh_save_nr = buf->b_u_save_nr_last;
}

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);