summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBram Moolenaar <Bram@vim.org>2019-07-21 21:51:59 +0200
committerBram Moolenaar <Bram@vim.org>2019-07-21 21:51:59 +0200
commit5f32ece459d1f310b1b48b72e07dcd77d3261a76 (patch)
tree32f770f03d408817c017596ae1836f2e29ebde32
parentdefa067c54874dd987121dd7252c62755e0aebfa (diff)
patch 8.1.1728: wrong place for command line history viminfo supportv8.1.1728
Problem: Wrong place for command line history viminfo support. Solution: Move it to viminfo.c.
-rw-r--r--src/ex_getln.c489
-rw-r--r--src/proto/ex_getln.pro13
-rw-r--r--src/structs.h11
-rw-r--r--src/version.c2
-rw-r--r--src/viminfo.c487
5 files changed, 525 insertions, 477 deletions
diff --git a/src/ex_getln.c b/src/ex_getln.c
index efeae94ed8..471479c2ca 100644
--- a/src/ex_getln.c
+++ b/src/ex_getln.c
@@ -60,21 +60,11 @@ static int extra_char = NUL; /* extra character to display when redrawing
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 *xp, regmatch_T *regmatch, int *num_file,
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,479 +6616,37 @@ 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
+get_hislen(void)
{
- 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;
- }
+ return hislen;
}
-/*
- * 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)
+ histentry_T *
+get_histentry(int hist_type)
{
- 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 history[hist_type];
}
-/*
- * 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)
+set_histentry(int hist_type, histentry_T *entry)
{
- 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]++;
- }
- }
- }
- }
- }
+ history[hist_type] = entry;
}
-/*
- * Concatenate history lines from viminfo after the lines typed in this Vim.
- */
- static void
-concat_history(int type)
+ int *
+get_hisidx(int hist_type)
{
- 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;
- }
+ return &hisidx[hist_type];
}
-#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
- static int
-sort_hist(const void *s1, const void *s2)
+ int *
+get_hisnum(int hist_type)
{
- 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;
+ return &hisnum[hist_type];
}
#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 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;
- }
-}
-
-/*
- * 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 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;
- }
-}
-#endif /* FEAT_VIMINFO */
-
#if defined(FEAT_CMDWIN) || defined(PROTO)
/*
* Open a window on the current command line and history. Allow editing in
diff --git a/src/proto/ex_getln.pro b/src/proto/ex_getln.pro
index 5d513707bf..cd30ed8c67 100644
--- a/src/proto/ex_getln.pro
+++ b/src/proto/ex_getln.pro
@@ -34,7 +34,10 @@ void set_cmd_context(expand_T *xp, char_u *str, int len, int col, int use_ccline
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 : */
diff --git a/src/structs.h b/src/structs.h
index 525289cb7f..7a3e8d2e57 100644
--- 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
diff --git a/src/version.c b/src/version.c
index f230387768..ce47c9b26f 100644
--- 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,
diff --git a/src/viminfo.c b/src/viminfo.c
index 3d3a410f96..d85ad119b8 100644
--- 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)
{