Patch 8.2.3139
Problem:    Functions for string manipulation are spread out.
Solution:   Move string related functions to a new source file. (Yegappan
            Lakshmanan, closes #8470)
Files:      Filelist, src/Make_ami.mak, src/Make_cyg_ming.mak,
            src/Make_mvc.mak, src/Make_vms.mms, src/Makefile, src/README.md,
            src/eval.c, src/evalfunc.c, src/mbyte.c, src/misc1.c, src/misc2.c,
            src/proto.h, src/proto/eval.pro, src/proto/evalfunc.pro,
            src/proto/mbyte.pro, src/proto/misc1.pro, src/proto/misc2.pro,
            src/proto/strings.pro, src/strings.c


*** ../vim-8.2.3138/Filelist    2021-06-02 17:07:02.276398163 +0200
--- Filelist    2021-07-10 21:23:10.451465397 +0200
***************
*** 134,139 ****
--- 134,140 ----
                src/spell.h \
                src/spellfile.c \
                src/spellsuggest.c \
+               src/strings.c \
                src/structs.h \
                src/syntax.c \
                src/tag.c \
***************
*** 296,301 ****
--- 297,303 ----
                src/proto/spell.pro \
                src/proto/spellfile.pro \
                src/proto/spellsuggest.pro \
+               src/proto/strings.pro \
                src/proto/syntax.pro \
                src/proto/tag.pro \
                src/proto/term.pro \
*** ../vim-8.2.3138/src/Make_ami.mak    2021-06-02 17:07:02.276398163 +0200
--- src/Make_ami.mak    2021-07-10 21:23:10.451465397 +0200
***************
*** 162,167 ****
--- 162,168 ----
        spell.c \
        spellfile.c \
        spellsuggest.c \
+       strings.c \
        syntax.c \
        tag.c \
        term.c \
*** ../vim-8.2.3138/src/Make_cyg_ming.mak       2021-06-20 14:01:25.980924619 
+0200
--- src/Make_cyg_ming.mak       2021-07-10 21:23:10.451465397 +0200
***************
*** 809,814 ****
--- 809,815 ----
        $(OUTDIR)/spell.o \
        $(OUTDIR)/spellfile.o \
        $(OUTDIR)/spellsuggest.o \
+       $(OUTDIR)/strings.o \
        $(OUTDIR)/syntax.o \
        $(OUTDIR)/tag.o \
        $(OUTDIR)/term.o \
*** ../vim-8.2.3138/src/Make_mvc.mak    2021-06-21 21:08:04.928547486 +0200
--- src/Make_mvc.mak    2021-07-10 21:23:10.451465397 +0200
***************
*** 819,824 ****
--- 819,825 ----
        $(OUTDIR)\spell.obj \
        $(OUTDIR)\spellfile.obj \
        $(OUTDIR)\spellsuggest.obj \
+       $(OUTDIR)\strings.obj \
        $(OUTDIR)\syntax.obj \
        $(OUTDIR)\tag.obj \
        $(OUTDIR)\term.obj \
***************
*** 1792,1797 ****
--- 1793,1800 ----
  
  $(OUTDIR)/spellsuggest.obj:   $(OUTDIR) spellsuggest.c  $(INCL)
  
+ $(OUTDIR)/strings.obj:        $(OUTDIR) strings.c  $(INCL)
+ 
  $(OUTDIR)/syntax.obj: $(OUTDIR) syntax.c  $(INCL)
  
  $(OUTDIR)/tag.obj:    $(OUTDIR) tag.c  $(INCL)
***************
*** 2012,2017 ****
--- 2015,2021 ----
        proto/spell.pro \
        proto/spellfile.pro \
        proto/spellsuggest.pro \
+       proto/strings.pro \
        proto/syntax.pro \
        proto/tag.pro \
        proto/term.pro \
*** ../vim-8.2.3138/src/Make_vms.mms    2021-06-02 17:07:02.276398163 +0200
--- src/Make_vms.mms    2021-07-10 21:23:10.451465397 +0200
***************
*** 393,398 ****
--- 393,399 ----
        spell.c \
        spellfile.c \
        spellsuggest.c \
+       strings.c \
        syntax.c \
        tag.c \
        term.c \
***************
*** 512,517 ****
--- 513,519 ----
        spell.obj \
        spellfile.obj \
        spellsuggest.obj \
+       strings.obj \
        syntax.obj \
        tag.obj \
        term.obj \
***************
*** 1048,1053 ****
--- 1050,1059 ----
   ascii.h keymap.h term.h macros.h option.h structs.h \
   regexp.h gui.h beval.h [.proto]gui_beval.pro alloc.h ex_cmds.h spell.h \
   proto.h errors.h globals.h
+ strings.obj : strings.c vim.h [.auto]config.h feature.h os_unix.h \
+  ascii.h keymap.h term.h macros.h option.h structs.h \
+  regexp.h gui.h beval.h [.proto]gui_beval.pro alloc.h ex_cmds.h spell.h \
+  proto.h errors.h globals.h
  syntax.obj : syntax.c vim.h [.auto]config.h feature.h os_unix.h \
   ascii.h keymap.h term.h macros.h structs.h regexp.h \
   gui.h beval.h [.proto]gui_beval.pro option.h ex_cmds.h proto.h \
*** ../vim-8.2.3138/src/Makefile        2021-06-02 19:22:41.541325153 +0200
--- src/Makefile        2021-07-10 21:23:10.451465397 +0200
***************
*** 1677,1682 ****
--- 1677,1683 ----
        spell.c \
        spellfile.c \
        spellsuggest.c \
+       strings.c \
        syntax.c \
        tag.c \
        term.c \
***************
*** 1828,1833 ****
--- 1829,1835 ----
        objects/spell.o \
        objects/spellfile.o \
        objects/spellsuggest.o \
+       objects/strings.o \
        objects/syntax.o \
        objects/tag.o \
        objects/term.o \
***************
*** 2011,2016 ****
--- 2013,2019 ----
        spell.pro \
        spellfile.pro \
        spellsuggest.pro \
+       strings.pro \
        syntax.pro \
        tag.pro \
        term.pro \
***************
*** 3516,3521 ****
--- 3519,3527 ----
  objects/spellsuggest.o: spellsuggest.c
        $(CCC) -o $@ spellsuggest.c
  
+ objects/strings.o: strings.c
+       $(CCC) -o $@ strings.c
+ 
  objects/syntax.o: syntax.c
        $(CCC) -o $@ syntax.c
  
***************
*** 4049,4054 ****
--- 4055,4064 ----
   os_unix.h auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \
   proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \
   proto.h errors.h globals.h
+ objects/strings.o: strings.c vim.h protodef.h auto/config.h feature.h 
os_unix.h \
+  auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \
+  proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \
+  proto.h errors.h globals.h
  objects/syntax.o: syntax.c vim.h protodef.h auto/config.h feature.h os_unix.h 
\
   auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \
   proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \
*** ../vim-8.2.3138/src/README.md       2021-06-02 17:07:02.276398163 +0200
--- src/README.md       2021-07-10 21:23:10.451465397 +0200
***************
*** 80,85 ****
--- 80,86 ----
  spell.c               | spell checking core
  spellfile.c   | spell file handling
  spellsuggest.c        | spell correction suggestions
+ strings.c     | string manipulation functions
  syntax.c      | syntax and other highlighting
  tag.c         | tags
  term.c                | terminal handling, termcap codes
*** ../vim-8.2.3138/src/eval.c  2021-07-08 21:38:45.339232318 +0200
--- src/eval.c  2021-07-10 21:23:10.451465397 +0200
***************
*** 57,62 ****
--- 57,63 ----
  
  static int free_unref_items(int copyID);
  static char_u *make_expanded_name(char_u *in_start, char_u *expr_start, 
char_u *expr_end, char_u *in_end);
+ static char_u *eval_next_line(evalarg_T *evalarg);
  
  /*
   * Return "n1" divided by "n2", taking care of dividing by zero.
***************
*** 2113,2119 ****
   * FALSE.
   * "arg" must point somewhere inside a line, not at the start.
   */
!     char_u *
  eval_next_non_blank(char_u *arg, evalarg_T *evalarg, int *getnext)
  {
      char_u *p = skipwhite(arg);
--- 2114,2120 ----
   * FALSE.
   * "arg" must point somewhere inside a line, not at the start.
   */
!     static char_u *
  eval_next_non_blank(char_u *arg, evalarg_T *evalarg, int *getnext)
  {
      char_u *p = skipwhite(arg);
***************
*** 2144,2150 ****
   * To be called after eval_next_non_blank() sets "getnext" to TRUE.
   * Only called for Vim9 script.
   */
!     char_u *
  eval_next_line(evalarg_T *evalarg)
  {
      garray_T  *gap = &evalarg->eval_ga;
--- 2145,2151 ----
   * To be called after eval_next_non_blank() sets "getnext" to TRUE.
   * Only called for Vim9 script.
   */
!     static char_u *
  eval_next_line(evalarg_T *evalarg)
  {
      garray_T  *gap = &evalarg->eval_ga;
***************
*** 5172,5221 ****
  }
  
  /*
-  * Return string "str" in ' quotes, doubling ' characters.
-  * If "str" is NULL an empty string is assumed.
-  * If "function" is TRUE make it function('string').
-  */
-     char_u *
- string_quote(char_u *str, int function)
- {
-     unsigned  len;
-     char_u    *p, *r, *s;
- 
-     len = (function ? 13 : 3);
-     if (str != NULL)
-     {
-       len += (unsigned)STRLEN(str);
-       for (p = str; *p != NUL; MB_PTR_ADV(p))
-           if (*p == '\'')
-               ++len;
-     }
-     s = r = alloc(len);
-     if (r != NULL)
-     {
-       if (function)
-       {
-           STRCPY(r, "function('");
-           r += 10;
-       }
-       else
-           *r++ = '\'';
-       if (str != NULL)
-           for (p = str; *p != NUL; )
-           {
-               if (*p == '\'')
-                   *r++ = '\'';
-               MB_COPY_CHAR(p, r);
-           }
-       *r++ = '\'';
-       if (function)
-           *r++ = ')';
-       *r++ = NUL;
-     }
-     return s;
- }
- 
- /*
   * Convert the specified byte index of line 'lnum' in buffer 'buf' to a
   * character index.  Works only for loaded buffers. Returns -1 on failure.
   * The index of the first byte and the first character is zero.
--- 5173,5178 ----
*** ../vim-8.2.3138/src/evalfunc.c      2021-07-10 13:15:35.291053015 +0200
--- src/evalfunc.c      2021-07-10 21:23:10.451465397 +0200
***************
*** 29,42 ****
  # endif
  #endif
  static void f_byte2line(typval_T *argvars, typval_T *rettv);
- static void byteidx(typval_T *argvars, typval_T *rettv, int comp);
- static void f_byteidx(typval_T *argvars, typval_T *rettv);
- static void f_byteidxcomp(typval_T *argvars, typval_T *rettv);
  static void f_call(typval_T *argvars, typval_T *rettv);
  static void f_changenr(typval_T *argvars, typval_T *rettv);
  static void f_char2nr(typval_T *argvars, typval_T *rettv);
  static void f_charcol(typval_T *argvars, typval_T *rettv);
- static void f_charidx(typval_T *argvars, typval_T *rettv);
  static void f_col(typval_T *argvars, typval_T *rettv);
  static void f_confirm(typval_T *argvars, typval_T *rettv);
  static void f_copy(typval_T *argvars, typval_T *rettv);
--- 29,38 ----
***************
*** 83,89 ****
  static void f_hlID(typval_T *argvars, typval_T *rettv);
  static void f_hlexists(typval_T *argvars, typval_T *rettv);
  static void f_hostname(typval_T *argvars, typval_T *rettv);
- static void f_iconv(typval_T *argvars, typval_T *rettv);
  static void f_index(typval_T *argvars, typval_T *rettv);
  static void f_input(typval_T *argvars, typval_T *rettv);
  static void f_inputdialog(typval_T *argvars, typval_T *rettv);
--- 79,84 ----
***************
*** 173,191 ****
  static void f_spellsuggest(typval_T *argvars, typval_T *rettv);
  static void f_split(typval_T *argvars, typval_T *rettv);
  static void f_srand(typval_T *argvars, typval_T *rettv);
- static void f_str2list(typval_T *argvars, typval_T *rettv);
- static void f_str2nr(typval_T *argvars, typval_T *rettv);
- static void f_strcharlen(typval_T *argvars, typval_T *rettv);
- static void f_strchars(typval_T *argvars, typval_T *rettv);
- static void f_strgetchar(typval_T *argvars, typval_T *rettv);
- static void f_stridx(typval_T *argvars, typval_T *rettv);
- static void f_strlen(typval_T *argvars, typval_T *rettv);
- static void f_strcharpart(typval_T *argvars, typval_T *rettv);
- static void f_strpart(typval_T *argvars, typval_T *rettv);
- static void f_strridx(typval_T *argvars, typval_T *rettv);
- static void f_strtrans(typval_T *argvars, typval_T *rettv);
- static void f_strdisplaywidth(typval_T *argvars, typval_T *rettv);
- static void f_strwidth(typval_T *argvars, typval_T *rettv);
  static void f_submatch(typval_T *argvars, typval_T *rettv);
  static void f_substitute(typval_T *argvars, typval_T *rettv);
  static void f_swapinfo(typval_T *argvars, typval_T *rettv);
--- 168,173 ----
***************
*** 198,207 ****
  static void f_tabpagebuflist(typval_T *argvars, typval_T *rettv);
  static void f_taglist(typval_T *argvars, typval_T *rettv);
  static void f_tagfiles(typval_T *argvars, typval_T *rettv);
- static void f_tolower(typval_T *argvars, typval_T *rettv);
- static void f_toupper(typval_T *argvars, typval_T *rettv);
- static void f_tr(typval_T *argvars, typval_T *rettv);
- static void f_trim(typval_T *argvars, typval_T *rettv);
  static void f_type(typval_T *argvars, typval_T *rettv);
  static void f_virtcol(typval_T *argvars, typval_T *rettv);
  static void f_visualmode(typval_T *argvars, typval_T *rettv);
--- 180,185 ----
***************
*** 2377,2426 ****
  #endif
  }
  
-     static void
- byteidx(typval_T *argvars, typval_T *rettv, int comp UNUSED)
- {
-     char_u    *t;
-     char_u    *str;
-     varnumber_T       idx;
- 
-     str = tv_get_string_chk(&argvars[0]);
-     idx = tv_get_number_chk(&argvars[1], NULL);
-     rettv->vval.v_number = -1;
-     if (str == NULL || idx < 0)
-       return;
- 
-     t = str;
-     for ( ; idx > 0; idx--)
-     {
-       if (*t == NUL)          // EOL reached
-           return;
-       if (enc_utf8 && comp)
-           t += utf_ptr2len(t);
-       else
-           t += (*mb_ptr2len)(t);
-     }
-     rettv->vval.v_number = (varnumber_T)(t - str);
- }
- 
- /*
-  * "byteidx()" function
-  */
-     static void
- f_byteidx(typval_T *argvars, typval_T *rettv)
- {
-     byteidx(argvars, rettv, FALSE);
- }
- 
- /*
-  * "byteidxcomp()" function
-  */
-     static void
- f_byteidxcomp(typval_T *argvars, typval_T *rettv)
- {
-     byteidx(argvars, rettv, TRUE);
- }
- 
  /*
   * "call(func, arglist [, dict])" function
   */
--- 2355,2360 ----
***************
*** 2552,2609 ****
      get_col(argvars, rettv, TRUE);
  }
  
- /*
-  * "charidx()" function
-  */
-     static void
- f_charidx(typval_T *argvars, typval_T *rettv)
- {
-     char_u    *str;
-     varnumber_T       idx;
-     varnumber_T       countcc = FALSE;
-     char_u    *p;
-     int               len;
-     int               (*ptr2len)(char_u *);
- 
-     rettv->vval.v_number = -1;
- 
-     if (argvars[0].v_type != VAR_STRING || argvars[1].v_type != VAR_NUMBER
-           || (argvars[2].v_type != VAR_UNKNOWN
-                                          && argvars[2].v_type != VAR_NUMBER
-                                          && argvars[2].v_type != VAR_BOOL))
-     {
-       emsg(_(e_invarg));
-       return;
-     }
- 
-     str = tv_get_string_chk(&argvars[0]);
-     idx = tv_get_number_chk(&argvars[1], NULL);
-     if (str == NULL || idx < 0)
-       return;
- 
-     if (argvars[2].v_type != VAR_UNKNOWN)
-       countcc = tv_get_bool(&argvars[2]);
-     if (countcc < 0 || countcc > 1)
-     {
-       semsg(_(e_using_number_as_bool_nr), countcc);
-       return;
-     }
- 
-     if (enc_utf8 && countcc)
-       ptr2len = utf_ptr2len;
-     else
-       ptr2len = mb_ptr2len;
- 
-     for (p = str, len = 0; p <= str + idx; len++)
-     {
-       if (*p == NUL)
-           return;
-       p += ptr2len(p);
-     }
- 
-     rettv->vval.v_number = len > 0 ? len - 1 : 0;
- }
- 
      win_T *
  get_optional_window(typval_T *argvars, int idx)
  {
--- 2486,2491 ----
***************
*** 5781,5817 ****
  }
  
  /*
-  * iconv() function
-  */
-     static void
- f_iconv(typval_T *argvars UNUSED, typval_T *rettv)
- {
-     char_u    buf1[NUMBUFLEN];
-     char_u    buf2[NUMBUFLEN];
-     char_u    *from, *to, *str;
-     vimconv_T vimconv;
- 
-     rettv->v_type = VAR_STRING;
-     rettv->vval.v_string = NULL;
- 
-     str = tv_get_string(&argvars[0]);
-     from = enc_canonize(enc_skip(tv_get_string_buf(&argvars[1], buf1)));
-     to = enc_canonize(enc_skip(tv_get_string_buf(&argvars[2], buf2)));
-     vimconv.vc_type = CONV_NONE;
-     convert_setup(&vimconv, from, to);
- 
-     // If the encodings are equal, no conversion needed.
-     if (vimconv.vc_type == CONV_NONE)
-       rettv->vval.v_string = vim_strsave(str);
-     else
-       rettv->vval.v_string = string_convert(&vimconv, str, NULL);
- 
-     convert_setup(&vimconv, NULL, NULL);
-     vim_free(from);
-     vim_free(to);
- }
- 
- /*
   * "index()" function
   */
      static void
--- 5663,5668 ----
***************
*** 8787,9238 ****
  }
  
  /*
-  * "str2list()" function
-  */
-     static void
- f_str2list(typval_T *argvars, typval_T *rettv)
- {
-     char_u    *p;
-     int               utf8 = FALSE;
- 
-     if (rettv_list_alloc(rettv) == FAIL)
-       return;
- 
-     if (argvars[1].v_type != VAR_UNKNOWN)
-       utf8 = (int)tv_get_bool_chk(&argvars[1], NULL);
- 
-     p = tv_get_string(&argvars[0]);
- 
-     if (has_mbyte || utf8)
-     {
-       int (*ptr2len)(char_u *);
-       int (*ptr2char)(char_u *);
- 
-       if (utf8 || enc_utf8)
-       {
-           ptr2len = utf_ptr2len;
-           ptr2char = utf_ptr2char;
-       }
-       else
-       {
-           ptr2len = mb_ptr2len;
-           ptr2char = mb_ptr2char;
-       }
- 
-       for ( ; *p != NUL; p += (*ptr2len)(p))
-           list_append_number(rettv->vval.v_list, (*ptr2char)(p));
-     }
-     else
-       for ( ; *p != NUL; ++p)
-           list_append_number(rettv->vval.v_list, *p);
- }
- 
- /*
-  * "str2nr()" function
-  */
-     static void
- f_str2nr(typval_T *argvars, typval_T *rettv)
- {
-     int               base = 10;
-     char_u    *p;
-     varnumber_T       n;
-     int               what = 0;
-     int               isneg;
- 
-     if (argvars[1].v_type != VAR_UNKNOWN)
-     {
-       base = (int)tv_get_number(&argvars[1]);
-       if (base != 2 && base != 8 && base != 10 && base != 16)
-       {
-           emsg(_(e_invarg));
-           return;
-       }
-       if (argvars[2].v_type != VAR_UNKNOWN && tv_get_bool(&argvars[2]))
-           what |= STR2NR_QUOTE;
-     }
- 
-     p = skipwhite(tv_get_string_strict(&argvars[0]));
-     isneg = (*p == '-');
-     if (*p == '+' || *p == '-')
-       p = skipwhite(p + 1);
-     switch (base)
-     {
-       case 2: what |= STR2NR_BIN + STR2NR_FORCE; break;
-       case 8: what |= STR2NR_OCT + STR2NR_OOCT + STR2NR_FORCE; break;
-       case 16: what |= STR2NR_HEX + STR2NR_FORCE; break;
-     }
-     vim_str2nr(p, NULL, NULL, what, &n, NULL, 0, FALSE);
-     // Text after the number is silently ignored.
-     if (isneg)
-       rettv->vval.v_number = -n;
-     else
-       rettv->vval.v_number = n;
- 
- }
- 
- /*
-  * "strgetchar()" function
-  */
-     static void
- f_strgetchar(typval_T *argvars, typval_T *rettv)
- {
-     char_u    *str;
-     int               len;
-     int               error = FALSE;
-     int               charidx;
-     int               byteidx = 0;
- 
-     rettv->vval.v_number = -1;
-     str = tv_get_string_chk(&argvars[0]);
-     if (str == NULL)
-       return;
-     len = (int)STRLEN(str);
-     charidx = (int)tv_get_number_chk(&argvars[1], &error);
-     if (error)
-       return;
- 
-     while (charidx >= 0 && byteidx < len)
-     {
-       if (charidx == 0)
-       {
-           rettv->vval.v_number = mb_ptr2char(str + byteidx);
-           break;
-       }
-       --charidx;
-       byteidx += MB_CPTR2LEN(str + byteidx);
-     }
- }
- 
- /*
-  * "stridx()" function
-  */
-     static void
- f_stridx(typval_T *argvars, typval_T *rettv)
- {
-     char_u    buf[NUMBUFLEN];
-     char_u    *needle;
-     char_u    *haystack;
-     char_u    *save_haystack;
-     char_u    *pos;
-     int               start_idx;
- 
-     needle = tv_get_string_chk(&argvars[1]);
-     save_haystack = haystack = tv_get_string_buf_chk(&argvars[0], buf);
-     rettv->vval.v_number = -1;
-     if (needle == NULL || haystack == NULL)
-       return;         // type error; errmsg already given
- 
-     if (argvars[2].v_type != VAR_UNKNOWN)
-     {
-       int         error = FALSE;
- 
-       start_idx = (int)tv_get_number_chk(&argvars[2], &error);
-       if (error || start_idx >= (int)STRLEN(haystack))
-           return;
-       if (start_idx >= 0)
-           haystack += start_idx;
-     }
- 
-     pos       = (char_u *)strstr((char *)haystack, (char *)needle);
-     if (pos != NULL)
-       rettv->vval.v_number = (varnumber_T)(pos - save_haystack);
- }
- 
- /*
-  * "string()" function
-  */
-     void
- f_string(typval_T *argvars, typval_T *rettv)
- {
-     char_u    *tofree;
-     char_u    numbuf[NUMBUFLEN];
- 
-     rettv->v_type = VAR_STRING;
-     rettv->vval.v_string = tv2string(&argvars[0], &tofree, numbuf,
-                                                               get_copyID());
-     // Make a copy if we have a value but it's not in allocated memory.
-     if (rettv->vval.v_string != NULL && tofree == NULL)
-       rettv->vval.v_string = vim_strsave(rettv->vval.v_string);
- }
- 
- /*
-  * "strlen()" function
-  */
-     static void
- f_strlen(typval_T *argvars, typval_T *rettv)
- {
-     rettv->vval.v_number = (varnumber_T)(STRLEN(
-                                             tv_get_string(&argvars[0])));
- }
- 
-     static void
- strchar_common(typval_T *argvars, typval_T *rettv, int skipcc)
- {
-     char_u            *s = tv_get_string(&argvars[0]);
-     varnumber_T               len = 0;
-     int                       (*func_mb_ptr2char_adv)(char_u **pp);
- 
-     func_mb_ptr2char_adv = skipcc ? mb_ptr2char_adv : mb_cptr2char_adv;
-     while (*s != NUL)
-     {
-       func_mb_ptr2char_adv(&s);
-       ++len;
-     }
-     rettv->vval.v_number = len;
- }
- 
- /*
-  * "strcharlen()" function
-  */
-     static void
- f_strcharlen(typval_T *argvars, typval_T *rettv)
- {
-     strchar_common(argvars, rettv, TRUE);
- }
- 
- /*
-  * "strchars()" function
-  */
-     static void
- f_strchars(typval_T *argvars, typval_T *rettv)
- {
-     varnumber_T               skipcc = FALSE;
- 
-     if (argvars[1].v_type != VAR_UNKNOWN)
-       skipcc = tv_get_bool(&argvars[1]);
-     if (skipcc < 0 || skipcc > 1)
-       semsg(_(e_using_number_as_bool_nr), skipcc);
-     else
-       strchar_common(argvars, rettv, skipcc);
- }
- 
- /*
-  * "strdisplaywidth()" function
-  */
-     static void
- f_strdisplaywidth(typval_T *argvars, typval_T *rettv)
- {
-     char_u    *s = tv_get_string(&argvars[0]);
-     int               col = 0;
- 
-     if (argvars[1].v_type != VAR_UNKNOWN)
-       col = (int)tv_get_number(&argvars[1]);
- 
-     rettv->vval.v_number = (varnumber_T)(linetabsize_col(col, s) - col);
- }
- 
- /*
-  * "strwidth()" function
-  */
-     static void
- f_strwidth(typval_T *argvars, typval_T *rettv)
- {
-     char_u    *s = tv_get_string_strict(&argvars[0]);
- 
-     rettv->vval.v_number = (varnumber_T)(mb_string2cells(s, -1));
- }
- 
- /*
-  * "strcharpart()" function
-  */
-     static void
- f_strcharpart(typval_T *argvars, typval_T *rettv)
- {
-     char_u    *p;
-     int               nchar;
-     int               nbyte = 0;
-     int               charlen;
-     int               skipcc = FALSE;
-     int               len = 0;
-     int               slen;
-     int               error = FALSE;
- 
-     p = tv_get_string(&argvars[0]);
-     slen = (int)STRLEN(p);
- 
-     nchar = (int)tv_get_number_chk(&argvars[1], &error);
-     if (!error)
-     {
-       if (argvars[2].v_type != VAR_UNKNOWN
-                                          && argvars[3].v_type != VAR_UNKNOWN)
-       {
-           skipcc = tv_get_bool(&argvars[3]);
-           if (skipcc < 0 || skipcc > 1)
-           {
-               semsg(_(e_using_number_as_bool_nr), skipcc);
-               return;
-           }
-       }
- 
-       if (nchar > 0)
-           while (nchar > 0 && nbyte < slen)
-           {
-               if (skipcc)
-                   nbyte += mb_ptr2len(p + nbyte);
-               else
-                   nbyte += MB_CPTR2LEN(p + nbyte);
-               --nchar;
-           }
-       else
-           nbyte = nchar;
-       if (argvars[2].v_type != VAR_UNKNOWN)
-       {
-           charlen = (int)tv_get_number(&argvars[2]);
-           while (charlen > 0 && nbyte + len < slen)
-           {
-               int off = nbyte + len;
- 
-               if (off < 0)
-                   len += 1;
-               else
-               {
-                   if (skipcc)
-                       len += mb_ptr2len(p + off);
-                   else
-                       len += MB_CPTR2LEN(p + off);
-               }
-               --charlen;
-           }
-       }
-       else
-           len = slen - nbyte;    // default: all bytes that are available.
-     }
- 
-     /*
-      * Only return the overlap between the specified part and the actual
-      * string.
-      */
-     if (nbyte < 0)
-     {
-       len += nbyte;
-       nbyte = 0;
-     }
-     else if (nbyte > slen)
-       nbyte = slen;
-     if (len < 0)
-       len = 0;
-     else if (nbyte + len > slen)
-       len = slen - nbyte;
- 
-     rettv->v_type = VAR_STRING;
-     rettv->vval.v_string = vim_strnsave(p + nbyte, len);
- }
- 
- /*
-  * "strpart()" function
-  */
-     static void
- f_strpart(typval_T *argvars, typval_T *rettv)
- {
-     char_u    *p;
-     int               n;
-     int               len;
-     int               slen;
-     int               error = FALSE;
- 
-     p = tv_get_string(&argvars[0]);
-     slen = (int)STRLEN(p);
- 
-     n = (int)tv_get_number_chk(&argvars[1], &error);
-     if (error)
-       len = 0;
-     else if (argvars[2].v_type != VAR_UNKNOWN)
-       len = (int)tv_get_number(&argvars[2]);
-     else
-       len = slen - n;     // default len: all bytes that are available.
- 
-     // Only return the overlap between the specified part and the actual
-     // string.
-     if (n < 0)
-     {
-       len += n;
-       n = 0;
-     }
-     else if (n > slen)
-       n = slen;
-     if (len < 0)
-       len = 0;
-     else if (n + len > slen)
-       len = slen - n;
- 
-     if (argvars[2].v_type != VAR_UNKNOWN && argvars[3].v_type != VAR_UNKNOWN)
-     {
-       int off;
- 
-       // length in characters
-       for (off = n; off < slen && len > 0; --len)
-           off += mb_ptr2len(p + off);
-       len = off - n;
-     }
- 
-     rettv->v_type = VAR_STRING;
-     rettv->vval.v_string = vim_strnsave(p + n, len);
- }
- 
- /*
-  * "strridx()" function
-  */
-     static void
- f_strridx(typval_T *argvars, typval_T *rettv)
- {
-     char_u    buf[NUMBUFLEN];
-     char_u    *needle;
-     char_u    *haystack;
-     char_u    *rest;
-     char_u    *lastmatch = NULL;
-     int               haystack_len, end_idx;
- 
-     needle = tv_get_string_chk(&argvars[1]);
-     haystack = tv_get_string_buf_chk(&argvars[0], buf);
- 
-     rettv->vval.v_number = -1;
-     if (needle == NULL || haystack == NULL)
-       return;         // type error; errmsg already given
- 
-     haystack_len = (int)STRLEN(haystack);
-     if (argvars[2].v_type != VAR_UNKNOWN)
-     {
-       // Third argument: upper limit for index
-       end_idx = (int)tv_get_number_chk(&argvars[2], NULL);
-       if (end_idx < 0)
-           return;     // can never find a match
-     }
-     else
-       end_idx = haystack_len;
- 
-     if (*needle == NUL)
-     {
-       // Empty string matches past the end.
-       lastmatch = haystack + end_idx;
-     }
-     else
-     {
-       for (rest = haystack; *rest != '\0'; ++rest)
-       {
-           rest = (char_u *)strstr((char *)rest, (char *)needle);
-           if (rest == NULL || rest > haystack + end_idx)
-               break;
-           lastmatch = rest;
-       }
-     }
- 
-     if (lastmatch == NULL)
-       rettv->vval.v_number = -1;
-     else
-       rettv->vval.v_number = (varnumber_T)(lastmatch - haystack);
- }
- 
- /*
-  * "strtrans()" function
-  */
-     static void
- f_strtrans(typval_T *argvars, typval_T *rettv)
- {
-     rettv->v_type = VAR_STRING;
-     rettv->vval.v_string = transstr(tv_get_string(&argvars[0]));
- }
- 
- /*
   * "submatch()" function
   */
      static void
--- 8638,8643 ----
***************
*** 9628,9863 ****
  }
  
  /*
-  * "tolower(string)" function
-  */
-     static void
- f_tolower(typval_T *argvars, typval_T *rettv)
- {
-     rettv->v_type = VAR_STRING;
-     rettv->vval.v_string = strlow_save(tv_get_string(&argvars[0]));
- }
- 
- /*
-  * "toupper(string)" function
-  */
-     static void
- f_toupper(typval_T *argvars, typval_T *rettv)
- {
-     rettv->v_type = VAR_STRING;
-     rettv->vval.v_string = strup_save(tv_get_string(&argvars[0]));
- }
- 
- /*
-  * "tr(string, fromstr, tostr)" function
-  */
-     static void
- f_tr(typval_T *argvars, typval_T *rettv)
- {
-     char_u    *in_str;
-     char_u    *fromstr;
-     char_u    *tostr;
-     char_u    *p;
-     int               inlen;
-     int               fromlen;
-     int               tolen;
-     int               idx;
-     char_u    *cpstr;
-     int               cplen;
-     int               first = TRUE;
-     char_u    buf[NUMBUFLEN];
-     char_u    buf2[NUMBUFLEN];
-     garray_T  ga;
- 
-     in_str = tv_get_string(&argvars[0]);
-     fromstr = tv_get_string_buf_chk(&argvars[1], buf);
-     tostr = tv_get_string_buf_chk(&argvars[2], buf2);
- 
-     // Default return value: empty string.
-     rettv->v_type = VAR_STRING;
-     rettv->vval.v_string = NULL;
-     if (fromstr == NULL || tostr == NULL)
-           return;             // type error; errmsg already given
-     ga_init2(&ga, (int)sizeof(char), 80);
- 
-     if (!has_mbyte)
-       // not multi-byte: fromstr and tostr must be the same length
-       if (STRLEN(fromstr) != STRLEN(tostr))
-       {
- error:
-           semsg(_(e_invarg2), fromstr);
-           ga_clear(&ga);
-           return;
-       }
- 
-     // fromstr and tostr have to contain the same number of chars
-     while (*in_str != NUL)
-     {
-       if (has_mbyte)
-       {
-           inlen = (*mb_ptr2len)(in_str);
-           cpstr = in_str;
-           cplen = inlen;
-           idx = 0;
-           for (p = fromstr; *p != NUL; p += fromlen)
-           {
-               fromlen = (*mb_ptr2len)(p);
-               if (fromlen == inlen && STRNCMP(in_str, p, inlen) == 0)
-               {
-                   for (p = tostr; *p != NUL; p += tolen)
-                   {
-                       tolen = (*mb_ptr2len)(p);
-                       if (idx-- == 0)
-                       {
-                           cplen = tolen;
-                           cpstr = p;
-                           break;
-                       }
-                   }
-                   if (*p == NUL)      // tostr is shorter than fromstr
-                       goto error;
-                   break;
-               }
-               ++idx;
-           }
- 
-           if (first && cpstr == in_str)
-           {
-               // Check that fromstr and tostr have the same number of
-               // (multi-byte) characters.  Done only once when a character
-               // of in_str doesn't appear in fromstr.
-               first = FALSE;
-               for (p = tostr; *p != NUL; p += tolen)
-               {
-                   tolen = (*mb_ptr2len)(p);
-                   --idx;
-               }
-               if (idx != 0)
-                   goto error;
-           }
- 
-           (void)ga_grow(&ga, cplen);
-           mch_memmove((char *)ga.ga_data + ga.ga_len, cpstr, (size_t)cplen);
-           ga.ga_len += cplen;
- 
-           in_str += inlen;
-       }
-       else
-       {
-           // When not using multi-byte chars we can do it faster.
-           p = vim_strchr(fromstr, *in_str);
-           if (p != NULL)
-               ga_append(&ga, tostr[p - fromstr]);
-           else
-               ga_append(&ga, *in_str);
-           ++in_str;
-       }
-     }
- 
-     // add a terminating NUL
-     (void)ga_grow(&ga, 1);
-     ga_append(&ga, NUL);
- 
-     rettv->vval.v_string = ga.ga_data;
- }
- 
- /*
-  * "trim({expr})" function
-  */
-     static void
- f_trim(typval_T *argvars, typval_T *rettv)
- {
-     char_u    buf1[NUMBUFLEN];
-     char_u    buf2[NUMBUFLEN];
-     char_u    *head = tv_get_string_buf_chk(&argvars[0], buf1);
-     char_u    *mask = NULL;
-     char_u    *tail;
-     char_u    *prev;
-     char_u    *p;
-     int               c1;
-     int               dir = 0;
- 
-     rettv->v_type = VAR_STRING;
-     rettv->vval.v_string = NULL;
-     if (head == NULL)
-       return;
- 
-     if (argvars[1].v_type != VAR_UNKNOWN && argvars[1].v_type != VAR_STRING)
-     {
-       semsg(_(e_invarg2), tv_get_string(&argvars[1]));
-       return;
-     }
- 
-     if (argvars[1].v_type == VAR_STRING)
-     {
-       mask = tv_get_string_buf_chk(&argvars[1], buf2);
- 
-       if (argvars[2].v_type != VAR_UNKNOWN)
-       {
-           int error = 0;
- 
-           // leading or trailing characters to trim
-           dir = (int)tv_get_number_chk(&argvars[2], &error);
-           if (error)
-               return;
-           if (dir < 0 || dir > 2)
-           {
-               semsg(_(e_invarg2), tv_get_string(&argvars[2]));
-               return;
-           }
-       }
-     }
- 
-     if (dir == 0 || dir == 1)
-     {
-       // Trim leading characters
-       while (*head != NUL)
-       {
-           c1 = PTR2CHAR(head);
-           if (mask == NULL)
-           {
-               if (c1 > ' ' && c1 != 0xa0)
-                   break;
-           }
-           else
-           {
-               for (p = mask; *p != NUL; MB_PTR_ADV(p))
-                   if (c1 == PTR2CHAR(p))
-                       break;
-               if (*p == NUL)
-                   break;
-           }
-           MB_PTR_ADV(head);
-       }
-     }
- 
-     tail = head + STRLEN(head);
-     if (dir == 0 || dir == 2)
-     {
-       // Trim trailing characters
-       for (; tail > head; tail = prev)
-       {
-           prev = tail;
-           MB_PTR_BACK(head, prev);
-           c1 = PTR2CHAR(prev);
-           if (mask == NULL)
-           {
-               if (c1 > ' ' && c1 != 0xa0)
-                   break;
-           }
-           else
-           {
-               for (p = mask; *p != NUL; MB_PTR_ADV(p))
-                   if (c1 == PTR2CHAR(p))
-                       break;
-               if (*p == NUL)
-                   break;
-           }
-       }
-     }
-     rettv->vval.v_string = vim_strnsave(head, tail - head);
- }
- 
- /*
   * "type(expr)" function
   */
      static void
--- 9033,9038 ----
*** ../vim-8.2.3138/src/mbyte.c 2021-07-02 20:19:27.964669621 +0200
--- src/mbyte.c 2021-07-10 21:23:10.455465392 +0200
***************
*** 4961,4966 ****
--- 4961,4997 ----
      rettv->vval.v_number = im_get_status();
  # endif
  }
+ 
+ /*
+  * iconv() function
+  */
+     void
+ f_iconv(typval_T *argvars UNUSED, typval_T *rettv)
+ {
+     char_u    buf1[NUMBUFLEN];
+     char_u    buf2[NUMBUFLEN];
+     char_u    *from, *to, *str;
+     vimconv_T vimconv;
+ 
+     rettv->v_type = VAR_STRING;
+     rettv->vval.v_string = NULL;
+ 
+     str = tv_get_string(&argvars[0]);
+     from = enc_canonize(enc_skip(tv_get_string_buf(&argvars[1], buf1)));
+     to = enc_canonize(enc_skip(tv_get_string_buf(&argvars[2], buf2)));
+     vimconv.vc_type = CONV_NONE;
+     convert_setup(&vimconv, from, to);
+ 
+     // If the encodings are equal, no conversion needed.
+     if (vimconv.vc_type == CONV_NONE)
+       rettv->vval.v_string = vim_strsave(str);
+     else
+       rettv->vval.v_string = string_convert(&vimconv, str, NULL);
+ 
+     convert_setup(&vimconv, NULL, NULL);
+     vim_free(from);
+     vim_free(to);
+ }
  #endif
  
  /*
*** ../vim-8.2.3138/src/misc1.c 2021-05-20 21:14:17.166986859 +0200
--- src/misc1.c 2021-07-10 21:23:10.455465392 +0200
***************
*** 695,701 ****
        if (finish_op)
        {
            buf[1] = 'o';
!           // to be able to detect force-linewise/blockwise/characterwise 
operations
            buf[2] = motion_force;
        }
        else if (restart_edit == 'I' || restart_edit == 'R'
--- 695,702 ----
        if (finish_op)
        {
            buf[1] = 'o';
!           // to be able to detect force-linewise/blockwise/characterwise
!           // operations
            buf[2] = motion_force;
        }
        else if (restart_edit == 'I' || restart_edit == 'R'
***************
*** 2099,2127 ****
      return result;
  }
  
- /*
-  * Concatenate two strings and return the result in allocated memory.
-  * Returns NULL when out of memory.
-  */
-     char_u  *
- concat_str(char_u *str1, char_u *str2)
- {
-     char_u  *dest;
-     size_t  l = str1 == NULL ? 0 : STRLEN(str1);
- 
-     dest = alloc(l + (str2 == NULL ? 0 : STRLEN(str2)) + 1L);
-     if (dest != NULL)
-     {
-       if (str1 == NULL)
-           *dest = NUL;
-       else
-           STRCPY(dest, str1);
-       if (str2 != NULL)
-           STRCPY(dest + l, str2);
-     }
-     return dest;
- }
- 
      static void
  prepare_to_exit(void)
  {
--- 2100,2105 ----
*** ../vim-8.2.3138/src/misc2.c 2021-06-30 20:54:30.696546341 +0200
--- src/misc2.c 2021-07-10 21:23:10.455465392 +0200
***************
*** 1268,1309 ****
  #endif
  
  /*
-  * Copy "string" into newly allocated memory.
-  */
-     char_u *
- vim_strsave(char_u *string)
- {
-     char_u    *p;
-     size_t    len;
- 
-     len = STRLEN(string) + 1;
-     p = alloc(len);
-     if (p != NULL)
-       mch_memmove(p, string, len);
-     return p;
- }
- 
- /*
-  * Copy up to "len" bytes of "string" into newly allocated memory and
-  * terminate with a NUL.
-  * The allocated memory always has size "len + 1", also when "string" is
-  * shorter.
-  */
-     char_u *
- vim_strnsave(char_u *string, size_t len)
- {
-     char_u    *p;
- 
-     p = alloc(len + 1);
-     if (p != NULL)
-     {
-       STRNCPY(p, string, len);
-       p[len] = NUL;
-     }
-     return p;
- }
- 
- /*
   * Copy "p[len]" into allocated memory, ignoring NUL characters.
   * Returns NULL when out of memory.
   */
--- 1268,1273 ----
***************
*** 1318,1782 ****
  }
  
  /*
-  * Same as vim_strsave(), but any characters found in esc_chars are preceded
-  * by a backslash.
-  */
-     char_u *
- vim_strsave_escaped(char_u *string, char_u *esc_chars)
- {
-     return vim_strsave_escaped_ext(string, esc_chars, '\\', FALSE);
- }
- 
- /*
-  * Same as vim_strsave_escaped(), but when "bsl" is TRUE also escape
-  * characters where rem_backslash() would remove the backslash.
-  * Escape the characters with "cc".
-  */
-     char_u *
- vim_strsave_escaped_ext(
-     char_u    *string,
-     char_u    *esc_chars,
-     int               cc,
-     int               bsl)
- {
-     char_u    *p;
-     char_u    *p2;
-     char_u    *escaped_string;
-     unsigned  length;
-     int               l;
- 
-     /*
-      * First count the number of backslashes required.
-      * Then allocate the memory and insert them.
-      */
-     length = 1;                               // count the trailing NUL
-     for (p = string; *p; p++)
-     {
-       if (has_mbyte && (l = (*mb_ptr2len)(p)) > 1)
-       {
-           length += l;                // count a multibyte char
-           p += l - 1;
-           continue;
-       }
-       if (vim_strchr(esc_chars, *p) != NULL || (bsl && rem_backslash(p)))
-           ++length;                   // count a backslash
-       ++length;                       // count an ordinary char
-     }
-     escaped_string = alloc(length);
-     if (escaped_string != NULL)
-     {
-       p2 = escaped_string;
-       for (p = string; *p; p++)
-       {
-           if (has_mbyte && (l = (*mb_ptr2len)(p)) > 1)
-           {
-               mch_memmove(p2, p, (size_t)l);
-               p2 += l;
-               p += l - 1;             // skip multibyte char
-               continue;
-           }
-           if (vim_strchr(esc_chars, *p) != NULL || (bsl && rem_backslash(p)))
-               *p2++ = cc;
-           *p2++ = *p;
-       }
-       *p2 = NUL;
-     }
-     return escaped_string;
- }
- 
- /*
-  * Return TRUE when 'shell' has "csh" in the tail.
-  */
-     int
- csh_like_shell(void)
- {
-     return (strstr((char *)gettail(p_sh), "csh") != NULL);
- }
- 
- /*
-  * Escape "string" for use as a shell argument with system().
-  * This uses single quotes, except when we know we need to use double quotes
-  * (MS-DOS and MS-Windows not using PowerShell and without 'shellslash' set).
-  * PowerShell also uses a novel escaping for enclosed single quotes - double
-  * them up.
-  * Escape a newline, depending on the 'shell' option.
-  * When "do_special" is TRUE also replace "!", "%", "#" and things starting
-  * with "<" like "<cfile>".
-  * When "do_newline" is FALSE do not escape newline unless it is csh shell.
-  * Returns the result in allocated memory, NULL if we have run out.
-  */
-     char_u *
- vim_strsave_shellescape(char_u *string, int do_special, int do_newline)
- {
-     unsigned  length;
-     char_u    *p;
-     char_u    *d;
-     char_u    *escaped_string;
-     int               l;
-     int               csh_like;
-     char_u    *shname;
-     int               powershell;
- # ifdef MSWIN
-     int               double_quotes;
- # endif
- 
-     // Only csh and similar shells expand '!' within single quotes.  For sh 
and
-     // the like we must not put a backslash before it, it will be taken
-     // literally.  If do_special is set the '!' will be escaped twice.
-     // Csh also needs to have "\n" escaped twice when do_special is set.
-     csh_like = csh_like_shell();
- 
-     // PowerShell uses it's own version for quoting single quotes
-     shname = gettail(p_sh);
-     powershell = strstr((char *)shname, "pwsh") != NULL;
- # ifdef MSWIN
-     powershell = powershell || strstr((char *)shname, "powershell") != NULL;
-     // PowerShell only accepts single quotes so override shellslash.
-     double_quotes = !powershell && !p_ssl;
- # endif
- 
-     // First count the number of extra bytes required.
-     length = (unsigned)STRLEN(string) + 3;  // two quotes and a trailing NUL
-     for (p = string; *p != NUL; MB_PTR_ADV(p))
-     {
- # ifdef MSWIN
-       if (double_quotes)
-       {
-           if (*p == '"')
-               ++length;               // " -> ""
-       }
-       else
- # endif
-       if (*p == '\'')
-       {
-           if (powershell)
-               length +=2;             // ' => ''
-           else
-               length += 3;            // ' => '\''
-       }
-       if ((*p == '\n' && (csh_like || do_newline))
-               || (*p == '!' && (csh_like || do_special)))
-       {
-           ++length;                   // insert backslash
-           if (csh_like && do_special)
-               ++length;               // insert backslash
-       }
-       if (do_special && find_cmdline_var(p, &l) >= 0)
-       {
-           ++length;                   // insert backslash
-           p += l - 1;
-       }
-     }
- 
-     // Allocate memory for the result and fill it.
-     escaped_string = alloc(length);
-     if (escaped_string != NULL)
-     {
-       d = escaped_string;
- 
-       // add opening quote
- # ifdef MSWIN
-       if (double_quotes)
-           *d++ = '"';
-       else
- # endif
-           *d++ = '\'';
- 
-       for (p = string; *p != NUL; )
-       {
- # ifdef MSWIN
-           if (double_quotes)
-           {
-               if (*p == '"')
-               {
-                   *d++ = '"';
-                   *d++ = '"';
-                   ++p;
-                   continue;
-               }
-           }
-           else
- # endif
-           if (*p == '\'')
-           {
-               if (powershell)
-               {
-                   *d++ = '\'';
-                   *d++ = '\'';
-               }
-               else
-               {
-                   *d++ = '\'';
-                   *d++ = '\\';
-                   *d++ = '\'';
-                   *d++ = '\'';
-               }
-               ++p;
-               continue;
-           }
-           if ((*p == '\n' && (csh_like || do_newline))
-                   || (*p == '!' && (csh_like || do_special)))
-           {
-               *d++ = '\\';
-               if (csh_like && do_special)
-                   *d++ = '\\';
-               *d++ = *p++;
-               continue;
-           }
-           if (do_special && find_cmdline_var(p, &l) >= 0)
-           {
-               *d++ = '\\';            // insert backslash
-               while (--l >= 0)        // copy the var
-                   *d++ = *p++;
-               continue;
-           }
- 
-           MB_COPY_CHAR(p, d);
-       }
- 
-       // add terminating quote and finish with a NUL
- # ifdef MSWIN
-       if (double_quotes)
-           *d++ = '"';
-       else
- # endif
-           *d++ = '\'';
-       *d = NUL;
-     }
- 
-     return escaped_string;
- }
- 
- /*
-  * Like vim_strsave(), but make all characters uppercase.
-  * This uses ASCII lower-to-upper case translation, language independent.
-  */
-     char_u *
- vim_strsave_up(char_u *string)
- {
-     char_u *p1;
- 
-     p1 = vim_strsave(string);
-     vim_strup(p1);
-     return p1;
- }
- 
- /*
-  * Like vim_strnsave(), but make all characters uppercase.
-  * This uses ASCII lower-to-upper case translation, language independent.
-  */
-     char_u *
- vim_strnsave_up(char_u *string, size_t len)
- {
-     char_u *p1;
- 
-     p1 = vim_strnsave(string, len);
-     vim_strup(p1);
-     return p1;
- }
- 
- /*
-  * ASCII lower-to-upper case translation, language independent.
-  */
-     void
- vim_strup(
-     char_u    *p)
- {
-     char_u  *p2;
-     int           c;
- 
-     if (p != NULL)
-     {
-       p2 = p;
-       while ((c = *p2) != NUL)
- #ifdef EBCDIC
-           *p2++ = isalpha(c) ? toupper(c) : c;
- #else
-           *p2++ = (c < 'a' || c > 'z') ? c : (c - 0x20);
- #endif
-     }
- }
- 
- #if defined(FEAT_EVAL) || defined(FEAT_SPELL) || defined(PROTO)
- /*
-  * Make string "s" all upper-case and return it in allocated memory.
-  * Handles multi-byte characters as well as possible.
-  * Returns NULL when out of memory.
-  */
-     char_u *
- strup_save(char_u *orig)
- {
-     char_u    *p;
-     char_u    *res;
- 
-     res = p = vim_strsave(orig);
- 
-     if (res != NULL)
-       while (*p != NUL)
-       {
-           int         l;
- 
-           if (enc_utf8)
-           {
-               int     c, uc;
-               int     newl;
-               char_u  *s;
- 
-               c = utf_ptr2char(p);
-               l = utf_ptr2len(p);
-               if (c == 0)
-               {
-                   // overlong sequence, use only the first byte
-                   c = *p;
-                   l = 1;
-               }
-               uc = utf_toupper(c);
- 
-               // Reallocate string when byte count changes.  This is rare,
-               // thus it's OK to do another malloc()/free().
-               newl = utf_char2len(uc);
-               if (newl != l)
-               {
-                   s = alloc(STRLEN(res) + 1 + newl - l);
-                   if (s == NULL)
-                   {
-                       vim_free(res);
-                       return NULL;
-                   }
-                   mch_memmove(s, res, p - res);
-                   STRCPY(s + (p - res) + newl, p + l);
-                   p = s + (p - res);
-                   vim_free(res);
-                   res = s;
-               }
- 
-               utf_char2bytes(uc, p);
-               p += newl;
-           }
-           else if (has_mbyte && (l = (*mb_ptr2len)(p)) > 1)
-               p += l;         // skip multi-byte character
-           else
-           {
-               *p = TOUPPER_LOC(*p); // note that toupper() can be a macro
-               p++;
-           }
-       }
- 
-     return res;
- }
- 
- /*
-  * Make string "s" all lower-case and return it in allocated memory.
-  * Handles multi-byte characters as well as possible.
-  * Returns NULL when out of memory.
-  */
-     char_u *
- strlow_save(char_u *orig)
- {
-     char_u    *p;
-     char_u    *res;
- 
-     res = p = vim_strsave(orig);
- 
-     if (res != NULL)
-       while (*p != NUL)
-       {
-           int         l;
- 
-           if (enc_utf8)
-           {
-               int     c, lc;
-               int     newl;
-               char_u  *s;
- 
-               c = utf_ptr2char(p);
-               l = utf_ptr2len(p);
-               if (c == 0)
-               {
-                   // overlong sequence, use only the first byte
-                   c = *p;
-                   l = 1;
-               }
-               lc = utf_tolower(c);
- 
-               // Reallocate string when byte count changes.  This is rare,
-               // thus it's OK to do another malloc()/free().
-               newl = utf_char2len(lc);
-               if (newl != l)
-               {
-                   s = alloc(STRLEN(res) + 1 + newl - l);
-                   if (s == NULL)
-                   {
-                       vim_free(res);
-                       return NULL;
-                   }
-                   mch_memmove(s, res, p - res);
-                   STRCPY(s + (p - res) + newl, p + l);
-                   p = s + (p - res);
-                   vim_free(res);
-                   res = s;
-               }
- 
-               utf_char2bytes(lc, p);
-               p += newl;
-           }
-           else if (has_mbyte && (l = (*mb_ptr2len)(p)) > 1)
-               p += l;         // skip multi-byte character
-           else
-           {
-               *p = TOLOWER_LOC(*p); // note that tolower() can be a macro
-               p++;
-           }
-       }
- 
-     return res;
- }
- #endif
- 
- /*
-  * delete spaces at the end of a string
-  */
-     void
- del_trailing_spaces(char_u *ptr)
- {
-     char_u    *q;
- 
-     q = ptr + STRLEN(ptr);
-     while (--q > ptr && VIM_ISWHITE(q[0]) && q[-1] != '\\' && q[-1] != Ctrl_V)
-       *q = NUL;
- }
- 
- /*
-  * Like strncpy(), but always terminate the result with one NUL.
-  * "to" must be "len + 1" long!
-  */
-     void
- vim_strncpy(char_u *to, char_u *from, size_t len)
- {
-     STRNCPY(to, from, len);
-     to[len] = NUL;
- }
- 
- /*
-  * Like strcat(), but make sure the result fits in "tosize" bytes and is
-  * always NUL terminated. "from" and "to" may overlap.
-  */
-     void
- vim_strcat(char_u *to, char_u *from, size_t tosize)
- {
-     size_t tolen = STRLEN(to);
-     size_t fromlen = STRLEN(from);
- 
-     if (tolen + fromlen + 1 > tosize)
-     {
-       mch_memmove(to + tolen, from, tosize - tolen - 1);
-       to[tosize - 1] = NUL;
-     }
-     else
-       mch_memmove(to + tolen, from, fromlen + 1);
- }
- 
- /*
   * Isolate one part of a string option where parts are separated with
   * "sep_chars".
   * The part is copied into "buf[maxlen]".
--- 1282,1287 ----
***************
*** 1848,2027 ****
  }
  #endif
  
- #if (!defined(HAVE_STRCASECMP) && !defined(HAVE_STRICMP)) || defined(PROTO)
- /*
-  * Compare two strings, ignoring case, using current locale.
-  * Doesn't work for multi-byte characters.
-  * return 0 for match, < 0 for smaller, > 0 for bigger
-  */
-     int
- vim_stricmp(char *s1, char *s2)
- {
-     int               i;
- 
-     for (;;)
-     {
-       i = (int)TOLOWER_LOC(*s1) - (int)TOLOWER_LOC(*s2);
-       if (i != 0)
-           return i;                       // this character different
-       if (*s1 == NUL)
-           break;                          // strings match until NUL
-       ++s1;
-       ++s2;
-     }
-     return 0;                             // strings match
- }
- #endif
- 
- #if (!defined(HAVE_STRNCASECMP) && !defined(HAVE_STRNICMP)) || defined(PROTO)
- /*
-  * Compare two strings, for length "len", ignoring case, using current locale.
-  * Doesn't work for multi-byte characters.
-  * return 0 for match, < 0 for smaller, > 0 for bigger
-  */
-     int
- vim_strnicmp(char *s1, char *s2, size_t len)
- {
-     int               i;
- 
-     while (len > 0)
-     {
-       i = (int)TOLOWER_LOC(*s1) - (int)TOLOWER_LOC(*s2);
-       if (i != 0)
-           return i;                       // this character different
-       if (*s1 == NUL)
-           break;                          // strings match until NUL
-       ++s1;
-       ++s2;
-       --len;
-     }
-     return 0;                             // strings match
- }
- #endif
- 
- /*
-  * Search for first occurrence of "c" in "string".
-  * Version of strchr() that handles unsigned char strings with characters from
-  * 128 to 255 correctly.  It also doesn't return a pointer to the NUL at the
-  * end of the string.
-  */
-     char_u  *
- vim_strchr(char_u *string, int c)
- {
-     char_u    *p;
-     int               b;
- 
-     p = string;
-     if (enc_utf8 && c >= 0x80)
-     {
-       while (*p != NUL)
-       {
-           int l = utfc_ptr2len(p);
- 
-           // Avoid matching an illegal byte here.
-           if (utf_ptr2char(p) == c && l > 1)
-               return p;
-           p += l;
-       }
-       return NULL;
-     }
-     if (enc_dbcs != 0 && c > 255)
-     {
-       int     n2 = c & 0xff;
- 
-       c = ((unsigned)c >> 8) & 0xff;
-       while ((b = *p) != NUL)
-       {
-           if (b == c && p[1] == n2)
-               return p;
-           p += (*mb_ptr2len)(p);
-       }
-       return NULL;
-     }
-     if (has_mbyte)
-     {
-       while ((b = *p) != NUL)
-       {
-           if (b == c)
-               return p;
-           p += (*mb_ptr2len)(p);
-       }
-       return NULL;
-     }
-     while ((b = *p) != NUL)
-     {
-       if (b == c)
-           return p;
-       ++p;
-     }
-     return NULL;
- }
- 
- /*
-  * Version of strchr() that only works for bytes and handles unsigned char
-  * strings with characters above 128 correctly. It also doesn't return a
-  * pointer to the NUL at the end of the string.
-  */
-     char_u  *
- vim_strbyte(char_u *string, int c)
- {
-     char_u    *p = string;
- 
-     while (*p != NUL)
-     {
-       if (*p == c)
-           return p;
-       ++p;
-     }
-     return NULL;
- }
- 
- /*
-  * Search for last occurrence of "c" in "string".
-  * Version of strrchr() that handles unsigned char strings with characters 
from
-  * 128 to 255 correctly.  It also doesn't return a pointer to the NUL at the
-  * end of the string.
-  * Return NULL if not found.
-  * Does not handle multi-byte char for "c"!
-  */
-     char_u  *
- vim_strrchr(char_u *string, int c)
- {
-     char_u    *retval = NULL;
-     char_u    *p = string;
- 
-     while (*p)
-     {
-       if (*p == c)
-           retval = p;
-       MB_PTR_ADV(p);
-     }
-     return retval;
- }
- 
- /*
-  * Vim's version of strpbrk(), in case it's missing.
-  * Don't generate a prototype for this, causes problems when it's not used.
-  */
- #ifndef PROTO
- # ifndef HAVE_STRPBRK
- #  ifdef vim_strpbrk
- #   undef vim_strpbrk
- #  endif
-     char_u *
- vim_strpbrk(char_u *s, char_u *charset)
- {
-     while (*s)
-     {
-       if (vim_strchr(charset, *s) != NULL)
-           return s;
-       MB_PTR_ADV(s);
-     }
-     return NULL;
- }
- # endif
- #endif
- 
  /*
   * Vim has its own isspace() function, because on some machines isspace()
   * can't handle characters above 128.
--- 1353,1358 ----
***************
*** 3975,3999 ****
  #endif
  
  /*
-  * Sort an array of strings.
-  */
- static int sort_compare(const void *s1, const void *s2);
- 
-     static int
- sort_compare(const void *s1, const void *s2)
- {
-     return STRCMP(*(char **)s1, *(char **)s2);
- }
- 
-     void
- sort_strings(
-     char_u    **files,
-     int               count)
- {
-     qsort((void *)files, (size_t)count, sizeof(char_u *), sort_compare);
- }
- 
- /*
   * The putenv() implementation below comes from the "screen" program.
   * Included with permission from Juergen Weigert.
   * See pty.c for the copyright notice.
--- 3306,3311 ----
***************
*** 4304,4327 ****
  
  #endif
  
- #if defined(FEAT_QUICKFIX) || defined(FEAT_SPELL) || defined(PROTO)
- /*
-  * Return TRUE if string "s" contains a non-ASCII character (128 or higher).
-  * When "s" is NULL FALSE is returned.
-  */
-     int
- has_non_ascii(char_u *s)
- {
-     char_u    *p;
- 
-     if (s != NULL)
-       for (p = s; *p != NUL; ++p)
-           if (*p >= 128)
-               return TRUE;
-     return FALSE;
- }
- #endif
- 
  #ifndef PROTO  // proto is defined in vim.h
  # ifdef ELAPSED_TIMEVAL
  /*
--- 3616,3621 ----
*** ../vim-8.2.3138/src/proto.h 2021-06-02 17:07:02.280398156 +0200
--- src/proto.h 2021-07-10 21:23:10.455465392 +0200
***************
*** 211,216 ****
--- 211,217 ----
  # include "spell.pro"
  # include "spellfile.pro"
  # include "spellsuggest.pro"
+ # include "strings.pro"
  # include "syntax.pro"
  # include "tag.pro"
  # include "term.pro"
*** ../vim-8.2.3138/src/proto/eval.pro  2021-06-02 17:07:02.280398156 +0200
--- src/proto/eval.pro  2021-07-10 21:23:10.455465392 +0200
***************
*** 32,39 ****
  void free_for_info(void *fi_void);
  void set_context_for_expression(expand_T *xp, char_u *arg, cmdidx_T cmdidx);
  int pattern_match(char_u *pat, char_u *text, int ic);
- char_u *eval_next_non_blank(char_u *arg, evalarg_T *evalarg, int *getnext);
- char_u *eval_next_line(evalarg_T *evalarg);
  char_u *skipwhite_and_linebreak(char_u *arg, evalarg_T *evalarg);
  void clear_evalarg(evalarg_T *evalarg, exarg_T *eap);
  int eval0(char_u *arg, typval_T *rettv, exarg_T *eap, evalarg_T *evalarg);
--- 32,37 ----
***************
*** 55,61 ****
  int set_ref_in_item(typval_T *tv, int copyID, ht_stack_T **ht_stack, 
list_stack_T **list_stack);
  char_u *echo_string_core(typval_T *tv, char_u **tofree, char_u *numbuf, int 
copyID, int echo_style, int restore_copyID, int composite_val);
  char_u *echo_string(typval_T *tv, char_u **tofree, char_u *numbuf, int 
copyID);
- char_u *string_quote(char_u *str, int function);
  int buf_byteidx_to_charidx(buf_T *buf, int lnum, int byteidx);
  int buf_charidx_to_byteidx(buf_T *buf, int lnum, int charidx);
  pos_T *var2fpos(typval_T *varp, int dollar_lnum, int *fnum, int charcol);
--- 53,58 ----
*** ../vim-8.2.3138/src/proto/evalfunc.pro      2021-07-04 15:54:04.935754560 
+0200
--- src/proto/evalfunc.pro      2021-07-10 21:23:10.455465392 +0200
***************
*** 23,27 ****
  void mzscheme_call_vim(char_u *name, typval_T *args, typval_T *rettv);
  void range_list_materialize(list_T *list);
  long do_searchpair(char_u *spat, char_u *mpat, char_u *epat, int dir, 
typval_T *skip, int flags, pos_T *match_pos, linenr_T lnum_stop, long 
time_limit);
- void f_string(typval_T *argvars, typval_T *rettv);
  /* vim: set ft=c : */
--- 23,26 ----
*** ../vim-8.2.3138/src/proto/mbyte.pro 2020-08-28 22:24:40.660494318 +0200
--- src/proto/mbyte.pro 2021-07-10 21:23:10.455465392 +0200
***************
*** 86,89 ****
--- 86,90 ----
  char_u *string_convert_ext(vimconv_T *vcp, char_u *ptr, int *lenp, int 
*unconvlenp);
  void f_setcellwidths(typval_T *argvars, typval_T *rettv);
  void f_charclass(typval_T *argvars, typval_T *rettv);
+ void f_iconv(typval_T *argvars UNUSED, typval_T *rettv);
  /* vim: set ft=c : */
*** ../vim-8.2.3138/src/proto/misc1.pro 2020-03-20 19:37:26.550539660 +0100
--- src/proto/misc1.pro 2021-07-10 21:23:10.455465392 +0200
***************
*** 36,42 ****
  char_u *get_env_name(expand_T *xp, int idx);
  char_u *get_users(expand_T *xp, int idx);
  int match_user(char_u *name);
- char_u *concat_str(char_u *str1, char_u *str2);
  void preserve_exit(void);
  void line_breakcheck(void);
  void fast_breakcheck(void);
--- 36,41 ----
*** ../vim-8.2.3138/src/proto/misc2.pro 2020-10-07 16:12:33.905930345 +0200
--- src/proto/misc2.pro 2021-07-10 21:23:10.455465392 +0200
***************
*** 31,58 ****
  void *mem_realloc(void *ptr, size_t size);
  void do_outofmem_msg(size_t size);
  void free_all_mem(void);
- char_u *vim_strsave(char_u *string);
- char_u *vim_strnsave(char_u *string, size_t len);
  char_u *vim_memsave(char_u *p, size_t len);
- char_u *vim_strsave_escaped(char_u *string, char_u *esc_chars);
- char_u *vim_strsave_escaped_ext(char_u *string, char_u *esc_chars, int cc, 
int bsl);
- int csh_like_shell(void);
- char_u *vim_strsave_shellescape(char_u *string, int do_special, int 
do_newline);
- char_u *vim_strsave_up(char_u *string);
- char_u *vim_strnsave_up(char_u *string, size_t len);
- void vim_strup(char_u *p);
- char_u *strup_save(char_u *orig);
- char_u *strlow_save(char_u *orig);
- void del_trailing_spaces(char_u *ptr);
- void vim_strncpy(char_u *to, char_u *from, size_t len);
- void vim_strcat(char_u *to, char_u *from, size_t tosize);
  int copy_option_part(char_u **option, char_u *buf, int maxlen, char 
*sep_chars);
  void vim_free(void *x);
- int vim_stricmp(char *s1, char *s2);
- int vim_strnicmp(char *s1, char *s2, size_t len);
- char_u *vim_strchr(char_u *string, int c);
- char_u *vim_strbyte(char_u *string, int c);
- char_u *vim_strrchr(char_u *string, int c);
  int vim_isspace(int x);
  void ga_clear(garray_T *gap);
  void ga_clear_strings(garray_T *gap);
--- 31,39 ----
***************
*** 93,106 ****
  void update_mouseshape(int shape_idx);
  int vim_chdir(char_u *new_dir);
  int get_user_name(char_u *buf, int len);
- void sort_strings(char_u **files, int count);
  int filewritable(char_u *fname);
  int get2c(FILE *fd);
  int get3c(FILE *fd);
  int get4c(FILE *fd);
  char_u *read_string(FILE *fd, int cnt);
  int put_bytes(FILE *fd, long_u nr, int len);
- int has_non_ascii(char_u *s);
  int mch_parse_cmd(char_u *cmd, int use_shcf, char ***argv, int *argc);
  int build_argv_from_string(char_u *cmd, char ***argv, int *argc);
  int build_argv_from_list(list_T *l, char ***argv, int *argc);
--- 74,85 ----
*** ../vim-8.2.3138/src/proto/strings.pro       2021-07-10 21:27:03.599187650 
+0200
--- src/proto/strings.pro       2021-07-10 21:23:10.455465392 +0200
***************
*** 0 ****
--- 1,45 ----
+ /* strings.c */
+ char_u *vim_strsave(char_u *string);
+ char_u *vim_strnsave(char_u *string, size_t len);
+ char_u *vim_strsave_escaped(char_u *string, char_u *esc_chars);
+ char_u *vim_strsave_escaped_ext(char_u *string, char_u *esc_chars, int cc, 
int bsl);
+ int csh_like_shell(void);
+ char_u *vim_strsave_shellescape(char_u *string, int do_special, int 
do_newline);
+ char_u *vim_strsave_up(char_u *string);
+ char_u *vim_strnsave_up(char_u *string, size_t len);
+ void vim_strup(char_u *p);
+ char_u *strlow_save(char_u *orig);
+ void del_trailing_spaces(char_u *ptr);
+ void vim_strncpy(char_u *to, char_u *from, size_t len);
+ void vim_strcat(char_u *to, char_u *from, size_t tosize);
+ int vim_stricmp(char *s1, char *s2);
+ int vim_strnicmp(char *s1, char *s2, size_t len);
+ char_u *vim_strchr(char_u *string, int c);
+ char_u *vim_strbyte(char_u *string, int c);
+ char_u *vim_strrchr(char_u *string, int c);
+ void sort_strings(char_u **files, int count);
+ int has_non_ascii(char_u *s);
+ char_u *concat_str(char_u *str1, char_u *str2);
+ char_u *string_quote(char_u *str, int function);
+ void f_byteidx(typval_T *argvars, typval_T *rettv);
+ void f_byteidxcomp(typval_T *argvars, typval_T *rettv);
+ void f_charidx(typval_T *argvars, typval_T *rettv);
+ void f_str2list(typval_T *argvars, typval_T *rettv);
+ void f_str2nr(typval_T *argvars, typval_T *rettv);
+ void f_strgetchar(typval_T *argvars, typval_T *rettv);
+ void f_stridx(typval_T *argvars, typval_T *rettv);
+ void f_string(typval_T *argvars, typval_T *rettv);
+ void f_strlen(typval_T *argvars, typval_T *rettv);
+ void f_strcharlen(typval_T *argvars, typval_T *rettv);
+ void f_strchars(typval_T *argvars, typval_T *rettv);
+ void f_strdisplaywidth(typval_T *argvars, typval_T *rettv);
+ void f_strwidth(typval_T *argvars, typval_T *rettv);
+ void f_strcharpart(typval_T *argvars, typval_T *rettv);
+ void f_strpart(typval_T *argvars, typval_T *rettv);
+ void f_strridx(typval_T *argvars, typval_T *rettv);
+ void f_strtrans(typval_T *argvars, typval_T *rettv);
+ void f_tolower(typval_T *argvars, typval_T *rettv);
+ void f_toupper(typval_T *argvars, typval_T *rettv);
+ void f_tr(typval_T *argvars, typval_T *rettv);
+ void f_trim(typval_T *argvars, typval_T *rettv);
+ /* vim: set ft=c : */
*** ../vim-8.2.3138/src/strings.c       2021-07-10 21:27:03.603187647 +0200
--- src/strings.c       2021-07-10 21:23:10.455465392 +0200
***************
*** 0 ****
--- 1,1563 ----
+ /* vi:set ts=8 sts=4 sw=4 noet:
+  *
+  * VIM - Vi IMproved  by Bram Moolenaar
+  *
+  * Do ":help uganda"  in Vim to read copying and usage conditions.
+  * Do ":help credits" in Vim to see a list of people who contributed.
+  * See README.txt for an overview of the Vim source code.
+  */
+ 
+ /*
+  * strings.c: string manipulation functions
+  */
+ 
+ #include "vim.h"
+ 
+ /*
+  * Copy "string" into newly allocated memory.
+  */
+     char_u *
+ vim_strsave(char_u *string)
+ {
+     char_u    *p;
+     size_t    len;
+ 
+     len = STRLEN(string) + 1;
+     p = alloc(len);
+     if (p != NULL)
+       mch_memmove(p, string, len);
+     return p;
+ }
+ 
+ /*
+  * Copy up to "len" bytes of "string" into newly allocated memory and
+  * terminate with a NUL.
+  * The allocated memory always has size "len + 1", also when "string" is
+  * shorter.
+  */
+     char_u *
+ vim_strnsave(char_u *string, size_t len)
+ {
+     char_u    *p;
+ 
+     p = alloc(len + 1);
+     if (p != NULL)
+     {
+       STRNCPY(p, string, len);
+       p[len] = NUL;
+     }
+     return p;
+ }
+ 
+ /*
+  * Same as vim_strsave(), but any characters found in esc_chars are preceded
+  * by a backslash.
+  */
+     char_u *
+ vim_strsave_escaped(char_u *string, char_u *esc_chars)
+ {
+     return vim_strsave_escaped_ext(string, esc_chars, '\\', FALSE);
+ }
+ 
+ /*
+  * Same as vim_strsave_escaped(), but when "bsl" is TRUE also escape
+  * characters where rem_backslash() would remove the backslash.
+  * Escape the characters with "cc".
+  */
+     char_u *
+ vim_strsave_escaped_ext(
+     char_u    *string,
+     char_u    *esc_chars,
+     int               cc,
+     int               bsl)
+ {
+     char_u    *p;
+     char_u    *p2;
+     char_u    *escaped_string;
+     unsigned  length;
+     int               l;
+ 
+     /*
+      * First count the number of backslashes required.
+      * Then allocate the memory and insert them.
+      */
+     length = 1;                               // count the trailing NUL
+     for (p = string; *p; p++)
+     {
+       if (has_mbyte && (l = (*mb_ptr2len)(p)) > 1)
+       {
+           length += l;                // count a multibyte char
+           p += l - 1;
+           continue;
+       }
+       if (vim_strchr(esc_chars, *p) != NULL || (bsl && rem_backslash(p)))
+           ++length;                   // count a backslash
+       ++length;                       // count an ordinary char
+     }
+     escaped_string = alloc(length);
+     if (escaped_string != NULL)
+     {
+       p2 = escaped_string;
+       for (p = string; *p; p++)
+       {
+           if (has_mbyte && (l = (*mb_ptr2len)(p)) > 1)
+           {
+               mch_memmove(p2, p, (size_t)l);
+               p2 += l;
+               p += l - 1;             // skip multibyte char
+               continue;
+           }
+           if (vim_strchr(esc_chars, *p) != NULL || (bsl && rem_backslash(p)))
+               *p2++ = cc;
+           *p2++ = *p;
+       }
+       *p2 = NUL;
+     }
+     return escaped_string;
+ }
+ 
+ /*
+  * Return TRUE when 'shell' has "csh" in the tail.
+  */
+     int
+ csh_like_shell(void)
+ {
+     return (strstr((char *)gettail(p_sh), "csh") != NULL);
+ }
+ 
+ /*
+  * Escape "string" for use as a shell argument with system().
+  * This uses single quotes, except when we know we need to use double quotes
+  * (MS-DOS and MS-Windows not using PowerShell and without 'shellslash' set).
+  * PowerShell also uses a novel escaping for enclosed single quotes - double
+  * them up.
+  * Escape a newline, depending on the 'shell' option.
+  * When "do_special" is TRUE also replace "!", "%", "#" and things starting
+  * with "<" like "<cfile>".
+  * When "do_newline" is FALSE do not escape newline unless it is csh shell.
+  * Returns the result in allocated memory, NULL if we have run out.
+  */
+     char_u *
+ vim_strsave_shellescape(char_u *string, int do_special, int do_newline)
+ {
+     unsigned  length;
+     char_u    *p;
+     char_u    *d;
+     char_u    *escaped_string;
+     int               l;
+     int               csh_like;
+     char_u    *shname;
+     int               powershell;
+ # ifdef MSWIN
+     int               double_quotes;
+ # endif
+ 
+     // Only csh and similar shells expand '!' within single quotes.  For sh 
and
+     // the like we must not put a backslash before it, it will be taken
+     // literally.  If do_special is set the '!' will be escaped twice.
+     // Csh also needs to have "\n" escaped twice when do_special is set.
+     csh_like = csh_like_shell();
+ 
+     // PowerShell uses it's own version for quoting single quotes
+     shname = gettail(p_sh);
+     powershell = strstr((char *)shname, "pwsh") != NULL;
+ # ifdef MSWIN
+     powershell = powershell || strstr((char *)shname, "powershell") != NULL;
+     // PowerShell only accepts single quotes so override shellslash.
+     double_quotes = !powershell && !p_ssl;
+ # endif
+ 
+     // First count the number of extra bytes required.
+     length = (unsigned)STRLEN(string) + 3;  // two quotes and a trailing NUL
+     for (p = string; *p != NUL; MB_PTR_ADV(p))
+     {
+ # ifdef MSWIN
+       if (double_quotes)
+       {
+           if (*p == '"')
+               ++length;               // " -> ""
+       }
+       else
+ # endif
+       if (*p == '\'')
+       {
+           if (powershell)
+               length +=2;             // ' => ''
+           else
+               length += 3;            // ' => '\''
+       }
+       if ((*p == '\n' && (csh_like || do_newline))
+               || (*p == '!' && (csh_like || do_special)))
+       {
+           ++length;                   // insert backslash
+           if (csh_like && do_special)
+               ++length;               // insert backslash
+       }
+       if (do_special && find_cmdline_var(p, &l) >= 0)
+       {
+           ++length;                   // insert backslash
+           p += l - 1;
+       }
+     }
+ 
+     // Allocate memory for the result and fill it.
+     escaped_string = alloc(length);
+     if (escaped_string != NULL)
+     {
+       d = escaped_string;
+ 
+       // add opening quote
+ # ifdef MSWIN
+       if (double_quotes)
+           *d++ = '"';
+       else
+ # endif
+           *d++ = '\'';
+ 
+       for (p = string; *p != NUL; )
+       {
+ # ifdef MSWIN
+           if (double_quotes)
+           {
+               if (*p == '"')
+               {
+                   *d++ = '"';
+                   *d++ = '"';
+                   ++p;
+                   continue;
+               }
+           }
+           else
+ # endif
+           if (*p == '\'')
+           {
+               if (powershell)
+               {
+                   *d++ = '\'';
+                   *d++ = '\'';
+               }
+               else
+               {
+                   *d++ = '\'';
+                   *d++ = '\\';
+                   *d++ = '\'';
+                   *d++ = '\'';
+               }
+               ++p;
+               continue;
+           }
+           if ((*p == '\n' && (csh_like || do_newline))
+                   || (*p == '!' && (csh_like || do_special)))
+           {
+               *d++ = '\\';
+               if (csh_like && do_special)
+                   *d++ = '\\';
+               *d++ = *p++;
+               continue;
+           }
+           if (do_special && find_cmdline_var(p, &l) >= 0)
+           {
+               *d++ = '\\';            // insert backslash
+               while (--l >= 0)        // copy the var
+                   *d++ = *p++;
+               continue;
+           }
+ 
+           MB_COPY_CHAR(p, d);
+       }
+ 
+       // add terminating quote and finish with a NUL
+ # ifdef MSWIN
+       if (double_quotes)
+           *d++ = '"';
+       else
+ # endif
+           *d++ = '\'';
+       *d = NUL;
+     }
+ 
+     return escaped_string;
+ }
+ 
+ /*
+  * Like vim_strsave(), but make all characters uppercase.
+  * This uses ASCII lower-to-upper case translation, language independent.
+  */
+     char_u *
+ vim_strsave_up(char_u *string)
+ {
+     char_u *p1;
+ 
+     p1 = vim_strsave(string);
+     vim_strup(p1);
+     return p1;
+ }
+ 
+ /*
+  * Like vim_strnsave(), but make all characters uppercase.
+  * This uses ASCII lower-to-upper case translation, language independent.
+  */
+     char_u *
+ vim_strnsave_up(char_u *string, size_t len)
+ {
+     char_u *p1;
+ 
+     p1 = vim_strnsave(string, len);
+     vim_strup(p1);
+     return p1;
+ }
+ 
+ /*
+  * ASCII lower-to-upper case translation, language independent.
+  */
+     void
+ vim_strup(
+     char_u    *p)
+ {
+     char_u  *p2;
+     int           c;
+ 
+     if (p != NULL)
+     {
+       p2 = p;
+       while ((c = *p2) != NUL)
+ #ifdef EBCDIC
+           *p2++ = isalpha(c) ? toupper(c) : c;
+ #else
+           *p2++ = (c < 'a' || c > 'z') ? c : (c - 0x20);
+ #endif
+     }
+ }
+ 
+ #if defined(FEAT_EVAL) || defined(FEAT_SPELL) || defined(PROTO)
+ /*
+  * Make string "s" all upper-case and return it in allocated memory.
+  * Handles multi-byte characters as well as possible.
+  * Returns NULL when out of memory.
+  */
+     static char_u *
+ strup_save(char_u *orig)
+ {
+     char_u    *p;
+     char_u    *res;
+ 
+     res = p = vim_strsave(orig);
+ 
+     if (res != NULL)
+       while (*p != NUL)
+       {
+           int         l;
+ 
+           if (enc_utf8)
+           {
+               int     c, uc;
+               int     newl;
+               char_u  *s;
+ 
+               c = utf_ptr2char(p);
+               l = utf_ptr2len(p);
+               if (c == 0)
+               {
+                   // overlong sequence, use only the first byte
+                   c = *p;
+                   l = 1;
+               }
+               uc = utf_toupper(c);
+ 
+               // Reallocate string when byte count changes.  This is rare,
+               // thus it's OK to do another malloc()/free().
+               newl = utf_char2len(uc);
+               if (newl != l)
+               {
+                   s = alloc(STRLEN(res) + 1 + newl - l);
+                   if (s == NULL)
+                   {
+                       vim_free(res);
+                       return NULL;
+                   }
+                   mch_memmove(s, res, p - res);
+                   STRCPY(s + (p - res) + newl, p + l);
+                   p = s + (p - res);
+                   vim_free(res);
+                   res = s;
+               }
+ 
+               utf_char2bytes(uc, p);
+               p += newl;
+           }
+           else if (has_mbyte && (l = (*mb_ptr2len)(p)) > 1)
+               p += l;         // skip multi-byte character
+           else
+           {
+               *p = TOUPPER_LOC(*p); // note that toupper() can be a macro
+               p++;
+           }
+       }
+ 
+     return res;
+ }
+ 
+ /*
+  * Make string "s" all lower-case and return it in allocated memory.
+  * Handles multi-byte characters as well as possible.
+  * Returns NULL when out of memory.
+  */
+     char_u *
+ strlow_save(char_u *orig)
+ {
+     char_u    *p;
+     char_u    *res;
+ 
+     res = p = vim_strsave(orig);
+ 
+     if (res != NULL)
+       while (*p != NUL)
+       {
+           int         l;
+ 
+           if (enc_utf8)
+           {
+               int     c, lc;
+               int     newl;
+               char_u  *s;
+ 
+               c = utf_ptr2char(p);
+               l = utf_ptr2len(p);
+               if (c == 0)
+               {
+                   // overlong sequence, use only the first byte
+                   c = *p;
+                   l = 1;
+               }
+               lc = utf_tolower(c);
+ 
+               // Reallocate string when byte count changes.  This is rare,
+               // thus it's OK to do another malloc()/free().
+               newl = utf_char2len(lc);
+               if (newl != l)
+               {
+                   s = alloc(STRLEN(res) + 1 + newl - l);
+                   if (s == NULL)
+                   {
+                       vim_free(res);
+                       return NULL;
+                   }
+                   mch_memmove(s, res, p - res);
+                   STRCPY(s + (p - res) + newl, p + l);
+                   p = s + (p - res);
+                   vim_free(res);
+                   res = s;
+               }
+ 
+               utf_char2bytes(lc, p);
+               p += newl;
+           }
+           else if (has_mbyte && (l = (*mb_ptr2len)(p)) > 1)
+               p += l;         // skip multi-byte character
+           else
+           {
+               *p = TOLOWER_LOC(*p); // note that tolower() can be a macro
+               p++;
+           }
+       }
+ 
+     return res;
+ }
+ #endif
+ 
+ /*
+  * delete spaces at the end of a string
+  */
+     void
+ del_trailing_spaces(char_u *ptr)
+ {
+     char_u    *q;
+ 
+     q = ptr + STRLEN(ptr);
+     while (--q > ptr && VIM_ISWHITE(q[0]) && q[-1] != '\\' && q[-1] != Ctrl_V)
+       *q = NUL;
+ }
+ 
+ /*
+  * Like strncpy(), but always terminate the result with one NUL.
+  * "to" must be "len + 1" long!
+  */
+     void
+ vim_strncpy(char_u *to, char_u *from, size_t len)
+ {
+     STRNCPY(to, from, len);
+     to[len] = NUL;
+ }
+ 
+ /*
+  * Like strcat(), but make sure the result fits in "tosize" bytes and is
+  * always NUL terminated. "from" and "to" may overlap.
+  */
+     void
+ vim_strcat(char_u *to, char_u *from, size_t tosize)
+ {
+     size_t tolen = STRLEN(to);
+     size_t fromlen = STRLEN(from);
+ 
+     if (tolen + fromlen + 1 > tosize)
+     {
+       mch_memmove(to + tolen, from, tosize - tolen - 1);
+       to[tosize - 1] = NUL;
+     }
+     else
+       mch_memmove(to + tolen, from, fromlen + 1);
+ }
+ 
+ #if (!defined(HAVE_STRCASECMP) && !defined(HAVE_STRICMP)) || defined(PROTO)
+ /*
+  * Compare two strings, ignoring case, using current locale.
+  * Doesn't work for multi-byte characters.
+  * return 0 for match, < 0 for smaller, > 0 for bigger
+  */
+     int
+ vim_stricmp(char *s1, char *s2)
+ {
+     int               i;
+ 
+     for (;;)
+     {
+       i = (int)TOLOWER_LOC(*s1) - (int)TOLOWER_LOC(*s2);
+       if (i != 0)
+           return i;                       // this character different
+       if (*s1 == NUL)
+           break;                          // strings match until NUL
+       ++s1;
+       ++s2;
+     }
+     return 0;                             // strings match
+ }
+ #endif
+ 
+ #if (!defined(HAVE_STRNCASECMP) && !defined(HAVE_STRNICMP)) || defined(PROTO)
+ /*
+  * Compare two strings, for length "len", ignoring case, using current locale.
+  * Doesn't work for multi-byte characters.
+  * return 0 for match, < 0 for smaller, > 0 for bigger
+  */
+     int
+ vim_strnicmp(char *s1, char *s2, size_t len)
+ {
+     int               i;
+ 
+     while (len > 0)
+     {
+       i = (int)TOLOWER_LOC(*s1) - (int)TOLOWER_LOC(*s2);
+       if (i != 0)
+           return i;                       // this character different
+       if (*s1 == NUL)
+           break;                          // strings match until NUL
+       ++s1;
+       ++s2;
+       --len;
+     }
+     return 0;                             // strings match
+ }
+ #endif
+ 
+ /*
+  * Search for first occurrence of "c" in "string".
+  * Version of strchr() that handles unsigned char strings with characters from
+  * 128 to 255 correctly.  It also doesn't return a pointer to the NUL at the
+  * end of the string.
+  */
+     char_u  *
+ vim_strchr(char_u *string, int c)
+ {
+     char_u    *p;
+     int               b;
+ 
+     p = string;
+     if (enc_utf8 && c >= 0x80)
+     {
+       while (*p != NUL)
+       {
+           int l = utfc_ptr2len(p);
+ 
+           // Avoid matching an illegal byte here.
+           if (utf_ptr2char(p) == c && l > 1)
+               return p;
+           p += l;
+       }
+       return NULL;
+     }
+     if (enc_dbcs != 0 && c > 255)
+     {
+       int     n2 = c & 0xff;
+ 
+       c = ((unsigned)c >> 8) & 0xff;
+       while ((b = *p) != NUL)
+       {
+           if (b == c && p[1] == n2)
+               return p;
+           p += (*mb_ptr2len)(p);
+       }
+       return NULL;
+     }
+     if (has_mbyte)
+     {
+       while ((b = *p) != NUL)
+       {
+           if (b == c)
+               return p;
+           p += (*mb_ptr2len)(p);
+       }
+       return NULL;
+     }
+     while ((b = *p) != NUL)
+     {
+       if (b == c)
+           return p;
+       ++p;
+     }
+     return NULL;
+ }
+ 
+ /*
+  * Version of strchr() that only works for bytes and handles unsigned char
+  * strings with characters above 128 correctly. It also doesn't return a
+  * pointer to the NUL at the end of the string.
+  */
+     char_u  *
+ vim_strbyte(char_u *string, int c)
+ {
+     char_u    *p = string;
+ 
+     while (*p != NUL)
+     {
+       if (*p == c)
+           return p;
+       ++p;
+     }
+     return NULL;
+ }
+ 
+ /*
+  * Search for last occurrence of "c" in "string".
+  * Version of strrchr() that handles unsigned char strings with characters 
from
+  * 128 to 255 correctly.  It also doesn't return a pointer to the NUL at the
+  * end of the string.
+  * Return NULL if not found.
+  * Does not handle multi-byte char for "c"!
+  */
+     char_u  *
+ vim_strrchr(char_u *string, int c)
+ {
+     char_u    *retval = NULL;
+     char_u    *p = string;
+ 
+     while (*p)
+     {
+       if (*p == c)
+           retval = p;
+       MB_PTR_ADV(p);
+     }
+     return retval;
+ }
+ 
+ /*
+  * Vim's version of strpbrk(), in case it's missing.
+  * Don't generate a prototype for this, causes problems when it's not used.
+  */
+ #ifndef PROTO
+ # ifndef HAVE_STRPBRK
+ #  ifdef vim_strpbrk
+ #   undef vim_strpbrk
+ #  endif
+     char_u *
+ vim_strpbrk(char_u *s, char_u *charset)
+ {
+     while (*s)
+     {
+       if (vim_strchr(charset, *s) != NULL)
+           return s;
+       MB_PTR_ADV(s);
+     }
+     return NULL;
+ }
+ # endif
+ #endif
+ 
+ /*
+  * Sort an array of strings.
+  */
+ static int sort_compare(const void *s1, const void *s2);
+ 
+     static int
+ sort_compare(const void *s1, const void *s2)
+ {
+     return STRCMP(*(char **)s1, *(char **)s2);
+ }
+ 
+     void
+ sort_strings(
+     char_u    **files,
+     int               count)
+ {
+     qsort((void *)files, (size_t)count, sizeof(char_u *), sort_compare);
+ }
+ 
+ #if defined(FEAT_QUICKFIX) || defined(FEAT_SPELL) || defined(PROTO)
+ /*
+  * Return TRUE if string "s" contains a non-ASCII character (128 or higher).
+  * When "s" is NULL FALSE is returned.
+  */
+     int
+ has_non_ascii(char_u *s)
+ {
+     char_u    *p;
+ 
+     if (s != NULL)
+       for (p = s; *p != NUL; ++p)
+           if (*p >= 128)
+               return TRUE;
+     return FALSE;
+ }
+ #endif
+ 
+ /*
+  * Concatenate two strings and return the result in allocated memory.
+  * Returns NULL when out of memory.
+  */
+     char_u  *
+ concat_str(char_u *str1, char_u *str2)
+ {
+     char_u  *dest;
+     size_t  l = str1 == NULL ? 0 : STRLEN(str1);
+ 
+     dest = alloc(l + (str2 == NULL ? 0 : STRLEN(str2)) + 1L);
+     if (dest != NULL)
+     {
+       if (str1 == NULL)
+           *dest = NUL;
+       else
+           STRCPY(dest, str1);
+       if (str2 != NULL)
+           STRCPY(dest + l, str2);
+     }
+     return dest;
+ }
+ 
+ #if defined(FEAT_EVAL) || defined(PROTO)
+ 
+ /*
+  * Return string "str" in ' quotes, doubling ' characters.
+  * If "str" is NULL an empty string is assumed.
+  * If "function" is TRUE make it function('string').
+  */
+     char_u *
+ string_quote(char_u *str, int function)
+ {
+     unsigned  len;
+     char_u    *p, *r, *s;
+ 
+     len = (function ? 13 : 3);
+     if (str != NULL)
+     {
+       len += (unsigned)STRLEN(str);
+       for (p = str; *p != NUL; MB_PTR_ADV(p))
+           if (*p == '\'')
+               ++len;
+     }
+     s = r = alloc(len);
+     if (r != NULL)
+     {
+       if (function)
+       {
+           STRCPY(r, "function('");
+           r += 10;
+       }
+       else
+           *r++ = '\'';
+       if (str != NULL)
+           for (p = str; *p != NUL; )
+           {
+               if (*p == '\'')
+                   *r++ = '\'';
+               MB_COPY_CHAR(p, r);
+           }
+       *r++ = '\'';
+       if (function)
+           *r++ = ')';
+       *r++ = NUL;
+     }
+     return s;
+ }
+ 
+     static void
+ byteidx(typval_T *argvars, typval_T *rettv, int comp UNUSED)
+ {
+     char_u    *t;
+     char_u    *str;
+     varnumber_T       idx;
+ 
+     str = tv_get_string_chk(&argvars[0]);
+     idx = tv_get_number_chk(&argvars[1], NULL);
+     rettv->vval.v_number = -1;
+     if (str == NULL || idx < 0)
+       return;
+ 
+     t = str;
+     for ( ; idx > 0; idx--)
+     {
+       if (*t == NUL)          // EOL reached
+           return;
+       if (enc_utf8 && comp)
+           t += utf_ptr2len(t);
+       else
+           t += (*mb_ptr2len)(t);
+     }
+     rettv->vval.v_number = (varnumber_T)(t - str);
+ }
+ 
+ /*
+  * "byteidx()" function
+  */
+     void
+ f_byteidx(typval_T *argvars, typval_T *rettv)
+ {
+     byteidx(argvars, rettv, FALSE);
+ }
+ 
+ /*
+  * "byteidxcomp()" function
+  */
+     void
+ f_byteidxcomp(typval_T *argvars, typval_T *rettv)
+ {
+     byteidx(argvars, rettv, TRUE);
+ }
+ 
+ /*
+  * "charidx()" function
+  */
+     void
+ f_charidx(typval_T *argvars, typval_T *rettv)
+ {
+     char_u    *str;
+     varnumber_T       idx;
+     varnumber_T       countcc = FALSE;
+     char_u    *p;
+     int               len;
+     int               (*ptr2len)(char_u *);
+ 
+     rettv->vval.v_number = -1;
+ 
+     if (argvars[0].v_type != VAR_STRING || argvars[1].v_type != VAR_NUMBER
+           || (argvars[2].v_type != VAR_UNKNOWN
+                                          && argvars[2].v_type != VAR_NUMBER
+                                          && argvars[2].v_type != VAR_BOOL))
+     {
+       emsg(_(e_invarg));
+       return;
+     }
+ 
+     str = tv_get_string_chk(&argvars[0]);
+     idx = tv_get_number_chk(&argvars[1], NULL);
+     if (str == NULL || idx < 0)
+       return;
+ 
+     if (argvars[2].v_type != VAR_UNKNOWN)
+       countcc = tv_get_bool(&argvars[2]);
+     if (countcc < 0 || countcc > 1)
+     {
+       semsg(_(e_using_number_as_bool_nr), countcc);
+       return;
+     }
+ 
+     if (enc_utf8 && countcc)
+       ptr2len = utf_ptr2len;
+     else
+       ptr2len = mb_ptr2len;
+ 
+     for (p = str, len = 0; p <= str + idx; len++)
+     {
+       if (*p == NUL)
+           return;
+       p += ptr2len(p);
+     }
+ 
+     rettv->vval.v_number = len > 0 ? len - 1 : 0;
+ }
+ 
+ /*
+  * "str2list()" function
+  */
+     void
+ f_str2list(typval_T *argvars, typval_T *rettv)
+ {
+     char_u    *p;
+     int               utf8 = FALSE;
+ 
+     if (rettv_list_alloc(rettv) == FAIL)
+       return;
+ 
+     if (argvars[1].v_type != VAR_UNKNOWN)
+       utf8 = (int)tv_get_bool_chk(&argvars[1], NULL);
+ 
+     p = tv_get_string(&argvars[0]);
+ 
+     if (has_mbyte || utf8)
+     {
+       int (*ptr2len)(char_u *);
+       int (*ptr2char)(char_u *);
+ 
+       if (utf8 || enc_utf8)
+       {
+           ptr2len = utf_ptr2len;
+           ptr2char = utf_ptr2char;
+       }
+       else
+       {
+           ptr2len = mb_ptr2len;
+           ptr2char = mb_ptr2char;
+       }
+ 
+       for ( ; *p != NUL; p += (*ptr2len)(p))
+           list_append_number(rettv->vval.v_list, (*ptr2char)(p));
+     }
+     else
+       for ( ; *p != NUL; ++p)
+           list_append_number(rettv->vval.v_list, *p);
+ }
+ 
+ /*
+  * "str2nr()" function
+  */
+     void
+ f_str2nr(typval_T *argvars, typval_T *rettv)
+ {
+     int               base = 10;
+     char_u    *p;
+     varnumber_T       n;
+     int               what = 0;
+     int               isneg;
+ 
+     if (argvars[1].v_type != VAR_UNKNOWN)
+     {
+       base = (int)tv_get_number(&argvars[1]);
+       if (base != 2 && base != 8 && base != 10 && base != 16)
+       {
+           emsg(_(e_invarg));
+           return;
+       }
+       if (argvars[2].v_type != VAR_UNKNOWN && tv_get_bool(&argvars[2]))
+           what |= STR2NR_QUOTE;
+     }
+ 
+     p = skipwhite(tv_get_string_strict(&argvars[0]));
+     isneg = (*p == '-');
+     if (*p == '+' || *p == '-')
+       p = skipwhite(p + 1);
+     switch (base)
+     {
+       case 2: what |= STR2NR_BIN + STR2NR_FORCE; break;
+       case 8: what |= STR2NR_OCT + STR2NR_OOCT + STR2NR_FORCE; break;
+       case 16: what |= STR2NR_HEX + STR2NR_FORCE; break;
+     }
+     vim_str2nr(p, NULL, NULL, what, &n, NULL, 0, FALSE);
+     // Text after the number is silently ignored.
+     if (isneg)
+       rettv->vval.v_number = -n;
+     else
+       rettv->vval.v_number = n;
+ 
+ }
+ 
+ /*
+  * "strgetchar()" function
+  */
+     void
+ f_strgetchar(typval_T *argvars, typval_T *rettv)
+ {
+     char_u    *str;
+     int               len;
+     int               error = FALSE;
+     int               charidx;
+     int               byteidx = 0;
+ 
+     rettv->vval.v_number = -1;
+     str = tv_get_string_chk(&argvars[0]);
+     if (str == NULL)
+       return;
+     len = (int)STRLEN(str);
+     charidx = (int)tv_get_number_chk(&argvars[1], &error);
+     if (error)
+       return;
+ 
+     while (charidx >= 0 && byteidx < len)
+     {
+       if (charidx == 0)
+       {
+           rettv->vval.v_number = mb_ptr2char(str + byteidx);
+           break;
+       }
+       --charidx;
+       byteidx += MB_CPTR2LEN(str + byteidx);
+     }
+ }
+ 
+ /*
+  * "stridx()" function
+  */
+     void
+ f_stridx(typval_T *argvars, typval_T *rettv)
+ {
+     char_u    buf[NUMBUFLEN];
+     char_u    *needle;
+     char_u    *haystack;
+     char_u    *save_haystack;
+     char_u    *pos;
+     int               start_idx;
+ 
+     needle = tv_get_string_chk(&argvars[1]);
+     save_haystack = haystack = tv_get_string_buf_chk(&argvars[0], buf);
+     rettv->vval.v_number = -1;
+     if (needle == NULL || haystack == NULL)
+       return;         // type error; errmsg already given
+ 
+     if (argvars[2].v_type != VAR_UNKNOWN)
+     {
+       int         error = FALSE;
+ 
+       start_idx = (int)tv_get_number_chk(&argvars[2], &error);
+       if (error || start_idx >= (int)STRLEN(haystack))
+           return;
+       if (start_idx >= 0)
+           haystack += start_idx;
+     }
+ 
+     pos       = (char_u *)strstr((char *)haystack, (char *)needle);
+     if (pos != NULL)
+       rettv->vval.v_number = (varnumber_T)(pos - save_haystack);
+ }
+ 
+ /*
+  * "string()" function
+  */
+     void
+ f_string(typval_T *argvars, typval_T *rettv)
+ {
+     char_u    *tofree;
+     char_u    numbuf[NUMBUFLEN];
+ 
+     rettv->v_type = VAR_STRING;
+     rettv->vval.v_string = tv2string(&argvars[0], &tofree, numbuf,
+                                                               get_copyID());
+     // Make a copy if we have a value but it's not in allocated memory.
+     if (rettv->vval.v_string != NULL && tofree == NULL)
+       rettv->vval.v_string = vim_strsave(rettv->vval.v_string);
+ }
+ 
+ /*
+  * "strlen()" function
+  */
+     void
+ f_strlen(typval_T *argvars, typval_T *rettv)
+ {
+     rettv->vval.v_number = (varnumber_T)(STRLEN(
+                                             tv_get_string(&argvars[0])));
+ }
+ 
+     static void
+ strchar_common(typval_T *argvars, typval_T *rettv, int skipcc)
+ {
+     char_u            *s = tv_get_string(&argvars[0]);
+     varnumber_T               len = 0;
+     int                       (*func_mb_ptr2char_adv)(char_u **pp);
+ 
+     func_mb_ptr2char_adv = skipcc ? mb_ptr2char_adv : mb_cptr2char_adv;
+     while (*s != NUL)
+     {
+       func_mb_ptr2char_adv(&s);
+       ++len;
+     }
+     rettv->vval.v_number = len;
+ }
+ 
+ /*
+  * "strcharlen()" function
+  */
+     void
+ f_strcharlen(typval_T *argvars, typval_T *rettv)
+ {
+     strchar_common(argvars, rettv, TRUE);
+ }
+ 
+ /*
+  * "strchars()" function
+  */
+     void
+ f_strchars(typval_T *argvars, typval_T *rettv)
+ {
+     varnumber_T               skipcc = FALSE;
+ 
+     if (argvars[1].v_type != VAR_UNKNOWN)
+       skipcc = tv_get_bool(&argvars[1]);
+     if (skipcc < 0 || skipcc > 1)
+       semsg(_(e_using_number_as_bool_nr), skipcc);
+     else
+       strchar_common(argvars, rettv, skipcc);
+ }
+ 
+ /*
+  * "strdisplaywidth()" function
+  */
+     void
+ f_strdisplaywidth(typval_T *argvars, typval_T *rettv)
+ {
+     char_u    *s = tv_get_string(&argvars[0]);
+     int               col = 0;
+ 
+     if (argvars[1].v_type != VAR_UNKNOWN)
+       col = (int)tv_get_number(&argvars[1]);
+ 
+     rettv->vval.v_number = (varnumber_T)(linetabsize_col(col, s) - col);
+ }
+ 
+ /*
+  * "strwidth()" function
+  */
+     void
+ f_strwidth(typval_T *argvars, typval_T *rettv)
+ {
+     char_u    *s = tv_get_string_strict(&argvars[0]);
+ 
+     rettv->vval.v_number = (varnumber_T)(mb_string2cells(s, -1));
+ }
+ 
+ /*
+  * "strcharpart()" function
+  */
+     void
+ f_strcharpart(typval_T *argvars, typval_T *rettv)
+ {
+     char_u    *p;
+     int               nchar;
+     int               nbyte = 0;
+     int               charlen;
+     int               skipcc = FALSE;
+     int               len = 0;
+     int               slen;
+     int               error = FALSE;
+ 
+     p = tv_get_string(&argvars[0]);
+     slen = (int)STRLEN(p);
+ 
+     nchar = (int)tv_get_number_chk(&argvars[1], &error);
+     if (!error)
+     {
+       if (argvars[2].v_type != VAR_UNKNOWN
+                                          && argvars[3].v_type != VAR_UNKNOWN)
+       {
+           skipcc = tv_get_bool(&argvars[3]);
+           if (skipcc < 0 || skipcc > 1)
+           {
+               semsg(_(e_using_number_as_bool_nr), skipcc);
+               return;
+           }
+       }
+ 
+       if (nchar > 0)
+           while (nchar > 0 && nbyte < slen)
+           {
+               if (skipcc)
+                   nbyte += mb_ptr2len(p + nbyte);
+               else
+                   nbyte += MB_CPTR2LEN(p + nbyte);
+               --nchar;
+           }
+       else
+           nbyte = nchar;
+       if (argvars[2].v_type != VAR_UNKNOWN)
+       {
+           charlen = (int)tv_get_number(&argvars[2]);
+           while (charlen > 0 && nbyte + len < slen)
+           {
+               int off = nbyte + len;
+ 
+               if (off < 0)
+                   len += 1;
+               else
+               {
+                   if (skipcc)
+                       len += mb_ptr2len(p + off);
+                   else
+                       len += MB_CPTR2LEN(p + off);
+               }
+               --charlen;
+           }
+       }
+       else
+           len = slen - nbyte;    // default: all bytes that are available.
+     }
+ 
+     /*
+      * Only return the overlap between the specified part and the actual
+      * string.
+      */
+     if (nbyte < 0)
+     {
+       len += nbyte;
+       nbyte = 0;
+     }
+     else if (nbyte > slen)
+       nbyte = slen;
+     if (len < 0)
+       len = 0;
+     else if (nbyte + len > slen)
+       len = slen - nbyte;
+ 
+     rettv->v_type = VAR_STRING;
+     rettv->vval.v_string = vim_strnsave(p + nbyte, len);
+ }
+ 
+ /*
+  * "strpart()" function
+  */
+     void
+ f_strpart(typval_T *argvars, typval_T *rettv)
+ {
+     char_u    *p;
+     int               n;
+     int               len;
+     int               slen;
+     int               error = FALSE;
+ 
+     p = tv_get_string(&argvars[0]);
+     slen = (int)STRLEN(p);
+ 
+     n = (int)tv_get_number_chk(&argvars[1], &error);
+     if (error)
+       len = 0;
+     else if (argvars[2].v_type != VAR_UNKNOWN)
+       len = (int)tv_get_number(&argvars[2]);
+     else
+       len = slen - n;     // default len: all bytes that are available.
+ 
+     // Only return the overlap between the specified part and the actual
+     // string.
+     if (n < 0)
+     {
+       len += n;
+       n = 0;
+     }
+     else if (n > slen)
+       n = slen;
+     if (len < 0)
+       len = 0;
+     else if (n + len > slen)
+       len = slen - n;
+ 
+     if (argvars[2].v_type != VAR_UNKNOWN && argvars[3].v_type != VAR_UNKNOWN)
+     {
+       int off;
+ 
+       // length in characters
+       for (off = n; off < slen && len > 0; --len)
+           off += mb_ptr2len(p + off);
+       len = off - n;
+     }
+ 
+     rettv->v_type = VAR_STRING;
+     rettv->vval.v_string = vim_strnsave(p + n, len);
+ }
+ 
+ /*
+  * "strridx()" function
+  */
+     void
+ f_strridx(typval_T *argvars, typval_T *rettv)
+ {
+     char_u    buf[NUMBUFLEN];
+     char_u    *needle;
+     char_u    *haystack;
+     char_u    *rest;
+     char_u    *lastmatch = NULL;
+     int               haystack_len, end_idx;
+ 
+     needle = tv_get_string_chk(&argvars[1]);
+     haystack = tv_get_string_buf_chk(&argvars[0], buf);
+ 
+     rettv->vval.v_number = -1;
+     if (needle == NULL || haystack == NULL)
+       return;         // type error; errmsg already given
+ 
+     haystack_len = (int)STRLEN(haystack);
+     if (argvars[2].v_type != VAR_UNKNOWN)
+     {
+       // Third argument: upper limit for index
+       end_idx = (int)tv_get_number_chk(&argvars[2], NULL);
+       if (end_idx < 0)
+           return;     // can never find a match
+     }
+     else
+       end_idx = haystack_len;
+ 
+     if (*needle == NUL)
+     {
+       // Empty string matches past the end.
+       lastmatch = haystack + end_idx;
+     }
+     else
+     {
+       for (rest = haystack; *rest != '\0'; ++rest)
+       {
+           rest = (char_u *)strstr((char *)rest, (char *)needle);
+           if (rest == NULL || rest > haystack + end_idx)
+               break;
+           lastmatch = rest;
+       }
+     }
+ 
+     if (lastmatch == NULL)
+       rettv->vval.v_number = -1;
+     else
+       rettv->vval.v_number = (varnumber_T)(lastmatch - haystack);
+ }
+ 
+ /*
+  * "strtrans()" function
+  */
+     void
+ f_strtrans(typval_T *argvars, typval_T *rettv)
+ {
+     rettv->v_type = VAR_STRING;
+     rettv->vval.v_string = transstr(tv_get_string(&argvars[0]));
+ }
+ 
+ /*
+  * "tolower(string)" function
+  */
+     void
+ f_tolower(typval_T *argvars, typval_T *rettv)
+ {
+     rettv->v_type = VAR_STRING;
+     rettv->vval.v_string = strlow_save(tv_get_string(&argvars[0]));
+ }
+ 
+ /*
+  * "toupper(string)" function
+  */
+     void
+ f_toupper(typval_T *argvars, typval_T *rettv)
+ {
+     rettv->v_type = VAR_STRING;
+     rettv->vval.v_string = strup_save(tv_get_string(&argvars[0]));
+ }
+ 
+ /*
+  * "tr(string, fromstr, tostr)" function
+  */
+     void
+ f_tr(typval_T *argvars, typval_T *rettv)
+ {
+     char_u    *in_str;
+     char_u    *fromstr;
+     char_u    *tostr;
+     char_u    *p;
+     int               inlen;
+     int               fromlen;
+     int               tolen;
+     int               idx;
+     char_u    *cpstr;
+     int               cplen;
+     int               first = TRUE;
+     char_u    buf[NUMBUFLEN];
+     char_u    buf2[NUMBUFLEN];
+     garray_T  ga;
+ 
+     in_str = tv_get_string(&argvars[0]);
+     fromstr = tv_get_string_buf_chk(&argvars[1], buf);
+     tostr = tv_get_string_buf_chk(&argvars[2], buf2);
+ 
+     // Default return value: empty string.
+     rettv->v_type = VAR_STRING;
+     rettv->vval.v_string = NULL;
+     if (fromstr == NULL || tostr == NULL)
+           return;             // type error; errmsg already given
+     ga_init2(&ga, (int)sizeof(char), 80);
+ 
+     if (!has_mbyte)
+       // not multi-byte: fromstr and tostr must be the same length
+       if (STRLEN(fromstr) != STRLEN(tostr))
+       {
+ error:
+           semsg(_(e_invarg2), fromstr);
+           ga_clear(&ga);
+           return;
+       }
+ 
+     // fromstr and tostr have to contain the same number of chars
+     while (*in_str != NUL)
+     {
+       if (has_mbyte)
+       {
+           inlen = (*mb_ptr2len)(in_str);
+           cpstr = in_str;
+           cplen = inlen;
+           idx = 0;
+           for (p = fromstr; *p != NUL; p += fromlen)
+           {
+               fromlen = (*mb_ptr2len)(p);
+               if (fromlen == inlen && STRNCMP(in_str, p, inlen) == 0)
+               {
+                   for (p = tostr; *p != NUL; p += tolen)
+                   {
+                       tolen = (*mb_ptr2len)(p);
+                       if (idx-- == 0)
+                       {
+                           cplen = tolen;
+                           cpstr = p;
+                           break;
+                       }
+                   }
+                   if (*p == NUL)      // tostr is shorter than fromstr
+                       goto error;
+                   break;
+               }
+               ++idx;
+           }
+ 
+           if (first && cpstr == in_str)
+           {
+               // Check that fromstr and tostr have the same number of
+               // (multi-byte) characters.  Done only once when a character
+               // of in_str doesn't appear in fromstr.
+               first = FALSE;
+               for (p = tostr; *p != NUL; p += tolen)
+               {
+                   tolen = (*mb_ptr2len)(p);
+                   --idx;
+               }
+               if (idx != 0)
+                   goto error;
+           }
+ 
+           (void)ga_grow(&ga, cplen);
+           mch_memmove((char *)ga.ga_data + ga.ga_len, cpstr, (size_t)cplen);
+           ga.ga_len += cplen;
+ 
+           in_str += inlen;
+       }
+       else
+       {
+           // When not using multi-byte chars we can do it faster.
+           p = vim_strchr(fromstr, *in_str);
+           if (p != NULL)
+               ga_append(&ga, tostr[p - fromstr]);
+           else
+               ga_append(&ga, *in_str);
+           ++in_str;
+       }
+     }
+ 
+     // add a terminating NUL
+     (void)ga_grow(&ga, 1);
+     ga_append(&ga, NUL);
+ 
+     rettv->vval.v_string = ga.ga_data;
+ }
+ 
+ /*
+  * "trim({expr})" function
+  */
+     void
+ f_trim(typval_T *argvars, typval_T *rettv)
+ {
+     char_u    buf1[NUMBUFLEN];
+     char_u    buf2[NUMBUFLEN];
+     char_u    *head = tv_get_string_buf_chk(&argvars[0], buf1);
+     char_u    *mask = NULL;
+     char_u    *tail;
+     char_u    *prev;
+     char_u    *p;
+     int               c1;
+     int               dir = 0;
+ 
+     rettv->v_type = VAR_STRING;
+     rettv->vval.v_string = NULL;
+     if (head == NULL)
+       return;
+ 
+     if (argvars[1].v_type != VAR_UNKNOWN && argvars[1].v_type != VAR_STRING)
+     {
+       semsg(_(e_invarg2), tv_get_string(&argvars[1]));
+       return;
+     }
+ 
+     if (argvars[1].v_type == VAR_STRING)
+     {
+       mask = tv_get_string_buf_chk(&argvars[1], buf2);
+ 
+       if (argvars[2].v_type != VAR_UNKNOWN)
+       {
+           int error = 0;
+ 
+           // leading or trailing characters to trim
+           dir = (int)tv_get_number_chk(&argvars[2], &error);
+           if (error)
+               return;
+           if (dir < 0 || dir > 2)
+           {
+               semsg(_(e_invarg2), tv_get_string(&argvars[2]));
+               return;
+           }
+       }
+     }
+ 
+     if (dir == 0 || dir == 1)
+     {
+       // Trim leading characters
+       while (*head != NUL)
+       {
+           c1 = PTR2CHAR(head);
+           if (mask == NULL)
+           {
+               if (c1 > ' ' && c1 != 0xa0)
+                   break;
+           }
+           else
+           {
+               for (p = mask; *p != NUL; MB_PTR_ADV(p))
+                   if (c1 == PTR2CHAR(p))
+                       break;
+               if (*p == NUL)
+                   break;
+           }
+           MB_PTR_ADV(head);
+       }
+     }
+ 
+     tail = head + STRLEN(head);
+     if (dir == 0 || dir == 2)
+     {
+       // Trim trailing characters
+       for (; tail > head; tail = prev)
+       {
+           prev = tail;
+           MB_PTR_BACK(head, prev);
+           c1 = PTR2CHAR(prev);
+           if (mask == NULL)
+           {
+               if (c1 > ' ' && c1 != 0xa0)
+                   break;
+           }
+           else
+           {
+               for (p = mask; *p != NUL; MB_PTR_ADV(p))
+                   if (c1 == PTR2CHAR(p))
+                       break;
+               if (*p == NUL)
+                   break;
+           }
+       }
+     }
+     rettv->vval.v_string = vim_strnsave(head, tail - head);
+ }
+ 
+ #endif
*** ../vim-8.2.3138/src/version.c       2021-07-10 20:43:56.218041994 +0200
--- src/version.c       2021-07-10 21:24:48.623350207 +0200
***************
*** 757,758 ****
--- 757,760 ----
  {   /* Add new patch number below this line */
+ /**/
+     3139,
  /**/

-- 
hundred-and-one symptoms of being an internet addict:
110. You actually volunteer to become your employer's webmaster.

 /// Bram Moolenaar -- [email protected] -- http://www.Moolenaar.net   \\\
///                                                                      \\\
\\\        sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ ///
 \\\            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 [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/vim_dev/202107101929.16AJTkp8644829%40masaka.moolenaar.net.

Raspunde prin e-mail lui