changeset 17970:684a15da9929 v8.1.1981

patch 8.1.1981: the evalfunc.c file is too big Commit: https://github.com/vim/vim/commit/08c308aeb5e7dfa18fa61f261b0bff79517a4883 Author: Bram Moolenaar <Bram@vim.org> Date: Wed Sep 4 17:48:15 2019 +0200 patch 8.1.1981: the evalfunc.c file is too big Problem: The evalfunc.c file is too big. Solution: Move undo functions to undo.c. Move cmdline functions to ex_getln.c. Move some container functions to list.c.
author Bram Moolenaar <Bram@vim.org>
date Wed, 04 Sep 2019 18:00:03 +0200
parents bfc33cda9075
children cf3bf7a0d3e2
files src/evalfunc.c src/ex_getln.c src/list.c src/proto/ex_getln.pro src/proto/list.pro src/proto/undo.pro src/undo.c src/version.c
diffstat 8 files changed, 492 insertions(+), 502 deletions(-) [+]
line wrap: on
line diff
--- a/src/evalfunc.c
+++ b/src/evalfunc.c
@@ -24,13 +24,10 @@
 # include <time.h>	// for time_t
 #endif
 
-static char *e_listblobarg = N_("E899: Argument of %s must be a List or Blob");
-
 #ifdef FEAT_FLOAT
 static void f_abs(typval_T *argvars, typval_T *rettv);
 static void f_acos(typval_T *argvars, typval_T *rettv);
 #endif
-static void f_add(typval_T *argvars, typval_T *rettv);
 static void f_and(typval_T *argvars, typval_T *rettv);
 static void f_append(typval_T *argvars, typval_T *rettv);
 static void f_appendbufline(typval_T *argvars, typval_T *rettv);
@@ -73,7 +70,6 @@ static void f_copy(typval_T *argvars, ty
 static void f_cos(typval_T *argvars, typval_T *rettv);
 static void f_cosh(typval_T *argvars, typval_T *rettv);
 #endif
-static void f_count(typval_T *argvars, typval_T *rettv);
 static void f_cscope_connection(typval_T *argvars, typval_T *rettv);
 static void f_cursor(typval_T *argsvars, typval_T *rettv);
 #ifdef MSWIN
@@ -96,7 +92,6 @@ static void f_exp(typval_T *argvars, typ
 #endif
 static void f_expand(typval_T *argvars, typval_T *rettv);
 static void f_expandcmd(typval_T *argvars, typval_T *rettv);
-static void f_extend(typval_T *argvars, typval_T *rettv);
 static void f_feedkeys(typval_T *argvars, typval_T *rettv);
 #ifdef FEAT_FLOAT
 static void f_float2nr(typval_T *argvars, typval_T *rettv);
@@ -115,9 +110,6 @@ static void f_getchangelist(typval_T *ar
 static void f_getchar(typval_T *argvars, typval_T *rettv);
 static void f_getcharmod(typval_T *argvars, typval_T *rettv);
 static void f_getcharsearch(typval_T *argvars, typval_T *rettv);
-static void f_getcmdline(typval_T *argvars, typval_T *rettv);
-static void f_getcmdpos(typval_T *argvars, typval_T *rettv);
-static void f_getcmdtype(typval_T *argvars, typval_T *rettv);
 static void f_getcmdwintype(typval_T *argvars, typval_T *rettv);
 static void f_getenv(typval_T *argvars, typval_T *rettv);
 static void f_getfontname(typval_T *argvars, typval_T *rettv);
@@ -149,7 +141,6 @@ static void f_inputlist(typval_T *argvar
 static void f_inputrestore(typval_T *argvars, typval_T *rettv);
 static void f_inputsave(typval_T *argvars, typval_T *rettv);
 static void f_inputsecret(typval_T *argvars, typval_T *rettv);
-static void f_insert(typval_T *argvars, typval_T *rettv);
 static void f_invert(typval_T *argvars, typval_T *rettv);
 static void f_islocked(typval_T *argvars, typval_T *rettv);
 #if defined(FEAT_FLOAT) && defined(HAVE_MATH_H)
@@ -221,10 +212,8 @@ static void f_remote_peek(typval_T *argv
 static void f_remote_read(typval_T *argvars, typval_T *rettv);
 static void f_remote_send(typval_T *argvars, typval_T *rettv);
 static void f_remote_startserver(typval_T *argvars, typval_T *rettv);
-static void f_remove(typval_T *argvars, typval_T *rettv);
 static void f_rename(typval_T *argvars, typval_T *rettv);
 static void f_repeat(typval_T *argvars, typval_T *rettv);
-static void f_reverse(typval_T *argvars, typval_T *rettv);
 #ifdef FEAT_FLOAT
 static void f_round(typval_T *argvars, typval_T *rettv);
 #endif
@@ -246,7 +235,6 @@ static void f_server2client(typval_T *ar
 static void f_serverlist(typval_T *argvars, typval_T *rettv);
 static void f_setbufline(typval_T *argvars, typval_T *rettv);
 static void f_setcharsearch(typval_T *argvars, typval_T *rettv);
-static void f_setcmdpos(typval_T *argvars, typval_T *rettv);
 static void f_setenv(typval_T *argvars, typval_T *rettv);
 static void f_setfperm(typval_T *argvars, typval_T *rettv);
 static void f_setline(typval_T *argvars, typval_T *rettv);
@@ -320,8 +308,6 @@ static void f_trim(typval_T *argvars, ty
 static void f_trunc(typval_T *argvars, typval_T *rettv);
 #endif
 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_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);
@@ -1218,44 +1204,6 @@ f_acos(typval_T *argvars, typval_T *rett
 #endif
 
 /*
- * "add(list, item)" function
- */
-    static void
-f_add(typval_T *argvars, typval_T *rettv)
-{
-    list_T	*l;
-    blob_T	*b;
-
-    rettv->vval.v_number = 1; /* Default: Failed */
-    if (argvars[0].v_type == VAR_LIST)
-    {
-	if ((l = argvars[0].vval.v_list) != NULL
-		&& !var_check_lock(l->lv_lock,
-					 (char_u *)N_("add() argument"), TRUE)
-		&& list_append_tv(l, &argvars[1]) == OK)
-	    copy_tv(&argvars[0], rettv);
-    }
-    else if (argvars[0].v_type == VAR_BLOB)
-    {
-	if ((b = argvars[0].vval.v_blob) != NULL
-		&& !var_check_lock(b->bv_lock,
-					 (char_u *)N_("add() argument"), TRUE))
-	{
-	    int		error = FALSE;
-	    varnumber_T n = tv_get_number_chk(&argvars[1], &error);
-
-	    if (!error)
-	    {
-		ga_append(&b->bv_ga, (int)n);
-		copy_tv(&argvars[0], rettv);
-	    }
-	}
-    }
-    else
-	emsg(_(e_listblobreq));
-}
-
-/*
  * "and(expr, expr)" function
  */
     static void
@@ -2149,113 +2097,6 @@ f_cosh(typval_T *argvars, typval_T *rett
 #endif
 
 /*
- * "count()" function
- */
-    static void
-f_count(typval_T *argvars, typval_T *rettv)
-{
-    long	n = 0;
-    int		ic = FALSE;
-    int		error = FALSE;
-
-    if (argvars[2].v_type != VAR_UNKNOWN)
-	ic = (int)tv_get_number_chk(&argvars[2], &error);
-
-    if (argvars[0].v_type == VAR_STRING)
-    {
-	char_u *expr = tv_get_string_chk(&argvars[1]);
-	char_u *p = argvars[0].vval.v_string;
-	char_u *next;
-
-	if (!error && expr != NULL && *expr != NUL && p != NULL)
-	{
-	    if (ic)
-	    {
-		size_t len = STRLEN(expr);
-
-		while (*p != NUL)
-		{
-		    if (MB_STRNICMP(p, expr, len) == 0)
-		    {
-			++n;
-			p += len;
-		    }
-		    else
-			MB_PTR_ADV(p);
-		}
-	    }
-	    else
-		while ((next = (char_u *)strstr((char *)p, (char *)expr))
-								       != NULL)
-		{
-		    ++n;
-		    p = next + STRLEN(expr);
-		}
-	}
-
-    }
-    else if (argvars[0].v_type == VAR_LIST)
-    {
-	listitem_T	*li;
-	list_T		*l;
-	long		idx;
-
-	if ((l = argvars[0].vval.v_list) != NULL)
-	{
-	    li = l->lv_first;
-	    if (argvars[2].v_type != VAR_UNKNOWN)
-	    {
-		if (argvars[3].v_type != VAR_UNKNOWN)
-		{
-		    idx = (long)tv_get_number_chk(&argvars[3], &error);
-		    if (!error)
-		    {
-			li = list_find(l, idx);
-			if (li == NULL)
-			    semsg(_(e_listidx), idx);
-		    }
-		}
-		if (error)
-		    li = NULL;
-	    }
-
-	    for ( ; li != NULL; li = li->li_next)
-		if (tv_equal(&li->li_tv, &argvars[1], ic, FALSE))
-		    ++n;
-	}
-    }
-    else if (argvars[0].v_type == VAR_DICT)
-    {
-	int		todo;
-	dict_T		*d;
-	hashitem_T	*hi;
-
-	if ((d = argvars[0].vval.v_dict) != NULL)
-	{
-	    if (argvars[2].v_type != VAR_UNKNOWN)
-	    {
-		if (argvars[3].v_type != VAR_UNKNOWN)
-		    emsg(_(e_invarg));
-	    }
-
-	    todo = error ? 0 : (int)d->dv_hashtab.ht_used;
-	    for (hi = d->dv_hashtab.ht_array; todo > 0; ++hi)
-	    {
-		if (!HASHITEM_EMPTY(hi))
-		{
-		    --todo;
-		    if (tv_equal(&HI2DI(hi)->di_tv, &argvars[1], ic, FALSE))
-			++n;
-		}
-	    }
-	}
-    }
-    else
-	semsg(_(e_listdictarg), "count()");
-    rettv->vval.v_number = n;
-}
-
-/*
  * "cscope_connection([{num} , {dbpath} [, {prepend}]])" function
  *
  * Checks the existence of a cscope connection.
@@ -3044,92 +2885,6 @@ f_expandcmd(typval_T *argvars, typval_T 
 }
 
 /*
- * "extend(list, list [, idx])" function
- * "extend(dict, dict [, action])" function
- */
-    static void
-f_extend(typval_T *argvars, typval_T *rettv)
-{
-    char_u      *arg_errmsg = (char_u *)N_("extend() argument");
-
-    if (argvars[0].v_type == VAR_LIST && argvars[1].v_type == VAR_LIST)
-    {
-	list_T		*l1, *l2;
-	listitem_T	*item;
-	long		before;
-	int		error = FALSE;
-
-	l1 = argvars[0].vval.v_list;
-	l2 = argvars[1].vval.v_list;
-	if (l1 != NULL && !var_check_lock(l1->lv_lock, arg_errmsg, TRUE)
-		&& l2 != NULL)
-	{
-	    if (argvars[2].v_type != VAR_UNKNOWN)
-	    {
-		before = (long)tv_get_number_chk(&argvars[2], &error);
-		if (error)
-		    return;		/* type error; errmsg already given */
-
-		if (before == l1->lv_len)
-		    item = NULL;
-		else
-		{
-		    item = list_find(l1, before);
-		    if (item == NULL)
-		    {
-			semsg(_(e_listidx), before);
-			return;
-		    }
-		}
-	    }
-	    else
-		item = NULL;
-	    list_extend(l1, l2, item);
-
-	    copy_tv(&argvars[0], rettv);
-	}
-    }
-    else if (argvars[0].v_type == VAR_DICT && argvars[1].v_type == VAR_DICT)
-    {
-	dict_T	*d1, *d2;
-	char_u	*action;
-	int	i;
-
-	d1 = argvars[0].vval.v_dict;
-	d2 = argvars[1].vval.v_dict;
-	if (d1 != NULL && !var_check_lock(d1->dv_lock, arg_errmsg, TRUE)
-		&& d2 != NULL)
-	{
-	    /* Check the third argument. */
-	    if (argvars[2].v_type != VAR_UNKNOWN)
-	    {
-		static char *(av[]) = {"keep", "force", "error"};
-
-		action = tv_get_string_chk(&argvars[2]);
-		if (action == NULL)
-		    return;		/* type error; errmsg already given */
-		for (i = 0; i < 3; ++i)
-		    if (STRCMP(action, av[i]) == 0)
-			break;
-		if (i == 3)
-		{
-		    semsg(_(e_invarg2), action);
-		    return;
-		}
-	    }
-	    else
-		action = (char_u *)"force";
-
-	    dict_extend(d1, d2, action);
-
-	    copy_tv(&argvars[0], rettv);
-	}
-    }
-    else
-	semsg(_(e_listdictarg), "extend()");
-}
-
-/*
  * "feedkeys()" function
  */
     static void
@@ -4059,40 +3814,6 @@ f_getcharsearch(typval_T *argvars UNUSED
 }
 
 /*
- * "getcmdline()" function
- */
-    static void
-f_getcmdline(typval_T *argvars UNUSED, typval_T *rettv)
-{
-    rettv->v_type = VAR_STRING;
-    rettv->vval.v_string = get_cmdline_str();
-}
-
-/*
- * "getcmdpos()" function
- */
-    static void
-f_getcmdpos(typval_T *argvars UNUSED, typval_T *rettv)
-{
-    rettv->vval.v_number = get_cmdline_pos() + 1;
-}
-
-/*
- * "getcmdtype()" function
- */
-    static void
-f_getcmdtype(typval_T *argvars UNUSED, typval_T *rettv)
-{
-    rettv->v_type = VAR_STRING;
-    rettv->vval.v_string = alloc(2);
-    if (rettv->vval.v_string != NULL)
-    {
-	rettv->vval.v_string[0] = get_cmdline_type();
-	rettv->vval.v_string[1] = NUL;
-    }
-}
-
-/*
  * "getcmdwintype()" function
  */
     static void
@@ -5735,82 +5456,6 @@ f_inputsecret(typval_T *argvars, typval_
 }
 
 /*
- * "insert()" function
- */
-    static void
-f_insert(typval_T *argvars, typval_T *rettv)
-{
-    long	before = 0;
-    listitem_T	*item;
-    list_T	*l;
-    int		error = FALSE;
-
-    if (argvars[0].v_type == VAR_BLOB)
-    {
-	int	    val, len;
-	char_u	    *p;
-
-	len = blob_len(argvars[0].vval.v_blob);
-	if (argvars[2].v_type != VAR_UNKNOWN)
-	{
-	    before = (long)tv_get_number_chk(&argvars[2], &error);
-	    if (error)
-		return;		// type error; errmsg already given
-	    if (before < 0 || before > len)
-	    {
-		semsg(_(e_invarg2), tv_get_string(&argvars[2]));
-		return;
-	    }
-	}
-	val = tv_get_number_chk(&argvars[1], &error);
-	if (error)
-	    return;
-	if (val < 0 || val > 255)
-	{
-	    semsg(_(e_invarg2), tv_get_string(&argvars[1]));
-	    return;
-	}
-
-	if (ga_grow(&argvars[0].vval.v_blob->bv_ga, 1) == FAIL)
-	    return;
-	p = (char_u *)argvars[0].vval.v_blob->bv_ga.ga_data;
-	mch_memmove(p + before + 1, p + before, (size_t)len - before);
-	*(p + before) = val;
-	++argvars[0].vval.v_blob->bv_ga.ga_len;
-
-	copy_tv(&argvars[0], rettv);
-    }
-    else if (argvars[0].v_type != VAR_LIST)
-	semsg(_(e_listblobarg), "insert()");
-    else if ((l = argvars[0].vval.v_list) != NULL
-	    && !var_check_lock(l->lv_lock,
-				     (char_u *)N_("insert() argument"), TRUE))
-    {
-	if (argvars[2].v_type != VAR_UNKNOWN)
-	    before = (long)tv_get_number_chk(&argvars[2], &error);
-	if (error)
-	    return;		/* type error; errmsg already given */
-
-	if (before == l->lv_len)
-	    item = NULL;
-	else
-	{
-	    item = list_find(l, before);
-	    if (item == NULL)
-	    {
-		semsg(_(e_listidx), before);
-		l = NULL;
-	    }
-	}
-	if (l != NULL)
-	{
-	    list_insert_tv(l, &argvars[1], item);
-	    copy_tv(&argvars[0], rettv);
-	}
-    }
-}
-
-/*
  * "invert(expr)" function
  */
     static void
@@ -7362,24 +7007,6 @@ f_remote_startserver(typval_T *argvars U
 }
 
 /*
- * "remove()" function
- */
-    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()");
-}
-
-/*
  * "rename({from}, {to})" function
  */
     static void
@@ -7439,51 +7066,6 @@ f_repeat(typval_T *argvars, typval_T *re
     }
 }
 
-/*
- * "reverse({list})" function
- */
-    static void
-f_reverse(typval_T *argvars, typval_T *rettv)
-{
-    list_T	*l;
-    listitem_T	*li, *ni;
-
-    if (argvars[0].v_type == VAR_BLOB)
-    {
-	blob_T	*b = argvars[0].vval.v_blob;
-	int	i, len = blob_len(b);
-
-	for (i = 0; i < len / 2; i++)
-	{
-	    int tmp = blob_get(b, i);
-
-	    blob_set(b, i, blob_get(b, len - i - 1));
-	    blob_set(b, len - i - 1, tmp);
-	}
-	rettv_blob_set(rettv, b);
-	return;
-    }
-
-    if (argvars[0].v_type != VAR_LIST)
-	semsg(_(e_listblobarg), "reverse()");
-    else if ((l = argvars[0].vval.v_list) != NULL
-	    && !var_check_lock(l->lv_lock,
-				    (char_u *)N_("reverse() argument"), TRUE))
-    {
-	li = l->lv_last;
-	l->lv_first = l->lv_last = NULL;
-	l->lv_len = 0;
-	while (li != NULL)
-	{
-	    ni = li->li_prev;
-	    list_append(l, li);
-	    li = ni;
-	}
-	rettv_list_set(rettv, l);
-	l->lv_idx = l->lv_len - l->lv_idx - 1;
-    }
-}
-
 #define SP_NOMOVE	0x01	    /* don't move cursor */
 #define SP_REPEAT	0x02	    /* repeat to find outer pair */
 #define SP_RETCOUNT	0x04	    /* return matchcount */
@@ -8287,18 +7869,6 @@ f_setcharsearch(typval_T *argvars, typva
 }
 
 /*
- * "setcmdpos()" function
- */
-    static void
-f_setcmdpos(typval_T *argvars, typval_T *rettv)
-{
-    int		pos = (int)tv_get_number(&argvars[0]) - 1;
-
-    if (pos >= 0)
-	rettv->vval.v_number = set_cmdline_pos(pos);
-}
-
-/*
  * "setenv()" function
  */
     static void
@@ -10573,63 +10143,6 @@ f_type(typval_T *argvars, typval_T *rett
 }
 
 /*
- * "undofile(name)" function
- */
-    static void
-f_undofile(typval_T *argvars UNUSED, typval_T *rettv)
-{
-    rettv->v_type = VAR_STRING;
-#ifdef FEAT_PERSISTENT_UNDO
-    {
-	char_u *fname = tv_get_string(&argvars[0]);
-
-	if (*fname == NUL)
-	{
-	    /* If there is no file name there will be no undo file. */
-	    rettv->vval.v_string = NULL;
-	}
-	else
-	{
-	    char_u *ffname = FullName_save(fname, TRUE);
-
-	    if (ffname != NULL)
-		rettv->vval.v_string = u_get_undo_file_name(ffname, FALSE);
-	    vim_free(ffname);
-	}
-    }
-#else
-    rettv->vval.v_string = NULL;
-#endif
-}
-
-/*
- * "undotree()" function
- */
-    static void
-f_undotree(typval_T *argvars UNUSED, typval_T *rettv)
-{
-    if (rettv_dict_alloc(rettv) == OK)
-    {
-	dict_T *dict = rettv->vval.v_dict;
-	list_T *list;
-
-	dict_add_number(dict, "synced", (long)curbuf->b_u_synced);
-	dict_add_number(dict, "seq_last", curbuf->b_u_seq_last);
-	dict_add_number(dict, "save_last", (long)curbuf->b_u_save_nr_last);
-	dict_add_number(dict, "seq_cur", curbuf->b_u_seq_cur);
-	dict_add_number(dict, "time_cur", (long)curbuf->b_u_time_cur);
-	dict_add_number(dict, "save_cur", (long)curbuf->b_u_save_nr_cur);
-
-	list = list_alloc();
-	if (list != NULL)
-	{
-	    u_eval_tree(curbuf->b_u_oldhead, list);
-	    dict_add_list(dict, "entries", list);
-	}
-    }
-}
-
-/*
  * "virtcol(string)" function
  */
     static void
--- a/src/ex_getln.c
+++ b/src/ex_getln.c
@@ -3868,7 +3868,7 @@ get_ccline_ptr(void)
  * Only works when the command line is being edited.
  * Returns NULL when something is wrong.
  */
-    char_u *
+    static char_u *
 get_cmdline_str(void)
 {
     cmdline_info_T *p;
@@ -3882,19 +3882,26 @@ get_cmdline_str(void)
 }
 
 /*
- * Get the current command line position, counted in bytes.
- * Zero is the first position.
- * Only works when the command line is being edited.
- * Returns -1 when something is wrong.
+ * "getcmdline()" function
  */
-    int
-get_cmdline_pos(void)
+    void
+f_getcmdline(typval_T *argvars UNUSED, typval_T *rettv)
+{
+    rettv->v_type = VAR_STRING;
+    rettv->vval.v_string = get_cmdline_str();
+}
+
+/*
+ * "getcmdpos()" function
+ */
+    void
+f_getcmdpos(typval_T *argvars UNUSED, typval_T *rettv)
 {
     cmdline_info_T *p = get_ccline_ptr();
 
-    if (p == NULL)
-	return -1;
-    return p->cmdpos;
+    rettv->vval.v_number = 0;
+    if (p != NULL)
+    rettv->vval.v_number = p->cmdpos + 1;
 }
 
 /*
@@ -3902,7 +3909,7 @@ get_cmdline_pos(void)
  * Only works when the command line is being edited.
  * Returns 1 when failed, 0 when OK.
  */
-    int
+    static int
 set_cmdline_pos(
     int		pos)
 {
@@ -3919,6 +3926,34 @@ set_cmdline_pos(
 	new_cmdpos = pos;
     return 0;
 }
+
+/*
+ * "setcmdpos()" function
+ */
+    void
+f_setcmdpos(typval_T *argvars, typval_T *rettv)
+{
+    int		pos = (int)tv_get_number(&argvars[0]) - 1;
+
+    if (pos >= 0)
+	rettv->vval.v_number = set_cmdline_pos(pos);
+}
+
+/*
+ * "getcmdtype()" function
+ */
+    void
+f_getcmdtype(typval_T *argvars UNUSED, typval_T *rettv)
+{
+    rettv->v_type = VAR_STRING;
+    rettv->vval.v_string = alloc(2);
+    if (rettv->vval.v_string != NULL)
+    {
+	rettv->vval.v_string[0] = get_cmdline_type();
+	rettv->vval.v_string[1] = NUL;
+    }
+}
+
 #endif
 
 #if defined(FEAT_EVAL) || defined(FEAT_CMDWIN) || defined(PROTO)
--- a/src/list.c
+++ b/src/list.c
@@ -8,13 +8,15 @@
  */
 
 /*
- * list.c: List support
+ * list.c: List support and container (List, Dict, Blob) functions.
  */
 
 #include "vim.h"
 
 #if defined(FEAT_EVAL) || defined(PROTO)
 
+static char *e_listblobarg = N_("E899: Argument of %s must be a List or Blob");
+
 /* List heads for garbage collection. */
 static list_T		*first_list = NULL;	/* list of all lists */
 
@@ -1763,4 +1765,374 @@ f_map(typval_T *argvars, typval_T *rettv
     filter_map(argvars, rettv, TRUE);
 }
 
+/*
+ * "add(list, item)" function
+ */
+    void
+f_add(typval_T *argvars, typval_T *rettv)
+{
+    list_T	*l;
+    blob_T	*b;
+
+    rettv->vval.v_number = 1; /* Default: Failed */
+    if (argvars[0].v_type == VAR_LIST)
+    {
+	if ((l = argvars[0].vval.v_list) != NULL
+		&& !var_check_lock(l->lv_lock,
+					 (char_u *)N_("add() argument"), TRUE)
+		&& list_append_tv(l, &argvars[1]) == OK)
+	    copy_tv(&argvars[0], rettv);
+    }
+    else if (argvars[0].v_type == VAR_BLOB)
+    {
+	if ((b = argvars[0].vval.v_blob) != NULL
+		&& !var_check_lock(b->bv_lock,
+					 (char_u *)N_("add() argument"), TRUE))
+	{
+	    int		error = FALSE;
+	    varnumber_T n = tv_get_number_chk(&argvars[1], &error);
+
+	    if (!error)
+	    {
+		ga_append(&b->bv_ga, (int)n);
+		copy_tv(&argvars[0], rettv);
+	    }
+	}
+    }
+    else
+	emsg(_(e_listblobreq));
+}
+
+/*
+ * "count()" function
+ */
+    void
+f_count(typval_T *argvars, typval_T *rettv)
+{
+    long	n = 0;
+    int		ic = FALSE;
+    int		error = FALSE;
+
+    if (argvars[2].v_type != VAR_UNKNOWN)
+	ic = (int)tv_get_number_chk(&argvars[2], &error);
+
+    if (argvars[0].v_type == VAR_STRING)
+    {
+	char_u *expr = tv_get_string_chk(&argvars[1]);
+	char_u *p = argvars[0].vval.v_string;
+	char_u *next;
+
+	if (!error && expr != NULL && *expr != NUL && p != NULL)
+	{
+	    if (ic)
+	    {
+		size_t len = STRLEN(expr);
+
+		while (*p != NUL)
+		{
+		    if (MB_STRNICMP(p, expr, len) == 0)
+		    {
+			++n;
+			p += len;
+		    }
+		    else
+			MB_PTR_ADV(p);
+		}
+	    }
+	    else
+		while ((next = (char_u *)strstr((char *)p, (char *)expr))
+								       != NULL)
+		{
+		    ++n;
+		    p = next + STRLEN(expr);
+		}
+	}
+
+    }
+    else if (argvars[0].v_type == VAR_LIST)
+    {
+	listitem_T	*li;
+	list_T		*l;
+	long		idx;
+
+	if ((l = argvars[0].vval.v_list) != NULL)
+	{
+	    li = l->lv_first;
+	    if (argvars[2].v_type != VAR_UNKNOWN)
+	    {
+		if (argvars[3].v_type != VAR_UNKNOWN)
+		{
+		    idx = (long)tv_get_number_chk(&argvars[3], &error);
+		    if (!error)
+		    {
+			li = list_find(l, idx);
+			if (li == NULL)
+			    semsg(_(e_listidx), idx);
+		    }
+		}
+		if (error)
+		    li = NULL;
+	    }
+
+	    for ( ; li != NULL; li = li->li_next)
+		if (tv_equal(&li->li_tv, &argvars[1], ic, FALSE))
+		    ++n;
+	}
+    }
+    else if (argvars[0].v_type == VAR_DICT)
+    {
+	int		todo;
+	dict_T		*d;
+	hashitem_T	*hi;
+
+	if ((d = argvars[0].vval.v_dict) != NULL)
+	{
+	    if (argvars[2].v_type != VAR_UNKNOWN)
+	    {
+		if (argvars[3].v_type != VAR_UNKNOWN)
+		    emsg(_(e_invarg));
+	    }
+
+	    todo = error ? 0 : (int)d->dv_hashtab.ht_used;
+	    for (hi = d->dv_hashtab.ht_array; todo > 0; ++hi)
+	    {
+		if (!HASHITEM_EMPTY(hi))
+		{
+		    --todo;
+		    if (tv_equal(&HI2DI(hi)->di_tv, &argvars[1], ic, FALSE))
+			++n;
+		}
+	    }
+	}
+    }
+    else
+	semsg(_(e_listdictarg), "count()");
+    rettv->vval.v_number = n;
+}
+
+/*
+ * "extend(list, list [, idx])" function
+ * "extend(dict, dict [, action])" function
+ */
+    void
+f_extend(typval_T *argvars, typval_T *rettv)
+{
+    char_u      *arg_errmsg = (char_u *)N_("extend() argument");
+
+    if (argvars[0].v_type == VAR_LIST && argvars[1].v_type == VAR_LIST)
+    {
+	list_T		*l1, *l2;
+	listitem_T	*item;
+	long		before;
+	int		error = FALSE;
+
+	l1 = argvars[0].vval.v_list;
+	l2 = argvars[1].vval.v_list;
+	if (l1 != NULL && !var_check_lock(l1->lv_lock, arg_errmsg, TRUE)
+		&& l2 != NULL)
+	{
+	    if (argvars[2].v_type != VAR_UNKNOWN)
+	    {
+		before = (long)tv_get_number_chk(&argvars[2], &error);
+		if (error)
+		    return;		/* type error; errmsg already given */
+
+		if (before == l1->lv_len)
+		    item = NULL;
+		else
+		{
+		    item = list_find(l1, before);
+		    if (item == NULL)
+		    {
+			semsg(_(e_listidx), before);
+			return;
+		    }
+		}
+	    }
+	    else
+		item = NULL;
+	    list_extend(l1, l2, item);
+
+	    copy_tv(&argvars[0], rettv);
+	}
+    }
+    else if (argvars[0].v_type == VAR_DICT && argvars[1].v_type == VAR_DICT)
+    {
+	dict_T	*d1, *d2;
+	char_u	*action;
+	int	i;
+
+	d1 = argvars[0].vval.v_dict;
+	d2 = argvars[1].vval.v_dict;
+	if (d1 != NULL && !var_check_lock(d1->dv_lock, arg_errmsg, TRUE)
+		&& d2 != NULL)
+	{
+	    /* Check the third argument. */
+	    if (argvars[2].v_type != VAR_UNKNOWN)
+	    {
+		static char *(av[]) = {"keep", "force", "error"};
+
+		action = tv_get_string_chk(&argvars[2]);
+		if (action == NULL)
+		    return;		/* type error; errmsg already given */
+		for (i = 0; i < 3; ++i)
+		    if (STRCMP(action, av[i]) == 0)
+			break;
+		if (i == 3)
+		{
+		    semsg(_(e_invarg2), action);
+		    return;
+		}
+	    }
+	    else
+		action = (char_u *)"force";
+
+	    dict_extend(d1, d2, action);
+
+	    copy_tv(&argvars[0], rettv);
+	}
+    }
+    else
+	semsg(_(e_listdictarg), "extend()");
+}
+
+/*
+ * "insert()" function
+ */
+    void
+f_insert(typval_T *argvars, typval_T *rettv)
+{
+    long	before = 0;
+    listitem_T	*item;
+    list_T	*l;
+    int		error = FALSE;
+
+    if (argvars[0].v_type == VAR_BLOB)
+    {
+	int	    val, len;
+	char_u	    *p;
+
+	len = blob_len(argvars[0].vval.v_blob);
+	if (argvars[2].v_type != VAR_UNKNOWN)
+	{
+	    before = (long)tv_get_number_chk(&argvars[2], &error);
+	    if (error)
+		return;		// type error; errmsg already given
+	    if (before < 0 || before > len)
+	    {
+		semsg(_(e_invarg2), tv_get_string(&argvars[2]));
+		return;
+	    }
+	}
+	val = tv_get_number_chk(&argvars[1], &error);
+	if (error)
+	    return;
+	if (val < 0 || val > 255)
+	{
+	    semsg(_(e_invarg2), tv_get_string(&argvars[1]));
+	    return;
+	}
+
+	if (ga_grow(&argvars[0].vval.v_blob->bv_ga, 1) == FAIL)
+	    return;
+	p = (char_u *)argvars[0].vval.v_blob->bv_ga.ga_data;
+	mch_memmove(p + before + 1, p + before, (size_t)len - before);
+	*(p + before) = val;
+	++argvars[0].vval.v_blob->bv_ga.ga_len;
+
+	copy_tv(&argvars[0], rettv);
+    }
+    else if (argvars[0].v_type != VAR_LIST)
+	semsg(_(e_listblobarg), "insert()");
+    else if ((l = argvars[0].vval.v_list) != NULL
+	    && !var_check_lock(l->lv_lock,
+				     (char_u *)N_("insert() argument"), TRUE))
+    {
+	if (argvars[2].v_type != VAR_UNKNOWN)
+	    before = (long)tv_get_number_chk(&argvars[2], &error);
+	if (error)
+	    return;		/* type error; errmsg already given */
+
+	if (before == l->lv_len)
+	    item = NULL;
+	else
+	{
+	    item = list_find(l, before);
+	    if (item == NULL)
+	    {
+		semsg(_(e_listidx), before);
+		l = NULL;
+	    }
+	}
+	if (l != NULL)
+	{
+	    list_insert_tv(l, &argvars[1], item);
+	    copy_tv(&argvars[0], rettv);
+	}
+    }
+}
+
+/*
+ * "remove()" function
+ */
+    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()");
+}
+
+/*
+ * "reverse({list})" function
+ */
+    void
+f_reverse(typval_T *argvars, typval_T *rettv)
+{
+    list_T	*l;
+    listitem_T	*li, *ni;
+
+    if (argvars[0].v_type == VAR_BLOB)
+    {
+	blob_T	*b = argvars[0].vval.v_blob;
+	int	i, len = blob_len(b);
+
+	for (i = 0; i < len / 2; i++)
+	{
+	    int tmp = blob_get(b, i);
+
+	    blob_set(b, i, blob_get(b, len - i - 1));
+	    blob_set(b, len - i - 1, tmp);
+	}
+	rettv_blob_set(rettv, b);
+	return;
+    }
+
+    if (argvars[0].v_type != VAR_LIST)
+	semsg(_(e_listblobarg), "reverse()");
+    else if ((l = argvars[0].vval.v_list) != NULL
+	    && !var_check_lock(l->lv_lock,
+				    (char_u *)N_("reverse() argument"), TRUE))
+    {
+	li = l->lv_last;
+	l->lv_first = l->lv_last = NULL;
+	l->lv_len = 0;
+	while (li != NULL)
+	{
+	    ni = li->li_prev;
+	    list_append(l, li);
+	    li = ni;
+	}
+	rettv_list_set(rettv, l);
+	l->lv_idx = l->lv_len - l->lv_idx - 1;
+    }
+}
+
 #endif // defined(FEAT_EVAL)
--- a/src/proto/ex_getln.pro
+++ b/src/proto/ex_getln.pro
@@ -28,9 +28,10 @@ char_u *vim_strsave_fnameescape(char_u *
 void escape_fname(char_u **pp);
 void tilde_replace(char_u *orig_pat, int num_files, char_u **files);
 cmdline_info_T *get_cmdline_info(void);
-char_u *get_cmdline_str(void);
-int get_cmdline_pos(void);
-int set_cmdline_pos(int pos);
+void f_getcmdline(typval_T *argvars, typval_T *rettv);
+void f_getcmdpos(typval_T *argvars, typval_T *rettv);
+void f_setcmdpos(typval_T *argvars, typval_T *rettv);
+void f_getcmdtype(typval_T *argvars, typval_T *rettv);
 int get_cmdline_type(void);
 int get_cmdline_firstc(void);
 int get_list_range(char_u **str, int *num1, int *num2);
--- a/src/proto/list.pro
+++ b/src/proto/list.pro
@@ -43,4 +43,10 @@ void f_sort(typval_T *argvars, typval_T 
 void f_uniq(typval_T *argvars, typval_T *rettv);
 void f_filter(typval_T *argvars, typval_T *rettv);
 void f_map(typval_T *argvars, typval_T *rettv);
+void f_add(typval_T *argvars, typval_T *rettv);
+void f_count(typval_T *argvars, typval_T *rettv);
+void f_extend(typval_T *argvars, typval_T *rettv);
+void f_insert(typval_T *argvars, typval_T *rettv);
+void f_remove(typval_T *argvars, typval_T *rettv);
+void f_reverse(typval_T *argvars, typval_T *rettv);
 /* vim: set ft=c : */
--- a/src/proto/undo.pro
+++ b/src/proto/undo.pro
@@ -28,4 +28,6 @@ int anyBufIsChanged(void);
 int bufIsChangedNotTerm(buf_T *buf);
 int curbufIsChanged(void);
 void u_eval_tree(u_header_T *first_uhp, list_T *list);
+void f_undofile(typval_T *argvars, typval_T *rettv);
+void f_undotree(typval_T *argvars, typval_T *rettv);
 /* vim: set ft=c : */
--- a/src/undo.c
+++ b/src/undo.c
@@ -3572,6 +3572,7 @@ curbufIsChanged(void)
 }
 
 #if defined(FEAT_EVAL) || defined(PROTO)
+
 /*
  * For undotree(): Append the list of undo blocks at "first_uhp" to "list".
  * Recursive.
@@ -3612,4 +3613,62 @@ u_eval_tree(u_header_T *first_uhp, list_
 	uhp = uhp->uh_prev.ptr;
     }
 }
+
+/*
+ * "undofile(name)" function
+ */
+    void
+f_undofile(typval_T *argvars UNUSED, typval_T *rettv)
+{
+    rettv->v_type = VAR_STRING;
+#ifdef FEAT_PERSISTENT_UNDO
+    {
+	char_u *fname = tv_get_string(&argvars[0]);
+
+	if (*fname == NUL)
+	{
+	    /* If there is no file name there will be no undo file. */
+	    rettv->vval.v_string = NULL;
+	}
+	else
+	{
+	    char_u *ffname = FullName_save(fname, TRUE);
+
+	    if (ffname != NULL)
+		rettv->vval.v_string = u_get_undo_file_name(ffname, FALSE);
+	    vim_free(ffname);
+	}
+    }
+#else
+    rettv->vval.v_string = NULL;
 #endif
+}
+
+/*
+ * "undotree()" function
+ */
+    void
+f_undotree(typval_T *argvars UNUSED, typval_T *rettv)
+{
+    if (rettv_dict_alloc(rettv) == OK)
+    {
+	dict_T *dict = rettv->vval.v_dict;
+	list_T *list;
+
+	dict_add_number(dict, "synced", (long)curbuf->b_u_synced);
+	dict_add_number(dict, "seq_last", curbuf->b_u_seq_last);
+	dict_add_number(dict, "save_last", (long)curbuf->b_u_save_nr_last);
+	dict_add_number(dict, "seq_cur", curbuf->b_u_seq_cur);
+	dict_add_number(dict, "time_cur", (long)curbuf->b_u_time_cur);
+	dict_add_number(dict, "save_cur", (long)curbuf->b_u_save_nr_cur);
+
+	list = list_alloc();
+	if (list != NULL)
+	{
+	    u_eval_tree(curbuf->b_u_oldhead, list);
+	    dict_add_list(dict, "entries", list);
+	}
+    }
+}
+
+#endif
--- a/src/version.c
+++ b/src/version.c
@@ -758,6 +758,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    1981,
+/**/
     1980,
 /**/
     1979,