summaryrefslogtreecommitdiffstats
path: root/src/list.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/list.c')
-rw-r--r--src/list.c374
1 files changed, 373 insertions, 1 deletions
diff --git a/src/list.c b/src/list.c
index 040c5799bb..3038b22f93 100644
--- 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)