Patch 8.2.3871

14 views
Skip to first unread message

Bram Moolenaar

unread,
Dec 22, 2021, 1:20:12 PM12/22/21
to vim...@googlegroups.com

Patch 8.2.3871
Problem: List.c contains code for dict and blob.
Solution: Refactor to put code where it belongs. (Yegappan Lakshmanan,
closes #9386)
Files: src/blob.c, src/dict.c, src/list.c, src/proto/blob.pro,
src/proto/dict.pro, src/proto/list.pro, src/proto/strings.pro,
src/strings.c, src/structs.h, src/testdir/test_filter_map.vim,
src/testdir/test_listdict.vim, src/testdir/test_sort.vim


*** ../vim-8.2.3870/src/blob.c 2021-12-19 19:19:27.818424761 +0000
--- src/blob.c 2021-12-22 18:04:56.843931664 +0000
***************
*** 410,487 ****
}

/*
! * "remove({blob})" function
*/
void
blob_remove(typval_T *argvars, typval_T *rettv, char_u *arg_errmsg)
{
blob_T *b = argvars[0].vval.v_blob;
int error = FALSE;
long idx;
long end;

if (b != NULL && value_check_lock(b->bv_lock, arg_errmsg, TRUE))
return;

idx = (long)tv_get_number_chk(&argvars[1], &error);
! if (!error)
{
! 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 blob 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;
!
! if (len - end - 1 > 0)
! mch_memmove(p + idx, p + end + 1, (size_t)(len - end - 1));
! b->bv_ga.ga_len -= end - idx + 1;
}
}
}

/*
--- 410,715 ----
}

/*
! * "add(blob, item)" function
! */
! void
! blob_add(typval_T *argvars, typval_T *rettv)
! {
! blob_T *b = argvars[0].vval.v_blob;
! int error = FALSE;
! varnumber_T n;
!
! if (b == NULL)
! {
! if (in_vim9script())
! emsg(_(e_cannot_add_to_null_blob));
! return;
! }
!
! if (value_check_lock(b->bv_lock, (char_u *)N_("add() argument"), TRUE))
! return;
!
! n = tv_get_number_chk(&argvars[1], &error);
! if (error)
! return;
!
! ga_append(&b->bv_ga, (int)n);
! copy_tv(&argvars[0], rettv);
! }
!
! /*
! * "remove({blob}, {idx} [, {end}])" function
*/
void
blob_remove(typval_T *argvars, typval_T *rettv, char_u *arg_errmsg)
{
blob_T *b = argvars[0].vval.v_blob;
+ blob_T *newblob;
int error = FALSE;
long idx;
long end;
+ int len;
+ char_u *p;

if (b != NULL && value_check_lock(b->bv_lock, arg_errmsg, TRUE))
return;

idx = (long)tv_get_number_chk(&argvars[1], &error);
! if (error)
! return;
!
! len = blob_len(b);
!
! 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;
! return;
! }

! // Remove range of items, return blob 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;
! }
! newblob = blob_alloc();
! if (newblob == NULL)
! return;
! newblob->bv_ga.ga_len = end - idx + 1;
! if (ga_grow(&newblob->bv_ga, end - idx + 1) == FAIL)
! {
! vim_free(newblob);
! return;
! }
! p = (char_u *)b->bv_ga.ga_data;
! mch_memmove((char_u *)newblob->bv_ga.ga_data, p + idx,
! (size_t)(end - idx + 1));
! ++newblob->bv_refcount;
! rettv->v_type = VAR_BLOB;
! rettv->vval.v_blob = newblob;
!
! if (len - end - 1 > 0)
! mch_memmove(p + idx, p + end + 1, (size_t)(len - end - 1));
! b->bv_ga.ga_len -= end - idx + 1;
! }
!
! /*
! * Implementation of map() and filter() for a Blob. Apply "expr" to every
! * number in Blob "blob_arg" and return the result in "rettv".
! */
! void
! blob_filter_map(
! blob_T *blob_arg,
! filtermap_T filtermap,
! typval_T *expr,
! typval_T *rettv)
! {
! blob_T *b;
! int i;
! typval_T tv;
! varnumber_T val;
! blob_T *b_ret;
! int idx = 0;
! int rem;
!
! if (filtermap == FILTERMAP_MAPNEW)
! {
! rettv->v_type = VAR_BLOB;
! rettv->vval.v_blob = NULL;
! }
! if ((b = blob_arg) == NULL)
! return;
!
! b_ret = b;
! if (filtermap == FILTERMAP_MAPNEW)
! {
! if (blob_copy(b, rettv) == FAIL)
return;
+ b_ret = rettv->vval.v_blob;
+ }
+
+ // set_vim_var_nr() doesn't set the type
+ set_vim_var_type(VV_KEY, VAR_NUMBER);
+
+ for (i = 0; i < b->bv_ga.ga_len; i++)
+ {
+ typval_T newtv;
+
+ tv.v_type = VAR_NUMBER;
+ val = blob_get(b, i);
+ tv.vval.v_number = val;
+ set_vim_var_nr(VV_KEY, idx);
+ if (filter_map_one(&tv, expr, filtermap, &newtv, &rem) == FAIL
+ || did_emsg)
+ break;
+ if (newtv.v_type != VAR_NUMBER && newtv.v_type != VAR_BOOL)
+ {
+ clear_tv(&newtv);
+ emsg(_(e_invalblob));
+ break;
}
! if (filtermap != FILTERMAP_FILTER)
{
! if (newtv.vval.v_number != val)
! blob_set(b_ret, i, newtv.vval.v_number);
! }
! else if (rem)
! {
! char_u *p = (char_u *)blob_arg->bv_ga.ga_data;
!
! mch_memmove(p + i, p + i + 1,
! (size_t)b->bv_ga.ga_len - i - 1);
--b->bv_ga.ga_len;
+ --i;
}
! ++idx;
! }
! }
!
! /*
! * "insert(blob, {item} [, {idx}])" function
! */
! void
! blob_insert_func(typval_T *argvars, typval_T *rettv)
! {
! blob_T *b = argvars[0].vval.v_blob;
! long before = 0;
! int error = FALSE;
! int val, len;
! char_u *p;
!
! if (b == NULL)
! {
! if (in_vim9script())
! emsg(_(e_cannot_add_to_null_blob));
! return;
! }
!
! if (value_check_lock(b->bv_lock, (char_u *)N_("insert() argument"), TRUE))
! return;
!
! len = blob_len(b);
! 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(&b->bv_ga, 1) == FAIL)
! return;
! p = (char_u *)b->bv_ga.ga_data;
! mch_memmove(p + before + 1, p + before, (size_t)len - before);
! *(p + before) = val;
! ++b->bv_ga.ga_len;

! copy_tv(&argvars[0], rettv);
! }
!
! /*
! * reduce() Blob argvars[0] using the function 'funcname' with arguments in
! * 'funcexe' starting with the initial value argvars[2] and return the result
! * in 'rettv'.
! */
! void
! blob_reduce(
! typval_T *argvars,
! char_u *func_name,
! funcexe_T *funcexe,
! typval_T *rettv)
! {
! blob_T *b = argvars[0].vval.v_blob;
! int called_emsg_start = called_emsg;
! int r;
! typval_T initial;
! typval_T argv[3];
! int i;
!
! if (argvars[2].v_type == VAR_UNKNOWN)
! {
! if (b == NULL || b->bv_ga.ga_len == 0)
! {
! semsg(_(e_reduceempty), "Blob");
! return;
}
+ initial.v_type = VAR_NUMBER;
+ initial.vval.v_number = blob_get(b, 0);
+ i = 1;
+ }
+ else if (argvars[2].v_type != VAR_NUMBER)
+ {
+ emsg(_(e_number_expected));
+ return;
+ }
+ else
+ {
+ initial = argvars[2];
+ i = 0;
+ }
+
+ copy_tv(&initial, rettv);
+ if (b == NULL)
+ return;
+
+ for ( ; i < b->bv_ga.ga_len; i++)
+ {
+ argv[0] = *rettv;
+ argv[1].v_type = VAR_NUMBER;
+ argv[1].vval.v_number = blob_get(b, i);
+ r = call_func(func_name, -1, rettv, 2, argv, funcexe);
+ clear_tv(&argv[0]);
+ if (r == FAIL || called_emsg != called_emsg_start)
+ return;
+ }
+ }
+
+ /*
+ * "reverse({blob})" function
+ */
+ void
+ blob_reverse(blob_T *b, typval_T *rettv)
+ {
+ 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);
}

/*
*** ../vim-8.2.3870/src/dict.c 2021-12-16 20:56:52.948098558 +0000
--- src/dict.c 2021-12-22 18:04:56.843931664 +0000
***************
*** 896,908 ****
int vim9script = in_vim9script();
int had_comma;

! /*
! * First check if it's not a curly-braces thing: {expr}.
! * Must do this without evaluating, otherwise a function may be called
! * twice. Unfortunately this means we need to call eval1() twice for the
! * first item.
! * But {} is an empty Dictionary.
! */
if (!vim9script
&& *curly_expr != '}'
&& eval1(&curly_expr, &tv, NULL) == OK
--- 896,906 ----
int vim9script = in_vim9script();
int had_comma;

! // First check if it's not a curly-braces thing: {expr}.
! // Must do this without evaluating, otherwise a function may be called
! // twice. Unfortunately this means we need to call eval1() twice for the
! // first item.
! // But {} is an empty Dictionary.
if (!vim9script
&& *curly_expr != '}'
&& eval1(&curly_expr, &tv, NULL) == OK
***************
*** 1184,1189 ****
--- 1182,1432 ----
}

/*
+ * Count the number of times item "needle" occurs in Dict "d". Case is ignored
+ * if "ic" is TRUE.
+ */
+ long
+ dict_count(dict_T *d, typval_T *needle, int ic)
+ {
+ int todo;
+ hashitem_T *hi;
+ long n = 0;
+
+ if (d == NULL)
+ return 0;
+
+ todo = (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, needle, ic, FALSE))
+ ++n;
+ }
+ }
+
+ return n;
+ }
+
+ /*
+ * extend() a Dict. Append Dict argvars[1] to Dict argvars[0] and return the
+ * resulting Dict in "rettv". "is_new" is TRUE for extendnew().
+ */
+ void
+ dict_extend_func(
+ typval_T *argvars,
+ type_T *type,
+ char *func_name,
+ char_u *arg_errmsg,
+ int is_new,
+ typval_T *rettv)
+ {
+ dict_T *d1, *d2;
+ char_u *action;
+ int i;
+
+ d1 = argvars[0].vval.v_dict;
+ if (d1 == NULL)
+ {
+ emsg(_(e_cannot_extend_null_dict));
+ return;
+ }
+ d2 = argvars[1].vval.v_dict;
+ if ((is_new || !value_check_lock(d1->dv_lock, arg_errmsg, TRUE))
+ && d2 != NULL)
+ {
+ if (is_new)
+ {
+ d1 = dict_copy(d1, FALSE, get_copyID());
+ if (d1 == NULL)
+ return;
+ }
+
+ // 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;
+ 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";
+
+ if (type != NULL && check_typval_arg_type(type, &argvars[1],
+ func_name, 2) == FAIL)
+ return;
+ dict_extend(d1, d2, action, func_name);
+
+ if (is_new)
+ {
+ rettv->v_type = VAR_DICT;
+ rettv->vval.v_dict = d1;
+ rettv->v_lock = FALSE;
+ }
+ else
+ copy_tv(&argvars[0], rettv);
+ }
+ }
+
+ /*
+ * Implementation of map() and filter() for a Dict. Apply "expr" to every
+ * item in Dict "d" and return the result in "rettv".
+ */
+ void
+ dict_filter_map(
+ dict_T *d,
+ filtermap_T filtermap,
+ type_T *argtype,
+ char *func_name,
+ char_u *arg_errmsg,
+ typval_T *expr,
+ typval_T *rettv)
+ {
+ int prev_lock;
+ dict_T *d_ret = NULL;
+ hashtab_T *ht;
+ hashitem_T *hi;
+ dictitem_T *di;
+ int todo;
+ int rem;
+
+ if (filtermap == FILTERMAP_MAPNEW)
+ {
+ rettv->v_type = VAR_DICT;
+ rettv->vval.v_dict = NULL;
+ }
+ if (d == NULL
+ || (filtermap == FILTERMAP_FILTER
+ && value_check_lock(d->dv_lock, arg_errmsg, TRUE)))
+ return;
+
+ prev_lock = d->dv_lock;
+
+ if (filtermap == FILTERMAP_MAPNEW)
+ {
+ if (rettv_dict_alloc(rettv) == FAIL)
+ return;
+ d_ret = rettv->vval.v_dict;
+ }
+
+ if (filtermap != FILTERMAP_FILTER && d->dv_lock == 0)
+ d->dv_lock = VAR_LOCKED;
+ ht = &d->dv_hashtab;
+ hash_lock(ht);
+ todo = (int)ht->ht_used;
+ for (hi = ht->ht_array; todo > 0; ++hi)
+ {
+ if (!HASHITEM_EMPTY(hi))
+ {
+ int r;
+ typval_T newtv;
+
+ --todo;
+ di = HI2DI(hi);
+ if (filtermap == FILTERMAP_MAP
+ && (value_check_lock(di->di_tv.v_lock,
+ arg_errmsg, TRUE)
+ || var_check_ro(di->di_flags,
+ arg_errmsg, TRUE)))
+ break;
+ set_vim_var_string(VV_KEY, di->di_key, -1);
+ newtv.v_type = VAR_UNKNOWN;
+ r = filter_map_one(&di->di_tv, expr, filtermap,
+ &newtv, &rem);
+ clear_tv(get_vim_var_tv(VV_KEY));
+ if (r == FAIL || did_emsg)
+ {
+ clear_tv(&newtv);
+ break;
+ }
+ if (filtermap == FILTERMAP_MAP)
+ {
+ if (argtype != NULL && check_typval_arg_type(
+ argtype->tt_member, &newtv,
+ func_name, 0) == FAIL)
+ {
+ clear_tv(&newtv);
+ break;
+ }
+ // map(): replace the dict item value
+ clear_tv(&di->di_tv);
+ newtv.v_lock = 0;
+ di->di_tv = newtv;
+ }
+ else if (filtermap == FILTERMAP_MAPNEW)
+ {
+ // mapnew(): add the item value to the new dict
+ r = dict_add_tv(d_ret, (char *)di->di_key, &newtv);
+ clear_tv(&newtv);
+ if (r == FAIL)
+ break;
+ }
+ else if (filtermap == FILTERMAP_FILTER && rem)
+ {
+ // filter(false): remove the item from the dict
+ if (var_check_fixed(di->di_flags, arg_errmsg, TRUE)
+ || var_check_ro(di->di_flags, arg_errmsg, TRUE))
+ break;
+ dictitem_remove(d, di);
+ }
+ }
+ }
+ hash_unlock(ht);
+ d->dv_lock = prev_lock;
+ }
+
+ /*
+ * "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_too_many_arguments_for_function_str), "remove()");
+ return;
+ }
+
+ d = argvars[0].vval.v_dict;
+ if (d == NULL || value_check_lock(d->dv_lock, arg_errmsg, TRUE))
+ return;
+
+ key = tv_get_string_chk(&argvars[1]);
+ if (key == NULL)
+ return;
+
+ di = dict_find(d, key, -1);
+ if (di == NULL)
+ {
+ semsg(_(e_dictkey), key);
+ return;
+ }
+
+ if (var_check_fixed(di->di_flags, arg_errmsg, TRUE)
+ || var_check_ro(di->di_flags, arg_errmsg, TRUE))
+ return;
+
+ *rettv = di->di_tv;
+ init_tv(&di->di_tv);
+ dictitem_remove(d, di);
+ }
+
+ /*
* Turn a dict into a list:
* "what" == 0: list of keys
* "what" == 1: list of values
***************
*** 1338,1373 ****
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_too_many_arguments_for_function_str), "remove()");
- else if ((d = argvars[0].vval.v_dict) != NULL
- && !value_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)
--- 1581,1584 ----
*** ../vim-8.2.3870/src/list.c 2021-12-21 13:19:38.745205784 +0000
--- src/list.c 2021-12-22 18:04:56.843931664 +0000
***************
*** 314,341 ****
}

/*
- * Make a typval_T of the first character of "input" and store it in "output".
- * Return OK or FAIL.
- */
- static int
- tv_get_first_char(char_u *input, typval_T *output)
- {
- char_u buf[MB_MAXBYTES + 1];
- int len;
-
- if (input == NULL || output == NULL)
- return FAIL;
-
- len = has_mbyte ? mb_ptr2len(input) : 1;
- STRNCPY(buf, input, len);
- buf[len] = NUL;
- output->v_type = VAR_STRING;
- output->vval.v_string = vim_strsave(buf);
-
- return output->vval.v_string == NULL ? FAIL : OK;
- }
-
- /*
* Free a list item, unless it was allocated together with the list itself.
* Does not clear the value. Does not notify watchers.
*/
--- 314,319 ----
***************
*** 876,884 ****
long idx;
type_T *member_type = NULL;

! /*
! * Check whether any of the list items is locked before making any changes.
! */
idx = idx1;
dest_li = first_li;
for (src_li = src->lv_first; src_li != NULL && dest_li != NULL; )
--- 854,860 ----
long idx;
type_T *member_type = NULL;

! // Check whether any of the list items is locked before making any changes.
idx = idx1;
dest_li = first_li;
for (src_li = src->lv_first; src_li != NULL && dest_li != NULL; )
***************
*** 896,904 ****
&& dest->lv_type->tt_member != NULL)
member_type = dest->lv_type->tt_member;

! /*
! * Assign the List values to the list items.
! */
idx = idx1;
dest_li = first_li;
for (src_li = src->lv_first; src_li != NULL; )
--- 872,878 ----
&& dest->lv_type->tt_member != NULL)
member_type = dest->lv_type->tt_member;

! // Assign the List values to the list items.
idx = idx1;
dest_li = first_li;
for (src_li = src->lv_first; src_li != NULL; )
***************
*** 1725,1730 ****
--- 1699,1708 ----
rettv->vval.v_string = ga.ga_data;
}

+ /*
+ * Remove item argvars[1] from List argvars[0]. If argvars[2] is supplied, then
+ * remove the range of items from argvars[1] to argvars[2] (inclusive).
+ */
static void
list_remove(typval_T *argvars, typval_T *rettv, char_u *arg_errmsg)
{
***************
*** 1733,1738 ****
--- 1711,1719 ----
listitem_T *li;
int error = FALSE;
long idx;
+ long end;
+ int cnt = 0;
+ list_T *rl;

if ((l = argvars[0].vval.v_list) == NULL
|| value_check_lock(l->lv_lock, arg_errmsg, TRUE))
***************
*** 1740,1814 ****

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;
! list_free_item(l, item);
! }
! else
! {
! // Remove range of items, return list with values.
! long 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_invalid_range));
! else
! {
! vimlist_remove(l, item, item2);
! if (rettv_list_alloc(rettv) == OK)
! {
! list_T *rl = rettv->vval.v_list;
!
! if (l->lv_with_items > 0)
! {
! // need to copy the list items and move the value
! while (item != NULL)
! {
! li = listitem_alloc();
! if (li == NULL)
! return;
! li->li_tv = item->li_tv;
! init_tv(&item->li_tv);
! list_append(rl, li);
! if (item == item2)
! break;
! item = item->li_next;
! }
! }
! else
! {
! rl->lv_first = item;
! rl->lv_u.mat.lv_last = item2;
! item->li_prev = NULL;
! item2->li_next = NULL;
! rl->lv_len = cnt;
! }
! }
! }
! }
}
}
}

static int item_compare(const void *s1, const void *s2);
--- 1721,1796 ----

idx = (long)tv_get_number_chk(&argvars[1], &error);
if (error)
! return; // type error: do nothing, errmsg already given
!
! if ((item = list_find(l, idx)) == NULL)
! {
semsg(_(e_listidx), idx);
! return;
! }
!
! if (argvars[2].v_type == VAR_UNKNOWN)
{
! // Remove one item, return its value.
! vimlist_remove(l, item, item);
! *rettv = item->li_tv;
! list_free_item(l, item);
! return;
! }

! // Remove range of items, return list with values.
! end = (long)tv_get_number_chk(&argvars[2], &error);
! if (error)
! return; // type error: do nothing

! if ((item2 = list_find(l, end)) == NULL)
! {
! semsg(_(e_listidx), end);
! return;
! }
!
! 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_invalid_range));
! return;
! }
!
! vimlist_remove(l, item, item2);
! if (rettv_list_alloc(rettv) != OK)
! return;
!
! rl = rettv->vval.v_list;
!
! if (l->lv_with_items > 0)
! {
! // need to copy the list items and move the value
! while (item != NULL)
! {
! li = listitem_alloc();
! if (li == NULL)
! return;
! li->li_tv = item->li_tv;
! init_tv(&item->li_tv);
! list_append(rl, li);
! if (item == item2)
! break;
! item = item->li_next;
}
}
+ else
+ {
+ rl->lv_first = item;
+ rl->lv_u.mat.lv_last = item2;
+ item->li_prev = NULL;
+ item2->li_next = NULL;
+ rl->lv_len = cnt;
+ }
}

static int item_compare(const void *s1, const void *s2);
***************
*** 2101,2108 ****
}

/*
! * Parse the optional arguments to sort() and uniq() and return the values in
! * 'info'.
*/
static int
parse_sort_uniq_args(typval_T *argvars, sortinfo_T *info)
--- 2083,2090 ----
}

/*
! * Parse the optional arguments supplied to the sort() or uniq() function and
! * return the values in "info".
*/
static int
parse_sort_uniq_args(typval_T *argvars, sortinfo_T *info)
***************
*** 2272,2288 ****
do_sort_uniq(argvars, rettv, FALSE);
}

- typedef enum {
- FILTERMAP_FILTER,
- FILTERMAP_MAP,
- FILTERMAP_MAPNEW
- } filtermap_T;
-
/*
* Handle one item for map() and filter().
* Sets v:val to "tv". Caller must set v:key.
*/
! static int
filter_map_one(
typval_T *tv, // original value
typval_T *expr, // callback
--- 2254,2264 ----
do_sort_uniq(argvars, rettv, FALSE);
}

/*
* Handle one item for map() and filter().
* Sets v:val to "tv". Caller must set v:key.
*/
! int
filter_map_one(
typval_T *tv, // original value
typval_T *expr, // callback
***************
*** 2320,2573 ****
}

/*
! * Implementation of map() and filter() for a Dict.
! */
! static void
! filter_map_dict(
! dict_T *d,
! filtermap_T filtermap,
! type_T *argtype,
! char *func_name,
! char_u *arg_errmsg,
! typval_T *expr,
! typval_T *rettv)
! {
! int prev_lock;
! dict_T *d_ret = NULL;
! hashtab_T *ht;
! hashitem_T *hi;
! dictitem_T *di;
! int todo;
! int rem;
!
! if (filtermap == FILTERMAP_MAPNEW)
! {
! rettv->v_type = VAR_DICT;
! rettv->vval.v_dict = NULL;
! }
! if (d == NULL
! || (filtermap == FILTERMAP_FILTER
! && value_check_lock(d->dv_lock, arg_errmsg, TRUE)))
! return;
!
! prev_lock = d->dv_lock;
!
! if (filtermap == FILTERMAP_MAPNEW)
! {
! if (rettv_dict_alloc(rettv) == FAIL)
! return;
! d_ret = rettv->vval.v_dict;
! }
!
! if (filtermap != FILTERMAP_FILTER && d->dv_lock == 0)
! d->dv_lock = VAR_LOCKED;
! ht = &d->dv_hashtab;
! hash_lock(ht);
! todo = (int)ht->ht_used;
! for (hi = ht->ht_array; todo > 0; ++hi)
! {
! if (!HASHITEM_EMPTY(hi))
! {
! int r;
! typval_T newtv;
!
! --todo;
! di = HI2DI(hi);
! if (filtermap == FILTERMAP_MAP
! && (value_check_lock(di->di_tv.v_lock,
! arg_errmsg, TRUE)
! || var_check_ro(di->di_flags,
! arg_errmsg, TRUE)))
! break;
! set_vim_var_string(VV_KEY, di->di_key, -1);
! newtv.v_type = VAR_UNKNOWN;
! r = filter_map_one(&di->di_tv, expr, filtermap,
! &newtv, &rem);
! clear_tv(get_vim_var_tv(VV_KEY));
! if (r == FAIL || did_emsg)
! {
! clear_tv(&newtv);
! break;
! }
! if (filtermap == FILTERMAP_MAP)
! {
! if (argtype != NULL && check_typval_arg_type(
! argtype->tt_member, &newtv,
! func_name, 0) == FAIL)
! {
! clear_tv(&newtv);
! break;
! }
! // map(): replace the dict item value
! clear_tv(&di->di_tv);
! newtv.v_lock = 0;
! di->di_tv = newtv;
! }
! else if (filtermap == FILTERMAP_MAPNEW)
! {
! // mapnew(): add the item value to the new dict
! r = dict_add_tv(d_ret, (char *)di->di_key, &newtv);
! clear_tv(&newtv);
! if (r == FAIL)
! break;
! }
! else if (filtermap == FILTERMAP_FILTER && rem)
! {
! // filter(false): remove the item from the dict
! if (var_check_fixed(di->di_flags, arg_errmsg, TRUE)
! || var_check_ro(di->di_flags, arg_errmsg, TRUE))
! break;
! dictitem_remove(d, di);
! }
! }
! }
! hash_unlock(ht);
! d->dv_lock = prev_lock;
! }
!
! /*
! * Implementation of map() and filter() for a Blob.
*/
static void
! filter_map_blob(
! blob_T *blob_arg,
! filtermap_T filtermap,
! typval_T *expr,
! typval_T *rettv)
! {
! blob_T *b;
! int i;
! typval_T tv;
! varnumber_T val;
! blob_T *b_ret;
! int idx = 0;
! int rem;
!
! if (filtermap == FILTERMAP_MAPNEW)
! {
! rettv->v_type = VAR_BLOB;
! rettv->vval.v_blob = NULL;
! }
! if ((b = blob_arg) == NULL)
! return;
!
! b_ret = b;
! if (filtermap == FILTERMAP_MAPNEW)
! {
! if (blob_copy(b, rettv) == FAIL)
! return;
! b_ret = rettv->vval.v_blob;
! }
!
! // set_vim_var_nr() doesn't set the type
! set_vim_var_type(VV_KEY, VAR_NUMBER);
!
! for (i = 0; i < b->bv_ga.ga_len; i++)
! {
! typval_T newtv;
!
! tv.v_type = VAR_NUMBER;
! val = blob_get(b, i);
! tv.vval.v_number = val;
! set_vim_var_nr(VV_KEY, idx);
! if (filter_map_one(&tv, expr, filtermap, &newtv, &rem) == FAIL
! || did_emsg)
! break;
! if (newtv.v_type != VAR_NUMBER && newtv.v_type != VAR_BOOL)
! {
! clear_tv(&newtv);
! emsg(_(e_invalblob));
! break;
! }
! if (filtermap != FILTERMAP_FILTER)
! {
! if (newtv.vval.v_number != val)
! blob_set(b_ret, i, newtv.vval.v_number);
! }
! else if (rem)
! {
! char_u *p = (char_u *)blob_arg->bv_ga.ga_data;
!
! mch_memmove(p + i, p + i + 1,
! (size_t)b->bv_ga.ga_len - i - 1);
! --b->bv_ga.ga_len;
! --i;
! }
! ++idx;
! }
! }
!
! /*
! * Implementation of map() and filter() for a String.
! */
! static void
! filter_map_string(
! char_u *str,
! filtermap_T filtermap,
! typval_T *expr,
! typval_T *rettv)
! {
! char_u *p;
! typval_T tv;
! garray_T ga;
! int len = 0;
! int idx = 0;
! int rem;
!
! rettv->v_type = VAR_STRING;
! rettv->vval.v_string = NULL;
!
! // set_vim_var_nr() doesn't set the type
! set_vim_var_type(VV_KEY, VAR_NUMBER);
!
! ga_init2(&ga, (int)sizeof(char), 80);
! for (p = str; *p != NUL; p += len)
! {
! typval_T newtv;
!
! if (tv_get_first_char(p, &tv) == FAIL)
! break;
! len = (int)STRLEN(tv.vval.v_string);
!
! set_vim_var_nr(VV_KEY, idx);
! if (filter_map_one(&tv, expr, filtermap, &newtv, &rem) == FAIL
! || did_emsg)
! break;
! if (did_emsg)
! {
! clear_tv(&newtv);
! clear_tv(&tv);
! break;
! }
! else if (filtermap != FILTERMAP_FILTER)
! {
! if (newtv.v_type != VAR_STRING)
! {
! clear_tv(&newtv);
! clear_tv(&tv);
! emsg(_(e_stringreq));
! break;
! }
! else
! ga_concat(&ga, newtv.vval.v_string);
! }
! else if (!rem)
! ga_concat(&ga, tv.vval.v_string);
!
! clear_tv(&newtv);
! clear_tv(&tv);
!
! ++idx;
! }
! ga_append(&ga, NUL);
! rettv->vval.v_string = ga.ga_data;
! }
!
! /*
! * Implementation of map() and filter() for a List.
! */
! static void
! filter_map_list(
list_T *l,
filtermap_T filtermap,
type_T *argtype,
--- 2296,2306 ----
}

/*
! * Implementation of map() and filter() for a List. Apply "expr" to every item
! * in List "l" and return the result in "rettv".
*/
static void
! list_filter_map(
list_T *l,
filtermap_T filtermap,
type_T *argtype,
***************
*** 2775,2789 ****
did_emsg = FALSE;

if (argvars[0].v_type == VAR_DICT)
! filter_map_dict(argvars[0].vval.v_dict, filtermap, type, func_name,
arg_errmsg, expr, rettv);
else if (argvars[0].v_type == VAR_BLOB)
! filter_map_blob(argvars[0].vval.v_blob, filtermap, expr, rettv);
else if (argvars[0].v_type == VAR_STRING)
! filter_map_string(tv_get_string(&argvars[0]), filtermap, expr,
rettv);
else // argvars[0].v_type == VAR_LIST
! filter_map_list(argvars[0].vval.v_list, filtermap, type, func_name,
arg_errmsg, expr, rettv);

restore_vimvar(VV_KEY, &save_key);
--- 2508,2522 ----
did_emsg = FALSE;

if (argvars[0].v_type == VAR_DICT)
! dict_filter_map(argvars[0].vval.v_dict, filtermap, type, func_name,
arg_errmsg, expr, rettv);
else if (argvars[0].v_type == VAR_BLOB)
! blob_filter_map(argvars[0].vval.v_blob, filtermap, expr, rettv);
else if (argvars[0].v_type == VAR_STRING)
! string_filter_map(tv_get_string(&argvars[0]), filtermap, expr,
rettv);
else // argvars[0].v_type == VAR_LIST
! list_filter_map(argvars[0].vval.v_list, filtermap, type, func_name,
arg_errmsg, expr, rettv);

restore_vimvar(VV_KEY, &save_key);
***************
*** 2827,2832 ****
--- 2560,2586 ----
/*
* "add(list, item)" function
*/
+ static void
+ list_add(typval_T *argvars, typval_T *rettv)
+ {
+ list_T *l = argvars[0].vval.v_list;
+
+ if (l == NULL)
+ {
+ if (in_vim9script())
+ emsg(_(e_cannot_add_to_null_list));
+ }
+ else if (!value_check_lock(l->lv_lock,
+ (char_u *)N_("add() argument"), TRUE)
+ && list_append_tv(l, &argvars[1]) == OK)
+ {
+ copy_tv(&argvars[0], rettv);
+ }
+ }
+
+ /*
+ * "add(object, item)" function
+ */
void
f_add(typval_T *argvars, typval_T *rettv)
{
***************
*** 2839,2930 ****
return;

if (argvars[0].v_type == VAR_LIST)
! {
! list_T *l = argvars[0].vval.v_list;
!
! if (l == NULL)
! {
! if (in_vim9script())
! emsg(_(e_cannot_add_to_null_list));
! }
! else if (!value_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)
! {
! blob_T *b = argvars[0].vval.v_blob;
!
! if (b == NULL)
! {
! if (in_vim9script())
! emsg(_(e_cannot_add_to_null_blob));
! }
! else if (!value_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 the number of times "needle" occurs in string "haystack". Case is
- * ignored if "ic" is TRUE.
- */
- static long
- count_string(char_u *haystack, char_u *needle, int ic)
- {
- long n = 0;
- char_u *p = haystack;
- char_u *next;
-
- if (p == NULL || needle == NULL || *needle == NUL)
- return 0;
-
- if (ic)
- {
- size_t len = STRLEN(needle);
-
- while (*p != NUL)
- {
- if (MB_STRNICMP(p, needle, len) == 0)
- {
- ++n;
- p += len;
- }
- else
- MB_PTR_ADV(p);
- }
- }
- else
- while ((next = (char_u *)strstr((char *)p, (char *)needle)) != NULL)
- {
- ++n;
- p = next + STRLEN(needle);
- }
-
- return n;
- }
-
- /*
* Count the number of times item "needle" occurs in List "l" starting at index
* "idx". Case is ignored if "ic" is TRUE.
*/
static long
! count_list(list_T *l, typval_T *needle, long idx, int ic)
{
long n = 0;
listitem_T *li;
--- 2593,2611 ----
return;

if (argvars[0].v_type == VAR_LIST)
! list_add(argvars, rettv);
else if (argvars[0].v_type == VAR_BLOB)
! blob_add(argvars, rettv);
else
emsg(_(e_listblobreq));
}

/*
* Count the number of times item "needle" occurs in List "l" starting at index
* "idx". Case is ignored if "ic" is TRUE.
*/
static long
! list_count(list_T *l, typval_T *needle, long idx, int ic)
{
long n = 0;
listitem_T *li;
***************
*** 2952,2985 ****
}

/*
- * Count the number of times item "needle" occurs in Dict "d". Case is ignored
- * if "ic" is TRUE.
- */
- static long
- count_dict(dict_T *d, typval_T *needle, int ic)
- {
- int todo;
- hashitem_T *hi;
- long n = 0;
-
- if (d == NULL)
- return 0;
-
- todo = (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, needle, ic, FALSE))
- ++n;
- }
- }
-
- return n;
- }
-
- /*
* "count()" function
*/
void
--- 2633,2638 ----
***************
*** 3000,3006 ****
ic = (int)tv_get_bool_chk(&argvars[2], &error);

if (!error && argvars[0].v_type == VAR_STRING)
! n = count_string(argvars[0].vval.v_string,
tv_get_string_chk(&argvars[1]), ic);
else if (!error && argvars[0].v_type == VAR_LIST)
{
--- 2653,2659 ----
ic = (int)tv_get_bool_chk(&argvars[2], &error);

if (!error && argvars[0].v_type == VAR_STRING)
! n = string_count(argvars[0].vval.v_string,
tv_get_string_chk(&argvars[1]), ic);
else if (!error && argvars[0].v_type == VAR_LIST)
{
***************
*** 3010,3016 ****
&& argvars[3].v_type != VAR_UNKNOWN)
idx = (long)tv_get_number_chk(&argvars[3], &error);
if (!error)
! n = count_list(argvars[0].vval.v_list, &argvars[1], idx, ic);
}
else if (!error && argvars[0].v_type == VAR_DICT)
{
--- 2663,2669 ----
&& argvars[3].v_type != VAR_UNKNOWN)
idx = (long)tv_get_number_chk(&argvars[3], &error);
if (!error)
! n = list_count(argvars[0].vval.v_list, &argvars[1], idx, ic);
}
else if (!error && argvars[0].v_type == VAR_DICT)
{
***************
*** 3018,3024 ****
&& argvars[3].v_type != VAR_UNKNOWN)
emsg(_(e_invarg));
else
! n = count_dict(argvars[0].vval.v_dict, &argvars[1], ic);
}
else
semsg(_(e_listdictarg), "count()");
--- 2671,2677 ----
&& argvars[3].v_type != VAR_UNKNOWN)
emsg(_(e_invarg));
else
! n = dict_count(argvars[0].vval.v_dict, &argvars[1], ic);
}
else
semsg(_(e_listdictarg), "count()");
***************
*** 3031,3037 ****
* extendnew().
*/
static void
! extend_list(
typval_T *argvars,
type_T *type,
char *func_name,
--- 2684,2690 ----
* extendnew().
*/
static void
! list_extend_func(
typval_T *argvars,
type_T *type,
char *func_name,
***************
*** 3098,3173 ****
}

/*
- * extend() a Dict. Append Dict argvars[1] to Dict argvars[0] and return the
- * resulting Dict in "rettv". "is_new" is TRUE for extendnew().
- */
- static void
- extend_dict(
- typval_T *argvars,
- type_T *type,
- char *func_name,
- char_u *arg_errmsg,
- int is_new,
- typval_T *rettv)
- {
- dict_T *d1, *d2;
- char_u *action;
- int i;
-
- d1 = argvars[0].vval.v_dict;
- if (d1 == NULL)
- {
- emsg(_(e_cannot_extend_null_dict));
- return;
- }
- d2 = argvars[1].vval.v_dict;
- if ((is_new || !value_check_lock(d1->dv_lock, arg_errmsg, TRUE))
- && d2 != NULL)
- {
- if (is_new)
- {
- d1 = dict_copy(d1, FALSE, get_copyID());
- if (d1 == NULL)
- return;
- }
-
- // 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;
- 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";
-
- if (type != NULL && check_typval_arg_type(type, &argvars[1],
- func_name, 2) == FAIL)
- return;
- dict_extend(d1, d2, action, func_name);
-
- if (is_new)
- {
- rettv->v_type = VAR_DICT;
- rettv->vval.v_dict = d1;
- rettv->v_lock = FALSE;
- }
- else
- copy_tv(&argvars[0], rettv);
- }
- }
-
- /*
* "extend()" or "extendnew()" function. "is_new" is TRUE for extendnew().
*/
static void
--- 2751,2756 ----
***************
*** 3185,3193 ****
}

if (argvars[0].v_type == VAR_LIST && argvars[1].v_type == VAR_LIST)
! extend_list(argvars, type, func_name, arg_errmsg, is_new, rettv);
else if (argvars[0].v_type == VAR_DICT && argvars[1].v_type == VAR_DICT)
! extend_dict(argvars, type, func_name, arg_errmsg, is_new, rettv);
else
semsg(_(e_listdictarg), func_name);

--- 2768,2776 ----
}

if (argvars[0].v_type == VAR_LIST && argvars[1].v_type == VAR_LIST)
! list_extend_func(argvars, type, func_name, arg_errmsg, is_new, rettv);
else if (argvars[0].v_type == VAR_DICT && argvars[1].v_type == VAR_DICT)
! dict_extend_func(argvars, type, func_name, arg_errmsg, is_new, rettv);
else
semsg(_(e_listdictarg), func_name);

***************
*** 3219,3234 ****
extend(argvars, rettv, errmsg, TRUE);
}

/*
* "insert()" function
*/
void
f_insert(typval_T *argvars, typval_T *rettv)
{
- long before = 0;
- listitem_T *item;
- int error = FALSE;
-
if (in_vim9script()
&& (check_for_list_or_blob_arg(argvars, 0) == FAIL
|| (argvars[0].v_type == VAR_BLOB
--- 2802,2854 ----
extend(argvars, rettv, errmsg, TRUE);
}

+ static void
+ list_insert_func(typval_T *argvars, typval_T *rettv)
+ {
+ list_T *l = argvars[0].vval.v_list;
+ long before = 0;
+ listitem_T *item;
+ int error = FALSE;
+
+ if (l == NULL)
+ {
+ if (in_vim9script())
+ emsg(_(e_cannot_add_to_null_list));
+ return;
+ }
+
+ if (value_check_lock(l->lv_lock, (char_u *)N_("insert() argument"), TRUE))
+ return;
+
+ 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)
+ {
+ (void)list_insert_tv(l, &argvars[1], item);
+ copy_tv(&argvars[0], rettv);
+ }
+ }
+
/*
* "insert()" function
*/
void
f_insert(typval_T *argvars, typval_T *rettv)
{
if (in_vim9script()
&& (check_for_list_or_blob_arg(argvars, 0) == FAIL
|| (argvars[0].v_type == VAR_BLOB
***************
*** 3237,3324 ****
return;

if (argvars[0].v_type == VAR_BLOB)
! {
! blob_T *b = argvars[0].vval.v_blob;
!
! if (b == NULL)
! {
! if (in_vim9script())
! emsg(_(e_cannot_add_to_null_blob));
! }
! else if (!value_check_lock(b->bv_lock,
! (char_u *)N_("insert() argument"), TRUE))
! {
! int val, len;
! char_u *p;
!
! len = blob_len(b);
! 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(&b->bv_ga, 1) == FAIL)
! return;
! p = (char_u *)b->bv_ga.ga_data;
! mch_memmove(p + before + 1, p + before, (size_t)len - before);
! *(p + before) = val;
! ++b->bv_ga.ga_len;
!
! copy_tv(&argvars[0], rettv);
! }
! }
else if (argvars[0].v_type != VAR_LIST)
semsg(_(e_listblobarg), "insert()");
else
! {
! list_T *l = argvars[0].vval.v_list;
!
! if (l == NULL)
! {
! if (in_vim9script())
! emsg(_(e_cannot_add_to_null_list));
! }
! else if (!value_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)
! {
! (void)list_insert_tv(l, &argvars[1], item);
! copy_tv(&argvars[0], rettv);
! }
! }
! }
}

/*
--- 2857,2867 ----
return;

if (argvars[0].v_type == VAR_BLOB)
! blob_insert_func(argvars, rettv);
else if (argvars[0].v_type != VAR_LIST)
semsg(_(e_listblobarg), "insert()");
else
! list_insert_func(argvars, rettv);
}

/*
***************
*** 3349,3414 ****
semsg(_(e_listdictblobarg), "remove()");
}

/*
* "reverse({list})" function
*/
void
f_reverse(typval_T *argvars, typval_T *rettv)
{
- list_T *l;
- listitem_T *li, *ni;
-
if (in_vim9script() && check_for_list_or_blob_arg(argvars, 0) == FAIL)
return;

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
! {
! l = argvars[0].vval.v_list;
! rettv_list_set(rettv, l);
! if (l != NULL
! && !value_check_lock(l->lv_lock,
! (char_u *)N_("reverse() argument"), TRUE))
! {
! if (l->lv_first == &range_list_item)
! {
! varnumber_T new_start = l->lv_u.nonmat.lv_start
! + (l->lv_len - 1) * l->lv_u.nonmat.lv_stride;
! l->lv_u.nonmat.lv_end = new_start
! - (l->lv_u.nonmat.lv_end - l->lv_u.nonmat.lv_start);
! l->lv_u.nonmat.lv_start = new_start;
! l->lv_u.nonmat.lv_stride = -l->lv_u.nonmat.lv_stride;
! return;
! }
! li = l->lv_u.mat.lv_last;
! l->lv_first = l->lv_u.mat.lv_last = NULL;
! l->lv_len = 0;
! while (li != NULL)
! {
! ni = li->li_prev;
! list_append(l, li);
! li = ni;
! }
! l->lv_u.mat.lv_idx = l->lv_len - l->lv_u.mat.lv_idx - 1;
! }
! }
}

/*
--- 2892,2945 ----
semsg(_(e_listdictblobarg), "remove()");
}

+ static void
+ list_reverse(list_T *l, typval_T *rettv)
+ {
+ listitem_T *li, *ni;
+
+ rettv_list_set(rettv, l);
+ if (l != NULL
+ && !value_check_lock(l->lv_lock,
+ (char_u *)N_("reverse() argument"), TRUE))
+ {
+ if (l->lv_first == &range_list_item)
+ {
+ varnumber_T new_start = l->lv_u.nonmat.lv_start
+ + (l->lv_len - 1) * l->lv_u.nonmat.lv_stride;
+ l->lv_u.nonmat.lv_end = new_start
+ - (l->lv_u.nonmat.lv_end - l->lv_u.nonmat.lv_start);
+ l->lv_u.nonmat.lv_start = new_start;
+ l->lv_u.nonmat.lv_stride = -l->lv_u.nonmat.lv_stride;
+ return;
+ }
+ li = l->lv_u.mat.lv_last;
+ l->lv_first = l->lv_u.mat.lv_last = NULL;
+ l->lv_len = 0;
+ while (li != NULL)
+ {
+ ni = li->li_prev;
+ list_append(l, li);
+ li = ni;
+ }
+ l->lv_u.mat.lv_idx = l->lv_len - l->lv_u.mat.lv_idx - 1;
+ }
+ }
+
/*
* "reverse({list})" function
*/
void
f_reverse(typval_T *argvars, typval_T *rettv)
{
if (in_vim9script() && check_for_list_or_blob_arg(argvars, 0) == FAIL)
return;

if (argvars[0].v_type == VAR_BLOB)
! blob_reverse(argvars[0].vval.v_blob, rettv);
! else if (argvars[0].v_type != VAR_LIST)
semsg(_(e_listblobarg), "reverse()");
else
! list_reverse(argvars[0].vval.v_list, rettv);
}

/*
***************
*** 3417,3423 ****
* in 'rettv'.
*/
static void
! reduce_list(
typval_T *argvars,
char_u *func_name,
funcexe_T *funcexe,
--- 2948,2954 ----
* in 'rettv'.
*/
static void
! list_reduce(
typval_T *argvars,
char_u *func_name,
funcexe_T *funcexe,
***************
*** 3471,3584 ****
}

/*
- * reduce() String argvars[0] using the function 'funcname' with arguments in
- * 'funcexe' starting with the initial value argvars[2] and return the result
- * in 'rettv'.
- */
- static void
- reduce_string(
- typval_T *argvars,
- char_u *func_name,
- funcexe_T *funcexe,
- typval_T *rettv)
- {
- char_u *p = tv_get_string(&argvars[0]);
- int len;
- typval_T argv[3];
- int r;
- int called_emsg_start = called_emsg;
-
- if (argvars[2].v_type == VAR_UNKNOWN)
- {
- if (*p == NUL)
- {
- semsg(_(e_reduceempty), "String");
- return;
- }
- if (tv_get_first_char(p, rettv) == FAIL)
- return;
- p += STRLEN(rettv->vval.v_string);
- }
- else if (argvars[2].v_type != VAR_STRING)
- {
- semsg(_(e_string_expected_for_argument_nr), 3);
- return;
- }
- else
- copy_tv(&argvars[2], rettv);
-
- for ( ; *p != NUL; p += len)
- {
- argv[0] = *rettv;
- if (tv_get_first_char(p, &argv[1]) == FAIL)
- break;
- len = (int)STRLEN(argv[1].vval.v_string);
- r = call_func(func_name, -1, rettv, 2, argv, funcexe);
- clear_tv(&argv[0]);
- clear_tv(&argv[1]);
- if (r == FAIL || called_emsg != called_emsg_start)
- return;
- }
- }
-
- /*
- * reduce() Blob argvars[0] using the function 'funcname' with arguments in
- * 'funcexe' starting with the initial value argvars[2] and return the result
- * in 'rettv'.
- */
- static void
- reduce_blob(
- typval_T *argvars,
- char_u *func_name,
- funcexe_T *funcexe,
- typval_T *rettv)
- {
- blob_T *b = argvars[0].vval.v_blob;
- int called_emsg_start = called_emsg;
- int r;
- typval_T initial;
- typval_T argv[3];
- int i;
-
- if (argvars[2].v_type == VAR_UNKNOWN)
- {
- if (b == NULL || b->bv_ga.ga_len == 0)
- {
- semsg(_(e_reduceempty), "Blob");
- return;
- }
- initial.v_type = VAR_NUMBER;
- initial.vval.v_number = blob_get(b, 0);
- i = 1;
- }
- else if (argvars[2].v_type != VAR_NUMBER)
- {
- emsg(_(e_number_expected));
- return;
- }
- else
- {
- initial = argvars[2];
- i = 0;
- }
-
- copy_tv(&initial, rettv);
- if (b == NULL)
- return;
-
- for ( ; i < b->bv_ga.ga_len; i++)
- {
- argv[0] = *rettv;
- argv[1].v_type = VAR_NUMBER;
- argv[1].vval.v_number = blob_get(b, i);
- r = call_func(func_name, -1, rettv, 2, argv, funcexe);
- clear_tv(&argv[0]);
- if (r == FAIL || called_emsg != called_emsg_start)
- return;
- }
- }
-
- /*
* "reduce(list, { accumulator, element -> value } [, initial])" function
* "reduce(blob, { accumulator, element -> value } [, initial])"
* "reduce(string, { accumulator, element -> value } [, initial])"
--- 3002,3007 ----
***************
*** 3622,3632 ****
funcexe.fe_partial = partial;

if (argvars[0].v_type == VAR_LIST)
! reduce_list(argvars, func_name, &funcexe, rettv);
else if (argvars[0].v_type == VAR_STRING)
! reduce_string(argvars, func_name, &funcexe, rettv);
else
! reduce_blob(argvars, func_name, &funcexe, rettv);
}

#endif // defined(FEAT_EVAL)
--- 3045,3055 ----
funcexe.fe_partial = partial;

if (argvars[0].v_type == VAR_LIST)
! list_reduce(argvars, func_name, &funcexe, rettv);
else if (argvars[0].v_type == VAR_STRING)
! string_reduce(argvars, func_name, &funcexe, rettv);
else
! blob_reduce(argvars, func_name, &funcexe, rettv);
}

#endif // defined(FEAT_EVAL)
*** ../vim-8.2.3870/src/proto/blob.pro 2021-09-14 16:53:39.316540671 +0100
--- src/proto/blob.pro 2021-12-22 18:04:56.843931664 +0000
***************
*** 18,24 ****
--- 18,29 ----
int check_blob_index(long bloblen, varnumber_T n1, int quiet);
int check_blob_range(long bloblen, varnumber_T n1, varnumber_T n2, int quiet);
int blob_set_range(blob_T *dest, long n1, long n2, typval_T *src);
+ void blob_add(typval_T *argvars, typval_T *rettv);
void blob_remove(typval_T *argvars, typval_T *rettv, char_u *arg_errmsg);
+ void blob_filter_map(blob_T *blob_arg, filtermap_T filtermap, typval_T *expr, typval_T *rettv);
+ void blob_insert_func(typval_T *argvars, typval_T *rettv);
+ void blob_reduce(typval_T *argvars, char_u *func_name, funcexe_T *funcexe, typval_T *rettv);
+ void blob_reverse(blob_T *b, typval_T *rettv);
void f_blob2list(typval_T *argvars, typval_T *rettv);
void f_list2blob(typval_T *argvars, typval_T *rettv);
/* vim: set ft=c : */
*** ../vim-8.2.3870/src/proto/dict.pro 2021-08-09 18:59:01.442811242 +0100
--- src/proto/dict.pro 2021-12-22 18:04:56.843931664 +0000
***************
*** 39,48 ****
void dict_extend(dict_T *d1, dict_T *d2, char_u *action, char *func_name);
dictitem_T *dict_lookup(hashitem_T *hi);
int dict_equal(dict_T *d1, dict_T *d2, int ic, int recursive);
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 : */
--- 39,51 ----
void dict_extend(dict_T *d1, dict_T *d2, char_u *action, char *func_name);
dictitem_T *dict_lookup(hashitem_T *hi);
int dict_equal(dict_T *d1, dict_T *d2, int ic, int recursive);
+ long dict_count(dict_T *d, typval_T *needle, int ic);
+ void dict_extend_func(typval_T *argvars, type_T *type, char *func_name, char_u *arg_errmsg, int is_new, typval_T *rettv);
+ void dict_filter_map(dict_T *d, filtermap_T filtermap, type_T *argtype, char *func_name, char_u *arg_errmsg, typval_T *expr, typval_T *rettv);
+ void dict_remove(typval_T *argvars, typval_T *rettv, char_u *arg_errmsg);
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);
/* vim: set ft=c : */
*** ../vim-8.2.3870/src/proto/list.pro 2021-08-11 20:49:19.626869328 +0100
--- src/proto/list.pro 2021-12-22 18:04:56.843931664 +0000
***************
*** 50,55 ****
--- 50,56 ----
void f_list2str(typval_T *argvars, typval_T *rettv);
void f_sort(typval_T *argvars, typval_T *rettv);
void f_uniq(typval_T *argvars, typval_T *rettv);
+ int filter_map_one(typval_T *tv, typval_T *expr, filtermap_T filtermap, typval_T *newtv, int *remp);
void f_filter(typval_T *argvars, typval_T *rettv);
void f_map(typval_T *argvars, typval_T *rettv);
void f_mapnew(typval_T *argvars, typval_T *rettv);
*** ../vim-8.2.3870/src/proto/strings.pro 2021-07-10 20:28:55.327050110 +0100
--- src/proto/strings.pro 2021-12-22 18:04:56.847931657 +0000
***************
*** 21,26 ****
--- 21,29 ----
int has_non_ascii(char_u *s);
char_u *concat_str(char_u *str1, char_u *str2);
char_u *string_quote(char_u *str, int function);
+ long string_count(char_u *haystack, char_u *needle, int ic);
+ void string_filter_map(char_u *str, filtermap_T filtermap, typval_T *expr, typval_T *rettv);
+ void string_reduce(typval_T *argvars, char_u *func_name, funcexe_T *funcexe, typval_T *rettv);
void f_byteidx(typval_T *argvars, typval_T *rettv);
void f_byteidxcomp(typval_T *argvars, typval_T *rettv);
void f_charidx(typval_T *argvars, typval_T *rettv);
*** ../vim-8.2.3870/src/strings.c 2021-11-24 15:32:53.723778915 +0000
--- src/strings.c 2021-12-22 18:04:56.847931657 +0000
***************
*** 764,770 ****
}

#if defined(FEAT_EVAL) || defined(PROTO)
-
/*
* Return string "str" in ' quotes, doubling ' characters.
* If "str" is NULL an empty string is assumed.
--- 764,769 ----
***************
*** 809,814 ****
--- 808,992 ----
return s;
}

+ /*
+ * Count the number of times "needle" occurs in string "haystack". Case is
+ * ignored if "ic" is TRUE.
+ */
+ long
+ string_count(char_u *haystack, char_u *needle, int ic)
+ {
+ long n = 0;
+ char_u *p = haystack;
+ char_u *next;
+
+ if (p == NULL || needle == NULL || *needle == NUL)
+ return 0;
+
+ if (ic)
+ {
+ size_t len = STRLEN(needle);
+
+ while (*p != NUL)
+ {
+ if (MB_STRNICMP(p, needle, len) == 0)
+ {
+ ++n;
+ p += len;
+ }
+ else
+ MB_PTR_ADV(p);
+ }
+ }
+ else
+ while ((next = (char_u *)strstr((char *)p, (char *)needle)) != NULL)
+ {
+ ++n;
+ p = next + STRLEN(needle);
+ }
+
+ return n;
+ }
+
+ /*
+ * Make a typval_T of the first character of "input" and store it in "output".
+ * Return OK or FAIL.
+ */
+ static int
+ copy_first_char_to_tv(char_u *input, typval_T *output)
+ {
+ char_u buf[MB_MAXBYTES + 1];
+ int len;
+
+ if (input == NULL || output == NULL)
+ return FAIL;
+
+ len = has_mbyte ? mb_ptr2len(input) : 1;
+ STRNCPY(buf, input, len);
+ buf[len] = NUL;
+ output->v_type = VAR_STRING;
+ output->vval.v_string = vim_strsave(buf);
+
+ return output->vval.v_string == NULL ? FAIL : OK;
+ }
+
+ /*
+ * Implementation of map() and filter() for a String. Apply "expr" to every
+ * character in string "str" and return the result in "rettv".
+ */
+ void
+ string_filter_map(
+ char_u *str,
+ filtermap_T filtermap,
+ typval_T *expr,
+ typval_T *rettv)
+ {
+ char_u *p;
+ typval_T tv;
+ garray_T ga;
+ int len = 0;
+ int idx = 0;
+ int rem;
+
+ rettv->v_type = VAR_STRING;
+ rettv->vval.v_string = NULL;
+
+ // set_vim_var_nr() doesn't set the type
+ set_vim_var_type(VV_KEY, VAR_NUMBER);
+
+ ga_init2(&ga, (int)sizeof(char), 80);
+ for (p = str; *p != NUL; p += len)
+ {
+ typval_T newtv;
+
+ if (copy_first_char_to_tv(p, &tv) == FAIL)
+ break;
+ len = (int)STRLEN(tv.vval.v_string);
+
+ set_vim_var_nr(VV_KEY, idx);
+ if (filter_map_one(&tv, expr, filtermap, &newtv, &rem) == FAIL
+ || did_emsg)
+ break;
+ if (did_emsg)
+ {
+ clear_tv(&newtv);
+ clear_tv(&tv);
+ break;
+ }
+ else if (filtermap != FILTERMAP_FILTER)
+ {
+ if (newtv.v_type != VAR_STRING)
+ {
+ clear_tv(&newtv);
+ clear_tv(&tv);
+ emsg(_(e_stringreq));
+ break;
+ }
+ else
+ ga_concat(&ga, newtv.vval.v_string);
+ }
+ else if (!rem)
+ ga_concat(&ga, tv.vval.v_string);
+
+ clear_tv(&newtv);
+ clear_tv(&tv);
+
+ ++idx;
+ }
+ ga_append(&ga, NUL);
+ rettv->vval.v_string = ga.ga_data;
+ }
+
+ /*
+ * reduce() String argvars[0] using the function 'funcname' with arguments in
+ * 'funcexe' starting with the initial value argvars[2] and return the result
+ * in 'rettv'.
+ */
+ void
+ string_reduce(
+ typval_T *argvars,
+ char_u *func_name,
+ funcexe_T *funcexe,
+ typval_T *rettv)
+ {
+ char_u *p = tv_get_string(&argvars[0]);
+ int len;
+ typval_T argv[3];
+ int r;
+ int called_emsg_start = called_emsg;
+
+ if (argvars[2].v_type == VAR_UNKNOWN)
+ {
+ if (*p == NUL)
+ {
+ semsg(_(e_reduceempty), "String");
+ return;
+ }
+ if (copy_first_char_to_tv(p, rettv) == FAIL)
+ return;
+ p += STRLEN(rettv->vval.v_string);
+ }
+ else if (argvars[2].v_type != VAR_STRING)
+ {
+ semsg(_(e_string_expected_for_argument_nr), 3);
+ return;
+ }
+ else
+ copy_tv(&argvars[2], rettv);
+
+ for ( ; *p != NUL; p += len)
+ {
+ argv[0] = *rettv;
+ if (copy_first_char_to_tv(p, &argv[1]) == FAIL)
+ break;
+ len = (int)STRLEN(argv[1].vval.v_string);
+ r = call_func(func_name, -1, rettv, 2, argv, funcexe);
+ clear_tv(&argv[0]);
+ clear_tv(&argv[1]);
+ if (r == FAIL || called_emsg != called_emsg_start)
+ return;
+ }
+ }
+
static void
byteidx(typval_T *argvars, typval_T *rettv, int comp UNUSED)
{
***************
*** 1686,1694 ****
rettv->vval.v_string = vim_strnsave(head, tail - head);
}

- #endif
-
- #if defined(FEAT_EVAL)
static char *e_printf = N_("E766: Insufficient arguments for printf()");

/*
--- 1864,1869 ----
***************
*** 1766,1771 ****
--- 1941,1947 ----
return f;
}
# endif
+
#endif

#ifdef FEAT_FLOAT
*** ../vim-8.2.3870/src/structs.h 2021-12-14 18:14:34.129509139 +0000
--- src/structs.h 2021-12-22 18:04:56.847931657 +0000
***************
*** 4492,4494 ****
--- 4492,4502 ----
int sve_did_save;
hashtab_T sve_hashtab;
} save_v_event_T;
+
+ // Enum used by filter(), map() and mapnew()
+ typedef enum {
+ FILTERMAP_FILTER,
+ FILTERMAP_MAP,
+ FILTERMAP_MAPNEW
+ } filtermap_T;
+
*** ../vim-8.2.3870/src/testdir/test_filter_map.vim 2021-12-16 08:21:05.302828003 +0000
--- src/testdir/test_filter_map.vim 2021-12-22 18:04:56.847931657 +0000
***************
*** 182,187 ****
--- 182,188 ----
call assert_equal('', map('abc', LSTART i, x LMIDDLE '' LEND))
call assert_equal('', map('', "v:val == 'a'"))
call assert_equal('', map(test_null_string(), "v:val == 'a'"))
+ call assert_fails('echo map("abc", "10")', 'E928:')
END
call CheckLegacyAndVim9Success(lines)

*** ../vim-8.2.3870/src/testdir/test_listdict.vim 2021-12-19 18:33:17.321954811 +0000
--- src/testdir/test_listdict.vim 2021-12-22 18:04:56.847931657 +0000
***************
*** 783,788 ****
--- 783,814 ----
call CheckScriptFailure(lines, 'E741:')
endfunc

+ " Lock one item in a list
+ func Test_list_item_lock_map()
+ let lines =<< trim END
+ VAR l = [99, 100, 101]
+ lockvar l[1]
+ call assert_fails("echo map(l, 'v:val + 200')", 'E741:')
+ call assert_equal([299, 100, 101], l)
+ END
+ " This won't work in a :def function
+ call CheckTransLegacySuccess(lines)
+ call CheckTransVim9Success(lines)
+ endfunc
+
+ " Lock one item in a dict
+ func Test_dict_item_lock_map()
+ let lines =<< trim END
+ VAR d = {'a': 99, 'b': 100, 'c': 101}
+ lockvar d.b
+ call assert_fails("echo map(d, 'v:val + 200')", 'E741:')
+ call assert_equal({'a': 299, 'b': 100, 'c': 101}, d)
+ END
+ " This won't work in a :def function
+ call CheckTransLegacySuccess(lines)
+ call CheckTransVim9Success(lines)
+ endfunc
+
" No extend() after lock on dict item
func Test_dict_lock_extend()
let d = {'a': 99, 'b': 100}
*** ../vim-8.2.3870/src/testdir/test_sort.vim 2021-09-19 16:01:35.502496563 +0100
--- src/testdir/test_sort.vim 2021-12-22 18:04:56.847931657 +0000
***************
*** 1548,1551 ****
--- 1548,1567 ----
close!
endfunc

+ " Test for sort() using a dict function
+ func Test_sort_using_dict_func()
+ func DictSort(a, b) dict
+ if self.order == 'reverse'
+ return a:b - a:a
+ else
+ return a:a - a:b
+ endif
+ endfunc
+ let d = #{order: ''}
+ call assert_equal([1, 2, 3], sort([2, 1, 3], 'DictSort', d))
+ let d = #{order: 'reverse'}
+ call assert_equal([3, 2, 1], sort([2, 1, 3], 'DictSort', d))
+ delfunc DictSort
+ endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
*** ../vim-8.2.3870/src/version.c 2021-12-22 15:17:43.258403793 +0000
--- src/version.c 2021-12-22 18:06:26.255766161 +0000
***************
*** 751,752 ****
--- 751,754 ----
{ /* Add new patch number below this line */
+ /**/
+ 3871,
/**/

--
From "know your smileys":
:-{} Too much lipstick

/// Bram Moolenaar -- Br...@Moolenaar.net -- http://www.Moolenaar.net \\\
/// \\\
\\\ sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ ///
\\\ help me help AIDS victims -- http://ICCF-Holland.org ///

John Marriott

unread,
Dec 22, 2021, 1:46:05 PM12/22/21
to vim...@googlegroups.com

On 23-Dec-2021 05:20, Bram Moolenaar wrote:
> Patch 8.2.3871
> Problem: List.c contains code for dict and blob.
> Solution: Refactor to put code where it belongs. (Yegappan Lakshmanan,
> closes #9386)
> Files: src/blob.c, src/dict.c, src/list.c, src/proto/blob.pro,
> src/proto/dict.pro, src/proto/list.pro, src/proto/strings.pro,
> src/strings.c, src/structs.h, src/testdir/test_filter_map.vim,
> src/testdir/test_listdict.vim, src/testdir/test_sort.vim
>
>
After this patch, mingw64 (gcc 11.2.0) spits out this warning:
<snip>
gcc -c -I. -Iproto -DWIN32 -DWINVER=0x0603 -D_WIN32_WINNT=0x0603
-DHAVE_PATHDEF -DFEAT_NORMAL -DHAVE_STDINT_H -D__USE_MINGW_ANSI_STDIO
-pipe -march=native -Wall -O3 -fomit-frame-pointer -freg-struct-return
-fpie -fPIE -DFEAT_GUI_MSWIN -DFEAT_CLIPBOARD dict.c -o gobjnative/dict.o
In file included from dict.c:14:
In function 'dictitem_alloc',
    inlined from 'dict_add_tv' at dict.c:491:12,
    inlined from 'dict_filter_map' at dict.c:1370:7:
vim.h:1629:29: warning: 'strcpy' offset 0 from the object at '<unknown>'
is out of the bounds of referenced subobject 'di_key' with type
'char_u[1]' {aka 'unsigned char[1]'} at offset 0 [-Warray-bounds]
 1629 | #define STRCPY(d, s)        strcpy((char *)(d), (char *)(s))
      |                             ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
vim.h:1629:29: note: in definition of macro 'STRCPY'
 1629 | #define STRCPY(d, s)        strcpy((char *)(d), (char *)(s))
      |                             ^~~~~~
In file included from vim.h:1878,
                 from dict.c:14:
dict.c: In function 'dict_filter_map':
structs.h:1538:17: note: subobject 'di_key' declared here
 1538 |     char_u      di_key[1];      // key (actually longer!)
      |                 ^~~~~~
</snip>

Cheers
John

Yegappan Lakshmanan

unread,
Dec 22, 2021, 2:06:13 PM12/22/21
to vim_dev
Hi,
This patch did not change the dictitem_T definition or modify the dict_add_tv()
or the dictitem_alloc() functions. It looks like GCC is complaining about using
the di_key buffer with size 1 (even though the size of the di_key array is
greater than 1). Interestingly GCC is not complaining about the use of this
function in other places.

- Yegappan

Bram Moolenaar

unread,
Dec 22, 2021, 2:19:42 PM12/22/21
to vim...@googlegroups.com, John Marriott
Is that because of the "actually longer" thing?
Any idea how to avoid the error?

--
From "know your smileys":
<<<:-{ Worf (Never smiles anyways, so he's a bad smiley)

John Marriott

unread,
Dec 22, 2021, 3:15:51 PM12/22/21
to vim...@googlegroups.com


On 23-Dec-2021 06:19, Bram Moolenaar wrote:
> John Marriott wrote:
>
>> On 23-Dec-2021 05:20, Bram Moolenaar wrote:
>>> Patch 8.2.3871
>>> Problem: List.c contains code for dict and blob.
>>> Solution: Refactor to put code where it belongs. (Yegappan Lakshmanan,
>>> closes #9386)
>>> Files: src/blob.c, src/dict.c, src/list.c, src/proto/blob.pro,
>>> src/proto/dict.pro, src/proto/list.pro, src/proto/strings.pro,
>>> src/strings.c, src/structs.h, src/testdir/test_filter_map.vim,
>>> src/testdir/test_listdict.vim, src/testdir/test_sort.vim
>>>
>>>
> Is that because of the "actually longer" thing?
Yeah I think so.
> Any idea how to avoid the error?
>

The attached patch changes dictitem_alloc() to use the same mechanism as
dictitem_copy() just below it.

What do you think?

Cheers
John
dict.c.3871.patch

Bram Moolenaar

unread,
Dec 22, 2021, 3:30:45 PM12/22/21
to vim...@googlegroups.com, John Marriott
If this avoids the warning then it's fine. Thanks for the patch.

--
% cat /usr/include/real_life.h
void life(void);
Reply all
Reply to author
Forward
0 new messages