changeset 17530:ef23ec1eee54 v8.1.1763

patch 8.1.1763: evalfunc.c is still too big commit https://github.com/vim/vim/commit/9f9fe37f6750f342255a51f158a7bb372c827f7f Author: Bram Moolenaar <Bram@vim.org> Date: Sat Jul 27 23:12:12 2019 +0200 patch 8.1.1763: evalfunc.c is still too big Problem: Evalfunc.c is still too big. Solution: Move dict and list functions to a better place.
author Bram Moolenaar <Bram@vim.org>
date Sat, 27 Jul 2019 23:15:05 +0200
parents 4bd2e3339ba3
children 3a6150469ecb
files src/blob.c src/dict.c src/evalfunc.c src/list.c src/proto/blob.pro src/proto/dict.pro src/proto/list.pro src/version.c
diffstat 8 files changed, 704 insertions(+), 673 deletions(-) [+]
line wrap: on
line diff
--- a/src/blob.c
+++ b/src/blob.c
@@ -258,4 +258,74 @@ failed:
     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) */
--- a/src/dict.c
+++ b/src/dict.c
@@ -709,7 +709,7 @@ dict2string(typval_T *tv, int copyID, in
 }
 
 /*
- * Get the key for *{key: val} into "tv" and advance "arg".
+ * Get the key for #{key: val} into "tv" and advance "arg".
  * Return FAIL when there is no valid key.
  */
     static int
@@ -731,7 +731,7 @@ get_literal_key(char_u **arg, typval_T *
 
 /*
  * Allocate a variable for a Dictionary and fill it from "*arg".
- * "literal" is TRUE for *{key: val}
+ * "literal" is TRUE for #{key: val}
  * Return OK or FAIL.  Returns NOTDONE for {expr}.
  */
     int
@@ -1034,6 +1034,33 @@ dict_list(typval_T *argvars, typval_T *r
 }
 
 /*
+ * "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,4 +1079,54 @@ dict_set_items_ro(dict_T *di)
     }
 }
 
+/*
+ * "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) */
--- a/src/evalfunc.c
+++ b/src/evalfunc.c
@@ -180,7 +180,6 @@ static void f_glob(typval_T *argvars, ty
 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);
@@ -207,9 +206,6 @@ static void f_islocked(typval_T *argvars
 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);
@@ -217,7 +213,6 @@ static void f_libcallnr(typval_T *argvar
 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);
@@ -330,7 +325,6 @@ static void f_simplify(typval_T *argvars
 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);
@@ -392,8 +386,6 @@ static void f_trunc(typval_T *argvars, t
 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);
@@ -6581,24 +6573,6 @@ f_has(typval_T *argvars, typval_T *rettv
 }
 
 /*
- * "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
@@ -7241,58 +7215,6 @@ f_isnan(typval_T *argvars, typval_T *ret
 #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
@@ -7460,61 +7382,6 @@ f_lispindent(typval_T *argvars UNUSED, t
 }
 
 /*
- * "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
@@ -9237,158 +9104,16 @@ f_remote_startserver(typval_T *argvars U
     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);
-		}
-	    }
-	}
-    }
+	dict_remove(argvars, rettv, arg_errmsg);
     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)
+	blob_remove(argvars, rettv);
+    else if (argvars[0].v_type == VAR_LIST)
+	list_remove(argvars, rettv, arg_errmsg);
+    else
 	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;
-			}
-		    }
-		}
-	    }
-	}
-    }
 }
 
 /*
@@ -11100,388 +10825,6 @@ f_sinh(typval_T *argvars, typval_T *rett
 }
 #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
  */
@@ -13482,15 +12825,6 @@ f_undotree(typval_T *argvars UNUSED, typ
 }
 
 /*
- * "values(dict)" function
- */
-    static void
-f_values(typval_T *argvars, typval_T *rettv)
-{
-    dict_list(argvars, rettv, 1);
-}
-
-/*
  * "virtcol(string)" function
  */
     static void
--- a/src/list.c
+++ b/src/list.c
@@ -875,6 +875,40 @@ list_join(
 }
 
 /*
+ * "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,4 +1041,507 @@ init_static_list(staticList10_T *sl)
     }
 }
 
+/*
+ * "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) */
--- a/src/proto/blob.pro
+++ b/src/proto/blob.pro
@@ -13,4 +13,5 @@ int read_blob(FILE *fd, blob_T *blob);
 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 : */
--- a/src/proto/dict.pro
+++ b/src/proto/dict.pro
@@ -33,5 +33,10 @@ void dict_extend(dict_T *d1, dict_T *d2,
 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 : */
--- a/src/proto/list.pro
+++ b/src/proto/list.pro
@@ -34,7 +34,12 @@ list_T *list_copy(list_T *orig, int deep
 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 : */
--- a/src/version.c
+++ b/src/version.c
@@ -778,6 +778,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    1763,
+/**/
     1762,
 /**/
     1761,