Patch 7.4.1903
Problem:    When writing viminfo merging current history with history in
            viminfo may drop recent history entries.
Solution:   Add new format for viminfo lines, use it for history entries.  Use
            a timestamp for ordering the entries. Add test_settime().
            Add the viminfo version.  Does not do merging on timestamp yet.
Files:      src/eval.c, src/ex_getln.c, src/ex_cmds.c, src/structs.h,
            src/globals.h, src/proto/ex_cmds.pro, src/proto/ex_getln.pro,
            src/testdir/test_viminfo.vim


*** ../vim-7.4.1902/src/eval.c  2016-06-04 18:49:15.382070039 +0200
--- src/eval.c  2016-06-06 20:06:23.415629375 +0200
***************
*** 820,825 ****
--- 820,826 ----
  static void f_test_null_list(typval_T *argvars, typval_T *rettv);
  static void f_test_null_partial(typval_T *argvars, typval_T *rettv);
  static void f_test_null_string(typval_T *argvars, typval_T *rettv);
+ static void f_test_settime(typval_T *argvars, typval_T *rettv);
  #ifdef FEAT_FLOAT
  static void f_tan(typval_T *argvars, typval_T *rettv);
  static void f_tanh(typval_T *argvars, typval_T *rettv);
***************
*** 8809,8821 ****
  #ifdef FEAT_JOB_CHANNEL
      {"test_null_channel", 0, 0, f_test_null_channel},
  #endif
!     {"test_null_dict", 0, 0, f_test_null_dict},
  #ifdef FEAT_JOB_CHANNEL
!     {"test_null_job", 0, 0, f_test_null_job},
  #endif
!     {"test_null_list", 0, 0, f_test_null_list},
      {"test_null_partial", 0, 0, f_test_null_partial},
      {"test_null_string", 0, 0, f_test_null_string},
  #ifdef FEAT_TIMERS
      {"timer_start",   2, 3, f_timer_start},
      {"timer_stop",    1, 1, f_timer_stop},
--- 8810,8823 ----
  #ifdef FEAT_JOB_CHANNEL
      {"test_null_channel", 0, 0, f_test_null_channel},
  #endif
!     {"test_null_dict",        0, 0, f_test_null_dict},
  #ifdef FEAT_JOB_CHANNEL
!     {"test_null_job", 0, 0, f_test_null_job},
  #endif
!     {"test_null_list",        0, 0, f_test_null_list},
      {"test_null_partial", 0, 0, f_test_null_partial},
      {"test_null_string", 0, 0, f_test_null_string},
+     {"test_settime",  1, 1, f_test_settime},
  #ifdef FEAT_TIMERS
      {"timer_start",   2, 3, f_timer_start},
      {"timer_stop",    1, 1, f_timer_stop},
***************
*** 20849,20855 ****
  
  #ifdef FEAT_JOB_CHANNEL
      static void
! f_test_null_channel(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
  {
      rettv->v_type = VAR_CHANNEL;
      rettv->vval.v_channel = NULL;
--- 20851,20857 ----
  
  #ifdef FEAT_JOB_CHANNEL
      static void
! f_test_null_channel(typval_T *argvars UNUSED, typval_T *rettv)
  {
      rettv->v_type = VAR_CHANNEL;
      rettv->vval.v_channel = NULL;
***************
*** 20857,20863 ****
  #endif
  
      static void
! f_test_null_dict(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
  {
      rettv->v_type = VAR_DICT;
      rettv->vval.v_dict = NULL;
--- 20859,20865 ----
  #endif
  
      static void
! f_test_null_dict(typval_T *argvars UNUSED, typval_T *rettv)
  {
      rettv->v_type = VAR_DICT;
      rettv->vval.v_dict = NULL;
***************
*** 20865,20871 ****
  
  #ifdef FEAT_JOB_CHANNEL
      static void
! f_test_null_job(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
  {
      rettv->v_type = VAR_JOB;
      rettv->vval.v_job = NULL;
--- 20867,20873 ----
  
  #ifdef FEAT_JOB_CHANNEL
      static void
! f_test_null_job(typval_T *argvars UNUSED, typval_T *rettv)
  {
      rettv->v_type = VAR_JOB;
      rettv->vval.v_job = NULL;
***************
*** 20873,20898 ****
  #endif
  
      static void
! f_test_null_list(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
  {
      rettv->v_type = VAR_LIST;
      rettv->vval.v_list = NULL;
  }
  
      static void
! f_test_null_partial(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
  {
      rettv->v_type = VAR_PARTIAL;
      rettv->vval.v_partial = NULL;
  }
  
      static void
! f_test_null_string(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
  {
      rettv->v_type = VAR_STRING;
      rettv->vval.v_string = NULL;
  }
  
  #if defined(FEAT_JOB_CHANNEL) || defined(FEAT_TIMERS) || defined(PROTO)
  /*
   * Get a callback from "arg".  It can be a Funcref or a function name.
--- 20875,20906 ----
  #endif
  
      static void
! f_test_null_list(typval_T *argvars UNUSED, typval_T *rettv)
  {
      rettv->v_type = VAR_LIST;
      rettv->vval.v_list = NULL;
  }
  
      static void
! f_test_null_partial(typval_T *argvars UNUSED, typval_T *rettv)
  {
      rettv->v_type = VAR_PARTIAL;
      rettv->vval.v_partial = NULL;
  }
  
      static void
! f_test_null_string(typval_T *argvars UNUSED, typval_T *rettv)
  {
      rettv->v_type = VAR_STRING;
      rettv->vval.v_string = NULL;
  }
  
+     static void
+ f_test_settime(typval_T *argvars, typval_T *rettv UNUSED)
+ {
+     time_for_testing = (time_t)get_tv_number(&argvars[0]);
+ }
+ 
  #if defined(FEAT_JOB_CHANNEL) || defined(FEAT_TIMERS) || defined(PROTO)
  /*
   * Get a callback from "arg".  It can be a Funcref or a function name.
*** ../vim-7.4.1902/src/ex_getln.c      2016-05-07 18:36:44.244210666 +0200
--- src/ex_getln.c      2016-06-06 21:03:36.871582145 +0200
***************
*** 58,63 ****
--- 58,64 ----
      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};
***************
*** 5407,5412 ****
--- 5408,5427 ----
      NULL
  };
  
+ /*
+  * Return the current time in seconds.  Calls time(), unless test_settime()
+  * was used.
+  */
+     static time_t
+ vim_time(void)
+ {
+ #ifdef FEAT_EVAL
+     return time_for_testing == 0 ? time(NULL) : time_for_testing;
+ #else
+     return time(NULL);
+ #endif
+ }
+ 
  #if defined(FEAT_CMDL_COMPL) || defined(PROTO)
  /*
   * Function given to ExpandGeneric() to obtain the possible first
***************
*** 5576,5581 ****
--- 5591,5597 ----
        history[type][i].hisnum = ++hisnum[type];
        history[type][i].viminfo = FALSE;
        history[type][i].hisstr = str;
+       history[type][i].time_set = vim_time();
        return TRUE;
      }
      return FALSE;
***************
*** 5663,5668 ****
--- 5679,5685 ----
  
        hisptr->hisnum = ++hisnum[histype];
        hisptr->viminfo = FALSE;
+       hisptr->time_set = vim_time();
        if (histype == HIST_SEARCH && in_map)
            last_maptick = maptick;
      }
***************
*** 6131,6139 ****
  /*
   * Buffers for history read from a viminfo file.  Only valid while reading.
   */
! static char_u **viminfo_history[HIST_COUNT] = {NULL, NULL, NULL, NULL};
! static int    viminfo_hisidx[HIST_COUNT] = {0, 0, 0, 0};
! static int    viminfo_hislen[HIST_COUNT] = {0, 0, 0, 0};
  static int    viminfo_add_at_front = FALSE;
  
  static int    hist_type2char(int type, int use_question);
--- 6148,6157 ----
  /*
   * 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;
  
  static int    hist_type2char(int type, int use_question);
***************
*** 6191,6198 ****
        if (len <= 0)
            viminfo_history[type] = NULL;
        else
!           viminfo_history[type] =
!                  (char_u **)lalloc((long_u)(len * sizeof(char_u *)), FALSE);
        if (viminfo_history[type] == NULL)
            len = 0;
        viminfo_hislen[type] = len;
--- 6209,6216 ----
        if (len <= 0)
            viminfo_history[type] = NULL;
        else
!           viminfo_history[type] = (histentry_T *)lalloc(
!                                 (long_u)(len * sizeof(histentry_T)), FALSE);
        if (viminfo_history[type] == NULL)
            len = 0;
        viminfo_hislen[type] = len;
***************
*** 6242,6248 ****
                        mch_memmove(p, val, (size_t)len + 1);
                        p[len + 1] = NUL;
                    }
!                   viminfo_history[type][viminfo_hisidx[type]++] = p;
                }
            }
        }
--- 6260,6268 ----
                        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_hisidx[type]++;
                }
            }
        }
***************
*** 6252,6257 ****
--- 6272,6352 ----
  }
  
  /*
+  * Accept a new style history line from the viminfo, store it in the history
+  * array when it's new.
+  */
+     void
+ handle_viminfo_history(
+       bval_T  *values,
+       int     count,
+       int     writing)
+ {
+     int               type;
+     long_u    len;
+     char_u    *val;
+     char_u    *p;
+ 
+     /* Check the format:
+      * |{bartype},{histtype},{timestamp},{separator},"text" */
+     if (count < 4
+           || values[0].bv_type != BVAL_NR
+           || values[1].bv_type != BVAL_NR
+           || (values[2].bv_type != BVAL_NR && values[2].bv_type != BVAL_EMPTY)
+           || values[3].bv_type != BVAL_STRING)
+       return;
+ 
+     type = values[0].bv_nr;
+     if (type >= HIST_COUNT)
+       return;
+     if (viminfo_hisidx[type] < viminfo_hislen[type])
+     {
+       val = values[3].bv_string;
+       if (val != NULL && *val != NUL)
+       {
+           int sep = type == HIST_SEARCH && values[2].bv_type == BVAL_NR
+                                                     ? values[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 = values[3].bv_len;
+                   p = lalloc(len + 2, TRUE);
+               }
+               if (p != NULL)
+               {
+                   viminfo_history[type][idx].time_set = values[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_hisidx[type]++;
+                   }
+               }
+           }
+       }
+     }
+ }
+ 
+ /*
   * Finish reading history lines from viminfo.  Not used when writing viminfo.
   */
      void
***************
*** 6290,6297 ****
        for (i = 0; i < viminfo_hisidx[type]; i++)
        {
            vim_free(history[type][idx].hisstr);
!           history[type][idx].hisstr = viminfo_history[type][i];
            history[type][idx].viminfo = TRUE;
            if (--idx < 0)
                idx = hislen - 1;
        }
--- 6385,6393 ----
        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;
        }
***************
*** 6315,6329 ****
   * 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;
-     char_u  *p;
-     int           c;
      int     round;
  
      init_history();
--- 6411,6421 ----
   * 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();
***************
*** 6339,6346 ****
        fprintf(fp, _("\n# %s History (newest to oldest):\n"),
                            type == HIST_CMD ? _("Command Line") :
                            type == HIST_SEARCH ? _("Search String") :
!                           type == HIST_EXPR ?  _("Expression") :
!                                       _("Input Line"));
        if (num_saved > hislen)
            num_saved = hislen;
  
--- 6431,6439 ----
        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;
  
***************
*** 6364,6372 ****
                while (num_saved > 0
                        && !(round == 2 && i >= viminfo_hisidx[type]))
                {
!                   p = round == 1 ? history[type][i].hisstr
!                                  : viminfo_history[type] == NULL ? NULL
!                                                  : viminfo_history[type][i];
                    if (p != NULL && (round == 2
                                       || !merge
                                       || !history[type][i].viminfo))
--- 6457,6479 ----
                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))
***************
*** 6381,6386 ****
--- 6488,6508 ----
                            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)
                    {
***************
*** 6400,6406 ****
        }
        for (i = 0; i < viminfo_hisidx[type]; ++i)
            if (viminfo_history[type] != NULL)
!               vim_free(viminfo_history[type][i]);
        vim_free(viminfo_history[type]);
        viminfo_history[type] = NULL;
        viminfo_hisidx[type] = 0;
--- 6522,6528 ----
        }
        for (i = 0; i < viminfo_hisidx[type]; ++i)
            if (viminfo_history[type] != NULL)
!               vim_free(viminfo_history[type][i].hisstr);
        vim_free(viminfo_history[type]);
        viminfo_history[type] = NULL;
        viminfo_hisidx[type] = 0;
*** ../vim-7.4.1902/src/ex_cmds.c       2016-06-04 22:31:23.969886694 +0200
--- src/ex_cmds.c       2016-06-06 21:06:24.771579836 +0200
***************
*** 1750,1758 ****
--- 1750,1763 ----
  #if defined(FEAT_VIMINFO) || defined(PROTO)
  
  static int no_viminfo(void);
+ static int read_viminfo_barline(vir_T *virp, int got_encoding, int writing);
+ static void write_viminfo_version(FILE *fp_out);
  static void write_viminfo_barlines(vir_T *virp, FILE *fp_out);
  static int  viminfo_errcnt;
  
+ #define VIMINFO_VERSION 2
+ #define VIMINFO_VERSION_WITH_HISTORY 2
+ 
      static int
  no_viminfo(void)
  {
***************
*** 2156,2161 ****
--- 2161,2167 ----
      vir.vir_conv.vc_type = CONV_NONE;
  #endif
      ga_init2(&vir.vir_barlines, (int)sizeof(char_u *), 100);
+     vir.vir_version = -1;
  
      if (fp_in != NULL)
      {
***************
*** 2177,2182 ****
--- 2183,2189 ----
        fprintf(fp_out, _("# This viminfo file was generated by Vim %s.\n"),
                                                          VIM_VERSION_MEDIUM);
        fputs(_("# You may edit it if you're careful!\n\n"), fp_out);
+       write_viminfo_version(fp_out);
  #ifdef FEAT_MBYTE
        fputs(_("# Value of 'encoding' when this file was written\n"), fp_out);
        fprintf(fp_out, "*encoding=%s\n\n", p_enc);
***************
*** 2220,2225 ****
--- 2227,2233 ----
  {
      int               eof;
      buf_T     *buf;
+     int               got_encoding = FALSE;
  
  #ifdef FEAT_CMDHIST
      prepare_viminfo_history(forceit ? 9999 : 0, writing);
***************
*** 2240,2251 ****
            case '#':
                eof = viminfo_readline(virp);
                break;
!           case '|': /* copy line (for future use) */
!               if (writing)
!                   ga_add_string(&virp->vir_barlines, virp->vir_line);
!               eof = viminfo_readline(virp);
                break;
            case '*': /* "*encoding=value" */
                eof = viminfo_encoding(virp);
                break;
            case '!': /* global variable */
--- 2248,2258 ----
            case '#':
                eof = viminfo_readline(virp);
                break;
!           case '|':
!               eof = read_viminfo_barline(virp, got_encoding, writing);
                break;
            case '*': /* "*encoding=value" */
+               got_encoding = TRUE;
                eof = viminfo_encoding(virp);
                break;
            case '!': /* global variable */
***************
*** 2274,2283 ****
            case '=':
            case '@':
  #ifdef FEAT_CMDHIST
!               eof = read_viminfo_history(virp, writing);
! #else
!               eof = viminfo_readline(virp);
  #endif
                break;
            case '-':
            case '\'':
--- 2281,2293 ----
            case '=':
            case '@':
  #ifdef FEAT_CMDHIST
!               /* When history is in bar lines skip the old style history
!                * lines. */
!               if (virp->vir_version < VIMINFO_VERSION_WITH_HISTORY)
!                   eof = read_viminfo_history(virp, writing);
!               else
  #endif
+                   eof = viminfo_readline(virp);
                break;
            case '-':
            case '\'':
***************
*** 2347,2354 ****
  }
  
  /*
!  * check string read from viminfo file
!  * remove '\n' at the end of the line
   * - replace CTRL-V CTRL-V with CTRL-V
   * - replace CTRL-V 'n'    with '\n'
   *
--- 2357,2364 ----
  }
  
  /*
!  * Check string read from viminfo file.
!  * Remove '\n' at the end of the line.
   * - replace CTRL-V CTRL-V with CTRL-V
   * - replace CTRL-V 'n'    with '\n'
   *
***************
*** 2463,2468 ****
--- 2473,2755 ----
      putc('\n', fd);
  }
  
+ /*
+  * Write a string in quotes that barline_parse() can read back.
+  * Breaks the line in less than LSIZE pieces when needed.
+  * Returns remaining characters in the line.
+  */
+     int
+ barline_writestring(FILE *fd, char_u *s, int remaining_start)
+ {
+     char_u *p;
+     int           remaining = remaining_start;
+     int           len = 2;
+ 
+     /* Count the number of characters produced, including quotes. */
+     for (p = s; *p != NUL; ++p)
+     {
+       if (*p == NL)
+           len += 2;
+       else if (*p == '"' || *p == '\\')
+           len += 2;
+       else
+           ++len;
+     }
+     if (len > remaining)
+     {
+       fprintf(fd, ">%d\n|<", len);
+       remaining = LSIZE - 20;
+     }
+ 
+     putc('"', fd);
+     for (p = s; *p != NUL; ++p)
+     {
+       if (*p == NL)
+       {
+           putc('\\', fd);
+           putc('n', fd);
+           --remaining;
+       }
+       else if (*p == '"' || *p == '\\')
+       {
+           putc('\\', fd);
+           putc(*p, fd);
+           --remaining;
+       }
+       else
+           putc(*p, fd);
+       --remaining;
+ 
+       if (remaining < 3)
+       {
+           putc('\n', fd);
+           putc('|', fd);
+           putc('<', fd);
+           /* Leave enough space for another continuation. */
+           remaining = LSIZE - 20;
+       }
+     }
+     putc('"', fd);
+     return remaining;
+ }
+ 
+ /*
+  * Parse a viminfo line starting with '|'.
+  * Put each decoded value in "values" and return the number of values found.
+  */
+     static int
+ barline_parse(vir_T *virp, char_u *text, bval_T *values)
+ {
+     char_u  *p = text;
+     char_u  *nextp = NULL;
+     char_u  *buf = NULL;;
+     int           count = 0;
+     int           i;
+     int           allocated = FALSE;
+ 
+     while (*p == ',')
+     {
+       if (count == BVAL_MAX)
+       {
+           EMSG2(e_intern2, "barline_parse()");
+           break;
+       }
+       ++p;
+ 
+       if (*p == '>')
+       {
+           /* Need to read a continuation line.  Need to put strings in
+            * allocated memory, because virp->vir_line is overwritten. */
+           if (!allocated)
+           {
+               for (i = 0; i < count; ++i)
+                   if (values[i].bv_type == BVAL_STRING)
+                   {
+                       values[i].bv_string = vim_strnsave(
+                                      values[i].bv_string, values[i].bv_len);
+                       values[i].bv_allocated = TRUE;
+                   }
+               allocated = TRUE;
+           }
+ 
+           if (vim_isdigit(p[1]))
+           {
+               int len;
+               int todo;
+               int n;
+ 
+               /* String value was split into lines that are each shorter
+                * than LSIZE:
+                *     |{bartype},>{length of "{text}{text2}"}
+                *     |<"{text1}
+                *     |<{text2}",{value}
+                */
+               ++p;
+               len = getdigits(&p);
+               buf = alloc(len + 1);
+               p = buf;
+               for (todo = len; todo > 0; todo -= n)
+               {
+                   if (viminfo_readline(virp) || virp->vir_line[0] != '|'
+                                                 || virp->vir_line[1] != '<')
+                       /* file was truncated or garbled */
+                       return 0;
+                   /* Get length of text, excluding |< and NL chars. */
+                   n = STRLEN(virp->vir_line);
+                   while (n > 0 && (virp->vir_line[n - 1] == NL
+                                            || virp->vir_line[n - 1] == CAR))
+                       --n;
+                   n -= 2;
+                   if (n > todo)
+                   {
+                       /* more values follow after the string */
+                       nextp = virp->vir_line + 2 + todo;
+                       n = todo;
+                   }
+                   mch_memmove(p, virp->vir_line + 2, n);
+                   p += n;
+               }
+               *p = NUL;
+               p = buf;
+           }
+           else
+           {
+               /* Line ending in ">" continues in the next line:
+                *     |{bartype},{lots of values},>
+                *     |<{value},{value}
+                */
+               if (viminfo_readline(virp) || virp->vir_line[0] != '|'
+                                             || virp->vir_line[1] != '<')
+                   /* file was truncated or garbled */
+                   return 0;
+               p = virp->vir_line + 2;
+           }
+       }
+ 
+       if (isdigit(*p))
+       {
+           values[count].bv_type = BVAL_NR;
+           values[count].bv_nr = getdigits(&p);
+           ++count;
+       }
+       else if (*p == '"')
+       {
+           int     len = 0;
+           char_u  *s = p;
+ 
+           /* Unescape special characters in-place. */
+           ++p;
+           while (*p != '"')
+           {
+               if (*p == NL || *p == NUL)
+                   return count;  /* syntax error, drop the value */
+               if (*p == '\\')
+               {
+                   ++p;
+                   if (*p == 'n')
+                       s[len++] = '\n';
+                   else
+                       s[len++] = *p;
+                   ++p;
+               }
+               else
+                   s[len++] = *p++;
+           }
+           s[len] = NUL;
+ 
+           if (s != buf && allocated)
+               s = vim_strsave(s);
+           values[count].bv_string = s;
+           values[count].bv_type = BVAL_STRING;
+           values[count].bv_len = len;
+           values[count].bv_allocated = allocated;
+           ++count;
+           if (nextp != NULL)
+           {
+               /* values following a long string */
+               p = nextp;
+               nextp = NULL;
+           }
+       }
+       else if (*p == ',')
+       {
+           values[count].bv_type = BVAL_EMPTY;
+           ++count;
+       }
+       else
+           break;
+     }
+ 
+     return count;
+ }
+ 
+     static int
+ read_viminfo_barline(vir_T *virp, int got_encoding, int writing)
+ {
+     char_u    *p = virp->vir_line + 1;
+     int               bartype;
+     bval_T    values[BVAL_MAX];
+     int               count = 0;
+     int               i;
+ 
+     /* The format is: |{bartype},{value},...
+      * For a very long string:
+      *     |{bartype},>{length of "{text}{text2}"}
+      *     |<{text1}
+      *     |<{text2},{value}
+      * For a long line not using a string
+      *     |{bartype},{lots of values},>
+      *     |<{value},{value}
+      */
+     if (*p == '<')
+     {
+       /* Continuation line of an unrecognized item. */
+       if (writing)
+           ga_add_string(&virp->vir_barlines, virp->vir_line);
+     }
+     else
+     {
+       bartype = getdigits(&p);
+       switch (bartype)
+       {
+           case BARTYPE_VERSION:
+               /* Only use the version when it comes before the encoding.
+                * If it comes later it was copied by a Vim version that
+                * doesn't understand the version. */
+               if (!got_encoding)
+               {
+                   count = barline_parse(virp, p, values);
+                   if (count > 0 && values[0].bv_type == BVAL_NR)
+                       virp->vir_version = values[0].bv_nr;
+               }
+               break;
+ 
+           case BARTYPE_HISTORY:
+               count = barline_parse(virp, p, values);
+               handle_viminfo_history(values, count, writing);
+               break;
+ 
+           default:
+               /* copy unrecognized line (for future use) */
+               if (writing)
+                   ga_add_string(&virp->vir_barlines, virp->vir_line);
+       }
+     }
+ 
+     for (i = 0; i < count; ++i)
+       if (values[i].bv_type == BVAL_STRING && values[i].bv_allocated)
+           vim_free(values[i].bv_string);
+ 
+     return viminfo_readline(virp);
+ }
+ 
+     static void
+ write_viminfo_version(FILE *fp_out)
+ {
+     fprintf(fp_out, "# Viminfo version\n|%d,%d\n\n",
+                                           BARTYPE_VERSION, VIMINFO_VERSION);
+ }
+ 
      static void
  write_viminfo_barlines(vir_T *virp, FILE *fp_out)
  {
*** ../vim-7.4.1902/src/structs.h       2016-06-04 17:17:07.042146086 +0200
--- src/structs.h       2016-06-05 17:28:47.096947955 +0200
***************
*** 1014,1019 ****
--- 1014,1020 ----
  #ifdef FEAT_MBYTE
      vimconv_T vir_conv;       /* encoding conversion */
  #endif
+     int               vir_version;    /* viminfo version detected or -1 */
      garray_T  vir_barlines;   /* lines starting with | */
  } vir_T;
  
*** ../vim-7.4.1902/src/globals.h       2016-06-02 14:29:59.136661030 +0200
--- src/globals.h       2016-06-06 19:49:45.187643107 +0200
***************
*** 1639,1644 ****
--- 1639,1648 ----
  EXTERN int  did_add_timer INIT(= FALSE);
  #endif
  
+ #ifdef FEAT_EVAL
+ EXTERN time_t time_for_testing INIT(= 0);
+ #endif
+ 
  /*
   * Optional Farsi support.  Include it here, so EXTERN and INIT are defined.
   */
*** ../vim-7.4.1902/src/proto/ex_cmds.pro       2016-01-19 13:21:55.837334377 
+0100
--- src/proto/ex_cmds.pro       2016-06-05 19:16:00.536859458 +0200
***************
*** 16,21 ****
--- 16,22 ----
  int viminfo_readline(vir_T *virp);
  char_u *viminfo_readstring(vir_T *virp, int off, int convert);
  void viminfo_writestring(FILE *fd, char_u *p);
+ int barline_writestring(FILE *fd, char_u *s, int remaining_start);
  void do_fixdel(exarg_T *eap);
  void print_line_no_prefix(linenr_T lnum, int use_number, int list);
  void print_line(linenr_T lnum, int use_number, int list);
*** ../vim-7.4.1902/src/proto/ex_getln.pro      2016-01-19 13:21:55.837334377 
+0100
--- src/proto/ex_getln.pro      2016-06-05 23:10:08.664666215 +0200
***************
*** 37,55 ****
  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);
- char_u *get_cmdline_str(void);
- int get_cmdline_pos(void);
- int set_cmdline_pos(int pos);
- int get_cmdline_type(void);
  char_u *get_history_entry(int histype, int idx);
  int clr_history(int histype);
  int del_history_entry(int histype, char_u *str);
  int del_history_idx(int histype, int idx);
  void remove_key_from_history(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 finish_viminfo_history(void);
  void write_viminfo_history(FILE *fp, int merge);
  void cmd_pchar(int c, int offset);
--- 37,56 ----
  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);
  char_u *get_history_entry(int histype, int idx);
  int clr_history(int histype);
  int del_history_entry(int histype, char_u *str);
  int del_history_idx(int histype, int idx);
  void remove_key_from_history(void);
+ char_u *get_cmdline_str(void);
+ int get_cmdline_pos(void);
+ 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(bval_T *values, int count, int writing);
  void finish_viminfo_history(void);
  void write_viminfo_history(FILE *fp, int merge);
  void cmd_pchar(int c, int offset);
*** ../vim-7.4.1902/src/testdir/test_viminfo.vim        2016-03-30 
20:50:41.905696041 +0200
--- src/testdir/test_viminfo.vim        2016-06-06 20:50:52.899592654 +0200
***************
*** 1,6 ****
--- 1,7 ----
  " Test for reading and writing .viminfo
  
  function Test_read_and_write()
+   call histdel(':')
    let lines = [
        \ '# comment line',
        \ '*encoding=utf-8',
***************
*** 18,31 ****
    for line in lines
      if line[0] == '|'
        if done == 0
!       call assert_equal('|copied as-is', line)
        elseif done == 1
        call assert_equal('|and one more', line)
        endif
        let done += 1
      endif
    endfor
!   call assert_equal(2, done)
  
    call delete('Xviminfo')
  endfunc
--- 19,34 ----
    for line in lines
      if line[0] == '|'
        if done == 0
!       call assert_equal('|1,2', line)
        elseif done == 1
+       call assert_equal('|copied as-is', line)
+       elseif done == 2
        call assert_equal('|and one more', line)
        endif
        let done += 1
      endif
    endfor
!   call assert_equal(3, done)
  
    call delete('Xviminfo')
  endfunc
***************
*** 48,50 ****
--- 51,118 ----
    call delete('Xviminfo')
    set viminfo-=!
  endfunc
+ 
+ func Test_cmdline_history()
+   call histdel(':')
+   call test_settime(11)
+   call histadd(':', "echo 'one'")
+   call test_settime(12)
+   " split into two lines
+   let long800 = repeat(" 'eight'", 100)
+   call histadd(':', "echo " . long800)
+   call test_settime(13)
+   " split into three lines
+   let long1400 = repeat(" 'fourteeeeen'", 100)
+   call histadd(':', "echo " . long1400)
+   wviminfo Xviminfo
+   let lines = readfile('Xviminfo')
+   let done_colon = 0
+   let done_bar = 0
+   let lnum = 0
+   while lnum < len(lines)
+     let line = lines[lnum] | let lnum += 1
+     if line[0] == ':'
+       if done_colon == 0
+       call assert_equal(":\x161408", line)
+       let line = lines[lnum] | let lnum += 1
+       call assert_equal('<echo ' . long1400, line)
+       elseif done_colon == 1
+       call assert_equal(":\x16808", line)
+       let line = lines[lnum] | let lnum += 1
+       call assert_equal("<echo " . long800, line)
+       elseif done_colon == 2
+       call assert_equal(":echo 'one'", line)
+       endif
+       let done_colon += 1
+     elseif line[0:4] == '|2,0,'
+       if done_bar == 0
+       call assert_equal("|2,0,13,,>1407", line)
+       let line = lines[lnum] | let lnum += 1
+       call assert_equal('|<"echo ' . long1400[0:484], line)
+       let line = lines[lnum] | let lnum += 1
+       call assert_equal('|<' . long1400[485:974], line)
+       let line = lines[lnum] | let lnum += 1
+       call assert_equal('|<' . long1400[975:] . '"', line)
+       elseif done_bar == 1
+       call assert_equal('|2,0,12,,>807', line)
+       let line = lines[lnum] | let lnum += 1
+       call assert_equal('|<"echo ' . long800[0:484], line)
+       let line = lines[lnum] | let lnum += 1
+       call assert_equal('|<' . long800[485:] . '"', line)
+       elseif done_bar == 2
+       call assert_equal("|2,0,11,,\"echo 'one'\"", line)
+       endif
+       let done_bar += 1
+     endif
+   endwhile
+   call assert_equal(3, done_colon)
+   call assert_equal(3, done_bar)
+ 
+   call histdel(':')
+   rviminfo Xviminfo
+   call assert_equal("echo " . long1400, histget(':', -1))
+   call assert_equal("echo " . long800, histget(':', -2))
+   call assert_equal("echo 'one'", histget(':', -3))
+ 
+   call delete('Xviminfo')
+ endfunc
*** ../vim-7.4.1902/src/version.c       2016-06-05 16:10:49.537012299 +0200
--- src/version.c       2016-06-06 21:02:25.207583131 +0200
***************
*** 755,756 ****
--- 755,758 ----
  {   /* Add new patch number below this line */
+ /**/
+     1903,
  /**/

-- 
"I simultaneously try to keep my head in the clouds and my feet on the
ground.  Sometimes it's a stretch, though."              -- Larry Wall

 /// Bram Moolenaar -- b...@moolenaar.net -- http://www.Moolenaar.net   \\\
///        sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\
\\\  an exciting new programming language -- http://www.Zimbu.org        ///
 \\\            help me help AIDS victims -- http://ICCF-Holland.org    ///

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to vim_dev+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Raspunde prin e-mail lui