Patch 8.1.1763
Problem:    Evalfunc.c is still too big.
Solution:   Move dict and list functions to a better place.
Files:      src/evalfunc.c, src/dict.c, src/proto/dict.pro, src/list.c,
            src/proto/list.pro, src/blob.c, src/proto/blob.pro


*** ../vim-8.1.1762/src/evalfunc.c      2019-07-27 17:38:55.474964950 +0200
--- src/evalfunc.c      2019-07-27 23:00:00.572303798 +0200
***************
*** 180,186 ****
  static void f_globpath(typval_T *argvars, typval_T *rettv);
  static void f_glob2regpat(typval_T *argvars, typval_T *rettv);
  static void f_has(typval_T *argvars, typval_T *rettv);
- static void f_has_key(typval_T *argvars, typval_T *rettv);
  static void f_haslocaldir(typval_T *argvars, typval_T *rettv);
  static void f_hasmapto(typval_T *argvars, typval_T *rettv);
  static void f_histadd(typval_T *argvars, typval_T *rettv);
--- 180,185 ----
***************
*** 207,215 ****
  static void f_isinf(typval_T *argvars, typval_T *rettv);
  static void f_isnan(typval_T *argvars, typval_T *rettv);
  #endif
- static void f_items(typval_T *argvars, typval_T *rettv);
- static void f_join(typval_T *argvars, typval_T *rettv);
- static void f_keys(typval_T *argvars, typval_T *rettv);
  static void f_last_buffer_nr(typval_T *argvars, typval_T *rettv);
  static void f_len(typval_T *argvars, typval_T *rettv);
  static void f_libcall(typval_T *argvars, typval_T *rettv);
--- 206,211 ----
***************
*** 217,223 ****
  static void f_line(typval_T *argvars, typval_T *rettv);
  static void f_line2byte(typval_T *argvars, typval_T *rettv);
  static void f_lispindent(typval_T *argvars, typval_T *rettv);
- static void f_list2str(typval_T *argvars, typval_T *rettv);
  static void f_localtime(typval_T *argvars, typval_T *rettv);
  #ifdef FEAT_FLOAT
  static void f_log(typval_T *argvars, typval_T *rettv);
--- 213,218 ----
***************
*** 330,336 ****
  static void f_sin(typval_T *argvars, typval_T *rettv);
  static void f_sinh(typval_T *argvars, typval_T *rettv);
  #endif
- static void f_sort(typval_T *argvars, typval_T *rettv);
  static void f_soundfold(typval_T *argvars, typval_T *rettv);
  static void f_spellbadword(typval_T *argvars, typval_T *rettv);
  static void f_spellsuggest(typval_T *argvars, typval_T *rettv);
--- 325,330 ----
***************
*** 392,399 ****
  static void f_type(typval_T *argvars, typval_T *rettv);
  static void f_undofile(typval_T *argvars, typval_T *rettv);
  static void f_undotree(typval_T *argvars, typval_T *rettv);
- static void f_uniq(typval_T *argvars, typval_T *rettv);
- static void f_values(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);
  static void f_wildmenumode(typval_T *argvars, typval_T *rettv);
--- 386,391 ----
***************
*** 6581,6604 ****
  }
  
  /*
-  * "has_key()" function
-  */
-     static void
- f_has_key(typval_T *argvars, typval_T *rettv)
- {
-     if (argvars[0].v_type != VAR_DICT)
-     {
-       emsg(_(e_dictreq));
-       return;
-     }
-     if (argvars[0].vval.v_dict == NULL)
-       return;
- 
-     rettv->vval.v_number = dict_find(argvars[0].vval.v_dict,
-                                     tv_get_string(&argvars[1]), -1) != NULL;
- }
- 
- /*
   * "haslocaldir()" function
   */
      static void
--- 6573,6578 ----
***************
*** 7241,7298 ****
  #endif
  
  /*
-  * "items(dict)" function
-  */
-     static void
- f_items(typval_T *argvars, typval_T *rettv)
- {
-     dict_list(argvars, rettv, 2);
- }
- 
- /*
-  * "join()" function
-  */
-     static void
- f_join(typval_T *argvars, typval_T *rettv)
- {
-     garray_T  ga;
-     char_u    *sep;
- 
-     if (argvars[0].v_type != VAR_LIST)
-     {
-       emsg(_(e_listreq));
-       return;
-     }
-     if (argvars[0].vval.v_list == NULL)
-       return;
-     if (argvars[1].v_type == VAR_UNKNOWN)
-       sep = (char_u *)" ";
-     else
-       sep = tv_get_string_chk(&argvars[1]);
- 
-     rettv->v_type = VAR_STRING;
- 
-     if (sep != NULL)
-     {
-       ga_init2(&ga, (int)sizeof(char), 80);
-       list_join(&ga, argvars[0].vval.v_list, sep, TRUE, FALSE, 0);
-       ga_append(&ga, NUL);
-       rettv->vval.v_string = (char_u *)ga.ga_data;
-     }
-     else
-       rettv->vval.v_string = NULL;
- }
- 
- /*
-  * "keys()" function
-  */
-     static void
- f_keys(typval_T *argvars, typval_T *rettv)
- {
-     dict_list(argvars, rettv, 0);
- }
- 
- /*
   * "last_buffer_nr()" function.
   */
      static void
--- 7215,7220 ----
***************
*** 7460,7520 ****
  }
  
  /*
-  * "list2str()" function
-  */
-     static void
- f_list2str(typval_T *argvars, typval_T *rettv)
- {
-     list_T    *l;
-     listitem_T        *li;
-     garray_T  ga;
-     int               utf8 = FALSE;
- 
-     rettv->v_type = VAR_STRING;
-     rettv->vval.v_string = NULL;
-     if (argvars[0].v_type != VAR_LIST)
-     {
-       emsg(_(e_invarg));
-       return;
-     }
- 
-     l = argvars[0].vval.v_list;
-     if (l == NULL)
-       return;  // empty list results in empty string
- 
-     if (argvars[1].v_type != VAR_UNKNOWN)
-       utf8 = (int)tv_get_number_chk(&argvars[1], NULL);
- 
-     ga_init2(&ga, 1, 80);
-     if (has_mbyte || utf8)
-     {
-       char_u  buf[MB_MAXBYTES + 1];
-       int     (*char2bytes)(int, char_u *);
- 
-       if (utf8 || enc_utf8)
-           char2bytes = utf_char2bytes;
-       else
-           char2bytes = mb_char2bytes;
- 
-       for (li = l->lv_first; li != NULL; li = li->li_next)
-       {
-           buf[(*char2bytes)(tv_get_number(&li->li_tv), buf)] = NUL;
-           ga_concat(&ga, buf);
-       }
-       ga_append(&ga, NUL);
-     }
-     else if (ga_grow(&ga, list_len(l) + 1) == OK)
-     {
-       for (li = l->lv_first; li != NULL; li = li->li_next)
-           ga_append(&ga, tv_get_number(&li->li_tv));
-       ga_append(&ga, NUL);
-     }
- 
-     rettv->v_type = VAR_STRING;
-     rettv->vval.v_string = ga.ga_data;
- }
- 
- /*
   * "localtime()" function
   */
      static void
--- 7382,7387 ----
***************
*** 9237,9394 ****
      static void
  f_remove(typval_T *argvars, typval_T *rettv)
  {
-     list_T    *l;
-     listitem_T        *item, *item2;
-     listitem_T        *li;
-     long      idx;
-     long      end;
-     char_u    *key;
-     dict_T    *d;
-     dictitem_T        *di;
      char_u    *arg_errmsg = (char_u *)N_("remove() argument");
-     int               error = FALSE;
  
      if (argvars[0].v_type == VAR_DICT)
!     {
!       if (argvars[2].v_type != VAR_UNKNOWN)
!           semsg(_(e_toomanyarg), "remove()");
!       else if ((d = argvars[0].vval.v_dict) != NULL
!               && !var_check_lock(d->dv_lock, arg_errmsg, TRUE))
!       {
!           key = tv_get_string_chk(&argvars[1]);
!           if (key != NULL)
!           {
!               di = dict_find(d, key, -1);
!               if (di == NULL)
!                   semsg(_(e_dictkey), key);
!               else if (!var_check_fixed(di->di_flags, arg_errmsg, TRUE)
!                           && !var_check_ro(di->di_flags, arg_errmsg, TRUE))
!               {
!                   *rettv = di->di_tv;
!                   init_tv(&di->di_tv);
!                   dictitem_remove(d, di);
!               }
!           }
!       }
!     }
      else if (argvars[0].v_type == VAR_BLOB)
!     {
!       idx = (long)tv_get_number_chk(&argvars[1], &error);
!       if (!error)
!       {
!           blob_T  *b = argvars[0].vval.v_blob;
!           int     len = blob_len(b);
!           char_u  *p;
! 
!           if (idx < 0)
!               // count from the end
!               idx = len + idx;
!           if (idx < 0 || idx >= len)
!           {
!               semsg(_(e_blobidx), idx);
!               return;
!           }
!           if (argvars[2].v_type == VAR_UNKNOWN)
!           {
!               // Remove one item, return its value.
!               p = (char_u *)b->bv_ga.ga_data;
!               rettv->vval.v_number = (varnumber_T) *(p + idx);
!               mch_memmove(p + idx, p + idx + 1, (size_t)len - idx - 1);
!               --b->bv_ga.ga_len;
!           }
!           else
!           {
!               blob_T  *blob;
! 
!               // Remove range of items, return list with values.
!               end = (long)tv_get_number_chk(&argvars[2], &error);
!               if (error)
!                   return;
!               if (end < 0)
!                   // count from the end
!                   end = len + end;
!               if (end >= len || idx > end)
!               {
!                   semsg(_(e_blobidx), end);
!                   return;
!               }
!               blob = blob_alloc();
!               if (blob == NULL)
!                   return;
!               blob->bv_ga.ga_len = end - idx + 1;
!               if (ga_grow(&blob->bv_ga, end - idx + 1) == FAIL)
!               {
!                   vim_free(blob);
!                   return;
!               }
!               p = (char_u *)b->bv_ga.ga_data;
!               mch_memmove((char_u *)blob->bv_ga.ga_data, p + idx,
!                                                     (size_t)(end - idx + 1));
!               ++blob->bv_refcount;
!               rettv->v_type = VAR_BLOB;
!               rettv->vval.v_blob = blob;
! 
!               mch_memmove(p + idx, p + end + 1, (size_t)(len - end));
!               b->bv_ga.ga_len -= end - idx + 1;
!           }
!       }
!     }
!     else if (argvars[0].v_type != VAR_LIST)
        semsg(_(e_listdictblobarg), "remove()");
-     else if ((l = argvars[0].vval.v_list) != NULL
-                             && !var_check_lock(l->lv_lock, arg_errmsg, TRUE))
-     {
-       idx = (long)tv_get_number_chk(&argvars[1], &error);
-       if (error)
-           ;           // type error: do nothing, errmsg already given
-       else if ((item = list_find(l, idx)) == NULL)
-           semsg(_(e_listidx), idx);
-       else
-       {
-           if (argvars[2].v_type == VAR_UNKNOWN)
-           {
-               /* Remove one item, return its value. */
-               vimlist_remove(l, item, item);
-               *rettv = item->li_tv;
-               vim_free(item);
-           }
-           else
-           {
-               // Remove range of items, return list with values.
-               end = (long)tv_get_number_chk(&argvars[2], &error);
-               if (error)
-                   ;           // type error: do nothing
-               else if ((item2 = list_find(l, end)) == NULL)
-                   semsg(_(e_listidx), end);
-               else
-               {
-                   int     cnt = 0;
- 
-                   for (li = item; li != NULL; li = li->li_next)
-                   {
-                       ++cnt;
-                       if (li == item2)
-                           break;
-                   }
-                   if (li == NULL)  /* didn't find "item2" after "item" */
-                       emsg(_(e_invrange));
-                   else
-                   {
-                       vimlist_remove(l, item, item2);
-                       if (rettv_list_alloc(rettv) == OK)
-                       {
-                           l = rettv->vval.v_list;
-                           l->lv_first = item;
-                           l->lv_last = item2;
-                           item->li_prev = NULL;
-                           item2->li_next = NULL;
-                           l->lv_len = cnt;
-                       }
-                   }
-               }
-           }
-       }
-     }
  }
  
  /*
--- 9104,9119 ----
      static void
  f_remove(typval_T *argvars, typval_T *rettv)
  {
      char_u    *arg_errmsg = (char_u *)N_("remove() argument");
  
      if (argvars[0].v_type == VAR_DICT)
!       dict_remove(argvars, rettv, arg_errmsg);
      else if (argvars[0].v_type == VAR_BLOB)
!       blob_remove(argvars, rettv);
!     else if (argvars[0].v_type == VAR_LIST)
!       list_remove(argvars, rettv, arg_errmsg);
!     else
        semsg(_(e_listdictblobarg), "remove()");
  }
  
  /*
***************
*** 11100,11487 ****
  }
  #endif
  
- static int item_compare(const void *s1, const void *s2);
- static int item_compare2(const void *s1, const void *s2);
- 
- /* struct used in the array that's given to qsort() */
- typedef struct
- {
-     listitem_T        *item;
-     int               idx;
- } sortItem_T;
- 
- /* struct storing information about current sort */
- typedef struct
- {
-     int               item_compare_ic;
-     int               item_compare_numeric;
-     int               item_compare_numbers;
- #ifdef FEAT_FLOAT
-     int               item_compare_float;
- #endif
-     char_u    *item_compare_func;
-     partial_T *item_compare_partial;
-     dict_T    *item_compare_selfdict;
-     int               item_compare_func_err;
-     int               item_compare_keep_zero;
- } sortinfo_T;
- static sortinfo_T     *sortinfo = NULL;
- #define ITEM_COMPARE_FAIL 999
- 
- /*
-  * Compare functions for f_sort() and f_uniq() below.
-  */
-     static int
- item_compare(const void *s1, const void *s2)
- {
-     sortItem_T  *si1, *si2;
-     typval_T  *tv1, *tv2;
-     char_u    *p1, *p2;
-     char_u    *tofree1 = NULL, *tofree2 = NULL;
-     int               res;
-     char_u    numbuf1[NUMBUFLEN];
-     char_u    numbuf2[NUMBUFLEN];
- 
-     si1 = (sortItem_T *)s1;
-     si2 = (sortItem_T *)s2;
-     tv1 = &si1->item->li_tv;
-     tv2 = &si2->item->li_tv;
- 
-     if (sortinfo->item_compare_numbers)
-     {
-       varnumber_T     v1 = tv_get_number(tv1);
-       varnumber_T     v2 = tv_get_number(tv2);
- 
-       return v1 == v2 ? 0 : v1 > v2 ? 1 : -1;
-     }
- 
- #ifdef FEAT_FLOAT
-     if (sortinfo->item_compare_float)
-     {
-       float_T v1 = tv_get_float(tv1);
-       float_T v2 = tv_get_float(tv2);
- 
-       return v1 == v2 ? 0 : v1 > v2 ? 1 : -1;
-     }
- #endif
- 
-     /* tv2string() puts quotes around a string and allocates memory.  Don't do
-      * that for string variables. Use a single quote when comparing with a
-      * non-string to do what the docs promise. */
-     if (tv1->v_type == VAR_STRING)
-     {
-       if (tv2->v_type != VAR_STRING || sortinfo->item_compare_numeric)
-           p1 = (char_u *)"'";
-       else
-           p1 = tv1->vval.v_string;
-     }
-     else
-       p1 = tv2string(tv1, &tofree1, numbuf1, 0);
-     if (tv2->v_type == VAR_STRING)
-     {
-       if (tv1->v_type != VAR_STRING || sortinfo->item_compare_numeric)
-           p2 = (char_u *)"'";
-       else
-           p2 = tv2->vval.v_string;
-     }
-     else
-       p2 = tv2string(tv2, &tofree2, numbuf2, 0);
-     if (p1 == NULL)
-       p1 = (char_u *)"";
-     if (p2 == NULL)
-       p2 = (char_u *)"";
-     if (!sortinfo->item_compare_numeric)
-     {
-       if (sortinfo->item_compare_ic)
-           res = STRICMP(p1, p2);
-       else
-           res = STRCMP(p1, p2);
-     }
-     else
-     {
-       double n1, n2;
-       n1 = strtod((char *)p1, (char **)&p1);
-       n2 = strtod((char *)p2, (char **)&p2);
-       res = n1 == n2 ? 0 : n1 > n2 ? 1 : -1;
-     }
- 
-     /* When the result would be zero, compare the item indexes.  Makes the
-      * sort stable. */
-     if (res == 0 && !sortinfo->item_compare_keep_zero)
-       res = si1->idx > si2->idx ? 1 : -1;
- 
-     vim_free(tofree1);
-     vim_free(tofree2);
-     return res;
- }
- 
-     static int
- item_compare2(const void *s1, const void *s2)
- {
-     sortItem_T  *si1, *si2;
-     int               res;
-     typval_T  rettv;
-     typval_T  argv[3];
-     int               dummy;
-     char_u    *func_name;
-     partial_T *partial = sortinfo->item_compare_partial;
- 
-     /* shortcut after failure in previous call; compare all items equal */
-     if (sortinfo->item_compare_func_err)
-       return 0;
- 
-     si1 = (sortItem_T *)s1;
-     si2 = (sortItem_T *)s2;
- 
-     if (partial == NULL)
-       func_name = sortinfo->item_compare_func;
-     else
-       func_name = partial_name(partial);
- 
-     /* Copy the values.  This is needed to be able to set v_lock to VAR_FIXED
-      * in the copy without changing the original list items. */
-     copy_tv(&si1->item->li_tv, &argv[0]);
-     copy_tv(&si2->item->li_tv, &argv[1]);
- 
-     rettv.v_type = VAR_UNKNOWN;               /* clear_tv() uses this */
-     res = call_func(func_name, -1, &rettv, 2, argv, NULL, 0L, 0L, &dummy, 
TRUE,
-                                partial, sortinfo->item_compare_selfdict);
-     clear_tv(&argv[0]);
-     clear_tv(&argv[1]);
- 
-     if (res == FAIL)
-       res = ITEM_COMPARE_FAIL;
-     else
-       res = (int)tv_get_number_chk(&rettv, &sortinfo->item_compare_func_err);
-     if (sortinfo->item_compare_func_err)
-       res = ITEM_COMPARE_FAIL;  /* return value has wrong type */
-     clear_tv(&rettv);
- 
-     /* When the result would be zero, compare the pointers themselves.  Makes
-      * the sort stable. */
-     if (res == 0 && !sortinfo->item_compare_keep_zero)
-       res = si1->idx > si2->idx ? 1 : -1;
- 
-     return res;
- }
- 
- /*
-  * "sort({list})" function
-  */
-     static void
- do_sort_uniq(typval_T *argvars, typval_T *rettv, int sort)
- {
-     list_T    *l;
-     listitem_T        *li;
-     sortItem_T        *ptrs;
-     sortinfo_T        *old_sortinfo;
-     sortinfo_T        info;
-     long      len;
-     long      i;
- 
-     /* Pointer to current info struct used in compare function. Save and
-      * restore the current one for nested calls. */
-     old_sortinfo = sortinfo;
-     sortinfo = &info;
- 
-     if (argvars[0].v_type != VAR_LIST)
-       semsg(_(e_listarg), sort ? "sort()" : "uniq()");
-     else
-     {
-       l = argvars[0].vval.v_list;
-       if (l == NULL || var_check_lock(l->lv_lock,
-            (char_u *)(sort ? N_("sort() argument") : N_("uniq() argument")),
-                                                                       TRUE))
-           goto theend;
-       rettv_list_set(rettv, l);
- 
-       len = list_len(l);
-       if (len <= 1)
-           goto theend;        /* short list sorts pretty quickly */
- 
-       info.item_compare_ic = FALSE;
-       info.item_compare_numeric = FALSE;
-       info.item_compare_numbers = FALSE;
- #ifdef FEAT_FLOAT
-       info.item_compare_float = FALSE;
- #endif
-       info.item_compare_func = NULL;
-       info.item_compare_partial = NULL;
-       info.item_compare_selfdict = NULL;
-       if (argvars[1].v_type != VAR_UNKNOWN)
-       {
-           /* optional second argument: {func} */
-           if (argvars[1].v_type == VAR_FUNC)
-               info.item_compare_func = argvars[1].vval.v_string;
-           else if (argvars[1].v_type == VAR_PARTIAL)
-               info.item_compare_partial = argvars[1].vval.v_partial;
-           else
-           {
-               int         error = FALSE;
- 
-               i = (long)tv_get_number_chk(&argvars[1], &error);
-               if (error)
-                   goto theend;        /* type error; errmsg already given */
-               if (i == 1)
-                   info.item_compare_ic = TRUE;
-               else if (argvars[1].v_type != VAR_NUMBER)
-                   info.item_compare_func = tv_get_string(&argvars[1]);
-               else if (i != 0)
-               {
-                   emsg(_(e_invarg));
-                   goto theend;
-               }
-               if (info.item_compare_func != NULL)
-               {
-                   if (*info.item_compare_func == NUL)
-                   {
-                       /* empty string means default sort */
-                       info.item_compare_func = NULL;
-                   }
-                   else if (STRCMP(info.item_compare_func, "n") == 0)
-                   {
-                       info.item_compare_func = NULL;
-                       info.item_compare_numeric = TRUE;
-                   }
-                   else if (STRCMP(info.item_compare_func, "N") == 0)
-                   {
-                       info.item_compare_func = NULL;
-                       info.item_compare_numbers = TRUE;
-                   }
- #ifdef FEAT_FLOAT
-                   else if (STRCMP(info.item_compare_func, "f") == 0)
-                   {
-                       info.item_compare_func = NULL;
-                       info.item_compare_float = TRUE;
-                   }
- #endif
-                   else if (STRCMP(info.item_compare_func, "i") == 0)
-                   {
-                       info.item_compare_func = NULL;
-                       info.item_compare_ic = TRUE;
-                   }
-               }
-           }
- 
-           if (argvars[2].v_type != VAR_UNKNOWN)
-           {
-               /* optional third argument: {dict} */
-               if (argvars[2].v_type != VAR_DICT)
-               {
-                   emsg(_(e_dictreq));
-                   goto theend;
-               }
-               info.item_compare_selfdict = argvars[2].vval.v_dict;
-           }
-       }
- 
-       /* Make an array with each entry pointing to an item in the List. */
-       ptrs = ALLOC_MULT(sortItem_T, len);
-       if (ptrs == NULL)
-           goto theend;
- 
-       i = 0;
-       if (sort)
-       {
-           /* sort(): ptrs will be the list to sort */
-           for (li = l->lv_first; li != NULL; li = li->li_next)
-           {
-               ptrs[i].item = li;
-               ptrs[i].idx = i;
-               ++i;
-           }
- 
-           info.item_compare_func_err = FALSE;
-           info.item_compare_keep_zero = FALSE;
-           /* test the compare function */
-           if ((info.item_compare_func != NULL
-                                        || info.item_compare_partial != NULL)
-                   && item_compare2((void *)&ptrs[0], (void *)&ptrs[1])
-                                                        == ITEM_COMPARE_FAIL)
-               emsg(_("E702: Sort compare function failed"));
-           else
-           {
-               /* Sort the array with item pointers. */
-               qsort((void *)ptrs, (size_t)len, sizeof(sortItem_T),
-                   info.item_compare_func == NULL
-                                         && info.item_compare_partial == NULL
-                                              ? item_compare : item_compare2);
- 
-               if (!info.item_compare_func_err)
-               {
-                   /* Clear the List and append the items in sorted order. */
-                   l->lv_first = l->lv_last = l->lv_idx_item = NULL;
-                   l->lv_len = 0;
-                   for (i = 0; i < len; ++i)
-                       list_append(l, ptrs[i].item);
-               }
-           }
-       }
-       else
-       {
-           int (*item_compare_func_ptr)(const void *, const void *);
- 
-           /* f_uniq(): ptrs will be a stack of items to remove */
-           info.item_compare_func_err = FALSE;
-           info.item_compare_keep_zero = TRUE;
-           item_compare_func_ptr = info.item_compare_func != NULL
-                                         || info.item_compare_partial != NULL
-                                              ? item_compare2 : item_compare;
- 
-           for (li = l->lv_first; li != NULL && li->li_next != NULL;
-                                                            li = li->li_next)
-           {
-               if (item_compare_func_ptr((void *)&li, (void *)&li->li_next)
-                                                                        == 0)
-                   ptrs[i++].item = li;
-               if (info.item_compare_func_err)
-               {
-                   emsg(_("E882: Uniq compare function failed"));
-                   break;
-               }
-           }
- 
-           if (!info.item_compare_func_err)
-           {
-               while (--i >= 0)
-               {
-                   li = ptrs[i].item->li_next;
-                   ptrs[i].item->li_next = li->li_next;
-                   if (li->li_next != NULL)
-                       li->li_next->li_prev = ptrs[i].item;
-                   else
-                       l->lv_last = ptrs[i].item;
-                   list_fix_watch(l, li);
-                   listitem_free(li);
-                   l->lv_len--;
-               }
-           }
-       }
- 
-       vim_free(ptrs);
-     }
- theend:
-     sortinfo = old_sortinfo;
- }
- 
- /*
-  * "sort({list})" function
-  */
-     static void
- f_sort(typval_T *argvars, typval_T *rettv)
- {
-     do_sort_uniq(argvars, rettv, TRUE);
- }
- 
- /*
-  * "uniq({list})" function
-  */
-     static void
- f_uniq(typval_T *argvars, typval_T *rettv)
- {
-     do_sort_uniq(argvars, rettv, FALSE);
- }
- 
  /*
   * "soundfold({word})" function
   */
--- 10825,10830 ----
***************
*** 13482,13496 ****
  }
  
  /*
-  * "values(dict)" function
-  */
-     static void
- f_values(typval_T *argvars, typval_T *rettv)
- {
-     dict_list(argvars, rettv, 1);
- }
- 
- /*
   * "virtcol(string)" function
   */
      static void
--- 12825,12830 ----
*** ../vim-8.1.1762/src/dict.c  2019-07-13 22:46:05.241628584 +0200
--- src/dict.c  2019-07-27 22:50:23.167117701 +0200
***************
*** 709,715 ****
  }
  
  /*
!  * Get the key for *{key: val} into "tv" and advance "arg".
   * Return FAIL when there is no valid key.
   */
      static int
--- 709,715 ----
  }
  
  /*
!  * Get the key for #{key: val} into "tv" and advance "arg".
   * Return FAIL when there is no valid key.
   */
      static int
***************
*** 731,737 ****
  
  /*
   * Allocate a variable for a Dictionary and fill it from "*arg".
!  * "literal" is TRUE for *{key: val}
   * Return OK or FAIL.  Returns NOTDONE for {expr}.
   */
      int
--- 731,737 ----
  
  /*
   * Allocate a variable for a Dictionary and fill it from "*arg".
!  * "literal" is TRUE for #{key: val}
   * Return OK or FAIL.  Returns NOTDONE for {expr}.
   */
      int
***************
*** 1034,1039 ****
--- 1034,1066 ----
  }
  
  /*
+  * "items(dict)" function
+  */
+     void
+ f_items(typval_T *argvars, typval_T *rettv)
+ {
+     dict_list(argvars, rettv, 2);
+ }
+ 
+ /*
+  * "keys()" function
+  */
+     void
+ f_keys(typval_T *argvars, typval_T *rettv)
+ {
+     dict_list(argvars, rettv, 0);
+ }
+ 
+ /*
+  * "values(dict)" function
+  */
+     void
+ f_values(typval_T *argvars, typval_T *rettv)
+ {
+     dict_list(argvars, rettv, 1);
+ }
+ 
+ /*
   * Make each item in the dict readonly (not the value of the item).
   */
      void
***************
*** 1052,1055 ****
--- 1079,1132 ----
      }
  }
  
+ /*
+  * "has_key()" function
+  */
+     void
+ f_has_key(typval_T *argvars, typval_T *rettv)
+ {
+     if (argvars[0].v_type != VAR_DICT)
+     {
+       emsg(_(e_dictreq));
+       return;
+     }
+     if (argvars[0].vval.v_dict == NULL)
+       return;
+ 
+     rettv->vval.v_number = dict_find(argvars[0].vval.v_dict,
+                                     tv_get_string(&argvars[1]), -1) != NULL;
+ }
+ 
+ /*
+  * "remove({dict})" function
+  */
+     void
+ dict_remove(typval_T *argvars, typval_T *rettv, char_u *arg_errmsg)
+ {
+     dict_T    *d;
+     char_u    *key;
+     dictitem_T        *di;
+ 
+     if (argvars[2].v_type != VAR_UNKNOWN)
+       semsg(_(e_toomanyarg), "remove()");
+     else if ((d = argvars[0].vval.v_dict) != NULL
+           && !var_check_lock(d->dv_lock, arg_errmsg, TRUE))
+     {
+       key = tv_get_string_chk(&argvars[1]);
+       if (key != NULL)
+       {
+           di = dict_find(d, key, -1);
+           if (di == NULL)
+               semsg(_(e_dictkey), key);
+           else if (!var_check_fixed(di->di_flags, arg_errmsg, TRUE)
+                       && !var_check_ro(di->di_flags, arg_errmsg, TRUE))
+           {
+               *rettv = di->di_tv;
+               init_tv(&di->di_tv);
+               dictitem_remove(d, di);
+           }
+       }
+     }
+ }
+ 
  #endif /* defined(FEAT_EVAL) */
*** ../vim-8.1.1762/src/proto/dict.pro  2019-07-13 22:46:05.241628584 +0200
--- src/proto/dict.pro  2019-07-27 22:50:26.515101568 +0200
***************
*** 33,37 ****
--- 33,42 ----
  dictitem_T *dict_lookup(hashitem_T *hi);
  int dict_equal(dict_T *d1, dict_T *d2, int ic, int recursive);
  void dict_list(typval_T *argvars, typval_T *rettv, int what);
+ void f_items(typval_T *argvars, typval_T *rettv);
+ void f_keys(typval_T *argvars, typval_T *rettv);
+ void f_values(typval_T *argvars, typval_T *rettv);
  void dict_set_items_ro(dict_T *di);
+ void f_has_key(typval_T *argvars, typval_T *rettv);
+ void dict_remove(typval_T *argvars, typval_T *rettv, char_u *arg_errmsg);
  /* vim: set ft=c : */
*** ../vim-8.1.1762/src/list.c  2019-07-04 17:35:01.115169990 +0200
--- src/list.c  2019-07-27 23:00:08.020267209 +0200
***************
*** 875,880 ****
--- 875,914 ----
  }
  
  /*
+  * "join()" function
+  */
+     void
+ f_join(typval_T *argvars, typval_T *rettv)
+ {
+     garray_T  ga;
+     char_u    *sep;
+ 
+     if (argvars[0].v_type != VAR_LIST)
+     {
+       emsg(_(e_listreq));
+       return;
+     }
+     if (argvars[0].vval.v_list == NULL)
+       return;
+     if (argvars[1].v_type == VAR_UNKNOWN)
+       sep = (char_u *)" ";
+     else
+       sep = tv_get_string_chk(&argvars[1]);
+ 
+     rettv->v_type = VAR_STRING;
+ 
+     if (sep != NULL)
+     {
+       ga_init2(&ga, (int)sizeof(char), 80);
+       list_join(&ga, argvars[0].vval.v_list, sep, TRUE, FALSE, 0);
+       ga_append(&ga, NUL);
+       rettv->vval.v_string = (char_u *)ga.ga_data;
+     }
+     else
+       rettv->vval.v_string = NULL;
+ }
+ 
+ /*
   * Allocate a variable for a List and fill it from "*arg".
   * Return OK or FAIL.
   */
***************
*** 1007,1010 ****
--- 1041,1547 ----
      }
  }
  
+ /*
+  * "list2str()" function
+  */
+     void
+ f_list2str(typval_T *argvars, typval_T *rettv)
+ {
+     list_T    *l;
+     listitem_T        *li;
+     garray_T  ga;
+     int               utf8 = FALSE;
+ 
+     rettv->v_type = VAR_STRING;
+     rettv->vval.v_string = NULL;
+     if (argvars[0].v_type != VAR_LIST)
+     {
+       emsg(_(e_invarg));
+       return;
+     }
+ 
+     l = argvars[0].vval.v_list;
+     if (l == NULL)
+       return;  // empty list results in empty string
+ 
+     if (argvars[1].v_type != VAR_UNKNOWN)
+       utf8 = (int)tv_get_number_chk(&argvars[1], NULL);
+ 
+     ga_init2(&ga, 1, 80);
+     if (has_mbyte || utf8)
+     {
+       char_u  buf[MB_MAXBYTES + 1];
+       int     (*char2bytes)(int, char_u *);
+ 
+       if (utf8 || enc_utf8)
+           char2bytes = utf_char2bytes;
+       else
+           char2bytes = mb_char2bytes;
+ 
+       for (li = l->lv_first; li != NULL; li = li->li_next)
+       {
+           buf[(*char2bytes)(tv_get_number(&li->li_tv), buf)] = NUL;
+           ga_concat(&ga, buf);
+       }
+       ga_append(&ga, NUL);
+     }
+     else if (ga_grow(&ga, list_len(l) + 1) == OK)
+     {
+       for (li = l->lv_first; li != NULL; li = li->li_next)
+           ga_append(&ga, tv_get_number(&li->li_tv));
+       ga_append(&ga, NUL);
+     }
+ 
+     rettv->v_type = VAR_STRING;
+     rettv->vval.v_string = ga.ga_data;
+ }
+ 
+     void
+ list_remove(typval_T *argvars, typval_T *rettv, char_u *arg_errmsg)
+ {
+     list_T    *l;
+     listitem_T        *item, *item2;
+     listitem_T        *li;
+     int               error = FALSE;
+     int               idx;
+ 
+     if ((l = argvars[0].vval.v_list) == NULL
+                             || var_check_lock(l->lv_lock, arg_errmsg, TRUE))
+       return;
+ 
+     idx = (long)tv_get_number_chk(&argvars[1], &error);
+     if (error)
+       ;               // type error: do nothing, errmsg already given
+     else if ((item = list_find(l, idx)) == NULL)
+       semsg(_(e_listidx), idx);
+     else
+     {
+       if (argvars[2].v_type == VAR_UNKNOWN)
+       {
+           /* Remove one item, return its value. */
+           vimlist_remove(l, item, item);
+           *rettv = item->li_tv;
+           vim_free(item);
+       }
+       else
+       {
+           // Remove range of items, return list with values.
+           int end = (long)tv_get_number_chk(&argvars[2], &error);
+ 
+           if (error)
+               ;               // type error: do nothing
+           else if ((item2 = list_find(l, end)) == NULL)
+               semsg(_(e_listidx), end);
+           else
+           {
+               int         cnt = 0;
+ 
+               for (li = item; li != NULL; li = li->li_next)
+               {
+                   ++cnt;
+                   if (li == item2)
+                       break;
+               }
+               if (li == NULL)  /* didn't find "item2" after "item" */
+                   emsg(_(e_invrange));
+               else
+               {
+                   vimlist_remove(l, item, item2);
+                   if (rettv_list_alloc(rettv) == OK)
+                   {
+                       l = rettv->vval.v_list;
+                       l->lv_first = item;
+                       l->lv_last = item2;
+                       item->li_prev = NULL;
+                       item2->li_next = NULL;
+                       l->lv_len = cnt;
+                   }
+               }
+           }
+       }
+     }
+ }
+ 
+ static int item_compare(const void *s1, const void *s2);
+ static int item_compare2(const void *s1, const void *s2);
+ 
+ /* struct used in the array that's given to qsort() */
+ typedef struct
+ {
+     listitem_T        *item;
+     int               idx;
+ } sortItem_T;
+ 
+ /* struct storing information about current sort */
+ typedef struct
+ {
+     int               item_compare_ic;
+     int               item_compare_numeric;
+     int               item_compare_numbers;
+ #ifdef FEAT_FLOAT
+     int               item_compare_float;
+ #endif
+     char_u    *item_compare_func;
+     partial_T *item_compare_partial;
+     dict_T    *item_compare_selfdict;
+     int               item_compare_func_err;
+     int               item_compare_keep_zero;
+ } sortinfo_T;
+ static sortinfo_T     *sortinfo = NULL;
+ #define ITEM_COMPARE_FAIL 999
+ 
+ /*
+  * Compare functions for f_sort() and f_uniq() below.
+  */
+     static int
+ item_compare(const void *s1, const void *s2)
+ {
+     sortItem_T  *si1, *si2;
+     typval_T  *tv1, *tv2;
+     char_u    *p1, *p2;
+     char_u    *tofree1 = NULL, *tofree2 = NULL;
+     int               res;
+     char_u    numbuf1[NUMBUFLEN];
+     char_u    numbuf2[NUMBUFLEN];
+ 
+     si1 = (sortItem_T *)s1;
+     si2 = (sortItem_T *)s2;
+     tv1 = &si1->item->li_tv;
+     tv2 = &si2->item->li_tv;
+ 
+     if (sortinfo->item_compare_numbers)
+     {
+       varnumber_T     v1 = tv_get_number(tv1);
+       varnumber_T     v2 = tv_get_number(tv2);
+ 
+       return v1 == v2 ? 0 : v1 > v2 ? 1 : -1;
+     }
+ 
+ #ifdef FEAT_FLOAT
+     if (sortinfo->item_compare_float)
+     {
+       float_T v1 = tv_get_float(tv1);
+       float_T v2 = tv_get_float(tv2);
+ 
+       return v1 == v2 ? 0 : v1 > v2 ? 1 : -1;
+     }
+ #endif
+ 
+     /* tv2string() puts quotes around a string and allocates memory.  Don't do
+      * that for string variables. Use a single quote when comparing with a
+      * non-string to do what the docs promise. */
+     if (tv1->v_type == VAR_STRING)
+     {
+       if (tv2->v_type != VAR_STRING || sortinfo->item_compare_numeric)
+           p1 = (char_u *)"'";
+       else
+           p1 = tv1->vval.v_string;
+     }
+     else
+       p1 = tv2string(tv1, &tofree1, numbuf1, 0);
+     if (tv2->v_type == VAR_STRING)
+     {
+       if (tv1->v_type != VAR_STRING || sortinfo->item_compare_numeric)
+           p2 = (char_u *)"'";
+       else
+           p2 = tv2->vval.v_string;
+     }
+     else
+       p2 = tv2string(tv2, &tofree2, numbuf2, 0);
+     if (p1 == NULL)
+       p1 = (char_u *)"";
+     if (p2 == NULL)
+       p2 = (char_u *)"";
+     if (!sortinfo->item_compare_numeric)
+     {
+       if (sortinfo->item_compare_ic)
+           res = STRICMP(p1, p2);
+       else
+           res = STRCMP(p1, p2);
+     }
+     else
+     {
+       double n1, n2;
+       n1 = strtod((char *)p1, (char **)&p1);
+       n2 = strtod((char *)p2, (char **)&p2);
+       res = n1 == n2 ? 0 : n1 > n2 ? 1 : -1;
+     }
+ 
+     /* When the result would be zero, compare the item indexes.  Makes the
+      * sort stable. */
+     if (res == 0 && !sortinfo->item_compare_keep_zero)
+       res = si1->idx > si2->idx ? 1 : -1;
+ 
+     vim_free(tofree1);
+     vim_free(tofree2);
+     return res;
+ }
+ 
+     static int
+ item_compare2(const void *s1, const void *s2)
+ {
+     sortItem_T  *si1, *si2;
+     int               res;
+     typval_T  rettv;
+     typval_T  argv[3];
+     int               dummy;
+     char_u    *func_name;
+     partial_T *partial = sortinfo->item_compare_partial;
+ 
+     /* shortcut after failure in previous call; compare all items equal */
+     if (sortinfo->item_compare_func_err)
+       return 0;
+ 
+     si1 = (sortItem_T *)s1;
+     si2 = (sortItem_T *)s2;
+ 
+     if (partial == NULL)
+       func_name = sortinfo->item_compare_func;
+     else
+       func_name = partial_name(partial);
+ 
+     /* Copy the values.  This is needed to be able to set v_lock to VAR_FIXED
+      * in the copy without changing the original list items. */
+     copy_tv(&si1->item->li_tv, &argv[0]);
+     copy_tv(&si2->item->li_tv, &argv[1]);
+ 
+     rettv.v_type = VAR_UNKNOWN;               /* clear_tv() uses this */
+     res = call_func(func_name, -1, &rettv, 2, argv, NULL, 0L, 0L, &dummy, 
TRUE,
+                                partial, sortinfo->item_compare_selfdict);
+     clear_tv(&argv[0]);
+     clear_tv(&argv[1]);
+ 
+     if (res == FAIL)
+       res = ITEM_COMPARE_FAIL;
+     else
+       res = (int)tv_get_number_chk(&rettv, &sortinfo->item_compare_func_err);
+     if (sortinfo->item_compare_func_err)
+       res = ITEM_COMPARE_FAIL;  /* return value has wrong type */
+     clear_tv(&rettv);
+ 
+     /* When the result would be zero, compare the pointers themselves.  Makes
+      * the sort stable. */
+     if (res == 0 && !sortinfo->item_compare_keep_zero)
+       res = si1->idx > si2->idx ? 1 : -1;
+ 
+     return res;
+ }
+ 
+ /*
+  * "sort()" or "uniq()" function
+  */
+     static void
+ do_sort_uniq(typval_T *argvars, typval_T *rettv, int sort)
+ {
+     list_T    *l;
+     listitem_T        *li;
+     sortItem_T        *ptrs;
+     sortinfo_T        *old_sortinfo;
+     sortinfo_T        info;
+     long      len;
+     long      i;
+ 
+     /* Pointer to current info struct used in compare function. Save and
+      * restore the current one for nested calls. */
+     old_sortinfo = sortinfo;
+     sortinfo = &info;
+ 
+     if (argvars[0].v_type != VAR_LIST)
+       semsg(_(e_listarg), sort ? "sort()" : "uniq()");
+     else
+     {
+       l = argvars[0].vval.v_list;
+       if (l == NULL || var_check_lock(l->lv_lock,
+            (char_u *)(sort ? N_("sort() argument") : N_("uniq() argument")),
+                                                                       TRUE))
+           goto theend;
+       rettv_list_set(rettv, l);
+ 
+       len = list_len(l);
+       if (len <= 1)
+           goto theend;        /* short list sorts pretty quickly */
+ 
+       info.item_compare_ic = FALSE;
+       info.item_compare_numeric = FALSE;
+       info.item_compare_numbers = FALSE;
+ #ifdef FEAT_FLOAT
+       info.item_compare_float = FALSE;
+ #endif
+       info.item_compare_func = NULL;
+       info.item_compare_partial = NULL;
+       info.item_compare_selfdict = NULL;
+       if (argvars[1].v_type != VAR_UNKNOWN)
+       {
+           /* optional second argument: {func} */
+           if (argvars[1].v_type == VAR_FUNC)
+               info.item_compare_func = argvars[1].vval.v_string;
+           else if (argvars[1].v_type == VAR_PARTIAL)
+               info.item_compare_partial = argvars[1].vval.v_partial;
+           else
+           {
+               int         error = FALSE;
+ 
+               i = (long)tv_get_number_chk(&argvars[1], &error);
+               if (error)
+                   goto theend;        /* type error; errmsg already given */
+               if (i == 1)
+                   info.item_compare_ic = TRUE;
+               else if (argvars[1].v_type != VAR_NUMBER)
+                   info.item_compare_func = tv_get_string(&argvars[1]);
+               else if (i != 0)
+               {
+                   emsg(_(e_invarg));
+                   goto theend;
+               }
+               if (info.item_compare_func != NULL)
+               {
+                   if (*info.item_compare_func == NUL)
+                   {
+                       /* empty string means default sort */
+                       info.item_compare_func = NULL;
+                   }
+                   else if (STRCMP(info.item_compare_func, "n") == 0)
+                   {
+                       info.item_compare_func = NULL;
+                       info.item_compare_numeric = TRUE;
+                   }
+                   else if (STRCMP(info.item_compare_func, "N") == 0)
+                   {
+                       info.item_compare_func = NULL;
+                       info.item_compare_numbers = TRUE;
+                   }
+ #ifdef FEAT_FLOAT
+                   else if (STRCMP(info.item_compare_func, "f") == 0)
+                   {
+                       info.item_compare_func = NULL;
+                       info.item_compare_float = TRUE;
+                   }
+ #endif
+                   else if (STRCMP(info.item_compare_func, "i") == 0)
+                   {
+                       info.item_compare_func = NULL;
+                       info.item_compare_ic = TRUE;
+                   }
+               }
+           }
+ 
+           if (argvars[2].v_type != VAR_UNKNOWN)
+           {
+               /* optional third argument: {dict} */
+               if (argvars[2].v_type != VAR_DICT)
+               {
+                   emsg(_(e_dictreq));
+                   goto theend;
+               }
+               info.item_compare_selfdict = argvars[2].vval.v_dict;
+           }
+       }
+ 
+       /* Make an array with each entry pointing to an item in the List. */
+       ptrs = ALLOC_MULT(sortItem_T, len);
+       if (ptrs == NULL)
+           goto theend;
+ 
+       i = 0;
+       if (sort)
+       {
+           /* sort(): ptrs will be the list to sort */
+           for (li = l->lv_first; li != NULL; li = li->li_next)
+           {
+               ptrs[i].item = li;
+               ptrs[i].idx = i;
+               ++i;
+           }
+ 
+           info.item_compare_func_err = FALSE;
+           info.item_compare_keep_zero = FALSE;
+           /* test the compare function */
+           if ((info.item_compare_func != NULL
+                                        || info.item_compare_partial != NULL)
+                   && item_compare2((void *)&ptrs[0], (void *)&ptrs[1])
+                                                        == ITEM_COMPARE_FAIL)
+               emsg(_("E702: Sort compare function failed"));
+           else
+           {
+               /* Sort the array with item pointers. */
+               qsort((void *)ptrs, (size_t)len, sizeof(sortItem_T),
+                   info.item_compare_func == NULL
+                                         && info.item_compare_partial == NULL
+                                              ? item_compare : item_compare2);
+ 
+               if (!info.item_compare_func_err)
+               {
+                   /* Clear the List and append the items in sorted order. */
+                   l->lv_first = l->lv_last = l->lv_idx_item = NULL;
+                   l->lv_len = 0;
+                   for (i = 0; i < len; ++i)
+                       list_append(l, ptrs[i].item);
+               }
+           }
+       }
+       else
+       {
+           int (*item_compare_func_ptr)(const void *, const void *);
+ 
+           /* f_uniq(): ptrs will be a stack of items to remove */
+           info.item_compare_func_err = FALSE;
+           info.item_compare_keep_zero = TRUE;
+           item_compare_func_ptr = info.item_compare_func != NULL
+                                         || info.item_compare_partial != NULL
+                                              ? item_compare2 : item_compare;
+ 
+           for (li = l->lv_first; li != NULL && li->li_next != NULL;
+                                                            li = li->li_next)
+           {
+               if (item_compare_func_ptr((void *)&li, (void *)&li->li_next)
+                                                                        == 0)
+                   ptrs[i++].item = li;
+               if (info.item_compare_func_err)
+               {
+                   emsg(_("E882: Uniq compare function failed"));
+                   break;
+               }
+           }
+ 
+           if (!info.item_compare_func_err)
+           {
+               while (--i >= 0)
+               {
+                   li = ptrs[i].item->li_next;
+                   ptrs[i].item->li_next = li->li_next;
+                   if (li->li_next != NULL)
+                       li->li_next->li_prev = ptrs[i].item;
+                   else
+                       l->lv_last = ptrs[i].item;
+                   list_fix_watch(l, li);
+                   listitem_free(li);
+                   l->lv_len--;
+               }
+           }
+       }
+ 
+       vim_free(ptrs);
+     }
+ theend:
+     sortinfo = old_sortinfo;
+ }
+ 
+ /*
+  * "sort({list})" function
+  */
+     void
+ f_sort(typval_T *argvars, typval_T *rettv)
+ {
+     do_sort_uniq(argvars, rettv, TRUE);
+ }
+ 
+ /*
+  * "uniq({list})" function
+  */
+     void
+ f_uniq(typval_T *argvars, typval_T *rettv)
+ {
+     do_sort_uniq(argvars, rettv, FALSE);
+ }
+ 
  #endif /* defined(FEAT_EVAL) */
*** ../vim-8.1.1762/src/proto/list.pro  2018-12-21 15:16:57.479579788 +0100
--- src/proto/list.pro  2019-07-27 22:59:13.660534103 +0200
***************
*** 34,40 ****
--- 34,45 ----
  void vimlist_remove(list_T *l, listitem_T *item, listitem_T *item2);
  char_u *list2string(typval_T *tv, int copyID, int restore_copyID);
  int list_join(garray_T *gap, list_T *l, char_u *sep, int echo_style, int 
restore_copyID, int copyID);
+ void f_join(typval_T *argvars, typval_T *rettv);
  int get_list_tv(char_u **arg, typval_T *rettv, int evaluate);
  int write_list(FILE *fd, list_T *list, int binary);
  void init_static_list(staticList10_T *sl);
+ void f_list2str(typval_T *argvars, typval_T *rettv);
+ void list_remove(typval_T *argvars, typval_T *rettv, char_u *arg_errmsg);
+ void f_sort(typval_T *argvars, typval_T *rettv);
+ void f_uniq(typval_T *argvars, typval_T *rettv);
  /* vim: set ft=c : */
*** ../vim-8.1.1762/src/blob.c  2019-07-12 20:16:57.075879743 +0200
--- src/blob.c  2019-07-27 22:53:38.198173522 +0200
***************
*** 258,261 ****
--- 258,331 ----
      return NULL;
  }
  
+ /*
+  * "remove({blob})" function
+  */
+     void
+ blob_remove(typval_T *argvars, typval_T *rettv)
+ {
+     int               error = FALSE;
+     long      idx = (long)tv_get_number_chk(&argvars[1], &error);
+     long      end;
+ 
+     if (!error)
+     {
+       blob_T  *b = argvars[0].vval.v_blob;
+       int     len = blob_len(b);
+       char_u  *p;
+ 
+       if (idx < 0)
+           // count from the end
+           idx = len + idx;
+       if (idx < 0 || idx >= len)
+       {
+           semsg(_(e_blobidx), idx);
+           return;
+       }
+       if (argvars[2].v_type == VAR_UNKNOWN)
+       {
+           // Remove one item, return its value.
+           p = (char_u *)b->bv_ga.ga_data;
+           rettv->vval.v_number = (varnumber_T) *(p + idx);
+           mch_memmove(p + idx, p + idx + 1, (size_t)len - idx - 1);
+           --b->bv_ga.ga_len;
+       }
+       else
+       {
+           blob_T  *blob;
+ 
+           // Remove range of items, return list with values.
+           end = (long)tv_get_number_chk(&argvars[2], &error);
+           if (error)
+               return;
+           if (end < 0)
+               // count from the end
+               end = len + end;
+           if (end >= len || idx > end)
+           {
+               semsg(_(e_blobidx), end);
+               return;
+           }
+           blob = blob_alloc();
+           if (blob == NULL)
+               return;
+           blob->bv_ga.ga_len = end - idx + 1;
+           if (ga_grow(&blob->bv_ga, end - idx + 1) == FAIL)
+           {
+               vim_free(blob);
+               return;
+           }
+           p = (char_u *)b->bv_ga.ga_data;
+           mch_memmove((char_u *)blob->bv_ga.ga_data, p + idx,
+                                                 (size_t)(end - idx + 1));
+           ++blob->bv_refcount;
+           rettv->v_type = VAR_BLOB;
+           rettv->vval.v_blob = blob;
+ 
+           mch_memmove(p + idx, p + end + 1, (size_t)(len - end));
+           b->bv_ga.ga_len -= end - idx + 1;
+       }
+     }
+ }
+ 
  #endif /* defined(FEAT_EVAL) */
*** ../vim-8.1.1762/src/proto/blob.pro  2019-01-24 12:31:40.752926550 +0100
--- src/proto/blob.pro  2019-07-27 22:53:56.682083624 +0200
***************
*** 13,16 ****
--- 13,17 ----
  int write_blob(FILE *fd, blob_T *blob);
  char_u *blob2string(blob_T *blob, char_u **tofree, char_u *numbuf);
  blob_T *string2blob(char_u *str);
+ void blob_remove(typval_T *argvars, typval_T *rettv);
  /* vim: set ft=c : */
*** ../vim-8.1.1762/src/version.c       2019-07-27 21:56:44.836171922 +0200
--- src/version.c       2019-07-27 23:11:10.720264034 +0200
***************
*** 779,780 ****
--- 779,782 ----
  {   /* Add new patch number below this line */
+ /**/
+     1763,
  /**/

-- 
BEDEVERE: Wait.  Wait ... tell me, what also floats on water?
ALL:      Bread?  No, no, no.  Apples .... gravy ... very small rocks ...
ARTHUR:   A duck.
                 "Monty Python and the Holy Grail" PYTHON (MONTY) PICTURES LTD

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

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

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/vim_dev/201907272112.x6RLCRDS030645%40masaka.moolenaar.net.

Raspunde prin e-mail lui