changeset 17460:e43f0c0c491c v8.1.1728

patch 8.1.1728: wrong place for command line history viminfo support commit https://github.com/vim/vim/commit/5f32ece459d1f310b1b48b72e07dcd77d3261a76 Author: Bram Moolenaar <Bram@vim.org> Date: Sun Jul 21 21:51:59 2019 +0200 patch 8.1.1728: wrong place for command line history viminfo support Problem: Wrong place for command line history viminfo support. Solution: Move it to viminfo.c.
author Bram Moolenaar <Bram@vim.org>
date Sun, 21 Jul 2019 22:00:05 +0200
parents 02dc4260ddbb
children 1fc9ec9b71a0
files src/ex_getln.c src/proto/ex_getln.pro src/structs.h src/version.c src/viminfo.c
diffstat 5 files changed, 527 insertions(+), 479 deletions(-) [+]
line wrap: on
line diff
--- a/src/ex_getln.c
+++ b/src/ex_getln.c
@@ -60,21 +60,11 @@ static int	extra_char = NUL;  /* extra c
 static int	extra_char_shift;
 
 #ifdef FEAT_CMDHIST
-typedef struct hist_entry
-{
-    int		hisnum;		/* identifying number */
-    int		viminfo;	/* when TRUE hisstr comes from viminfo */
-    char_u	*hisstr;	/* actual entry, separator char after the NUL */
-    time_t	time_set;	/* when it was typed, zero if unknown */
-} histentry_T;
-
 static histentry_T *(history[HIST_COUNT]) = {NULL, NULL, NULL, NULL, NULL};
 static int	hisidx[HIST_COUNT] = {-1, -1, -1, -1, -1};  /* lastused entry */
 static int	hisnum[HIST_COUNT] = {0, 0, 0, 0, 0};
 		    /* identifying (unique) number of newest history entry */
 static int	hislen = 0;		/* actual length of history tables */
-
-static int	hist_char2type(int c);
 #endif
 
 #ifdef FEAT_RIGHTLEFT
@@ -116,9 +106,6 @@ static int	ExpandUserDefined(expand_T *x
 static int	ExpandUserList(expand_T *xp, int *num_file, char_u ***file);
 # endif
 #endif
-#ifdef FEAT_CMDHIST
-static void	clear_hist_entry(histentry_T *hisptr);
-#endif
 
 #ifdef FEAT_CMDWIN
 static int	open_cmdwin(void);
@@ -5873,7 +5860,7 @@ globpath(
 /*
  * Translate a history character to the associated type number.
  */
-    static int
+    int
 hist_char2type(int c)
 {
     if (c == ':')
@@ -6010,7 +5997,7 @@ init_history(void)
     }
 }
 
-    static void
+    void
 clear_hist_entry(histentry_T *hisptr)
 {
     hisptr->hisnum = 0;
@@ -6023,7 +6010,7 @@ clear_hist_entry(histentry_T *hisptr)
  * Check if command line 'str' is already in history.
  * If 'move_to_front' is TRUE, matching entry is moved to end of history.
  */
-    static int
+    int
 in_history(
     int	    type,
     char_u  *str,
@@ -6629,478 +6616,36 @@ ex_history(exarg_T *eap)
 #endif
 
 #if (defined(FEAT_VIMINFO) && defined(FEAT_CMDHIST)) || defined(PROTO)
-/*
- * Buffers for history read from a viminfo file.  Only valid while reading.
- */
-static histentry_T *viminfo_history[HIST_COUNT] =
-					       {NULL, NULL, NULL, NULL, NULL};
-static int	viminfo_hisidx[HIST_COUNT] = {0, 0, 0, 0, 0};
-static int	viminfo_hislen[HIST_COUNT] = {0, 0, 0, 0, 0};
-static int	viminfo_add_at_front = FALSE;
-
-/*
- * Translate a history type number to the associated character.
- */
-    static int
-hist_type2char(
-    int	    type,
-    int	    use_question)	    /* use '?' instead of '/' */
-{
-    if (type == HIST_CMD)
-	return ':';
-    if (type == HIST_SEARCH)
-    {
-	if (use_question)
-	    return '?';
-	else
-	    return '/';
-    }
-    if (type == HIST_EXPR)
-	return '=';
-    return '@';
-}
-
-/*
- * Prepare for reading the history from the viminfo file.
- * This allocates history arrays to store the read history lines.
- */
-    void
-prepare_viminfo_history(int asklen, int writing)
-{
-    int	    i;
-    int	    num;
-    int	    type;
-    int	    len;
-
-    init_history();
-    viminfo_add_at_front = (asklen != 0 && !writing);
-    if (asklen > hislen)
-	asklen = hislen;
-
-    for (type = 0; type < HIST_COUNT; ++type)
-    {
-	/* Count the number of empty spaces in the history list.  Entries read
-	 * from viminfo previously are also considered empty.  If there are
-	 * more spaces available than we request, then fill them up. */
-	for (i = 0, num = 0; i < hislen; i++)
-	    if (history[type][i].hisstr == NULL || history[type][i].viminfo)
-		num++;
-	len = asklen;
-	if (num > len)
-	    len = num;
-	if (len <= 0)
-	    viminfo_history[type] = NULL;
-	else
-	    viminfo_history[type] = LALLOC_MULT(histentry_T, len);
-	if (viminfo_history[type] == NULL)
-	    len = 0;
-	viminfo_hislen[type] = len;
-	viminfo_hisidx[type] = 0;
-    }
-}
-
-/*
- * Accept a line from the viminfo, store it in the history array when it's
- * new.
- */
     int
-read_viminfo_history(vir_T *virp, int writing)
+get_hislen(void)
 {
-    int		type;
-    long_u	len;
-    char_u	*val;
-    char_u	*p;
-
-    type = hist_char2type(virp->vir_line[0]);
-    if (viminfo_hisidx[type] < viminfo_hislen[type])
-    {
-	val = viminfo_readstring(virp, 1, TRUE);
-	if (val != NULL && *val != NUL)
-	{
-	    int sep = (*val == ' ' ? NUL : *val);
-
-	    if (!in_history(type, val + (type == HIST_SEARCH),
-					  viminfo_add_at_front, sep, writing))
-	    {
-		/* Need to re-allocate to append the separator byte. */
-		len = STRLEN(val);
-		p = alloc(len + 2);
-		if (p != NULL)
-		{
-		    if (type == HIST_SEARCH)
-		    {
-			/* Search entry: Move the separator from the first
-			 * column to after the NUL. */
-			mch_memmove(p, val + 1, (size_t)len);
-			p[len] = sep;
-		    }
-		    else
-		    {
-			/* Not a search entry: No separator in the viminfo
-			 * file, add a NUL separator. */
-			mch_memmove(p, val, (size_t)len + 1);
-			p[len + 1] = NUL;
-		    }
-		    viminfo_history[type][viminfo_hisidx[type]].hisstr = p;
-		    viminfo_history[type][viminfo_hisidx[type]].time_set = 0;
-		    viminfo_history[type][viminfo_hisidx[type]].viminfo = TRUE;
-		    viminfo_history[type][viminfo_hisidx[type]].hisnum = 0;
-		    viminfo_hisidx[type]++;
-		}
-	    }
-	}
-	vim_free(val);
-    }
-    return viminfo_readline(virp);
+    return hislen;
 }
 
-/*
- * Accept a new style history line from the viminfo, store it in the history
- * array when it's new.
- */
-    void
-handle_viminfo_history(
-	garray_T    *values,
-	int	    writing)
+    histentry_T *
+get_histentry(int hist_type)
 {
-    int		type;
-    long_u	len;
-    char_u	*val;
-    char_u	*p;
-    bval_T	*vp = (bval_T *)values->ga_data;
-
-    /* Check the format:
-     * |{bartype},{histtype},{timestamp},{separator},"text" */
-    if (values->ga_len < 4
-	    || vp[0].bv_type != BVAL_NR
-	    || vp[1].bv_type != BVAL_NR
-	    || (vp[2].bv_type != BVAL_NR && vp[2].bv_type != BVAL_EMPTY)
-	    || vp[3].bv_type != BVAL_STRING)
-	return;
-
-    type = vp[0].bv_nr;
-    if (type >= HIST_COUNT)
-	return;
-    if (viminfo_hisidx[type] < viminfo_hislen[type])
-    {
-	val = vp[3].bv_string;
-	if (val != NULL && *val != NUL)
-	{
-	    int sep = type == HIST_SEARCH && vp[2].bv_type == BVAL_NR
-						      ? vp[2].bv_nr : NUL;
-	    int idx;
-	    int overwrite = FALSE;
-
-	    if (!in_history(type, val, viminfo_add_at_front, sep, writing))
-	    {
-		/* If lines were written by an older Vim we need to avoid
-		 * getting duplicates. See if the entry already exists. */
-		for (idx = 0; idx < viminfo_hisidx[type]; ++idx)
-		{
-		    p = viminfo_history[type][idx].hisstr;
-		    if (STRCMP(val, p) == 0
-			  && (type != HIST_SEARCH || sep == p[STRLEN(p) + 1]))
-		    {
-			overwrite = TRUE;
-			break;
-		    }
-		}
-
-		if (!overwrite)
-		{
-		    /* Need to re-allocate to append the separator byte. */
-		    len = vp[3].bv_len;
-		    p = alloc(len + 2);
-		}
-		else
-		    len = 0; /* for picky compilers */
-		if (p != NULL)
-		{
-		    viminfo_history[type][idx].time_set = vp[1].bv_nr;
-		    if (!overwrite)
-		    {
-			mch_memmove(p, val, (size_t)len + 1);
-			/* Put the separator after the NUL. */
-			p[len + 1] = sep;
-			viminfo_history[type][idx].hisstr = p;
-			viminfo_history[type][idx].hisnum = 0;
-			viminfo_history[type][idx].viminfo = TRUE;
-			viminfo_hisidx[type]++;
-		    }
-		}
-	    }
-	}
-    }
+    return history[hist_type];
 }
 
-/*
- * Concatenate history lines from viminfo after the lines typed in this Vim.
- */
-    static void
-concat_history(int type)
+    void
+set_histentry(int hist_type, histentry_T *entry)
 {
-    int idx;
-    int i;
-
-    idx = hisidx[type] + viminfo_hisidx[type];
-    if (idx >= hislen)
-	idx -= hislen;
-    else if (idx < 0)
-	idx = hislen - 1;
-    if (viminfo_add_at_front)
-	hisidx[type] = idx;
-    else
-    {
-	if (hisidx[type] == -1)
-	    hisidx[type] = hislen - 1;
-	do
-	{
-	    if (history[type][idx].hisstr != NULL
-					    || history[type][idx].viminfo)
-		break;
-	    if (++idx == hislen)
-		idx = 0;
-	} while (idx != hisidx[type]);
-	if (idx != hisidx[type] && --idx < 0)
-	    idx = hislen - 1;
-    }
-    for (i = 0; i < viminfo_hisidx[type]; i++)
-    {
-	vim_free(history[type][idx].hisstr);
-	history[type][idx].hisstr = viminfo_history[type][i].hisstr;
-	history[type][idx].viminfo = TRUE;
-	history[type][idx].time_set = viminfo_history[type][i].time_set;
-	if (--idx < 0)
-	    idx = hislen - 1;
-    }
-    idx += 1;
-    idx %= hislen;
-    for (i = 0; i < viminfo_hisidx[type]; i++)
-    {
-	history[type][idx++].hisnum = ++hisnum[type];
-	idx %= hislen;
-    }
-}
-
-#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
-    static int
-sort_hist(const void *s1, const void *s2)
-{
-    histentry_T *p1 = *(histentry_T **)s1;
-    histentry_T *p2 = *(histentry_T **)s2;
-
-    if (p1->time_set < p2->time_set) return -1;
-    if (p1->time_set > p2->time_set) return 1;
-    return 0;
+    history[hist_type] = entry;
 }
-#endif
-
-/*
- * Merge history lines from viminfo and lines typed in this Vim based on the
- * timestamp;
- */
-    static void
-merge_history(int type)
-{
-    int		max_len;
-    histentry_T **tot_hist;
-    histentry_T *new_hist;
-    int		i;
-    int		len;
-
-    /* Make one long list with all entries. */
-    max_len = hislen + viminfo_hisidx[type];
-    tot_hist = ALLOC_MULT(histentry_T *, max_len);
-    new_hist = ALLOC_MULT(histentry_T, hislen );
-    if (tot_hist == NULL || new_hist == NULL)
-    {
-	vim_free(tot_hist);
-	vim_free(new_hist);
-	return;
-    }
-    for (i = 0; i < viminfo_hisidx[type]; i++)
-	tot_hist[i] = &viminfo_history[type][i];
-    len = i;
-    for (i = 0; i < hislen; i++)
-	if (history[type][i].hisstr != NULL)
-	    tot_hist[len++] = &history[type][i];
-
-    /* Sort the list on timestamp. */
-    qsort((void *)tot_hist, (size_t)len, sizeof(histentry_T *), sort_hist);
-
-    /* Keep the newest ones. */
-    for (i = 0; i < hislen; i++)
-    {
-	if (i < len)
-	{
-	    new_hist[i] = *tot_hist[i];
-	    tot_hist[i]->hisstr = NULL;
-	    if (new_hist[i].hisnum == 0)
-		new_hist[i].hisnum = ++hisnum[type];
-	}
-	else
-	    clear_hist_entry(&new_hist[i]);
-    }
-    hisidx[type] = (i < len ? i : len) - 1;
-
-    /* Free what is not kept. */
-    for (i = 0; i < viminfo_hisidx[type]; i++)
-	vim_free(viminfo_history[type][i].hisstr);
-    for (i = 0; i < hislen; i++)
-	vim_free(history[type][i].hisstr);
-    vim_free(history[type]);
-    history[type] = new_hist;
-    vim_free(tot_hist);
-}
-
-/*
- * Finish reading history lines from viminfo.  Not used when writing viminfo.
- */
-    void
-finish_viminfo_history(vir_T *virp)
+
+    int *
+get_hisidx(int hist_type)
 {
-    int	type;
-    int merge = virp->vir_version >= VIMINFO_VERSION_WITH_HISTORY;
-
-    for (type = 0; type < HIST_COUNT; ++type)
-    {
-	if (history[type] == NULL)
-	    continue;
-
-	if (merge)
-	    merge_history(type);
-	else
-	    concat_history(type);
-
-	VIM_CLEAR(viminfo_history[type]);
-	viminfo_hisidx[type] = 0;
-    }
+    return &hisidx[hist_type];
 }
 
-/*
- * Write history to viminfo file in "fp".
- * When "merge" is TRUE merge history lines with a previously read viminfo
- * file, data is in viminfo_history[].
- * When "merge" is FALSE just write all history lines.  Used for ":wviminfo!".
- */
-    void
-write_viminfo_history(FILE *fp, int merge)
+    int *
+get_hisnum(int hist_type)
 {
-    int	    i;
-    int	    type;
-    int	    num_saved;
-    int     round;
-
-    init_history();
-    if (hislen == 0)
-	return;
-    for (type = 0; type < HIST_COUNT; ++type)
-    {
-	num_saved = get_viminfo_parameter(hist_type2char(type, FALSE));
-	if (num_saved == 0)
-	    continue;
-	if (num_saved < 0)  /* Use default */
-	    num_saved = hislen;
-	fprintf(fp, _("\n# %s History (newest to oldest):\n"),
-			    type == HIST_CMD ? _("Command Line") :
-			    type == HIST_SEARCH ? _("Search String") :
-			    type == HIST_EXPR ? _("Expression") :
-			    type == HIST_INPUT ? _("Input Line") :
-					_("Debug Line"));
-	if (num_saved > hislen)
-	    num_saved = hislen;
-
-	/*
-	 * Merge typed and viminfo history:
-	 * round 1: history of typed commands.
-	 * round 2: history from recently read viminfo.
-	 */
-	for (round = 1; round <= 2; ++round)
-	{
-	    if (round == 1)
-		/* start at newest entry, somewhere in the list */
-		i = hisidx[type];
-	    else if (viminfo_hisidx[type] > 0)
-		/* start at newest entry, first in the list */
-		i = 0;
-	    else
-		/* empty list */
-		i = -1;
-	    if (i >= 0)
-		while (num_saved > 0
-			&& !(round == 2 && i >= viminfo_hisidx[type]))
-		{
-		    char_u  *p;
-		    time_t  timestamp;
-		    int	    c = NUL;
-
-		    if (round == 1)
-		    {
-			p = history[type][i].hisstr;
-			timestamp = history[type][i].time_set;
-		    }
-		    else
-		    {
-			p = viminfo_history[type] == NULL ? NULL
-					    : viminfo_history[type][i].hisstr;
-			timestamp = viminfo_history[type] == NULL ? 0
-					  : viminfo_history[type][i].time_set;
-		    }
-
-		    if (p != NULL && (round == 2
-				       || !merge
-				       || !history[type][i].viminfo))
-		    {
-			--num_saved;
-			fputc(hist_type2char(type, TRUE), fp);
-			/* For the search history: put the separator in the
-			 * second column; use a space if there isn't one. */
-			if (type == HIST_SEARCH)
-			{
-			    c = p[STRLEN(p) + 1];
-			    putc(c == NUL ? ' ' : c, fp);
-			}
-			viminfo_writestring(fp, p);
-
-			{
-			    char    cbuf[NUMBUFLEN];
-
-			    /* New style history with a bar line. Format:
-			     * |{bartype},{histtype},{timestamp},{separator},"text" */
-			    if (c == NUL)
-				cbuf[0] = NUL;
-			    else
-				sprintf(cbuf, "%d", c);
-			    fprintf(fp, "|%d,%d,%ld,%s,", BARTYPE_HISTORY,
-						 type, (long)timestamp, cbuf);
-			    barline_writestring(fp, p, LSIZE - 20);
-			    putc('\n', fp);
-			}
-		    }
-		    if (round == 1)
-		    {
-			/* Decrement index, loop around and stop when back at
-			 * the start. */
-			if (--i < 0)
-			    i = hislen - 1;
-			if (i == hisidx[type])
-			    break;
-		    }
-		    else
-		    {
-			/* Increment index. Stop at the end in the while. */
-			++i;
-		    }
-		}
-	}
-	for (i = 0; i < viminfo_hisidx[type]; ++i)
-	    if (viminfo_history[type] != NULL)
-		vim_free(viminfo_history[type][i].hisstr);
-	VIM_CLEAR(viminfo_history[type]);
-	viminfo_hisidx[type] = 0;
-    }
+    return &hisnum[hist_type];
 }
-#endif /* FEAT_VIMINFO */
+#endif
 
 #if defined(FEAT_CMDWIN) || defined(PROTO)
 /*
--- a/src/proto/ex_getln.pro
+++ b/src/proto/ex_getln.pro
@@ -34,7 +34,10 @@ void set_cmd_context(expand_T *xp, char_
 int expand_cmdline(expand_T *xp, char_u *str, int col, int *matchcount, char_u ***matches);
 int ExpandGeneric(expand_T *xp, regmatch_T *regmatch, int *num_file, char_u ***file, char_u *((*func)(expand_T *, int)), int escaped);
 void globpath(char_u *path, char_u *file, garray_T *ga, int expand_options);
+int hist_char2type(int c);
 void init_history(void);
+void clear_hist_entry(histentry_T *hisptr);
+int in_history(int type, char_u *str, int move_to_front, int sep, int writing);
 int get_histtype(char_u *name);
 void add_to_history(int histype, char_u *new_entry, int in_map, int sep);
 int get_history_idx(int histype);
@@ -49,10 +52,10 @@ int set_cmdline_pos(int pos);
 int get_cmdline_type(void);
 int get_list_range(char_u **str, int *num1, int *num2);
 void ex_history(exarg_T *eap);
-void prepare_viminfo_history(int asklen, int writing);
-int read_viminfo_history(vir_T *virp, int writing);
-void handle_viminfo_history(garray_T *values, int writing);
-void finish_viminfo_history(vir_T *virp);
-void write_viminfo_history(FILE *fp, int merge);
+int get_hislen(void);
+histentry_T *get_histentry(int hist_type);
+void set_histentry(int hist_type, histentry_T *entry);
+int *get_hisidx(int hist_type);
+int *get_hisnum(int hist_type);
 char_u *script_get(exarg_T *eap, char_u *cmd);
 /* vim: set ft=c : */
--- a/src/structs.h
+++ b/src/structs.h
@@ -1115,6 +1115,17 @@ typedef struct
     garray_T	vir_barlines;	// lines starting with |
 } vir_T;
 
+/*
+ * Structure used for the command line history.
+ */
+typedef struct hist_entry
+{
+    int		hisnum;		/* identifying number */
+    int		viminfo;	/* when TRUE hisstr comes from viminfo */
+    char_u	*hisstr;	/* actual entry, separator char after the NUL */
+    time_t	time_set;	/* when it was typed, zero if unknown */
+} histentry_T;
+
 #define CONV_NONE		0
 #define CONV_TO_UTF8		1
 #define CONV_9_TO_UTF8		2
--- a/src/version.c
+++ b/src/version.c
@@ -778,6 +778,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    1728,
+/**/
     1727,
 /**/
     1726,
--- a/src/viminfo.c
+++ b/src/viminfo.c
@@ -169,6 +169,493 @@ write_viminfo_bufferlist(FILE *fp)
     vim_free(line);
 }
 
+#if defined(FEAT_CMDHIST) || defined(PROTO)
+/*
+ * Buffers for history read from a viminfo file.  Only valid while reading.
+ */
+static histentry_T *viminfo_history[HIST_COUNT] =
+					       {NULL, NULL, NULL, NULL, NULL};
+static int	viminfo_hisidx[HIST_COUNT] = {0, 0, 0, 0, 0};
+static int	viminfo_hislen[HIST_COUNT] = {0, 0, 0, 0, 0};
+static int	viminfo_add_at_front = FALSE;
+
+/*
+ * Translate a history type number to the associated character.
+ */
+    static int
+hist_type2char(
+    int	    type,
+    int	    use_question)	    // use '?' instead of '/'
+{
+    if (type == HIST_CMD)
+	return ':';
+    if (type == HIST_SEARCH)
+    {
+	if (use_question)
+	    return '?';
+	else
+	    return '/';
+    }
+    if (type == HIST_EXPR)
+	return '=';
+    return '@';
+}
+
+/*
+ * Prepare for reading the history from the viminfo file.
+ * This allocates history arrays to store the read history lines.
+ */
+    static void
+prepare_viminfo_history(int asklen, int writing)
+{
+    int	    i;
+    int	    num;
+    int	    type;
+    int	    len;
+    int	    hislen = get_hislen();
+
+    init_history();
+    viminfo_add_at_front = (asklen != 0 && !writing);
+    if (asklen > hislen)
+	asklen = hislen;
+
+    for (type = 0; type < HIST_COUNT; ++type)
+    {
+	histentry_T *histentry = get_histentry(type);
+
+	// Count the number of empty spaces in the history list.  Entries read
+	// from viminfo previously are also considered empty.  If there are
+	// more spaces available than we request, then fill them up.
+	for (i = 0, num = 0; i < hislen; i++)
+	    if (histentry[i].hisstr == NULL || histentry[i].viminfo)
+		num++;
+	len = asklen;
+	if (num > len)
+	    len = num;
+	if (len <= 0)
+	    viminfo_history[type] = NULL;
+	else
+	    viminfo_history[type] = LALLOC_MULT(histentry_T, len);
+	if (viminfo_history[type] == NULL)
+	    len = 0;
+	viminfo_hislen[type] = len;
+	viminfo_hisidx[type] = 0;
+    }
+}
+
+/*
+ * Accept a line from the viminfo, store it in the history array when it's
+ * new.
+ */
+    static int
+read_viminfo_history(vir_T *virp, int writing)
+{
+    int		type;
+    long_u	len;
+    char_u	*val;
+    char_u	*p;
+
+    type = hist_char2type(virp->vir_line[0]);
+    if (viminfo_hisidx[type] < viminfo_hislen[type])
+    {
+	val = viminfo_readstring(virp, 1, TRUE);
+	if (val != NULL && *val != NUL)
+	{
+	    int sep = (*val == ' ' ? NUL : *val);
+
+	    if (!in_history(type, val + (type == HIST_SEARCH),
+					  viminfo_add_at_front, sep, writing))
+	    {
+		// Need to re-allocate to append the separator byte.
+		len = STRLEN(val);
+		p = alloc(len + 2);
+		if (p != NULL)
+		{
+		    if (type == HIST_SEARCH)
+		    {
+			// Search entry: Move the separator from the first
+			// column to after the NUL.
+			mch_memmove(p, val + 1, (size_t)len);
+			p[len] = sep;
+		    }
+		    else
+		    {
+			// Not a search entry: No separator in the viminfo
+			// file, add a NUL separator.
+			mch_memmove(p, val, (size_t)len + 1);
+			p[len + 1] = NUL;
+		    }
+		    viminfo_history[type][viminfo_hisidx[type]].hisstr = p;
+		    viminfo_history[type][viminfo_hisidx[type]].time_set = 0;
+		    viminfo_history[type][viminfo_hisidx[type]].viminfo = TRUE;
+		    viminfo_history[type][viminfo_hisidx[type]].hisnum = 0;
+		    viminfo_hisidx[type]++;
+		}
+	    }
+	}
+	vim_free(val);
+    }
+    return viminfo_readline(virp);
+}
+
+/*
+ * Accept a new style history line from the viminfo, store it in the history
+ * array when it's new.
+ */
+    static void
+handle_viminfo_history(
+	garray_T    *values,
+	int	    writing)
+{
+    int		type;
+    long_u	len;
+    char_u	*val;
+    char_u	*p;
+    bval_T	*vp = (bval_T *)values->ga_data;
+
+    // Check the format:
+    // |{bartype},{histtype},{timestamp},{separator},"text"
+    if (values->ga_len < 4
+	    || vp[0].bv_type != BVAL_NR
+	    || vp[1].bv_type != BVAL_NR
+	    || (vp[2].bv_type != BVAL_NR && vp[2].bv_type != BVAL_EMPTY)
+	    || vp[3].bv_type != BVAL_STRING)
+	return;
+
+    type = vp[0].bv_nr;
+    if (type >= HIST_COUNT)
+	return;
+    if (viminfo_hisidx[type] < viminfo_hislen[type])
+    {
+	val = vp[3].bv_string;
+	if (val != NULL && *val != NUL)
+	{
+	    int sep = type == HIST_SEARCH && vp[2].bv_type == BVAL_NR
+						      ? vp[2].bv_nr : NUL;
+	    int idx;
+	    int overwrite = FALSE;
+
+	    if (!in_history(type, val, viminfo_add_at_front, sep, writing))
+	    {
+		// If lines were written by an older Vim we need to avoid
+		// getting duplicates. See if the entry already exists.
+		for (idx = 0; idx < viminfo_hisidx[type]; ++idx)
+		{
+		    p = viminfo_history[type][idx].hisstr;
+		    if (STRCMP(val, p) == 0
+			  && (type != HIST_SEARCH || sep == p[STRLEN(p) + 1]))
+		    {
+			overwrite = TRUE;
+			break;
+		    }
+		}
+
+		if (!overwrite)
+		{
+		    // Need to re-allocate to append the separator byte.
+		    len = vp[3].bv_len;
+		    p = alloc(len + 2);
+		}
+		else
+		    len = 0; // for picky compilers
+		if (p != NULL)
+		{
+		    viminfo_history[type][idx].time_set = vp[1].bv_nr;
+		    if (!overwrite)
+		    {
+			mch_memmove(p, val, (size_t)len + 1);
+			// Put the separator after the NUL.
+			p[len + 1] = sep;
+			viminfo_history[type][idx].hisstr = p;
+			viminfo_history[type][idx].hisnum = 0;
+			viminfo_history[type][idx].viminfo = TRUE;
+			viminfo_hisidx[type]++;
+		    }
+		}
+	    }
+	}
+    }
+}
+
+/*
+ * Concatenate history lines from viminfo after the lines typed in this Vim.
+ */
+    static void
+concat_history(int type)
+{
+    int		idx;
+    int		i;
+    int		hislen = get_hislen();
+    histentry_T *histentry = get_histentry(type);
+    int		*hisidx = get_hisidx(type);
+    int		*hisnum = get_hisnum(type);
+
+    idx = *hisidx + viminfo_hisidx[type];
+    if (idx >= hislen)
+	idx -= hislen;
+    else if (idx < 0)
+	idx = hislen - 1;
+    if (viminfo_add_at_front)
+	*hisidx = idx;
+    else
+    {
+	if (*hisidx == -1)
+	    *hisidx = hislen - 1;
+	do
+	{
+	    if (histentry[idx].hisstr != NULL || histentry[idx].viminfo)
+		break;
+	    if (++idx == hislen)
+		idx = 0;
+	} while (idx != *hisidx);
+	if (idx != *hisidx && --idx < 0)
+	    idx = hislen - 1;
+    }
+    for (i = 0; i < viminfo_hisidx[type]; i++)
+    {
+	vim_free(histentry[idx].hisstr);
+	histentry[idx].hisstr = viminfo_history[type][i].hisstr;
+	histentry[idx].viminfo = TRUE;
+	histentry[idx].time_set = viminfo_history[type][i].time_set;
+	if (--idx < 0)
+	    idx = hislen - 1;
+    }
+    idx += 1;
+    idx %= hislen;
+    for (i = 0; i < viminfo_hisidx[type]; i++)
+    {
+	histentry[idx++].hisnum = ++*hisnum;
+	idx %= hislen;
+    }
+}
+
+    static int
+sort_hist(const void *s1, const void *s2)
+{
+    histentry_T *p1 = *(histentry_T **)s1;
+    histentry_T *p2 = *(histentry_T **)s2;
+
+    if (p1->time_set < p2->time_set) return -1;
+    if (p1->time_set > p2->time_set) return 1;
+    return 0;
+}
+
+/*
+ * Merge history lines from viminfo and lines typed in this Vim based on the
+ * timestamp;
+ */
+    static void
+merge_history(int type)
+{
+    int		max_len;
+    histentry_T **tot_hist;
+    histentry_T *new_hist;
+    int		i;
+    int		len;
+    int		hislen = get_hislen();
+    histentry_T *histentry = get_histentry(type);
+    int		*hisidx = get_hisidx(type);
+    int		*hisnum = get_hisnum(type);
+
+    // Make one long list with all entries.
+    max_len = hislen + viminfo_hisidx[type];
+    tot_hist = ALLOC_MULT(histentry_T *, max_len);
+    new_hist = ALLOC_MULT(histentry_T, hislen );
+    if (tot_hist == NULL || new_hist == NULL)
+    {
+	vim_free(tot_hist);
+	vim_free(new_hist);
+	return;
+    }
+    for (i = 0; i < viminfo_hisidx[type]; i++)
+	tot_hist[i] = &viminfo_history[type][i];
+    len = i;
+    for (i = 0; i < hislen; i++)
+	if (histentry[i].hisstr != NULL)
+	    tot_hist[len++] = &histentry[i];
+
+    // Sort the list on timestamp.
+    qsort((void *)tot_hist, (size_t)len, sizeof(histentry_T *), sort_hist);
+
+    // Keep the newest ones.
+    for (i = 0; i < hislen; i++)
+    {
+	if (i < len)
+	{
+	    new_hist[i] = *tot_hist[i];
+	    tot_hist[i]->hisstr = NULL;
+	    if (new_hist[i].hisnum == 0)
+		new_hist[i].hisnum = ++*hisnum;
+	}
+	else
+	    clear_hist_entry(&new_hist[i]);
+    }
+    *hisidx = (i < len ? i : len) - 1;
+
+    // Free what is not kept.
+    for (i = 0; i < viminfo_hisidx[type]; i++)
+	vim_free(viminfo_history[type][i].hisstr);
+    for (i = 0; i < hislen; i++)
+	vim_free(histentry[i].hisstr);
+    vim_free(histentry);
+    set_histentry(type, new_hist);
+    vim_free(tot_hist);
+}
+
+/*
+ * Finish reading history lines from viminfo.  Not used when writing viminfo.
+ */
+    static void
+finish_viminfo_history(vir_T *virp)
+{
+    int	type;
+    int merge = virp->vir_version >= VIMINFO_VERSION_WITH_HISTORY;
+
+    for (type = 0; type < HIST_COUNT; ++type)
+    {
+	if (get_histentry(type) == NULL)
+	    continue;
+
+	if (merge)
+	    merge_history(type);
+	else
+	    concat_history(type);
+
+	VIM_CLEAR(viminfo_history[type]);
+	viminfo_hisidx[type] = 0;
+    }
+}
+
+/*
+ * Write history to viminfo file in "fp".
+ * When "merge" is TRUE merge history lines with a previously read viminfo
+ * file, data is in viminfo_history[].
+ * When "merge" is FALSE just write all history lines.  Used for ":wviminfo!".
+ */
+    static void
+write_viminfo_history(FILE *fp, int merge)
+{
+    int	    i;
+    int	    type;
+    int	    num_saved;
+    int     round;
+    int	    hislen;
+
+    init_history();
+    hislen = get_hislen();
+    if (hislen == 0)
+	return;
+    for (type = 0; type < HIST_COUNT; ++type)
+    {
+	histentry_T *histentry = get_histentry(type);
+	int	    *hisidx = get_hisidx(type);
+
+	num_saved = get_viminfo_parameter(hist_type2char(type, FALSE));
+	if (num_saved == 0)
+	    continue;
+	if (num_saved < 0)  // Use default
+	    num_saved = hislen;
+	fprintf(fp, _("\n# %s History (newest to oldest):\n"),
+			    type == HIST_CMD ? _("Command Line") :
+			    type == HIST_SEARCH ? _("Search String") :
+			    type == HIST_EXPR ? _("Expression") :
+			    type == HIST_INPUT ? _("Input Line") :
+					_("Debug Line"));
+	if (num_saved > hislen)
+	    num_saved = hislen;
+
+	/*
+	 * Merge typed and viminfo history:
+	 * round 1: history of typed commands.
+	 * round 2: history from recently read viminfo.
+	 */
+	for (round = 1; round <= 2; ++round)
+	{
+	    if (round == 1)
+		// start at newest entry, somewhere in the list
+		i = *hisidx;
+	    else if (viminfo_hisidx[type] > 0)
+		// start at newest entry, first in the list
+		i = 0;
+	    else
+		// empty list
+		i = -1;
+	    if (i >= 0)
+		while (num_saved > 0
+			&& !(round == 2 && i >= viminfo_hisidx[type]))
+		{
+		    char_u  *p;
+		    time_t  timestamp;
+		    int	    c = NUL;
+
+		    if (round == 1)
+		    {
+			p = histentry[i].hisstr;
+			timestamp = histentry[i].time_set;
+		    }
+		    else
+		    {
+			p = viminfo_history[type] == NULL ? NULL
+					    : viminfo_history[type][i].hisstr;
+			timestamp = viminfo_history[type] == NULL ? 0
+					  : viminfo_history[type][i].time_set;
+		    }
+
+		    if (p != NULL && (round == 2
+				       || !merge
+				       || !histentry[i].viminfo))
+		    {
+			--num_saved;
+			fputc(hist_type2char(type, TRUE), fp);
+			// For the search history: put the separator in the
+			// second column; use a space if there isn't one.
+			if (type == HIST_SEARCH)
+			{
+			    c = p[STRLEN(p) + 1];
+			    putc(c == NUL ? ' ' : c, fp);
+			}
+			viminfo_writestring(fp, p);
+
+			{
+			    char    cbuf[NUMBUFLEN];
+
+			    // New style history with a bar line. Format:
+			    // |{bartype},{histtype},{timestamp},{separator},"text"
+			    if (c == NUL)
+				cbuf[0] = NUL;
+			    else
+				sprintf(cbuf, "%d", c);
+			    fprintf(fp, "|%d,%d,%ld,%s,", BARTYPE_HISTORY,
+						 type, (long)timestamp, cbuf);
+			    barline_writestring(fp, p, LSIZE - 20);
+			    putc('\n', fp);
+			}
+		    }
+		    if (round == 1)
+		    {
+			// Decrement index, loop around and stop when back at
+			// the start.
+			if (--i < 0)
+			    i = hislen - 1;
+			if (i == *hisidx)
+			    break;
+		    }
+		    else
+		    {
+			// Increment index. Stop at the end in the while.
+			++i;
+		    }
+		}
+	}
+	for (i = 0; i < viminfo_hisidx[type]; ++i)
+	    if (viminfo_history[type] != NULL)
+		vim_free(viminfo_history[type][i].hisstr);
+	VIM_CLEAR(viminfo_history[type]);
+	viminfo_hisidx[type] = 0;
+    }
+}
+#endif // FEAT_VIMINFO
+
     static void
 write_viminfo_barlines(vir_T *virp, FILE *fp_out)
 {