blob: 94b5a0b5d7d087d5b4580d4190bbd427bd621e6a [file] [log] [blame]
Bram Moolenaaredf3f972016-08-29 22:49:24 +02001/* vi:set ts=8 sts=4 sw=4 noet:
Bram Moolenaarda861d62016-07-17 15:46:27 +02002 *
3 * VIM - Vi IMproved by Bram Moolenaar
4 *
5 * Do ":help uganda" in Vim to read copying and usage conditions.
6 * Do ":help credits" in Vim to see a list of people who contributed.
7 * See README.txt for an overview of the Vim source code.
8 */
9
10/*
Bram Moolenaar08c308a2019-09-04 17:48:15 +020011 * list.c: List support and container (List, Dict, Blob) functions.
Bram Moolenaarda861d62016-07-17 15:46:27 +020012 */
13
14#include "vim.h"
15
16#if defined(FEAT_EVAL) || defined(PROTO)
17
Bram Moolenaar08c308a2019-09-04 17:48:15 +020018static char *e_listblobarg = N_("E899: Argument of %s must be a List or Blob");
19
Bram Moolenaar4ba37b52019-12-04 21:57:43 +010020// List heads for garbage collection.
21static list_T *first_list = NULL; // list of all lists
Bram Moolenaarda861d62016-07-17 15:46:27 +020022
Bram Moolenaar00d253e2020-04-06 22:13:01 +020023#define FOR_ALL_WATCHERS(l, lw) \
24 for ((lw) = (l)->lv_watch; (lw) != NULL; (lw) = (lw)->lw_next)
25
Bram Moolenaarbdff0122020-04-05 18:56:05 +020026static void list_free_item(list_T *l, listitem_T *item);
27
Bram Moolenaarda861d62016-07-17 15:46:27 +020028/*
29 * Add a watcher to a list.
30 */
31 void
32list_add_watch(list_T *l, listwatch_T *lw)
33{
34 lw->lw_next = l->lv_watch;
35 l->lv_watch = lw;
36}
37
38/*
39 * Remove a watcher from a list.
40 * No warning when it isn't found...
41 */
42 void
43list_rem_watch(list_T *l, listwatch_T *lwrem)
44{
45 listwatch_T *lw, **lwp;
46
47 lwp = &l->lv_watch;
Bram Moolenaar00d253e2020-04-06 22:13:01 +020048 FOR_ALL_WATCHERS(l, lw)
Bram Moolenaarda861d62016-07-17 15:46:27 +020049 {
50 if (lw == lwrem)
51 {
52 *lwp = lw->lw_next;
53 break;
54 }
55 lwp = &lw->lw_next;
56 }
57}
58
59/*
60 * Just before removing an item from a list: advance watchers to the next
61 * item.
62 */
Bram Moolenaar5843f5f2019-08-20 20:13:45 +020063 static void
Bram Moolenaarda861d62016-07-17 15:46:27 +020064list_fix_watch(list_T *l, listitem_T *item)
65{
66 listwatch_T *lw;
67
Bram Moolenaar00d253e2020-04-06 22:13:01 +020068 FOR_ALL_WATCHERS(l, lw)
Bram Moolenaarda861d62016-07-17 15:46:27 +020069 if (lw->lw_item == item)
70 lw->lw_item = item->li_next;
71}
72
Bram Moolenaar8a7d6542020-01-26 15:56:19 +010073 static void
74list_init(list_T *l)
75{
76 // Prepend the list to the list of lists for garbage collection.
77 if (first_list != NULL)
78 first_list->lv_used_prev = l;
79 l->lv_used_prev = NULL;
80 l->lv_used_next = first_list;
81 first_list = l;
82}
83
Bram Moolenaarda861d62016-07-17 15:46:27 +020084/*
85 * Allocate an empty header for a list.
86 * Caller should take care of the reference count.
87 */
88 list_T *
89list_alloc(void)
90{
91 list_T *l;
92
Bram Moolenaarc799fe22019-05-28 23:08:19 +020093 l = ALLOC_CLEAR_ONE(list_T);
Bram Moolenaarda861d62016-07-17 15:46:27 +020094 if (l != NULL)
Bram Moolenaar8a7d6542020-01-26 15:56:19 +010095 list_init(l);
Bram Moolenaarda861d62016-07-17 15:46:27 +020096 return l;
97}
98
99/*
Bram Moolenaarf49cc602018-11-11 15:21:05 +0100100 * list_alloc() with an ID for alloc_fail().
101 */
102 list_T *
103list_alloc_id(alloc_id_T id UNUSED)
104{
105#ifdef FEAT_EVAL
Bram Moolenaar51e14382019-05-25 20:21:28 +0200106 if (alloc_fail_id == id && alloc_does_fail(sizeof(list_T)))
Bram Moolenaarf49cc602018-11-11 15:21:05 +0100107 return NULL;
108#endif
109 return (list_alloc());
110}
111
112/*
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100113 * Allocate space for a list, plus "count" items.
114 * Next list_set_item() must be called for each item.
115 */
116 list_T *
117list_alloc_with_items(int count)
118{
119 list_T *l;
120
121 l = (list_T *)alloc_clear(sizeof(list_T) + count * sizeof(listitem_T));
122 if (l != NULL)
123 {
124 list_init(l);
125
126 if (count > 0)
127 {
128 listitem_T *li = (listitem_T *)(l + 1);
129 int i;
130
131 l->lv_len = count;
132 l->lv_with_items = count;
133 l->lv_first = li;
Bram Moolenaar0ff6aad2020-01-29 21:27:21 +0100134 l->lv_u.mat.lv_last = li + count - 1;
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100135 for (i = 0; i < count; ++i)
136 {
137 if (i == 0)
138 li->li_prev = NULL;
139 else
140 li->li_prev = li - 1;
141 if (i == count - 1)
142 li->li_next = NULL;
143 else
144 li->li_next = li + 1;
145 ++li;
146 }
147 }
148 }
149 return l;
150}
151
152/*
153 * Set item "idx" for a list previously allocated with list_alloc_with_items().
154 * The contents of "tv" is moved into the list item.
155 * Each item must be set exactly once.
156 */
157 void
158list_set_item(list_T *l, int idx, typval_T *tv)
159{
160 listitem_T *li = (listitem_T *)(l + 1) + idx;
161
162 li->li_tv = *tv;
163}
164
165/*
Bram Moolenaarda861d62016-07-17 15:46:27 +0200166 * Allocate an empty list for a return value, with reference count set.
167 * Returns OK or FAIL.
168 */
169 int
170rettv_list_alloc(typval_T *rettv)
171{
172 list_T *l = list_alloc();
173
174 if (l == NULL)
175 return FAIL;
176
Bram Moolenaarda861d62016-07-17 15:46:27 +0200177 rettv->v_lock = 0;
Bram Moolenaar45cf6e92017-04-30 20:25:19 +0200178 rettv_list_set(rettv, l);
Bram Moolenaarda861d62016-07-17 15:46:27 +0200179 return OK;
180}
181
182/*
Bram Moolenaar162b7142018-12-21 15:17:36 +0100183 * Same as rettv_list_alloc() but uses an allocation id for testing.
184 */
185 int
186rettv_list_alloc_id(typval_T *rettv, alloc_id_T id UNUSED)
187{
188#ifdef FEAT_EVAL
Bram Moolenaar51e14382019-05-25 20:21:28 +0200189 if (alloc_fail_id == id && alloc_does_fail(sizeof(list_T)))
Bram Moolenaar162b7142018-12-21 15:17:36 +0100190 return FAIL;
191#endif
192 return rettv_list_alloc(rettv);
193}
194
195
196/*
Bram Moolenaare809a4e2019-07-04 17:35:05 +0200197 * Set a list as the return value. Increments the reference count.
Bram Moolenaar45cf6e92017-04-30 20:25:19 +0200198 */
199 void
200rettv_list_set(typval_T *rettv, list_T *l)
201{
202 rettv->v_type = VAR_LIST;
203 rettv->vval.v_list = l;
204 if (l != NULL)
205 ++l->lv_refcount;
206}
207
208/*
Bram Moolenaarda861d62016-07-17 15:46:27 +0200209 * Unreference a list: decrement the reference count and free it when it
210 * becomes zero.
211 */
212 void
213list_unref(list_T *l)
214{
215 if (l != NULL && --l->lv_refcount <= 0)
216 list_free(l);
217}
218
219/*
220 * Free a list, including all non-container items it points to.
221 * Ignores the reference count.
222 */
223 static void
224list_free_contents(list_T *l)
225{
226 listitem_T *item;
227
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100228 if (l->lv_first != &range_list_item)
229 for (item = l->lv_first; item != NULL; item = l->lv_first)
230 {
231 // Remove the item before deleting it.
232 l->lv_first = item->li_next;
233 clear_tv(&item->li_tv);
234 list_free_item(l, item);
235 }
Bram Moolenaarda861d62016-07-17 15:46:27 +0200236}
237
238/*
239 * Go through the list of lists and free items without the copyID.
240 * But don't free a list that has a watcher (used in a for loop), these
241 * are not referenced anywhere.
242 */
243 int
244list_free_nonref(int copyID)
245{
246 list_T *ll;
247 int did_free = FALSE;
248
249 for (ll = first_list; ll != NULL; ll = ll->lv_used_next)
250 if ((ll->lv_copyID & COPYID_MASK) != (copyID & COPYID_MASK)
251 && ll->lv_watch == NULL)
252 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100253 // Free the List and ordinary items it contains, but don't recurse
254 // into Lists and Dictionaries, they will be in the list of dicts
255 // or list of lists.
Bram Moolenaarda861d62016-07-17 15:46:27 +0200256 list_free_contents(ll);
257 did_free = TRUE;
258 }
259 return did_free;
260}
261
262 static void
263list_free_list(list_T *l)
264{
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100265 // Remove the list from the list of lists for garbage collection.
Bram Moolenaarda861d62016-07-17 15:46:27 +0200266 if (l->lv_used_prev == NULL)
267 first_list = l->lv_used_next;
268 else
269 l->lv_used_prev->lv_used_next = l->lv_used_next;
270 if (l->lv_used_next != NULL)
271 l->lv_used_next->lv_used_prev = l->lv_used_prev;
272
Bram Moolenaaraa210a32021-01-02 15:41:03 +0100273 free_type(l->lv_type);
Bram Moolenaarda861d62016-07-17 15:46:27 +0200274 vim_free(l);
275}
276
277 void
278list_free_items(int copyID)
279{
280 list_T *ll, *ll_next;
281
282 for (ll = first_list; ll != NULL; ll = ll_next)
283 {
284 ll_next = ll->lv_used_next;
285 if ((ll->lv_copyID & COPYID_MASK) != (copyID & COPYID_MASK)
286 && ll->lv_watch == NULL)
287 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100288 // Free the List and ordinary items it contains, but don't recurse
289 // into Lists and Dictionaries, they will be in the list of dicts
290 // or list of lists.
Bram Moolenaarda861d62016-07-17 15:46:27 +0200291 list_free_list(ll);
292 }
293 }
294}
295
296 void
297list_free(list_T *l)
298{
299 if (!in_free_unref_items)
300 {
301 list_free_contents(l);
302 list_free_list(l);
303 }
304}
305
306/*
307 * Allocate a list item.
308 * It is not initialized, don't forget to set v_lock.
309 */
310 listitem_T *
311listitem_alloc(void)
312{
Bram Moolenaarc799fe22019-05-28 23:08:19 +0200313 return ALLOC_ONE(listitem_T);
Bram Moolenaarda861d62016-07-17 15:46:27 +0200314}
315
316/*
rbtnn0ccb5842021-12-18 18:33:46 +0000317 * Make a typval_T of the first character of "input" and store it in "output".
318 * Return OK or FAIL.
319 */
320 static int
321tv_get_first_char(char_u *input, typval_T *output)
322{
323 char_u buf[MB_MAXBYTES + 1];
324 int len;
325
326 if (input == NULL || output == NULL)
327 return FAIL;
328
329 len = has_mbyte ? mb_ptr2len(input) : 1;
330 STRNCPY(buf, input, len);
331 buf[len] = NUL;
332 output->v_type = VAR_STRING;
333 output->vval.v_string = vim_strsave(buf);
334
335 return output->vval.v_string == NULL ? FAIL : OK;
336}
337
338/*
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100339 * Free a list item, unless it was allocated together with the list itself.
340 * Does not clear the value. Does not notify watchers.
Bram Moolenaarda861d62016-07-17 15:46:27 +0200341 */
Bram Moolenaarbdff0122020-04-05 18:56:05 +0200342 static void
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100343list_free_item(list_T *l, listitem_T *item)
344{
345 if (l->lv_with_items == 0 || item < (listitem_T *)l
346 || item >= (listitem_T *)(l + 1) + l->lv_with_items)
347 vim_free(item);
348}
349
350/*
351 * Free a list item, unless it was allocated together with the list itself.
352 * Also clears the value. Does not notify watchers.
353 */
354 void
355listitem_free(list_T *l, listitem_T *item)
Bram Moolenaarda861d62016-07-17 15:46:27 +0200356{
357 clear_tv(&item->li_tv);
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100358 list_free_item(l, item);
Bram Moolenaarda861d62016-07-17 15:46:27 +0200359}
360
361/*
362 * Remove a list item from a List and free it. Also clears the value.
363 */
364 void
365listitem_remove(list_T *l, listitem_T *item)
366{
367 vimlist_remove(l, item, item);
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100368 listitem_free(l, item);
Bram Moolenaarda861d62016-07-17 15:46:27 +0200369}
370
371/*
372 * Get the number of items in a list.
373 */
374 long
375list_len(list_T *l)
376{
377 if (l == NULL)
378 return 0L;
379 return l->lv_len;
380}
381
382/*
383 * Return TRUE when two lists have exactly the same values.
384 */
385 int
386list_equal(
387 list_T *l1,
388 list_T *l2,
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100389 int ic, // ignore case for strings
390 int recursive) // TRUE when used recursively
Bram Moolenaarda861d62016-07-17 15:46:27 +0200391{
392 listitem_T *item1, *item2;
393
Bram Moolenaarda861d62016-07-17 15:46:27 +0200394 if (l1 == l2)
395 return TRUE;
396 if (list_len(l1) != list_len(l2))
397 return FALSE;
Bram Moolenaar7b293c72020-04-09 21:33:22 +0200398 if (list_len(l1) == 0)
399 // empty and NULL list are considered equal
400 return TRUE;
401 if (l1 == NULL || l2 == NULL)
402 return FALSE;
Bram Moolenaarda861d62016-07-17 15:46:27 +0200403
Bram Moolenaar7e9f3512020-05-13 22:44:22 +0200404 CHECK_LIST_MATERIALIZE(l1);
405 CHECK_LIST_MATERIALIZE(l2);
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100406
Bram Moolenaarda861d62016-07-17 15:46:27 +0200407 for (item1 = l1->lv_first, item2 = l2->lv_first;
408 item1 != NULL && item2 != NULL;
409 item1 = item1->li_next, item2 = item2->li_next)
410 if (!tv_equal(&item1->li_tv, &item2->li_tv, ic, recursive))
411 return FALSE;
412 return item1 == NULL && item2 == NULL;
413}
414
415/*
416 * Locate item with index "n" in list "l" and return it.
417 * A negative index is counted from the end; -1 is the last item.
418 * Returns NULL when "n" is out of range.
419 */
420 listitem_T *
421list_find(list_T *l, long n)
422{
423 listitem_T *item;
424 long idx;
425
426 if (l == NULL)
427 return NULL;
428
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100429 // Negative index is relative to the end.
Bram Moolenaarda861d62016-07-17 15:46:27 +0200430 if (n < 0)
431 n = l->lv_len + n;
432
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100433 // Check for index out of range.
Bram Moolenaarda861d62016-07-17 15:46:27 +0200434 if (n < 0 || n >= l->lv_len)
435 return NULL;
436
Bram Moolenaar7e9f3512020-05-13 22:44:22 +0200437 CHECK_LIST_MATERIALIZE(l);
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100438
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100439 // When there is a cached index may start search from there.
Bram Moolenaar0ff6aad2020-01-29 21:27:21 +0100440 if (l->lv_u.mat.lv_idx_item != NULL)
Bram Moolenaarda861d62016-07-17 15:46:27 +0200441 {
Bram Moolenaar0ff6aad2020-01-29 21:27:21 +0100442 if (n < l->lv_u.mat.lv_idx / 2)
Bram Moolenaarda861d62016-07-17 15:46:27 +0200443 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100444 // closest to the start of the list
Bram Moolenaarda861d62016-07-17 15:46:27 +0200445 item = l->lv_first;
446 idx = 0;
447 }
Bram Moolenaar0ff6aad2020-01-29 21:27:21 +0100448 else if (n > (l->lv_u.mat.lv_idx + l->lv_len) / 2)
Bram Moolenaarda861d62016-07-17 15:46:27 +0200449 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100450 // closest to the end of the list
Bram Moolenaar0ff6aad2020-01-29 21:27:21 +0100451 item = l->lv_u.mat.lv_last;
Bram Moolenaarda861d62016-07-17 15:46:27 +0200452 idx = l->lv_len - 1;
453 }
454 else
455 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100456 // closest to the cached index
Bram Moolenaar0ff6aad2020-01-29 21:27:21 +0100457 item = l->lv_u.mat.lv_idx_item;
458 idx = l->lv_u.mat.lv_idx;
Bram Moolenaarda861d62016-07-17 15:46:27 +0200459 }
460 }
461 else
462 {
463 if (n < l->lv_len / 2)
464 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100465 // closest to the start of the list
Bram Moolenaarda861d62016-07-17 15:46:27 +0200466 item = l->lv_first;
467 idx = 0;
468 }
469 else
470 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100471 // closest to the end of the list
Bram Moolenaar0ff6aad2020-01-29 21:27:21 +0100472 item = l->lv_u.mat.lv_last;
Bram Moolenaarda861d62016-07-17 15:46:27 +0200473 idx = l->lv_len - 1;
474 }
475 }
476
477 while (n > idx)
478 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100479 // search forward
Bram Moolenaarda861d62016-07-17 15:46:27 +0200480 item = item->li_next;
481 ++idx;
482 }
483 while (n < idx)
484 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100485 // search backward
Bram Moolenaarda861d62016-07-17 15:46:27 +0200486 item = item->li_prev;
487 --idx;
488 }
489
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100490 // cache the used index
Bram Moolenaar0ff6aad2020-01-29 21:27:21 +0100491 l->lv_u.mat.lv_idx = idx;
492 l->lv_u.mat.lv_idx_item = item;
Bram Moolenaarda861d62016-07-17 15:46:27 +0200493
494 return item;
495}
496
497/*
498 * Get list item "l[idx]" as a number.
499 */
500 long
501list_find_nr(
502 list_T *l,
503 long idx,
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100504 int *errorp) // set to TRUE when something wrong
Bram Moolenaarda861d62016-07-17 15:46:27 +0200505{
506 listitem_T *li;
507
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100508 if (l != NULL && l->lv_first == &range_list_item)
509 {
510 long n = idx;
511
512 // not materialized range() list: compute the value.
513 // Negative index is relative to the end.
514 if (n < 0)
515 n = l->lv_len + n;
516
517 // Check for index out of range.
518 if (n < 0 || n >= l->lv_len)
519 {
520 if (errorp != NULL)
521 *errorp = TRUE;
522 return -1L;
523 }
524
Bram Moolenaar0ff6aad2020-01-29 21:27:21 +0100525 return l->lv_u.nonmat.lv_start + n * l->lv_u.nonmat.lv_stride;
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100526 }
527
Bram Moolenaarda861d62016-07-17 15:46:27 +0200528 li = list_find(l, idx);
529 if (li == NULL)
530 {
531 if (errorp != NULL)
532 *errorp = TRUE;
533 return -1L;
534 }
Bram Moolenaard155d7a2018-12-21 16:04:21 +0100535 return (long)tv_get_number_chk(&li->li_tv, errorp);
Bram Moolenaarda861d62016-07-17 15:46:27 +0200536}
537
538/*
539 * Get list item "l[idx - 1]" as a string. Returns NULL for failure.
540 */
541 char_u *
542list_find_str(list_T *l, long idx)
543{
544 listitem_T *li;
545
546 li = list_find(l, idx - 1);
547 if (li == NULL)
548 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100549 semsg(_(e_listidx), idx);
Bram Moolenaarda861d62016-07-17 15:46:27 +0200550 return NULL;
551 }
Bram Moolenaard155d7a2018-12-21 16:04:21 +0100552 return tv_get_string(&li->li_tv);
Bram Moolenaarda861d62016-07-17 15:46:27 +0200553}
554
555/*
Bram Moolenaar5b5ae292021-02-20 17:04:02 +0100556 * Like list_find() but when a negative index is used that is not found use
557 * zero and set "idx" to zero. Used for first index of a range.
558 */
559 listitem_T *
560list_find_index(list_T *l, long *idx)
561{
562 listitem_T *li = list_find(l, *idx);
563
564 if (li == NULL)
565 {
566 if (*idx < 0)
567 {
568 *idx = 0;
569 li = list_find(l, *idx);
570 }
571 }
572 return li;
573}
574
575/*
Bram Moolenaarda861d62016-07-17 15:46:27 +0200576 * Locate "item" list "l" and return its index.
577 * Returns -1 when "item" is not in the list.
578 */
579 long
580list_idx_of_item(list_T *l, listitem_T *item)
581{
582 long idx = 0;
583 listitem_T *li;
584
585 if (l == NULL)
586 return -1;
Bram Moolenaar7e9f3512020-05-13 22:44:22 +0200587 CHECK_LIST_MATERIALIZE(l);
Bram Moolenaarda861d62016-07-17 15:46:27 +0200588 idx = 0;
589 for (li = l->lv_first; li != NULL && li != item; li = li->li_next)
590 ++idx;
591 if (li == NULL)
592 return -1;
593 return idx;
594}
595
596/*
597 * Append item "item" to the end of list "l".
598 */
599 void
600list_append(list_T *l, listitem_T *item)
601{
Bram Moolenaar7e9f3512020-05-13 22:44:22 +0200602 CHECK_LIST_MATERIALIZE(l);
Bram Moolenaar0ff6aad2020-01-29 21:27:21 +0100603 if (l->lv_u.mat.lv_last == NULL)
Bram Moolenaarda861d62016-07-17 15:46:27 +0200604 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100605 // empty list
Bram Moolenaarda861d62016-07-17 15:46:27 +0200606 l->lv_first = item;
Bram Moolenaar0ff6aad2020-01-29 21:27:21 +0100607 l->lv_u.mat.lv_last = item;
Bram Moolenaarda861d62016-07-17 15:46:27 +0200608 item->li_prev = NULL;
609 }
610 else
611 {
Bram Moolenaar0ff6aad2020-01-29 21:27:21 +0100612 l->lv_u.mat.lv_last->li_next = item;
613 item->li_prev = l->lv_u.mat.lv_last;
614 l->lv_u.mat.lv_last = item;
Bram Moolenaarda861d62016-07-17 15:46:27 +0200615 }
616 ++l->lv_len;
617 item->li_next = NULL;
618}
619
620/*
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100621 * Append typval_T "tv" to the end of list "l". "tv" is copied.
Bram Moolenaarf32f0992021-07-08 20:53:40 +0200622 * Return FAIL when out of memory or the type is wrong.
Bram Moolenaarda861d62016-07-17 15:46:27 +0200623 */
624 int
625list_append_tv(list_T *l, typval_T *tv)
626{
Bram Moolenaar90fba562021-07-09 19:17:55 +0200627 listitem_T *li;
Bram Moolenaarda861d62016-07-17 15:46:27 +0200628
Bram Moolenaarf32f0992021-07-08 20:53:40 +0200629 if (l->lv_type != NULL && l->lv_type->tt_member != NULL
Bram Moolenaar7a3fe3e2021-07-22 14:58:47 +0200630 && check_typval_arg_type(l->lv_type->tt_member, tv,
631 NULL, 0) == FAIL)
Bram Moolenaarf32f0992021-07-08 20:53:40 +0200632 return FAIL;
Bram Moolenaar90fba562021-07-09 19:17:55 +0200633 li = listitem_alloc();
Bram Moolenaarda861d62016-07-17 15:46:27 +0200634 if (li == NULL)
635 return FAIL;
636 copy_tv(tv, &li->li_tv);
637 list_append(l, li);
638 return OK;
639}
640
641/*
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100642 * As list_append_tv() but move the value instead of copying it.
643 * Return FAIL when out of memory.
644 */
Yegappan Lakshmanan8ee52af2021-08-09 19:59:06 +0200645 static int
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100646list_append_tv_move(list_T *l, typval_T *tv)
647{
648 listitem_T *li = listitem_alloc();
649
650 if (li == NULL)
651 return FAIL;
652 li->li_tv = *tv;
653 list_append(l, li);
654 return OK;
655}
656
657/*
Bram Moolenaarda861d62016-07-17 15:46:27 +0200658 * Add a dictionary to a list. Used by getqflist().
659 * Return FAIL when out of memory.
660 */
661 int
662list_append_dict(list_T *list, dict_T *dict)
663{
664 listitem_T *li = listitem_alloc();
665
666 if (li == NULL)
667 return FAIL;
668 li->li_tv.v_type = VAR_DICT;
669 li->li_tv.v_lock = 0;
670 li->li_tv.vval.v_dict = dict;
671 list_append(list, li);
672 ++dict->dv_refcount;
673 return OK;
674}
675
676/*
Bram Moolenaar4f505882018-02-10 21:06:32 +0100677 * Append list2 to list1.
678 * Return FAIL when out of memory.
679 */
680 int
Bram Moolenaar6f8d2ac2018-07-25 19:49:45 +0200681list_append_list(list_T *list1, list_T *list2)
Bram Moolenaar4f505882018-02-10 21:06:32 +0100682{
683 listitem_T *li = listitem_alloc();
684
685 if (li == NULL)
686 return FAIL;
687 li->li_tv.v_type = VAR_LIST;
688 li->li_tv.v_lock = 0;
689 li->li_tv.vval.v_list = list2;
690 list_append(list1, li);
691 ++list2->lv_refcount;
692 return OK;
693}
694
695/*
Bram Moolenaarda861d62016-07-17 15:46:27 +0200696 * Make a copy of "str" and append it as an item to list "l".
697 * When "len" >= 0 use "str[len]".
698 * Returns FAIL when out of memory.
699 */
700 int
701list_append_string(list_T *l, char_u *str, int len)
702{
703 listitem_T *li = listitem_alloc();
704
705 if (li == NULL)
706 return FAIL;
707 list_append(l, li);
708 li->li_tv.v_type = VAR_STRING;
709 li->li_tv.v_lock = 0;
710 if (str == NULL)
711 li->li_tv.vval.v_string = NULL;
712 else if ((li->li_tv.vval.v_string = (len >= 0 ? vim_strnsave(str, len)
713 : vim_strsave(str))) == NULL)
714 return FAIL;
715 return OK;
716}
717
718/*
719 * Append "n" to list "l".
720 * Returns FAIL when out of memory.
721 */
722 int
723list_append_number(list_T *l, varnumber_T n)
724{
725 listitem_T *li;
726
727 li = listitem_alloc();
728 if (li == NULL)
729 return FAIL;
730 li->li_tv.v_type = VAR_NUMBER;
731 li->li_tv.v_lock = 0;
732 li->li_tv.vval.v_number = n;
733 list_append(l, li);
734 return OK;
735}
736
737/*
738 * Insert typval_T "tv" in list "l" before "item".
739 * If "item" is NULL append at the end.
Bram Moolenaaraa210a32021-01-02 15:41:03 +0100740 * Return FAIL when out of memory or the type is wrong.
Bram Moolenaarda861d62016-07-17 15:46:27 +0200741 */
742 int
743list_insert_tv(list_T *l, typval_T *tv, listitem_T *item)
744{
Bram Moolenaaraa210a32021-01-02 15:41:03 +0100745 listitem_T *ni;
Bram Moolenaarda861d62016-07-17 15:46:27 +0200746
Bram Moolenaaraa210a32021-01-02 15:41:03 +0100747 if (l->lv_type != NULL && l->lv_type->tt_member != NULL
Bram Moolenaar7a3fe3e2021-07-22 14:58:47 +0200748 && check_typval_arg_type(l->lv_type->tt_member, tv,
749 NULL, 0) == FAIL)
Bram Moolenaaraa210a32021-01-02 15:41:03 +0100750 return FAIL;
751 ni = listitem_alloc();
Bram Moolenaarda861d62016-07-17 15:46:27 +0200752 if (ni == NULL)
753 return FAIL;
754 copy_tv(tv, &ni->li_tv);
755 list_insert(l, ni, item);
756 return OK;
757}
758
759 void
760list_insert(list_T *l, listitem_T *ni, listitem_T *item)
761{
Bram Moolenaar7e9f3512020-05-13 22:44:22 +0200762 CHECK_LIST_MATERIALIZE(l);
Bram Moolenaarda861d62016-07-17 15:46:27 +0200763 if (item == NULL)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100764 // Append new item at end of list.
Bram Moolenaarda861d62016-07-17 15:46:27 +0200765 list_append(l, ni);
766 else
767 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100768 // Insert new item before existing item.
Bram Moolenaarda861d62016-07-17 15:46:27 +0200769 ni->li_prev = item->li_prev;
770 ni->li_next = item;
771 if (item->li_prev == NULL)
772 {
773 l->lv_first = ni;
Bram Moolenaar0ff6aad2020-01-29 21:27:21 +0100774 ++l->lv_u.mat.lv_idx;
Bram Moolenaarda861d62016-07-17 15:46:27 +0200775 }
776 else
777 {
778 item->li_prev->li_next = ni;
Bram Moolenaar0ff6aad2020-01-29 21:27:21 +0100779 l->lv_u.mat.lv_idx_item = NULL;
Bram Moolenaarda861d62016-07-17 15:46:27 +0200780 }
781 item->li_prev = ni;
782 ++l->lv_len;
783 }
784}
785
786/*
Bram Moolenaar4f0884d2021-08-11 21:49:23 +0200787 * Get the list item in "l" with index "n1". "n1" is adjusted if needed.
788 * In Vim9, it is at the end of the list, add an item.
789 * Return NULL if there is no such item.
790 */
791 listitem_T *
792check_range_index_one(list_T *l, long *n1, int quiet)
793{
794 listitem_T *li = list_find_index(l, n1);
795
796 if (li == NULL)
797 {
798 // Vim9: Allow for adding an item at the end.
799 if (in_vim9script() && *n1 == l->lv_len && l->lv_lock == 0)
800 {
801 list_append_number(l, 0);
802 li = list_find_index(l, n1);
803 }
804 if (li == NULL)
805 {
806 if (!quiet)
807 semsg(_(e_listidx), *n1);
808 return NULL;
809 }
810 }
811 return li;
812}
813
814/*
815 * Check that "n2" can be used as the second index in a range of list "l".
816 * If "n1" or "n2" is negative it is changed to the positive index.
817 * "li1" is the item for item "n1".
818 * Return OK or FAIL.
819 */
820 int
821check_range_index_two(
822 list_T *l,
823 long *n1,
824 listitem_T *li1,
825 long *n2,
826 int quiet)
827{
828 if (*n2 < 0)
829 {
830 listitem_T *ni = list_find(l, *n2);
831
832 if (ni == NULL)
833 {
834 if (!quiet)
835 semsg(_(e_listidx), *n2);
836 return FAIL;
837 }
838 *n2 = list_idx_of_item(l, ni);
839 }
840
841 // Check that n2 isn't before n1.
842 if (*n1 < 0)
843 *n1 = list_idx_of_item(l, li1);
844 if (*n2 < *n1)
845 {
846 if (!quiet)
847 semsg(_(e_listidx), *n2);
848 return FAIL;
849 }
850 return OK;
851}
852
853/*
854 * Assign values from list "src" into a range of "dest".
855 * "idx1_arg" is the index of the first item in "dest" to be replaced.
856 * "idx2" is the index of last item to be replaced, but when "empty_idx2" is
857 * TRUE then replace all items after "idx1".
858 * "op" is the operator, normally "=" but can be "+=" and the like.
859 * "varname" is used for error messages.
860 * Returns OK or FAIL.
861 */
862 int
863list_assign_range(
864 list_T *dest,
865 list_T *src,
866 long idx1_arg,
867 long idx2,
868 int empty_idx2,
869 char_u *op,
870 char_u *varname)
871{
872 listitem_T *src_li;
873 listitem_T *dest_li;
874 long idx1 = idx1_arg;
875 listitem_T *first_li = list_find_index(dest, &idx1);
876 long idx;
Bram Moolenaar89071cb2021-08-13 18:20:09 +0200877 type_T *member_type = NULL;
Bram Moolenaar4f0884d2021-08-11 21:49:23 +0200878
879 /*
880 * Check whether any of the list items is locked before making any changes.
881 */
882 idx = idx1;
883 dest_li = first_li;
884 for (src_li = src->lv_first; src_li != NULL && dest_li != NULL; )
885 {
886 if (value_check_lock(dest_li->li_tv.v_lock, varname, FALSE))
887 return FAIL;
888 src_li = src_li->li_next;
889 if (src_li == NULL || (!empty_idx2 && idx2 == idx))
890 break;
891 dest_li = dest_li->li_next;
892 ++idx;
893 }
894
Bram Moolenaar89071cb2021-08-13 18:20:09 +0200895 if (in_vim9script() && dest->lv_type != NULL
896 && dest->lv_type->tt_member != NULL)
897 member_type = dest->lv_type->tt_member;
898
Bram Moolenaar4f0884d2021-08-11 21:49:23 +0200899 /*
900 * Assign the List values to the list items.
901 */
902 idx = idx1;
903 dest_li = first_li;
904 for (src_li = src->lv_first; src_li != NULL; )
905 {
906 if (op != NULL && *op != '=')
907 tv_op(&dest_li->li_tv, &src_li->li_tv, op);
908 else
909 {
Bram Moolenaar89071cb2021-08-13 18:20:09 +0200910 if (member_type != NULL
911 && check_typval_arg_type(member_type, &src_li->li_tv,
912 NULL, 0) == FAIL)
913 return FAIL;
Bram Moolenaar4f0884d2021-08-11 21:49:23 +0200914 clear_tv(&dest_li->li_tv);
915 copy_tv(&src_li->li_tv, &dest_li->li_tv);
916 }
917 src_li = src_li->li_next;
918 if (src_li == NULL || (!empty_idx2 && idx2 == idx))
919 break;
920 if (dest_li->li_next == NULL)
921 {
922 // Need to add an empty item.
923 if (list_append_number(dest, 0) == FAIL)
924 {
925 src_li = NULL;
926 break;
927 }
928 }
929 dest_li = dest_li->li_next;
930 ++idx;
931 }
932 if (src_li != NULL)
933 {
934 emsg(_(e_list_value_has_more_items_than_targets));
935 return FAIL;
936 }
937 if (empty_idx2
938 ? (dest_li != NULL && dest_li->li_next != NULL)
939 : idx != idx2)
940 {
941 emsg(_(e_list_value_does_not_have_enough_items));
942 return FAIL;
943 }
944 return OK;
945}
946
947/*
Bram Moolenaar077a1e62020-06-08 20:50:43 +0200948 * Flatten "list" to depth "maxdepth".
949 * It does nothing if "maxdepth" is 0.
950 * Returns FAIL when out of memory.
951 */
Bram Moolenaar3b690062021-02-01 20:14:51 +0100952 static void
Bram Moolenaar077a1e62020-06-08 20:50:43 +0200953list_flatten(list_T *list, long maxdepth)
954{
955 listitem_T *item;
Bram Moolenaardcf59c32020-06-09 17:30:04 +0200956 listitem_T *tofree;
Bram Moolenaar077a1e62020-06-08 20:50:43 +0200957 int n;
958
959 if (maxdepth == 0)
Bram Moolenaar3b690062021-02-01 20:14:51 +0100960 return;
Bram Moolenaar077a1e62020-06-08 20:50:43 +0200961 CHECK_LIST_MATERIALIZE(list);
962
963 n = 0;
964 item = list->lv_first;
965 while (item != NULL)
966 {
967 fast_breakcheck();
968 if (got_int)
Bram Moolenaar3b690062021-02-01 20:14:51 +0100969 return;
Bram Moolenaar077a1e62020-06-08 20:50:43 +0200970
971 if (item->li_tv.v_type == VAR_LIST)
972 {
973 listitem_T *next = item->li_next;
974
975 vimlist_remove(list, item, item);
976 if (list_extend(list, item->li_tv.vval.v_list, next) == FAIL)
Bram Moolenaarb3bf33a2021-09-11 20:20:38 +0200977 {
978 list_free_item(list, item);
Bram Moolenaar3b690062021-02-01 20:14:51 +0100979 return;
Bram Moolenaarb3bf33a2021-09-11 20:20:38 +0200980 }
Bram Moolenaardcf59c32020-06-09 17:30:04 +0200981 clear_tv(&item->li_tv);
982 tofree = item;
Bram Moolenaar077a1e62020-06-08 20:50:43 +0200983
984 if (item->li_prev == NULL)
985 item = list->lv_first;
986 else
987 item = item->li_prev->li_next;
Bram Moolenaardcf59c32020-06-09 17:30:04 +0200988 list_free_item(list, tofree);
Bram Moolenaar077a1e62020-06-08 20:50:43 +0200989
990 if (++n >= maxdepth)
991 {
992 n = 0;
993 item = next;
994 }
995 }
996 else
997 {
998 n = 0;
999 item = item->li_next;
1000 }
1001 }
Bram Moolenaar077a1e62020-06-08 20:50:43 +02001002}
1003
1004/*
Bram Moolenaar3b690062021-02-01 20:14:51 +01001005 * "flatten()" and "flattennew()" functions
Bram Moolenaar077a1e62020-06-08 20:50:43 +02001006 */
Bram Moolenaar3b690062021-02-01 20:14:51 +01001007 static void
1008flatten_common(typval_T *argvars, typval_T *rettv, int make_copy)
Bram Moolenaar077a1e62020-06-08 20:50:43 +02001009{
1010 list_T *l;
1011 long maxdepth;
1012 int error = FALSE;
1013
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +02001014 if (in_vim9script()
1015 && (check_for_list_arg(argvars, 0) == FAIL
1016 || check_for_opt_number_arg(argvars, 1) == FAIL))
1017 return;
1018
Bram Moolenaar077a1e62020-06-08 20:50:43 +02001019 if (argvars[0].v_type != VAR_LIST)
1020 {
1021 semsg(_(e_listarg), "flatten()");
1022 return;
1023 }
1024
1025 if (argvars[1].v_type == VAR_UNKNOWN)
1026 maxdepth = 999999;
1027 else
1028 {
1029 maxdepth = (long)tv_get_number_chk(&argvars[1], &error);
1030 if (error)
1031 return;
1032 if (maxdepth < 0)
1033 {
1034 emsg(_("E900: maxdepth must be non-negative number"));
1035 return;
1036 }
1037 }
1038
1039 l = argvars[0].vval.v_list;
Bram Moolenaar3b690062021-02-01 20:14:51 +01001040 rettv->v_type = VAR_LIST;
1041 rettv->vval.v_list = l;
1042 if (l == NULL)
1043 return;
1044
1045 if (make_copy)
1046 {
1047 l = list_copy(l, TRUE, get_copyID());
1048 rettv->vval.v_list = l;
1049 if (l == NULL)
1050 return;
Bram Moolenaarb3bf33a2021-09-11 20:20:38 +02001051 // The type will change.
1052 free_type(l->lv_type);
1053 l->lv_type = NULL;
Bram Moolenaar3b690062021-02-01 20:14:51 +01001054 }
1055 else
1056 {
1057 if (value_check_lock(l->lv_lock,
1058 (char_u *)N_("flatten() argument"), TRUE))
1059 return;
1060 ++l->lv_refcount;
1061 }
1062
1063 list_flatten(l, maxdepth);
1064}
1065
1066/*
1067 * "flatten(list[, {maxdepth}])" function
1068 */
1069 void
1070f_flatten(typval_T *argvars, typval_T *rettv)
1071{
1072 if (in_vim9script())
1073 emsg(_(e_cannot_use_flatten_in_vim9_script));
1074 else
1075 flatten_common(argvars, rettv, FALSE);
1076}
1077
1078/*
1079 * "flattennew(list[, {maxdepth}])" function
1080 */
1081 void
1082f_flattennew(typval_T *argvars, typval_T *rettv)
1083{
1084 flatten_common(argvars, rettv, TRUE);
Bram Moolenaar077a1e62020-06-08 20:50:43 +02001085}
1086
1087/*
Bram Moolenaar1a739232020-10-10 15:37:58 +02001088 * Extend "l1" with "l2". "l1" must not be NULL.
Bram Moolenaarda861d62016-07-17 15:46:27 +02001089 * If "bef" is NULL append at the end, otherwise insert before this item.
1090 * Returns FAIL when out of memory.
1091 */
1092 int
1093list_extend(list_T *l1, list_T *l2, listitem_T *bef)
1094{
1095 listitem_T *item;
Bram Moolenaar1a739232020-10-10 15:37:58 +02001096 int todo;
Bram Moolenaardcae51f2021-04-08 20:10:10 +02001097 listitem_T *bef_prev;
Bram Moolenaarda861d62016-07-17 15:46:27 +02001098
Bram Moolenaar1a739232020-10-10 15:37:58 +02001099 // NULL list is equivalent to an empty list: nothing to do.
1100 if (l2 == NULL || l2->lv_len == 0)
1101 return OK;
1102
1103 todo = l2->lv_len;
Bram Moolenaar7e9f3512020-05-13 22:44:22 +02001104 CHECK_LIST_MATERIALIZE(l1);
1105 CHECK_LIST_MATERIALIZE(l2);
Bram Moolenaar8a7d6542020-01-26 15:56:19 +01001106
Bram Moolenaardcae51f2021-04-08 20:10:10 +02001107 // When exending a list with itself, at some point we run into the item
1108 // that was before "bef" and need to skip over the already inserted items
1109 // to "bef".
1110 bef_prev = bef == NULL ? NULL : bef->li_prev;
1111
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001112 // We also quit the loop when we have inserted the original item count of
1113 // the list, avoid a hang when we extend a list with itself.
Bram Moolenaardcae51f2021-04-08 20:10:10 +02001114 for (item = l2->lv_first; item != NULL && --todo >= 0;
1115 item = item == bef_prev ? bef : item->li_next)
Bram Moolenaarda861d62016-07-17 15:46:27 +02001116 if (list_insert_tv(l1, &item->li_tv, bef) == FAIL)
1117 return FAIL;
1118 return OK;
1119}
1120
1121/*
1122 * Concatenate lists "l1" and "l2" into a new list, stored in "tv".
1123 * Return FAIL when out of memory.
1124 */
1125 int
1126list_concat(list_T *l1, list_T *l2, typval_T *tv)
1127{
1128 list_T *l;
1129
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001130 // make a copy of the first list.
Bram Moolenaar1a739232020-10-10 15:37:58 +02001131 if (l1 == NULL)
1132 l = list_alloc();
1133 else
1134 l = list_copy(l1, FALSE, 0);
Bram Moolenaarda861d62016-07-17 15:46:27 +02001135 if (l == NULL)
1136 return FAIL;
1137 tv->v_type = VAR_LIST;
Bram Moolenaar9681f712020-11-21 13:58:50 +01001138 tv->v_lock = 0;
Bram Moolenaarda861d62016-07-17 15:46:27 +02001139 tv->vval.v_list = l;
Bram Moolenaar1a739232020-10-10 15:37:58 +02001140 if (l1 == NULL)
1141 ++l->lv_refcount;
Bram Moolenaarda861d62016-07-17 15:46:27 +02001142
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001143 // append all items from the second list
Bram Moolenaarda861d62016-07-17 15:46:27 +02001144 return list_extend(l, l2, NULL);
1145}
1146
Bram Moolenaar9af78762020-06-16 11:34:42 +02001147 list_T *
1148list_slice(list_T *ol, long n1, long n2)
1149{
1150 listitem_T *item;
1151 list_T *l = list_alloc();
1152
1153 if (l == NULL)
1154 return NULL;
1155 for (item = list_find(ol, n1); n1 <= n2; ++n1)
1156 {
1157 if (list_append_tv(l, &item->li_tv) == FAIL)
1158 {
1159 list_free(l);
1160 return NULL;
1161 }
1162 item = item->li_next;
1163 }
1164 return l;
1165}
1166
Bram Moolenaared591872020-08-15 22:14:53 +02001167 int
1168list_slice_or_index(
1169 list_T *list,
1170 int range,
Bram Moolenaar6601b622021-01-13 21:47:15 +01001171 varnumber_T n1_arg,
1172 varnumber_T n2_arg,
1173 int exclusive,
Bram Moolenaared591872020-08-15 22:14:53 +02001174 typval_T *rettv,
1175 int verbose)
1176{
1177 long len = list_len(list);
Bram Moolenaar6601b622021-01-13 21:47:15 +01001178 varnumber_T n1 = n1_arg;
1179 varnumber_T n2 = n2_arg;
Bram Moolenaared591872020-08-15 22:14:53 +02001180 typval_T var1;
1181
1182 if (n1 < 0)
1183 n1 = len + n1;
1184 if (n1 < 0 || n1 >= len)
1185 {
Bram Moolenaar92f05f22021-08-12 21:12:56 +02001186 // For a range we allow invalid values and for legacy script return an
1187 // empty list, for Vim9 script start at the first item.
1188 // A list index out of range is an error.
Bram Moolenaared591872020-08-15 22:14:53 +02001189 if (!range)
1190 {
1191 if (verbose)
Bram Moolenaar239f8d92021-01-17 13:21:20 +01001192 semsg(_(e_listidx), (long)n1_arg);
Bram Moolenaared591872020-08-15 22:14:53 +02001193 return FAIL;
1194 }
Bram Moolenaar92f05f22021-08-12 21:12:56 +02001195 if (in_vim9script())
1196 n1 = n1 < 0 ? 0 : len;
1197 else
1198 n1 = len;
Bram Moolenaared591872020-08-15 22:14:53 +02001199 }
1200 if (range)
1201 {
1202 list_T *l;
1203
1204 if (n2 < 0)
1205 n2 = len + n2;
1206 else if (n2 >= len)
Bram Moolenaar6601b622021-01-13 21:47:15 +01001207 n2 = len - (exclusive ? 0 : 1);
1208 if (exclusive)
1209 --n2;
Bram Moolenaared591872020-08-15 22:14:53 +02001210 if (n2 < 0 || n2 + 1 < n1)
1211 n2 = -1;
1212 l = list_slice(list, n1, n2);
1213 if (l == NULL)
1214 return FAIL;
1215 clear_tv(rettv);
1216 rettv_list_set(rettv, l);
1217 }
1218 else
1219 {
1220 // copy the item to "var1" to avoid that freeing the list makes it
1221 // invalid.
1222 copy_tv(&list_find(list, n1)->li_tv, &var1);
1223 clear_tv(rettv);
1224 *rettv = var1;
1225 }
1226 return OK;
1227}
1228
Bram Moolenaarda861d62016-07-17 15:46:27 +02001229/*
1230 * Make a copy of list "orig". Shallow if "deep" is FALSE.
1231 * The refcount of the new list is set to 1.
1232 * See item_copy() for "copyID".
1233 * Returns NULL when out of memory.
1234 */
1235 list_T *
1236list_copy(list_T *orig, int deep, int copyID)
1237{
1238 list_T *copy;
1239 listitem_T *item;
1240 listitem_T *ni;
1241
1242 if (orig == NULL)
1243 return NULL;
1244
1245 copy = list_alloc();
1246 if (copy != NULL)
1247 {
Bram Moolenaarb3bf33a2021-09-11 20:20:38 +02001248 copy->lv_type = alloc_type(orig->lv_type);
Bram Moolenaarda861d62016-07-17 15:46:27 +02001249 if (copyID != 0)
1250 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001251 // Do this before adding the items, because one of the items may
1252 // refer back to this list.
Bram Moolenaarda861d62016-07-17 15:46:27 +02001253 orig->lv_copyID = copyID;
1254 orig->lv_copylist = copy;
1255 }
Bram Moolenaar7e9f3512020-05-13 22:44:22 +02001256 CHECK_LIST_MATERIALIZE(orig);
Bram Moolenaarda861d62016-07-17 15:46:27 +02001257 for (item = orig->lv_first; item != NULL && !got_int;
1258 item = item->li_next)
1259 {
1260 ni = listitem_alloc();
1261 if (ni == NULL)
1262 break;
1263 if (deep)
1264 {
1265 if (item_copy(&item->li_tv, &ni->li_tv, deep, copyID) == FAIL)
1266 {
1267 vim_free(ni);
1268 break;
1269 }
1270 }
1271 else
1272 copy_tv(&item->li_tv, &ni->li_tv);
1273 list_append(copy, ni);
1274 }
1275 ++copy->lv_refcount;
1276 if (item != NULL)
1277 {
1278 list_unref(copy);
1279 copy = NULL;
1280 }
1281 }
1282
1283 return copy;
1284}
1285
1286/*
1287 * Remove items "item" to "item2" from list "l".
1288 * Does not free the listitem or the value!
1289 * This used to be called list_remove, but that conflicts with a Sun header
1290 * file.
1291 */
1292 void
1293vimlist_remove(list_T *l, listitem_T *item, listitem_T *item2)
1294{
1295 listitem_T *ip;
1296
Bram Moolenaar7e9f3512020-05-13 22:44:22 +02001297 CHECK_LIST_MATERIALIZE(l);
Bram Moolenaar8a7d6542020-01-26 15:56:19 +01001298
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001299 // notify watchers
Bram Moolenaarda861d62016-07-17 15:46:27 +02001300 for (ip = item; ip != NULL; ip = ip->li_next)
1301 {
1302 --l->lv_len;
1303 list_fix_watch(l, ip);
1304 if (ip == item2)
1305 break;
1306 }
1307
1308 if (item2->li_next == NULL)
Bram Moolenaar0ff6aad2020-01-29 21:27:21 +01001309 l->lv_u.mat.lv_last = item->li_prev;
Bram Moolenaarda861d62016-07-17 15:46:27 +02001310 else
1311 item2->li_next->li_prev = item->li_prev;
1312 if (item->li_prev == NULL)
1313 l->lv_first = item2->li_next;
1314 else
1315 item->li_prev->li_next = item2->li_next;
Bram Moolenaar0ff6aad2020-01-29 21:27:21 +01001316 l->lv_u.mat.lv_idx_item = NULL;
Bram Moolenaarda861d62016-07-17 15:46:27 +02001317}
1318
1319/*
1320 * Return an allocated string with the string representation of a list.
1321 * May return NULL.
1322 */
1323 char_u *
1324list2string(typval_T *tv, int copyID, int restore_copyID)
1325{
1326 garray_T ga;
1327
1328 if (tv->vval.v_list == NULL)
1329 return NULL;
1330 ga_init2(&ga, (int)sizeof(char), 80);
1331 ga_append(&ga, '[');
Bram Moolenaar7e9f3512020-05-13 22:44:22 +02001332 CHECK_LIST_MATERIALIZE(tv->vval.v_list);
Bram Moolenaarda861d62016-07-17 15:46:27 +02001333 if (list_join(&ga, tv->vval.v_list, (char_u *)", ",
1334 FALSE, restore_copyID, copyID) == FAIL)
1335 {
1336 vim_free(ga.ga_data);
1337 return NULL;
1338 }
1339 ga_append(&ga, ']');
1340 ga_append(&ga, NUL);
1341 return (char_u *)ga.ga_data;
1342}
1343
1344typedef struct join_S {
1345 char_u *s;
1346 char_u *tofree;
1347} join_T;
1348
1349 static int
1350list_join_inner(
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001351 garray_T *gap, // to store the result in
Bram Moolenaarda861d62016-07-17 15:46:27 +02001352 list_T *l,
1353 char_u *sep,
1354 int echo_style,
1355 int restore_copyID,
1356 int copyID,
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001357 garray_T *join_gap) // to keep each list item string
Bram Moolenaarda861d62016-07-17 15:46:27 +02001358{
1359 int i;
1360 join_T *p;
1361 int len;
1362 int sumlen = 0;
1363 int first = TRUE;
1364 char_u *tofree;
1365 char_u numbuf[NUMBUFLEN];
1366 listitem_T *item;
1367 char_u *s;
1368
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001369 // Stringify each item in the list.
Bram Moolenaar7e9f3512020-05-13 22:44:22 +02001370 CHECK_LIST_MATERIALIZE(l);
Bram Moolenaarda861d62016-07-17 15:46:27 +02001371 for (item = l->lv_first; item != NULL && !got_int; item = item->li_next)
1372 {
1373 s = echo_string_core(&item->li_tv, &tofree, numbuf, copyID,
Bram Moolenaar35422f42017-08-05 16:33:56 +02001374 echo_style, restore_copyID, !echo_style);
Bram Moolenaarda861d62016-07-17 15:46:27 +02001375 if (s == NULL)
1376 return FAIL;
1377
1378 len = (int)STRLEN(s);
1379 sumlen += len;
1380
1381 (void)ga_grow(join_gap, 1);
1382 p = ((join_T *)join_gap->ga_data) + (join_gap->ga_len++);
1383 if (tofree != NULL || s != numbuf)
1384 {
1385 p->s = s;
1386 p->tofree = tofree;
1387 }
1388 else
1389 {
1390 p->s = vim_strnsave(s, len);
1391 p->tofree = p->s;
1392 }
1393
1394 line_breakcheck();
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001395 if (did_echo_string_emsg) // recursion error, bail out
Bram Moolenaarda861d62016-07-17 15:46:27 +02001396 break;
1397 }
1398
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001399 // Allocate result buffer with its total size, avoid re-allocation and
1400 // multiple copy operations. Add 2 for a tailing ']' and NUL.
Bram Moolenaarda861d62016-07-17 15:46:27 +02001401 if (join_gap->ga_len >= 2)
1402 sumlen += (int)STRLEN(sep) * (join_gap->ga_len - 1);
1403 if (ga_grow(gap, sumlen + 2) == FAIL)
1404 return FAIL;
1405
1406 for (i = 0; i < join_gap->ga_len && !got_int; ++i)
1407 {
1408 if (first)
1409 first = FALSE;
1410 else
1411 ga_concat(gap, sep);
1412 p = ((join_T *)join_gap->ga_data) + i;
1413
1414 if (p->s != NULL)
1415 ga_concat(gap, p->s);
1416 line_breakcheck();
1417 }
1418
1419 return OK;
1420}
1421
1422/*
1423 * Join list "l" into a string in "*gap", using separator "sep".
1424 * When "echo_style" is TRUE use String as echoed, otherwise as inside a List.
1425 * Return FAIL or OK.
1426 */
1427 int
1428list_join(
1429 garray_T *gap,
1430 list_T *l,
1431 char_u *sep,
1432 int echo_style,
1433 int restore_copyID,
1434 int copyID)
1435{
1436 garray_T join_ga;
1437 int retval;
1438 join_T *p;
1439 int i;
1440
1441 if (l->lv_len < 1)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001442 return OK; // nothing to do
Bram Moolenaarda861d62016-07-17 15:46:27 +02001443 ga_init2(&join_ga, (int)sizeof(join_T), l->lv_len);
1444 retval = list_join_inner(gap, l, sep, echo_style, restore_copyID,
1445 copyID, &join_ga);
1446
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001447 // Dispose each item in join_ga.
Bram Moolenaarda861d62016-07-17 15:46:27 +02001448 if (join_ga.ga_data != NULL)
1449 {
1450 p = (join_T *)join_ga.ga_data;
1451 for (i = 0; i < join_ga.ga_len; ++i)
1452 {
1453 vim_free(p->tofree);
1454 ++p;
1455 }
1456 ga_clear(&join_ga);
1457 }
1458
1459 return retval;
1460}
1461
1462/*
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001463 * "join()" function
1464 */
1465 void
1466f_join(typval_T *argvars, typval_T *rettv)
1467{
1468 garray_T ga;
1469 char_u *sep;
1470
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +02001471 if (in_vim9script()
1472 && (check_for_list_arg(argvars, 0) == FAIL
1473 || check_for_opt_string_arg(argvars, 1) == FAIL))
1474 return;
1475
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001476 if (argvars[0].v_type != VAR_LIST)
1477 {
1478 emsg(_(e_listreq));
1479 return;
1480 }
Bram Moolenaaref982572021-08-12 19:27:57 +02001481 rettv->v_type = VAR_STRING;
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001482 if (argvars[0].vval.v_list == NULL)
1483 return;
Bram Moolenaaref982572021-08-12 19:27:57 +02001484
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001485 if (argvars[1].v_type == VAR_UNKNOWN)
1486 sep = (char_u *)" ";
1487 else
1488 sep = tv_get_string_chk(&argvars[1]);
1489
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001490 if (sep != NULL)
1491 {
1492 ga_init2(&ga, (int)sizeof(char), 80);
1493 list_join(&ga, argvars[0].vval.v_list, sep, TRUE, FALSE, 0);
1494 ga_append(&ga, NUL);
1495 rettv->vval.v_string = (char_u *)ga.ga_data;
1496 }
1497 else
1498 rettv->vval.v_string = NULL;
1499}
1500
1501/*
Bram Moolenaarda861d62016-07-17 15:46:27 +02001502 * Allocate a variable for a List and fill it from "*arg".
Bram Moolenaar71478202020-06-26 22:46:27 +02001503 * "*arg" points to the "[".
Bram Moolenaarda861d62016-07-17 15:46:27 +02001504 * Return OK or FAIL.
1505 */
1506 int
Bram Moolenaar9a78e6d2020-07-01 18:29:55 +02001507eval_list(char_u **arg, typval_T *rettv, evalarg_T *evalarg, int do_error)
Bram Moolenaarda861d62016-07-17 15:46:27 +02001508{
Bram Moolenaar71478202020-06-26 22:46:27 +02001509 int evaluate = evalarg == NULL ? FALSE
1510 : evalarg->eval_flags & EVAL_EVALUATE;
Bram Moolenaarda861d62016-07-17 15:46:27 +02001511 list_T *l = NULL;
1512 typval_T tv;
1513 listitem_T *item;
Bram Moolenaareb6880b2020-07-12 17:07:05 +02001514 int vim9script = in_vim9script();
Bram Moolenaar71478202020-06-26 22:46:27 +02001515 int had_comma;
Bram Moolenaarda861d62016-07-17 15:46:27 +02001516
1517 if (evaluate)
1518 {
1519 l = list_alloc();
1520 if (l == NULL)
1521 return FAIL;
1522 }
1523
Bram Moolenaar962d7212020-07-04 14:15:00 +02001524 *arg = skipwhite_and_linebreak(*arg + 1, evalarg);
Bram Moolenaarda861d62016-07-17 15:46:27 +02001525 while (**arg != ']' && **arg != NUL)
1526 {
Bram Moolenaar71478202020-06-26 22:46:27 +02001527 if (eval1(arg, &tv, evalarg) == FAIL) // recursive!
Bram Moolenaarda861d62016-07-17 15:46:27 +02001528 goto failret;
1529 if (evaluate)
1530 {
1531 item = listitem_alloc();
1532 if (item != NULL)
1533 {
1534 item->li_tv = tv;
1535 item->li_tv.v_lock = 0;
1536 list_append(l, item);
1537 }
1538 else
1539 clear_tv(&tv);
1540 }
Bram Moolenaar4d4d1cd2020-07-30 22:14:33 +02001541 // Legacy Vim script allowed a space before the comma.
1542 if (!vim9script)
1543 *arg = skipwhite(*arg);
Bram Moolenaarda861d62016-07-17 15:46:27 +02001544
Bram Moolenaare6e03172020-06-27 16:36:05 +02001545 // the comma must come after the value
Bram Moolenaar71478202020-06-26 22:46:27 +02001546 had_comma = **arg == ',';
1547 if (had_comma)
Bram Moolenaare6e03172020-06-27 16:36:05 +02001548 {
Bram Moolenaar659bb222020-11-12 20:16:39 +01001549 if (vim9script && !IS_WHITE_OR_NUL((*arg)[1]) && (*arg)[1] != ']')
Bram Moolenaare6e03172020-06-27 16:36:05 +02001550 {
Bram Moolenaarc3fc75d2021-02-07 15:28:09 +01001551 semsg(_(e_white_space_required_after_str_str), ",", *arg);
Bram Moolenaare6e03172020-06-27 16:36:05 +02001552 goto failret;
1553 }
Bram Moolenaar71478202020-06-26 22:46:27 +02001554 *arg = skipwhite(*arg + 1);
Bram Moolenaare6e03172020-06-27 16:36:05 +02001555 }
Bram Moolenaar71478202020-06-26 22:46:27 +02001556
Bram Moolenaare6b53242020-07-01 17:28:33 +02001557 // The "]" can be on the next line. But a double quoted string may
1558 // follow, not a comment.
Bram Moolenaar962d7212020-07-04 14:15:00 +02001559 *arg = skipwhite_and_linebreak(*arg, evalarg);
Bram Moolenaarda861d62016-07-17 15:46:27 +02001560 if (**arg == ']')
1561 break;
Bram Moolenaar71478202020-06-26 22:46:27 +02001562
1563 if (!had_comma)
Bram Moolenaarda861d62016-07-17 15:46:27 +02001564 {
Bram Moolenaar8a7d6542020-01-26 15:56:19 +01001565 if (do_error)
Bram Moolenaardb199212020-08-12 18:01:53 +02001566 {
1567 if (**arg == ',')
Bram Moolenaarba98fb52021-02-07 18:06:29 +01001568 semsg(_(e_no_white_space_allowed_before_str_str),
1569 ",", *arg);
Bram Moolenaardb199212020-08-12 18:01:53 +02001570 else
1571 semsg(_("E696: Missing comma in List: %s"), *arg);
1572 }
Bram Moolenaarda861d62016-07-17 15:46:27 +02001573 goto failret;
1574 }
Bram Moolenaarda861d62016-07-17 15:46:27 +02001575 }
1576
1577 if (**arg != ']')
1578 {
Bram Moolenaar8a7d6542020-01-26 15:56:19 +01001579 if (do_error)
Bram Moolenaaree619e52020-03-28 21:38:06 +01001580 semsg(_(e_list_end), *arg);
Bram Moolenaarda861d62016-07-17 15:46:27 +02001581failret:
1582 if (evaluate)
1583 list_free(l);
1584 return FAIL;
1585 }
1586
Bram Moolenaar9d489562020-07-30 20:08:50 +02001587 *arg += 1;
Bram Moolenaarda861d62016-07-17 15:46:27 +02001588 if (evaluate)
Bram Moolenaar45cf6e92017-04-30 20:25:19 +02001589 rettv_list_set(rettv, l);
Bram Moolenaarda861d62016-07-17 15:46:27 +02001590
1591 return OK;
1592}
1593
Bram Moolenaar73dad1e2016-07-17 22:13:49 +02001594/*
Bram Moolenaarcaa55b62017-01-10 13:51:09 +01001595 * Write "list" of strings to file "fd".
Bram Moolenaar73dad1e2016-07-17 22:13:49 +02001596 */
1597 int
1598write_list(FILE *fd, list_T *list, int binary)
1599{
1600 listitem_T *li;
1601 int c;
1602 int ret = OK;
1603 char_u *s;
1604
Bram Moolenaar7e9f3512020-05-13 22:44:22 +02001605 CHECK_LIST_MATERIALIZE(list);
Bram Moolenaaraeea7212020-04-02 18:50:46 +02001606 FOR_ALL_LIST_ITEMS(list, li)
Bram Moolenaar73dad1e2016-07-17 22:13:49 +02001607 {
Bram Moolenaard155d7a2018-12-21 16:04:21 +01001608 for (s = tv_get_string(&li->li_tv); *s != NUL; ++s)
Bram Moolenaar73dad1e2016-07-17 22:13:49 +02001609 {
1610 if (*s == '\n')
1611 c = putc(NUL, fd);
1612 else
1613 c = putc(*s, fd);
1614 if (c == EOF)
1615 {
1616 ret = FAIL;
1617 break;
1618 }
1619 }
1620 if (!binary || li->li_next != NULL)
1621 if (putc('\n', fd) == EOF)
1622 {
1623 ret = FAIL;
1624 break;
1625 }
1626 if (ret == FAIL)
1627 {
Bram Moolenaar40bcec12021-12-05 22:19:27 +00001628 emsg(_(e_error_while_writing));
Bram Moolenaar73dad1e2016-07-17 22:13:49 +02001629 break;
1630 }
1631 }
1632 return ret;
1633}
1634
Bram Moolenaardf48fb42016-07-22 21:50:18 +02001635/*
1636 * Initialize a static list with 10 items.
1637 */
1638 void
1639init_static_list(staticList10_T *sl)
1640{
1641 list_T *l = &sl->sl_list;
1642 int i;
1643
1644 memset(sl, 0, sizeof(staticList10_T));
1645 l->lv_first = &sl->sl_items[0];
Bram Moolenaar0ff6aad2020-01-29 21:27:21 +01001646 l->lv_u.mat.lv_last = &sl->sl_items[9];
Bram Moolenaardf48fb42016-07-22 21:50:18 +02001647 l->lv_refcount = DO_NOT_FREE_CNT;
1648 l->lv_lock = VAR_FIXED;
1649 sl->sl_list.lv_len = 10;
1650
1651 for (i = 0; i < 10; ++i)
1652 {
1653 listitem_T *li = &sl->sl_items[i];
1654
1655 if (i == 0)
1656 li->li_prev = NULL;
1657 else
1658 li->li_prev = li - 1;
1659 if (i == 9)
1660 li->li_next = NULL;
1661 else
1662 li->li_next = li + 1;
1663 }
1664}
1665
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001666/*
1667 * "list2str()" function
1668 */
1669 void
1670f_list2str(typval_T *argvars, typval_T *rettv)
1671{
1672 list_T *l;
1673 listitem_T *li;
1674 garray_T ga;
1675 int utf8 = FALSE;
1676
1677 rettv->v_type = VAR_STRING;
1678 rettv->vval.v_string = NULL;
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +02001679
1680 if (in_vim9script()
1681 && (check_for_list_arg(argvars, 0) == FAIL
1682 || check_for_opt_bool_arg(argvars, 1) == FAIL))
1683 return;
1684
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001685 if (argvars[0].v_type != VAR_LIST)
1686 {
1687 emsg(_(e_invarg));
1688 return;
1689 }
1690
1691 l = argvars[0].vval.v_list;
1692 if (l == NULL)
1693 return; // empty list results in empty string
1694
1695 if (argvars[1].v_type != VAR_UNKNOWN)
Bram Moolenaara48f7862020-09-05 20:16:57 +02001696 utf8 = (int)tv_get_bool_chk(&argvars[1], NULL);
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001697
Bram Moolenaar7e9f3512020-05-13 22:44:22 +02001698 CHECK_LIST_MATERIALIZE(l);
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001699 ga_init2(&ga, 1, 80);
1700 if (has_mbyte || utf8)
1701 {
1702 char_u buf[MB_MAXBYTES + 1];
1703 int (*char2bytes)(int, char_u *);
1704
1705 if (utf8 || enc_utf8)
1706 char2bytes = utf_char2bytes;
1707 else
1708 char2bytes = mb_char2bytes;
1709
Bram Moolenaaraeea7212020-04-02 18:50:46 +02001710 FOR_ALL_LIST_ITEMS(l, li)
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001711 {
1712 buf[(*char2bytes)(tv_get_number(&li->li_tv), buf)] = NUL;
1713 ga_concat(&ga, buf);
1714 }
1715 ga_append(&ga, NUL);
1716 }
1717 else if (ga_grow(&ga, list_len(l) + 1) == OK)
1718 {
Bram Moolenaaraeea7212020-04-02 18:50:46 +02001719 FOR_ALL_LIST_ITEMS(l, li)
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001720 ga_append(&ga, tv_get_number(&li->li_tv));
1721 ga_append(&ga, NUL);
1722 }
1723
1724 rettv->v_type = VAR_STRING;
1725 rettv->vval.v_string = ga.ga_data;
1726}
1727
Bram Moolenaarbdff0122020-04-05 18:56:05 +02001728 static void
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001729list_remove(typval_T *argvars, typval_T *rettv, char_u *arg_errmsg)
1730{
1731 list_T *l;
1732 listitem_T *item, *item2;
1733 listitem_T *li;
1734 int error = FALSE;
Bram Moolenaar239f8d92021-01-17 13:21:20 +01001735 long idx;
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001736
1737 if ((l = argvars[0].vval.v_list) == NULL
Bram Moolenaara187c432020-09-16 21:08:28 +02001738 || value_check_lock(l->lv_lock, arg_errmsg, TRUE))
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001739 return;
1740
1741 idx = (long)tv_get_number_chk(&argvars[1], &error);
1742 if (error)
1743 ; // type error: do nothing, errmsg already given
1744 else if ((item = list_find(l, idx)) == NULL)
1745 semsg(_(e_listidx), idx);
1746 else
1747 {
1748 if (argvars[2].v_type == VAR_UNKNOWN)
1749 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001750 // Remove one item, return its value.
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001751 vimlist_remove(l, item, item);
1752 *rettv = item->li_tv;
Bram Moolenaar8a7d6542020-01-26 15:56:19 +01001753 list_free_item(l, item);
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001754 }
1755 else
1756 {
1757 // Remove range of items, return list with values.
Bram Moolenaar239f8d92021-01-17 13:21:20 +01001758 long end = (long)tv_get_number_chk(&argvars[2], &error);
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001759
1760 if (error)
1761 ; // type error: do nothing
1762 else if ((item2 = list_find(l, end)) == NULL)
1763 semsg(_(e_listidx), end);
1764 else
1765 {
1766 int cnt = 0;
1767
1768 for (li = item; li != NULL; li = li->li_next)
1769 {
1770 ++cnt;
1771 if (li == item2)
1772 break;
1773 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001774 if (li == NULL) // didn't find "item2" after "item"
Bram Moolenaar108010a2021-06-27 22:03:33 +02001775 emsg(_(e_invalid_range));
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001776 else
1777 {
1778 vimlist_remove(l, item, item2);
1779 if (rettv_list_alloc(rettv) == OK)
1780 {
Bram Moolenaar885971e2021-07-18 22:25:29 +02001781 list_T *rl = rettv->vval.v_list;
1782
1783 if (l->lv_with_items > 0)
1784 {
1785 // need to copy the list items and move the value
1786 while (item != NULL)
1787 {
1788 li = listitem_alloc();
1789 if (li == NULL)
1790 return;
1791 li->li_tv = item->li_tv;
1792 init_tv(&item->li_tv);
1793 list_append(rl, li);
1794 if (item == item2)
1795 break;
1796 item = item->li_next;
1797 }
1798 }
1799 else
1800 {
1801 rl->lv_first = item;
1802 rl->lv_u.mat.lv_last = item2;
1803 item->li_prev = NULL;
1804 item2->li_next = NULL;
1805 rl->lv_len = cnt;
1806 }
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001807 }
1808 }
1809 }
1810 }
1811 }
1812}
1813
1814static int item_compare(const void *s1, const void *s2);
1815static int item_compare2(const void *s1, const void *s2);
1816
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001817// struct used in the array that's given to qsort()
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001818typedef struct
1819{
1820 listitem_T *item;
1821 int idx;
1822} sortItem_T;
1823
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001824// struct storing information about current sort
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001825typedef struct
1826{
1827 int item_compare_ic;
Bram Moolenaar55e29612020-11-01 13:57:44 +01001828 int item_compare_lc;
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001829 int item_compare_numeric;
1830 int item_compare_numbers;
1831#ifdef FEAT_FLOAT
1832 int item_compare_float;
1833#endif
1834 char_u *item_compare_func;
1835 partial_T *item_compare_partial;
1836 dict_T *item_compare_selfdict;
1837 int item_compare_func_err;
1838 int item_compare_keep_zero;
1839} sortinfo_T;
1840static sortinfo_T *sortinfo = NULL;
1841#define ITEM_COMPARE_FAIL 999
1842
1843/*
1844 * Compare functions for f_sort() and f_uniq() below.
1845 */
1846 static int
1847item_compare(const void *s1, const void *s2)
1848{
1849 sortItem_T *si1, *si2;
1850 typval_T *tv1, *tv2;
1851 char_u *p1, *p2;
1852 char_u *tofree1 = NULL, *tofree2 = NULL;
1853 int res;
1854 char_u numbuf1[NUMBUFLEN];
1855 char_u numbuf2[NUMBUFLEN];
1856
1857 si1 = (sortItem_T *)s1;
1858 si2 = (sortItem_T *)s2;
1859 tv1 = &si1->item->li_tv;
1860 tv2 = &si2->item->li_tv;
1861
1862 if (sortinfo->item_compare_numbers)
1863 {
1864 varnumber_T v1 = tv_get_number(tv1);
1865 varnumber_T v2 = tv_get_number(tv2);
1866
1867 return v1 == v2 ? 0 : v1 > v2 ? 1 : -1;
1868 }
1869
1870#ifdef FEAT_FLOAT
1871 if (sortinfo->item_compare_float)
1872 {
1873 float_T v1 = tv_get_float(tv1);
1874 float_T v2 = tv_get_float(tv2);
1875
1876 return v1 == v2 ? 0 : v1 > v2 ? 1 : -1;
1877 }
1878#endif
1879
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001880 // tv2string() puts quotes around a string and allocates memory. Don't do
1881 // that for string variables. Use a single quote when comparing with a
1882 // non-string to do what the docs promise.
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001883 if (tv1->v_type == VAR_STRING)
1884 {
1885 if (tv2->v_type != VAR_STRING || sortinfo->item_compare_numeric)
1886 p1 = (char_u *)"'";
1887 else
1888 p1 = tv1->vval.v_string;
1889 }
1890 else
1891 p1 = tv2string(tv1, &tofree1, numbuf1, 0);
1892 if (tv2->v_type == VAR_STRING)
1893 {
1894 if (tv1->v_type != VAR_STRING || sortinfo->item_compare_numeric)
1895 p2 = (char_u *)"'";
1896 else
1897 p2 = tv2->vval.v_string;
1898 }
1899 else
1900 p2 = tv2string(tv2, &tofree2, numbuf2, 0);
1901 if (p1 == NULL)
1902 p1 = (char_u *)"";
1903 if (p2 == NULL)
1904 p2 = (char_u *)"";
1905 if (!sortinfo->item_compare_numeric)
1906 {
Bram Moolenaar55e29612020-11-01 13:57:44 +01001907 if (sortinfo->item_compare_lc)
1908 res = strcoll((char *)p1, (char *)p2);
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001909 else
Bram Moolenaar55e29612020-11-01 13:57:44 +01001910 res = sortinfo->item_compare_ic ? STRICMP(p1, p2): STRCMP(p1, p2);
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001911 }
1912 else
1913 {
1914 double n1, n2;
1915 n1 = strtod((char *)p1, (char **)&p1);
1916 n2 = strtod((char *)p2, (char **)&p2);
1917 res = n1 == n2 ? 0 : n1 > n2 ? 1 : -1;
1918 }
1919
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001920 // When the result would be zero, compare the item indexes. Makes the
1921 // sort stable.
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001922 if (res == 0 && !sortinfo->item_compare_keep_zero)
1923 res = si1->idx > si2->idx ? 1 : -1;
1924
1925 vim_free(tofree1);
1926 vim_free(tofree2);
1927 return res;
1928}
1929
1930 static int
1931item_compare2(const void *s1, const void *s2)
1932{
1933 sortItem_T *si1, *si2;
1934 int res;
1935 typval_T rettv;
1936 typval_T argv[3];
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001937 char_u *func_name;
1938 partial_T *partial = sortinfo->item_compare_partial;
Bram Moolenaarc6538bc2019-08-03 18:17:11 +02001939 funcexe_T funcexe;
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001940
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001941 // shortcut after failure in previous call; compare all items equal
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001942 if (sortinfo->item_compare_func_err)
1943 return 0;
1944
1945 si1 = (sortItem_T *)s1;
1946 si2 = (sortItem_T *)s2;
1947
1948 if (partial == NULL)
1949 func_name = sortinfo->item_compare_func;
1950 else
1951 func_name = partial_name(partial);
1952
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001953 // Copy the values. This is needed to be able to set v_lock to VAR_FIXED
1954 // in the copy without changing the original list items.
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001955 copy_tv(&si1->item->li_tv, &argv[0]);
1956 copy_tv(&si2->item->li_tv, &argv[1]);
1957
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001958 rettv.v_type = VAR_UNKNOWN; // clear_tv() uses this
Bram Moolenaara80faa82020-04-12 19:37:17 +02001959 CLEAR_FIELD(funcexe);
Bram Moolenaar851f86b2021-12-13 14:26:44 +00001960 funcexe.fe_evaluate = TRUE;
1961 funcexe.fe_partial = partial;
1962 funcexe.fe_selfdict = sortinfo->item_compare_selfdict;
Bram Moolenaarc6538bc2019-08-03 18:17:11 +02001963 res = call_func(func_name, -1, &rettv, 2, argv, &funcexe);
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001964 clear_tv(&argv[0]);
1965 clear_tv(&argv[1]);
1966
1967 if (res == FAIL)
1968 res = ITEM_COMPARE_FAIL;
1969 else
Yasuhiro Matsumotoc04f6232021-09-19 17:01:39 +02001970 {
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001971 res = (int)tv_get_number_chk(&rettv, &sortinfo->item_compare_func_err);
Yasuhiro Matsumotoc04f6232021-09-19 17:01:39 +02001972 if (res > 0)
1973 res = 1;
1974 else if (res < 0)
1975 res = -1;
1976 }
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001977 if (sortinfo->item_compare_func_err)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001978 res = ITEM_COMPARE_FAIL; // return value has wrong type
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001979 clear_tv(&rettv);
1980
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001981 // When the result would be zero, compare the pointers themselves. Makes
1982 // the sort stable.
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001983 if (res == 0 && !sortinfo->item_compare_keep_zero)
1984 res = si1->idx > si2->idx ? 1 : -1;
1985
1986 return res;
1987}
1988
1989/*
1990 * "sort()" or "uniq()" function
1991 */
1992 static void
1993do_sort_uniq(typval_T *argvars, typval_T *rettv, int sort)
1994{
1995 list_T *l;
1996 listitem_T *li;
1997 sortItem_T *ptrs;
1998 sortinfo_T *old_sortinfo;
1999 sortinfo_T info;
2000 long len;
2001 long i;
2002
Yegappan Lakshmanan0ad871d2021-07-23 20:37:56 +02002003 if (in_vim9script()
2004 && (check_for_list_arg(argvars, 0) == FAIL
2005 || (argvars[1].v_type != VAR_UNKNOWN
2006 && check_for_opt_dict_arg(argvars, 2) == FAIL)))
2007 return;
2008
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002009 // Pointer to current info struct used in compare function. Save and
2010 // restore the current one for nested calls.
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02002011 old_sortinfo = sortinfo;
2012 sortinfo = &info;
2013
2014 if (argvars[0].v_type != VAR_LIST)
2015 semsg(_(e_listarg), sort ? "sort()" : "uniq()");
2016 else
2017 {
2018 l = argvars[0].vval.v_list;
Bram Moolenaaref982572021-08-12 19:27:57 +02002019 if (l != NULL && value_check_lock(l->lv_lock,
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02002020 (char_u *)(sort ? N_("sort() argument") : N_("uniq() argument")),
2021 TRUE))
2022 goto theend;
2023 rettv_list_set(rettv, l);
Bram Moolenaaref982572021-08-12 19:27:57 +02002024 if (l == NULL)
2025 goto theend;
Bram Moolenaar7e9f3512020-05-13 22:44:22 +02002026 CHECK_LIST_MATERIALIZE(l);
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02002027
2028 len = list_len(l);
2029 if (len <= 1)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002030 goto theend; // short list sorts pretty quickly
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02002031
2032 info.item_compare_ic = FALSE;
Bram Moolenaar55e29612020-11-01 13:57:44 +01002033 info.item_compare_lc = FALSE;
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02002034 info.item_compare_numeric = FALSE;
2035 info.item_compare_numbers = FALSE;
2036#ifdef FEAT_FLOAT
2037 info.item_compare_float = FALSE;
2038#endif
2039 info.item_compare_func = NULL;
2040 info.item_compare_partial = NULL;
2041 info.item_compare_selfdict = NULL;
2042 if (argvars[1].v_type != VAR_UNKNOWN)
2043 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002044 // optional second argument: {func}
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02002045 if (argvars[1].v_type == VAR_FUNC)
2046 info.item_compare_func = argvars[1].vval.v_string;
2047 else if (argvars[1].v_type == VAR_PARTIAL)
2048 info.item_compare_partial = argvars[1].vval.v_partial;
2049 else
2050 {
2051 int error = FALSE;
Bram Moolenaar08e51f42020-09-16 23:23:36 +02002052 int nr = 0;
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02002053
Bram Moolenaar08e51f42020-09-16 23:23:36 +02002054 if (argvars[1].v_type == VAR_NUMBER)
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02002055 {
Bram Moolenaar08e51f42020-09-16 23:23:36 +02002056 nr = tv_get_number_chk(&argvars[1], &error);
2057 if (error)
2058 goto theend; // type error; errmsg already given
2059 if (nr == 1)
2060 info.item_compare_ic = TRUE;
2061 }
2062 if (nr != 1)
2063 {
2064 if (argvars[1].v_type != VAR_NUMBER)
2065 info.item_compare_func = tv_get_string(&argvars[1]);
2066 else if (nr != 0)
2067 {
2068 emsg(_(e_invarg));
2069 goto theend;
2070 }
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02002071 }
2072 if (info.item_compare_func != NULL)
2073 {
2074 if (*info.item_compare_func == NUL)
2075 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002076 // empty string means default sort
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02002077 info.item_compare_func = NULL;
2078 }
2079 else if (STRCMP(info.item_compare_func, "n") == 0)
2080 {
2081 info.item_compare_func = NULL;
2082 info.item_compare_numeric = TRUE;
2083 }
2084 else if (STRCMP(info.item_compare_func, "N") == 0)
2085 {
2086 info.item_compare_func = NULL;
2087 info.item_compare_numbers = TRUE;
2088 }
2089#ifdef FEAT_FLOAT
2090 else if (STRCMP(info.item_compare_func, "f") == 0)
2091 {
2092 info.item_compare_func = NULL;
2093 info.item_compare_float = TRUE;
2094 }
2095#endif
2096 else if (STRCMP(info.item_compare_func, "i") == 0)
2097 {
2098 info.item_compare_func = NULL;
2099 info.item_compare_ic = TRUE;
2100 }
Bram Moolenaar55e29612020-11-01 13:57:44 +01002101 else if (STRCMP(info.item_compare_func, "l") == 0)
2102 {
2103 info.item_compare_func = NULL;
2104 info.item_compare_lc = TRUE;
2105 }
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02002106 }
2107 }
2108
2109 if (argvars[2].v_type != VAR_UNKNOWN)
2110 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002111 // optional third argument: {dict}
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02002112 if (argvars[2].v_type != VAR_DICT)
2113 {
2114 emsg(_(e_dictreq));
2115 goto theend;
2116 }
2117 info.item_compare_selfdict = argvars[2].vval.v_dict;
2118 }
2119 }
2120
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002121 // Make an array with each entry pointing to an item in the List.
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02002122 ptrs = ALLOC_MULT(sortItem_T, len);
2123 if (ptrs == NULL)
2124 goto theend;
2125
2126 i = 0;
2127 if (sort)
2128 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002129 // sort(): ptrs will be the list to sort
Bram Moolenaaraeea7212020-04-02 18:50:46 +02002130 FOR_ALL_LIST_ITEMS(l, li)
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02002131 {
2132 ptrs[i].item = li;
2133 ptrs[i].idx = i;
2134 ++i;
2135 }
2136
2137 info.item_compare_func_err = FALSE;
2138 info.item_compare_keep_zero = FALSE;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002139 // test the compare function
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02002140 if ((info.item_compare_func != NULL
2141 || info.item_compare_partial != NULL)
2142 && item_compare2((void *)&ptrs[0], (void *)&ptrs[1])
2143 == ITEM_COMPARE_FAIL)
2144 emsg(_("E702: Sort compare function failed"));
2145 else
2146 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002147 // Sort the array with item pointers.
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02002148 qsort((void *)ptrs, (size_t)len, sizeof(sortItem_T),
2149 info.item_compare_func == NULL
2150 && info.item_compare_partial == NULL
2151 ? item_compare : item_compare2);
2152
2153 if (!info.item_compare_func_err)
2154 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002155 // Clear the List and append the items in sorted order.
Bram Moolenaar0ff6aad2020-01-29 21:27:21 +01002156 l->lv_first = l->lv_u.mat.lv_last
2157 = l->lv_u.mat.lv_idx_item = NULL;
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02002158 l->lv_len = 0;
2159 for (i = 0; i < len; ++i)
2160 list_append(l, ptrs[i].item);
2161 }
2162 }
2163 }
2164 else
2165 {
2166 int (*item_compare_func_ptr)(const void *, const void *);
2167
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002168 // f_uniq(): ptrs will be a stack of items to remove
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02002169 info.item_compare_func_err = FALSE;
2170 info.item_compare_keep_zero = TRUE;
2171 item_compare_func_ptr = info.item_compare_func != NULL
2172 || info.item_compare_partial != NULL
2173 ? item_compare2 : item_compare;
2174
2175 for (li = l->lv_first; li != NULL && li->li_next != NULL;
2176 li = li->li_next)
2177 {
2178 if (item_compare_func_ptr((void *)&li, (void *)&li->li_next)
2179 == 0)
2180 ptrs[i++].item = li;
2181 if (info.item_compare_func_err)
2182 {
2183 emsg(_("E882: Uniq compare function failed"));
2184 break;
2185 }
2186 }
2187
2188 if (!info.item_compare_func_err)
2189 {
2190 while (--i >= 0)
2191 {
2192 li = ptrs[i].item->li_next;
2193 ptrs[i].item->li_next = li->li_next;
2194 if (li->li_next != NULL)
2195 li->li_next->li_prev = ptrs[i].item;
2196 else
Bram Moolenaar0ff6aad2020-01-29 21:27:21 +01002197 l->lv_u.mat.lv_last = ptrs[i].item;
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02002198 list_fix_watch(l, li);
Bram Moolenaar8a7d6542020-01-26 15:56:19 +01002199 listitem_free(l, li);
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02002200 l->lv_len--;
2201 }
2202 }
2203 }
2204
2205 vim_free(ptrs);
2206 }
2207theend:
2208 sortinfo = old_sortinfo;
2209}
2210
2211/*
2212 * "sort({list})" function
2213 */
2214 void
2215f_sort(typval_T *argvars, typval_T *rettv)
2216{
2217 do_sort_uniq(argvars, rettv, TRUE);
2218}
2219
2220/*
2221 * "uniq({list})" function
2222 */
2223 void
2224f_uniq(typval_T *argvars, typval_T *rettv)
2225{
2226 do_sort_uniq(argvars, rettv, FALSE);
2227}
2228
Bram Moolenaarea696852020-11-09 18:31:39 +01002229typedef enum {
2230 FILTERMAP_FILTER,
2231 FILTERMAP_MAP,
2232 FILTERMAP_MAPNEW
2233} filtermap_T;
2234
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02002235/*
2236 * Handle one item for map() and filter().
Bram Moolenaarea696852020-11-09 18:31:39 +01002237 * Sets v:val to "tv". Caller must set v:key.
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02002238 */
2239 static int
Bram Moolenaarea696852020-11-09 18:31:39 +01002240filter_map_one(
2241 typval_T *tv, // original value
2242 typval_T *expr, // callback
2243 filtermap_T filtermap,
2244 typval_T *newtv, // for map() and mapnew(): new value
2245 int *remp) // for filter(): remove flag
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02002246{
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02002247 typval_T argv[3];
2248 int retval = FAIL;
2249
2250 copy_tv(tv, get_vim_var_tv(VV_VAL));
2251 argv[0] = *get_vim_var_tv(VV_KEY);
2252 argv[1] = *get_vim_var_tv(VV_VAL);
Bram Moolenaarea696852020-11-09 18:31:39 +01002253 if (eval_expr_typval(expr, argv, 2, newtv) == FAIL)
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02002254 goto theend;
Bram Moolenaarea696852020-11-09 18:31:39 +01002255 if (filtermap == FILTERMAP_FILTER)
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02002256 {
2257 int error = FALSE;
2258
2259 // filter(): when expr is zero remove the item
Bram Moolenaar56acb092020-08-16 14:48:19 +02002260 if (in_vim9script())
Bram Moolenaarea696852020-11-09 18:31:39 +01002261 *remp = !tv2bool(newtv);
Bram Moolenaar56acb092020-08-16 14:48:19 +02002262 else
Bram Moolenaarea696852020-11-09 18:31:39 +01002263 *remp = (tv_get_number_chk(newtv, &error) == 0);
2264 clear_tv(newtv);
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02002265 // On type error, nothing has been removed; return FAIL to stop the
2266 // loop. The error message was given by tv_get_number_chk().
2267 if (error)
2268 goto theend;
2269 }
2270 retval = OK;
2271theend:
2272 clear_tv(get_vim_var_tv(VV_VAL));
2273 return retval;
2274}
2275
2276/*
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00002277 * Implementation of map() and filter() for a Dict.
2278 */
2279 static void
2280filter_map_dict(
2281 dict_T *d,
2282 filtermap_T filtermap,
2283 type_T *argtype,
2284 char *func_name,
2285 char_u *arg_errmsg,
2286 typval_T *expr,
2287 typval_T *rettv)
2288{
2289 int prev_lock;
2290 dict_T *d_ret = NULL;
2291 hashtab_T *ht;
2292 hashitem_T *hi;
2293 dictitem_T *di;
2294 int todo;
2295 int rem;
2296
2297 if (filtermap == FILTERMAP_MAPNEW)
2298 {
2299 rettv->v_type = VAR_DICT;
2300 rettv->vval.v_dict = NULL;
2301 }
2302 if (d == NULL
2303 || (filtermap == FILTERMAP_FILTER
2304 && value_check_lock(d->dv_lock, arg_errmsg, TRUE)))
2305 return;
2306
2307 prev_lock = d->dv_lock;
2308
2309 if (filtermap == FILTERMAP_MAPNEW)
2310 {
2311 if (rettv_dict_alloc(rettv) == FAIL)
2312 return;
2313 d_ret = rettv->vval.v_dict;
2314 }
2315
2316 if (filtermap != FILTERMAP_FILTER && d->dv_lock == 0)
2317 d->dv_lock = VAR_LOCKED;
2318 ht = &d->dv_hashtab;
2319 hash_lock(ht);
2320 todo = (int)ht->ht_used;
2321 for (hi = ht->ht_array; todo > 0; ++hi)
2322 {
2323 if (!HASHITEM_EMPTY(hi))
2324 {
2325 int r;
2326 typval_T newtv;
2327
2328 --todo;
2329 di = HI2DI(hi);
2330 if (filtermap == FILTERMAP_MAP
2331 && (value_check_lock(di->di_tv.v_lock,
2332 arg_errmsg, TRUE)
2333 || var_check_ro(di->di_flags,
2334 arg_errmsg, TRUE)))
2335 break;
2336 set_vim_var_string(VV_KEY, di->di_key, -1);
2337 newtv.v_type = VAR_UNKNOWN;
2338 r = filter_map_one(&di->di_tv, expr, filtermap,
2339 &newtv, &rem);
2340 clear_tv(get_vim_var_tv(VV_KEY));
2341 if (r == FAIL || did_emsg)
2342 {
2343 clear_tv(&newtv);
2344 break;
2345 }
2346 if (filtermap == FILTERMAP_MAP)
2347 {
2348 if (argtype != NULL && check_typval_arg_type(
2349 argtype->tt_member, &newtv,
2350 func_name, 0) == FAIL)
2351 {
2352 clear_tv(&newtv);
2353 break;
2354 }
2355 // map(): replace the dict item value
2356 clear_tv(&di->di_tv);
2357 newtv.v_lock = 0;
2358 di->di_tv = newtv;
2359 }
2360 else if (filtermap == FILTERMAP_MAPNEW)
2361 {
2362 // mapnew(): add the item value to the new dict
2363 r = dict_add_tv(d_ret, (char *)di->di_key, &newtv);
2364 clear_tv(&newtv);
2365 if (r == FAIL)
2366 break;
2367 }
2368 else if (filtermap == FILTERMAP_FILTER && rem)
2369 {
2370 // filter(false): remove the item from the dict
2371 if (var_check_fixed(di->di_flags, arg_errmsg, TRUE)
2372 || var_check_ro(di->di_flags, arg_errmsg, TRUE))
2373 break;
2374 dictitem_remove(d, di);
2375 }
2376 }
2377 }
2378 hash_unlock(ht);
2379 d->dv_lock = prev_lock;
2380}
2381
2382/*
2383 * Implementation of map() and filter() for a Blob.
2384 */
2385 static void
2386filter_map_blob(
2387 blob_T *blob_arg,
2388 filtermap_T filtermap,
2389 typval_T *expr,
2390 typval_T *rettv)
2391{
2392 blob_T *b;
2393 int i;
2394 typval_T tv;
2395 varnumber_T val;
2396 blob_T *b_ret;
2397 int idx = 0;
2398 int rem;
2399
2400 if (filtermap == FILTERMAP_MAPNEW)
2401 {
2402 rettv->v_type = VAR_BLOB;
2403 rettv->vval.v_blob = NULL;
2404 }
2405 if ((b = blob_arg) == NULL)
2406 return;
2407
2408 b_ret = b;
2409 if (filtermap == FILTERMAP_MAPNEW)
2410 {
2411 if (blob_copy(b, rettv) == FAIL)
2412 return;
2413 b_ret = rettv->vval.v_blob;
2414 }
2415
2416 // set_vim_var_nr() doesn't set the type
2417 set_vim_var_type(VV_KEY, VAR_NUMBER);
2418
2419 for (i = 0; i < b->bv_ga.ga_len; i++)
2420 {
2421 typval_T newtv;
2422
2423 tv.v_type = VAR_NUMBER;
2424 val = blob_get(b, i);
2425 tv.vval.v_number = val;
2426 set_vim_var_nr(VV_KEY, idx);
2427 if (filter_map_one(&tv, expr, filtermap, &newtv, &rem) == FAIL
2428 || did_emsg)
2429 break;
2430 if (newtv.v_type != VAR_NUMBER && newtv.v_type != VAR_BOOL)
2431 {
2432 clear_tv(&newtv);
2433 emsg(_(e_invalblob));
2434 break;
2435 }
2436 if (filtermap != FILTERMAP_FILTER)
2437 {
2438 if (newtv.vval.v_number != val)
2439 blob_set(b_ret, i, newtv.vval.v_number);
2440 }
2441 else if (rem)
2442 {
2443 char_u *p = (char_u *)blob_arg->bv_ga.ga_data;
2444
2445 mch_memmove(p + i, p + i + 1,
2446 (size_t)b->bv_ga.ga_len - i - 1);
2447 --b->bv_ga.ga_len;
2448 --i;
2449 }
2450 ++idx;
2451 }
2452}
2453
2454/*
2455 * Implementation of map() and filter() for a String.
2456 */
2457 static void
2458filter_map_string(
2459 char_u *str,
2460 filtermap_T filtermap,
2461 typval_T *expr,
2462 typval_T *rettv)
2463{
2464 char_u *p;
2465 typval_T tv;
2466 garray_T ga;
2467 int len = 0;
2468 int idx = 0;
2469 int rem;
2470
2471 rettv->v_type = VAR_STRING;
2472 rettv->vval.v_string = NULL;
2473
2474 // set_vim_var_nr() doesn't set the type
2475 set_vim_var_type(VV_KEY, VAR_NUMBER);
2476
2477 ga_init2(&ga, (int)sizeof(char), 80);
2478 for (p = str; *p != NUL; p += len)
2479 {
2480 typval_T newtv;
2481
2482 if (tv_get_first_char(p, &tv) == FAIL)
2483 break;
2484 len = (int)STRLEN(tv.vval.v_string);
2485
2486 set_vim_var_nr(VV_KEY, idx);
2487 if (filter_map_one(&tv, expr, filtermap, &newtv, &rem) == FAIL
2488 || did_emsg)
2489 break;
2490 if (did_emsg)
2491 {
2492 clear_tv(&newtv);
2493 clear_tv(&tv);
2494 break;
2495 }
2496 else if (filtermap != FILTERMAP_FILTER)
2497 {
2498 if (newtv.v_type != VAR_STRING)
2499 {
2500 clear_tv(&newtv);
2501 clear_tv(&tv);
2502 emsg(_(e_stringreq));
2503 break;
2504 }
2505 else
2506 ga_concat(&ga, newtv.vval.v_string);
2507 }
2508 else if (!rem)
2509 ga_concat(&ga, tv.vval.v_string);
2510
2511 clear_tv(&newtv);
2512 clear_tv(&tv);
2513
2514 ++idx;
2515 }
2516 ga_append(&ga, NUL);
2517 rettv->vval.v_string = ga.ga_data;
2518}
2519
2520/*
2521 * Implementation of map() and filter() for a List.
2522 */
2523 static void
2524filter_map_list(
2525 list_T *l,
2526 filtermap_T filtermap,
2527 type_T *argtype,
2528 char *func_name,
2529 char_u *arg_errmsg,
2530 typval_T *expr,
2531 typval_T *rettv)
2532{
2533 int prev_lock;
2534 list_T *l_ret = NULL;
2535 int idx = 0;
2536 int rem;
2537 listitem_T *li, *nli;
2538
2539 if (filtermap == FILTERMAP_MAPNEW)
2540 {
2541 rettv->v_type = VAR_LIST;
2542 rettv->vval.v_list = NULL;
2543 }
2544 if (l == NULL
2545 || (filtermap == FILTERMAP_FILTER
2546 && value_check_lock(l->lv_lock, arg_errmsg, TRUE)))
2547 return;
2548
2549 prev_lock = l->lv_lock;
2550
2551 if (filtermap == FILTERMAP_MAPNEW)
2552 {
2553 if (rettv_list_alloc(rettv) == FAIL)
2554 return;
2555 l_ret = rettv->vval.v_list;
2556 }
2557 // set_vim_var_nr() doesn't set the type
2558 set_vim_var_type(VV_KEY, VAR_NUMBER);
2559
2560 if (filtermap != FILTERMAP_FILTER && l->lv_lock == 0)
2561 l->lv_lock = VAR_LOCKED;
2562
2563 if (l->lv_first == &range_list_item)
2564 {
2565 varnumber_T val = l->lv_u.nonmat.lv_start;
2566 int len = l->lv_len;
2567 int stride = l->lv_u.nonmat.lv_stride;
2568
2569 // List from range(): loop over the numbers
2570 if (filtermap != FILTERMAP_MAPNEW)
2571 {
2572 l->lv_first = NULL;
2573 l->lv_u.mat.lv_last = NULL;
2574 l->lv_len = 0;
2575 l->lv_u.mat.lv_idx_item = NULL;
2576 }
2577
2578 for (idx = 0; idx < len; ++idx)
2579 {
2580 typval_T tv;
2581 typval_T newtv;
2582
2583 tv.v_type = VAR_NUMBER;
2584 tv.v_lock = 0;
2585 tv.vval.v_number = val;
2586 set_vim_var_nr(VV_KEY, idx);
2587 if (filter_map_one(&tv, expr, filtermap, &newtv, &rem)
2588 == FAIL)
2589 break;
2590 if (did_emsg)
2591 {
2592 clear_tv(&newtv);
2593 break;
2594 }
2595 if (filtermap != FILTERMAP_FILTER)
2596 {
2597 if (filtermap == FILTERMAP_MAP && argtype != NULL
2598 && check_typval_arg_type(
2599 argtype->tt_member, &newtv,
2600 func_name, 0) == FAIL)
2601 {
2602 clear_tv(&newtv);
2603 break;
2604 }
2605 // map(), mapnew(): always append the new value to the
2606 // list
2607 if (list_append_tv_move(filtermap == FILTERMAP_MAP
2608 ? l : l_ret, &newtv) == FAIL)
2609 break;
2610 }
2611 else if (!rem)
2612 {
2613 // filter(): append the list item value when not rem
2614 if (list_append_tv_move(l, &tv) == FAIL)
2615 break;
2616 }
2617
2618 val += stride;
2619 }
2620 }
2621 else
2622 {
2623 // Materialized list: loop over the items
2624 for (li = l->lv_first; li != NULL; li = nli)
2625 {
2626 typval_T newtv;
2627
2628 if (filtermap == FILTERMAP_MAP && value_check_lock(
2629 li->li_tv.v_lock, arg_errmsg, TRUE))
2630 break;
2631 nli = li->li_next;
2632 set_vim_var_nr(VV_KEY, idx);
2633 if (filter_map_one(&li->li_tv, expr, filtermap,
2634 &newtv, &rem) == FAIL)
2635 break;
2636 if (did_emsg)
2637 {
2638 clear_tv(&newtv);
2639 break;
2640 }
2641 if (filtermap == FILTERMAP_MAP)
2642 {
2643 if (argtype != NULL && check_typval_arg_type(
2644 argtype->tt_member, &newtv, func_name, 0) == FAIL)
2645 {
2646 clear_tv(&newtv);
2647 break;
2648 }
2649 // map(): replace the list item value
2650 clear_tv(&li->li_tv);
2651 newtv.v_lock = 0;
2652 li->li_tv = newtv;
2653 }
2654 else if (filtermap == FILTERMAP_MAPNEW)
2655 {
2656 // mapnew(): append the list item value
2657 if (list_append_tv_move(l_ret, &newtv) == FAIL)
2658 break;
2659 }
2660 else if (filtermap == FILTERMAP_FILTER && rem)
2661 listitem_remove(l, li);
2662 ++idx;
2663 }
2664 }
2665
2666 l->lv_lock = prev_lock;
2667}
2668
2669/*
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02002670 * Implementation of map() and filter().
2671 */
2672 static void
Bram Moolenaarea696852020-11-09 18:31:39 +01002673filter_map(typval_T *argvars, typval_T *rettv, filtermap_T filtermap)
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02002674{
2675 typval_T *expr;
Bram Moolenaar7a3fe3e2021-07-22 14:58:47 +02002676 char *func_name = filtermap == FILTERMAP_MAP ? "map()"
Bram Moolenaarea696852020-11-09 18:31:39 +01002677 : filtermap == FILTERMAP_MAPNEW ? "mapnew()"
Bram Moolenaar7a3fe3e2021-07-22 14:58:47 +02002678 : "filter()";
Bram Moolenaarea696852020-11-09 18:31:39 +01002679 char_u *arg_errmsg = (char_u *)(filtermap == FILTERMAP_MAP
2680 ? N_("map() argument")
2681 : filtermap == FILTERMAP_MAPNEW
2682 ? N_("mapnew() argument")
2683 : N_("filter() argument"));
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02002684 int save_did_emsg;
Bram Moolenaar70250fb2021-01-16 19:01:53 +01002685 type_T *type = NULL;
2686 garray_T type_list;
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02002687
Bram Moolenaarea696852020-11-09 18:31:39 +01002688 // map() and filter() return the first argument, also on failure.
Bram Moolenaar2d877592021-12-16 08:21:09 +00002689 if (filtermap != FILTERMAP_MAPNEW && argvars[0].v_type != VAR_STRING)
Bram Moolenaarea696852020-11-09 18:31:39 +01002690 copy_tv(&argvars[0], rettv);
Yegappan Lakshmanan0ad871d2021-07-23 20:37:56 +02002691
2692 if (in_vim9script()
Bram Moolenaar2d877592021-12-16 08:21:09 +00002693 && (check_for_list_or_dict_or_blob_or_string_arg(argvars, 0)
2694 == FAIL))
Yegappan Lakshmanan0ad871d2021-07-23 20:37:56 +02002695 return;
2696
Bram Moolenaar70250fb2021-01-16 19:01:53 +01002697 if (filtermap == FILTERMAP_MAP && in_vim9script())
2698 {
2699 // Check that map() does not change the type of the dict.
2700 ga_init2(&type_list, sizeof(type_T *), 10);
Bram Moolenaarf2253962021-04-13 20:53:13 +02002701 type = typval2type(argvars, get_copyID(), &type_list, TRUE);
Bram Moolenaar70250fb2021-01-16 19:01:53 +01002702 }
Bram Moolenaarffdf8ad2020-10-15 22:29:17 +02002703
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00002704 if (argvars[0].v_type != VAR_BLOB
2705 && argvars[0].v_type != VAR_LIST
2706 && argvars[0].v_type != VAR_DICT
2707 && argvars[0].v_type != VAR_STRING)
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02002708 {
rbtnnc479ce02021-12-15 19:14:54 +00002709 semsg(_(e_argument_of_str_must_be_list_string_dictionary_or_blob),
2710 func_name);
Bram Moolenaar70250fb2021-01-16 19:01:53 +01002711 goto theend;
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02002712 }
2713
2714 expr = &argvars[1];
2715 // On type errors, the preceding call has already displayed an error
2716 // message. Avoid a misleading error message for an empty string that
2717 // was not passed as argument.
2718 if (expr->v_type != VAR_UNKNOWN)
2719 {
2720 typval_T save_val;
2721 typval_T save_key;
2722
2723 prepare_vimvar(VV_VAL, &save_val);
2724 prepare_vimvar(VV_KEY, &save_key);
2725
2726 // We reset "did_emsg" to be able to detect whether an error
2727 // occurred during evaluation of the expression.
2728 save_did_emsg = did_emsg;
2729 did_emsg = FALSE;
2730
2731 if (argvars[0].v_type == VAR_DICT)
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00002732 filter_map_dict(argvars[0].vval.v_dict, filtermap, type, func_name,
2733 arg_errmsg, expr, rettv);
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02002734 else if (argvars[0].v_type == VAR_BLOB)
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00002735 filter_map_blob(argvars[0].vval.v_blob, filtermap, expr, rettv);
rbtnnc479ce02021-12-15 19:14:54 +00002736 else if (argvars[0].v_type == VAR_STRING)
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00002737 filter_map_string(tv_get_string(&argvars[0]), filtermap, expr,
2738 rettv);
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02002739 else // argvars[0].v_type == VAR_LIST
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00002740 filter_map_list(argvars[0].vval.v_list, filtermap, type, func_name,
2741 arg_errmsg, expr, rettv);
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02002742
2743 restore_vimvar(VV_KEY, &save_key);
2744 restore_vimvar(VV_VAL, &save_val);
2745
2746 did_emsg |= save_did_emsg;
2747 }
Bram Moolenaar70250fb2021-01-16 19:01:53 +01002748
2749theend:
2750 if (type != NULL)
2751 clear_type_list(&type_list);
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02002752}
2753
2754/*
2755 * "filter()" function
2756 */
2757 void
2758f_filter(typval_T *argvars, typval_T *rettv)
2759{
Bram Moolenaarea696852020-11-09 18:31:39 +01002760 filter_map(argvars, rettv, FILTERMAP_FILTER);
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02002761}
2762
2763/*
2764 * "map()" function
2765 */
2766 void
2767f_map(typval_T *argvars, typval_T *rettv)
2768{
Bram Moolenaarea696852020-11-09 18:31:39 +01002769 filter_map(argvars, rettv, FILTERMAP_MAP);
2770}
2771
2772/*
2773 * "mapnew()" function
2774 */
2775 void
2776f_mapnew(typval_T *argvars, typval_T *rettv)
2777{
2778 filter_map(argvars, rettv, FILTERMAP_MAPNEW);
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02002779}
2780
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002781/*
2782 * "add(list, item)" function
2783 */
2784 void
2785f_add(typval_T *argvars, typval_T *rettv)
2786{
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002787 rettv->vval.v_number = 1; // Default: Failed
Yegappan Lakshmanan0ad871d2021-07-23 20:37:56 +02002788
2789 if (in_vim9script()
2790 && (check_for_list_or_blob_arg(argvars, 0) == FAIL
2791 || (argvars[0].v_type == VAR_BLOB
2792 && check_for_number_arg(argvars, 1) == FAIL)))
2793 return;
2794
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002795 if (argvars[0].v_type == VAR_LIST)
2796 {
Bram Moolenaarb7c21af2021-04-18 14:12:31 +02002797 list_T *l = argvars[0].vval.v_list;
2798
2799 if (l == NULL)
2800 {
2801 if (in_vim9script())
2802 emsg(_(e_cannot_add_to_null_list));
2803 }
2804 else if (!value_check_lock(l->lv_lock,
2805 (char_u *)N_("add() argument"), TRUE)
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002806 && list_append_tv(l, &argvars[1]) == OK)
Bram Moolenaarb7c21af2021-04-18 14:12:31 +02002807 {
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002808 copy_tv(&argvars[0], rettv);
Bram Moolenaarb7c21af2021-04-18 14:12:31 +02002809 }
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002810 }
2811 else if (argvars[0].v_type == VAR_BLOB)
2812 {
Bram Moolenaarb7c21af2021-04-18 14:12:31 +02002813 blob_T *b = argvars[0].vval.v_blob;
2814
2815 if (b == NULL)
2816 {
2817 if (in_vim9script())
2818 emsg(_(e_cannot_add_to_null_blob));
2819 }
2820 else if (!value_check_lock(b->bv_lock,
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002821 (char_u *)N_("add() argument"), TRUE))
2822 {
2823 int error = FALSE;
2824 varnumber_T n = tv_get_number_chk(&argvars[1], &error);
2825
2826 if (!error)
2827 {
2828 ga_append(&b->bv_ga, (int)n);
2829 copy_tv(&argvars[0], rettv);
2830 }
2831 }
2832 }
2833 else
2834 emsg(_(e_listblobreq));
2835}
2836
2837/*
2838 * "count()" function
2839 */
2840 void
2841f_count(typval_T *argvars, typval_T *rettv)
2842{
2843 long n = 0;
2844 int ic = FALSE;
2845 int error = FALSE;
2846
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +02002847 if (in_vim9script()
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02002848 && (check_for_string_or_list_or_dict_arg(argvars, 0) == FAIL
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +02002849 || check_for_opt_bool_arg(argvars, 2) == FAIL
2850 || (argvars[2].v_type != VAR_UNKNOWN
2851 && check_for_opt_number_arg(argvars, 3) == FAIL)))
2852 return;
2853
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002854 if (argvars[2].v_type != VAR_UNKNOWN)
Bram Moolenaar119f5572020-09-02 21:31:22 +02002855 ic = (int)tv_get_bool_chk(&argvars[2], &error);
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002856
2857 if (argvars[0].v_type == VAR_STRING)
2858 {
2859 char_u *expr = tv_get_string_chk(&argvars[1]);
2860 char_u *p = argvars[0].vval.v_string;
2861 char_u *next;
2862
2863 if (!error && expr != NULL && *expr != NUL && p != NULL)
2864 {
2865 if (ic)
2866 {
2867 size_t len = STRLEN(expr);
2868
2869 while (*p != NUL)
2870 {
2871 if (MB_STRNICMP(p, expr, len) == 0)
2872 {
2873 ++n;
2874 p += len;
2875 }
2876 else
2877 MB_PTR_ADV(p);
2878 }
2879 }
2880 else
2881 while ((next = (char_u *)strstr((char *)p, (char *)expr))
2882 != NULL)
2883 {
2884 ++n;
2885 p = next + STRLEN(expr);
2886 }
2887 }
2888
2889 }
2890 else if (argvars[0].v_type == VAR_LIST)
2891 {
2892 listitem_T *li;
2893 list_T *l;
2894 long idx;
2895
2896 if ((l = argvars[0].vval.v_list) != NULL)
2897 {
Bram Moolenaar7e9f3512020-05-13 22:44:22 +02002898 CHECK_LIST_MATERIALIZE(l);
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002899 li = l->lv_first;
2900 if (argvars[2].v_type != VAR_UNKNOWN)
2901 {
2902 if (argvars[3].v_type != VAR_UNKNOWN)
2903 {
2904 idx = (long)tv_get_number_chk(&argvars[3], &error);
2905 if (!error)
2906 {
2907 li = list_find(l, idx);
2908 if (li == NULL)
2909 semsg(_(e_listidx), idx);
2910 }
2911 }
2912 if (error)
2913 li = NULL;
2914 }
2915
2916 for ( ; li != NULL; li = li->li_next)
2917 if (tv_equal(&li->li_tv, &argvars[1], ic, FALSE))
2918 ++n;
2919 }
2920 }
2921 else if (argvars[0].v_type == VAR_DICT)
2922 {
2923 int todo;
2924 dict_T *d;
2925 hashitem_T *hi;
2926
2927 if ((d = argvars[0].vval.v_dict) != NULL)
2928 {
2929 if (argvars[2].v_type != VAR_UNKNOWN)
2930 {
2931 if (argvars[3].v_type != VAR_UNKNOWN)
2932 emsg(_(e_invarg));
2933 }
2934
2935 todo = error ? 0 : (int)d->dv_hashtab.ht_used;
2936 for (hi = d->dv_hashtab.ht_array; todo > 0; ++hi)
2937 {
2938 if (!HASHITEM_EMPTY(hi))
2939 {
2940 --todo;
2941 if (tv_equal(&HI2DI(hi)->di_tv, &argvars[1], ic, FALSE))
2942 ++n;
2943 }
2944 }
2945 }
2946 }
2947 else
2948 semsg(_(e_listdictarg), "count()");
2949 rettv->vval.v_number = n;
2950}
2951
2952/*
Bram Moolenaarb0e6b512021-01-12 20:23:40 +01002953 * "extend()" or "extendnew()" function. "is_new" is TRUE for extendnew().
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002954 */
Bram Moolenaarb0e6b512021-01-12 20:23:40 +01002955 static void
2956extend(typval_T *argvars, typval_T *rettv, char_u *arg_errmsg, int is_new)
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002957{
Bram Moolenaarc03f5c62021-01-31 17:48:30 +01002958 type_T *type = NULL;
2959 garray_T type_list;
Bram Moolenaar7a3fe3e2021-07-22 14:58:47 +02002960 char *func_name = is_new ? "extendnew()" : "extend()";
Bram Moolenaarc03f5c62021-01-31 17:48:30 +01002961
2962 if (!is_new && in_vim9script())
2963 {
2964 // Check that map() does not change the type of the dict.
2965 ga_init2(&type_list, sizeof(type_T *), 10);
Bram Moolenaarf2253962021-04-13 20:53:13 +02002966 type = typval2type(argvars, get_copyID(), &type_list, TRUE);
Bram Moolenaarc03f5c62021-01-31 17:48:30 +01002967 }
2968
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002969 if (argvars[0].v_type == VAR_LIST && argvars[1].v_type == VAR_LIST)
2970 {
2971 list_T *l1, *l2;
2972 listitem_T *item;
2973 long before;
2974 int error = FALSE;
2975
2976 l1 = argvars[0].vval.v_list;
Bram Moolenaar348be7e2020-11-04 11:36:35 +01002977 if (l1 == NULL)
2978 {
2979 emsg(_(e_cannot_extend_null_list));
Bram Moolenaarc03f5c62021-01-31 17:48:30 +01002980 goto theend;
Bram Moolenaar348be7e2020-11-04 11:36:35 +01002981 }
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002982 l2 = argvars[1].vval.v_list;
Bram Moolenaarb0e6b512021-01-12 20:23:40 +01002983 if ((is_new || !value_check_lock(l1->lv_lock, arg_errmsg, TRUE))
2984 && l2 != NULL)
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002985 {
Bram Moolenaarb0e6b512021-01-12 20:23:40 +01002986 if (is_new)
2987 {
2988 l1 = list_copy(l1, FALSE, get_copyID());
2989 if (l1 == NULL)
Bram Moolenaarc03f5c62021-01-31 17:48:30 +01002990 goto theend;
Bram Moolenaarb0e6b512021-01-12 20:23:40 +01002991 }
2992
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002993 if (argvars[2].v_type != VAR_UNKNOWN)
2994 {
2995 before = (long)tv_get_number_chk(&argvars[2], &error);
2996 if (error)
Bram Moolenaarc03f5c62021-01-31 17:48:30 +01002997 goto theend; // type error; errmsg already given
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002998
2999 if (before == l1->lv_len)
3000 item = NULL;
3001 else
3002 {
3003 item = list_find(l1, before);
3004 if (item == NULL)
3005 {
3006 semsg(_(e_listidx), before);
Bram Moolenaarc03f5c62021-01-31 17:48:30 +01003007 goto theend;
Bram Moolenaar08c308a2019-09-04 17:48:15 +02003008 }
3009 }
3010 }
3011 else
3012 item = NULL;
Bram Moolenaarf785aa12021-02-11 21:19:34 +01003013 if (type != NULL && check_typval_arg_type(
Bram Moolenaar7a3fe3e2021-07-22 14:58:47 +02003014 type, &argvars[1], func_name, 2) == FAIL)
Bram Moolenaarc03f5c62021-01-31 17:48:30 +01003015 goto theend;
Bram Moolenaar08c308a2019-09-04 17:48:15 +02003016 list_extend(l1, l2, item);
3017
Bram Moolenaarb0e6b512021-01-12 20:23:40 +01003018 if (is_new)
3019 {
3020 rettv->v_type = VAR_LIST;
3021 rettv->vval.v_list = l1;
3022 rettv->v_lock = FALSE;
3023 }
3024 else
3025 copy_tv(&argvars[0], rettv);
Bram Moolenaar08c308a2019-09-04 17:48:15 +02003026 }
3027 }
3028 else if (argvars[0].v_type == VAR_DICT && argvars[1].v_type == VAR_DICT)
3029 {
3030 dict_T *d1, *d2;
3031 char_u *action;
3032 int i;
3033
3034 d1 = argvars[0].vval.v_dict;
Bram Moolenaar348be7e2020-11-04 11:36:35 +01003035 if (d1 == NULL)
3036 {
3037 emsg(_(e_cannot_extend_null_dict));
Bram Moolenaarc03f5c62021-01-31 17:48:30 +01003038 goto theend;
Bram Moolenaar348be7e2020-11-04 11:36:35 +01003039 }
Bram Moolenaar08c308a2019-09-04 17:48:15 +02003040 d2 = argvars[1].vval.v_dict;
Bram Moolenaarb0e6b512021-01-12 20:23:40 +01003041 if ((is_new || !value_check_lock(d1->dv_lock, arg_errmsg, TRUE))
3042 && d2 != NULL)
Bram Moolenaar08c308a2019-09-04 17:48:15 +02003043 {
Bram Moolenaarb0e6b512021-01-12 20:23:40 +01003044 if (is_new)
3045 {
3046 d1 = dict_copy(d1, FALSE, get_copyID());
3047 if (d1 == NULL)
Bram Moolenaarc03f5c62021-01-31 17:48:30 +01003048 goto theend;
Bram Moolenaarb0e6b512021-01-12 20:23:40 +01003049 }
3050
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003051 // Check the third argument.
Bram Moolenaar08c308a2019-09-04 17:48:15 +02003052 if (argvars[2].v_type != VAR_UNKNOWN)
3053 {
3054 static char *(av[]) = {"keep", "force", "error"};
3055
3056 action = tv_get_string_chk(&argvars[2]);
3057 if (action == NULL)
Bram Moolenaarc03f5c62021-01-31 17:48:30 +01003058 goto theend; // type error; errmsg already given
Bram Moolenaar08c308a2019-09-04 17:48:15 +02003059 for (i = 0; i < 3; ++i)
3060 if (STRCMP(action, av[i]) == 0)
3061 break;
3062 if (i == 3)
3063 {
3064 semsg(_(e_invarg2), action);
Bram Moolenaarc03f5c62021-01-31 17:48:30 +01003065 goto theend;
Bram Moolenaar08c308a2019-09-04 17:48:15 +02003066 }
3067 }
3068 else
3069 action = (char_u *)"force";
3070
Bram Moolenaar7a3fe3e2021-07-22 14:58:47 +02003071 if (type != NULL && check_typval_arg_type(type, &argvars[1],
3072 func_name, 2) == FAIL)
Bram Moolenaarc03f5c62021-01-31 17:48:30 +01003073 goto theend;
Bram Moolenaar7a3fe3e2021-07-22 14:58:47 +02003074 dict_extend(d1, d2, action, func_name);
Bram Moolenaar08c308a2019-09-04 17:48:15 +02003075
Bram Moolenaarb0e6b512021-01-12 20:23:40 +01003076 if (is_new)
3077 {
3078 rettv->v_type = VAR_DICT;
3079 rettv->vval.v_dict = d1;
3080 rettv->v_lock = FALSE;
3081 }
3082 else
3083 copy_tv(&argvars[0], rettv);
Bram Moolenaar08c308a2019-09-04 17:48:15 +02003084 }
3085 }
3086 else
Bram Moolenaar7a3fe3e2021-07-22 14:58:47 +02003087 semsg(_(e_listdictarg), func_name);
Bram Moolenaarc03f5c62021-01-31 17:48:30 +01003088
3089theend:
3090 if (type != NULL)
3091 clear_type_list(&type_list);
Bram Moolenaarb0e6b512021-01-12 20:23:40 +01003092}
3093
3094/*
3095 * "extend(list, list [, idx])" function
3096 * "extend(dict, dict [, action])" function
3097 */
3098 void
3099f_extend(typval_T *argvars, typval_T *rettv)
3100{
3101 char_u *errmsg = (char_u *)N_("extend() argument");
3102
3103 extend(argvars, rettv, errmsg, FALSE);
3104}
3105
3106/*
3107 * "extendnew(list, list [, idx])" function
3108 * "extendnew(dict, dict [, action])" function
3109 */
3110 void
3111f_extendnew(typval_T *argvars, typval_T *rettv)
3112{
3113 char_u *errmsg = (char_u *)N_("extendnew() argument");
3114
3115 extend(argvars, rettv, errmsg, TRUE);
Bram Moolenaar08c308a2019-09-04 17:48:15 +02003116}
3117
3118/*
3119 * "insert()" function
3120 */
3121 void
3122f_insert(typval_T *argvars, typval_T *rettv)
3123{
3124 long before = 0;
3125 listitem_T *item;
Bram Moolenaar08c308a2019-09-04 17:48:15 +02003126 int error = FALSE;
3127
Yegappan Lakshmanan0ad871d2021-07-23 20:37:56 +02003128 if (in_vim9script()
3129 && (check_for_list_or_blob_arg(argvars, 0) == FAIL
3130 || (argvars[0].v_type == VAR_BLOB
3131 && check_for_number_arg(argvars, 1) == FAIL)
3132 || check_for_opt_number_arg(argvars, 2) == FAIL))
3133 return;
3134
Bram Moolenaar08c308a2019-09-04 17:48:15 +02003135 if (argvars[0].v_type == VAR_BLOB)
3136 {
Sean Dewar80d73952021-08-04 19:25:54 +02003137 blob_T *b = argvars[0].vval.v_blob;
Bram Moolenaar08c308a2019-09-04 17:48:15 +02003138
Sean Dewar80d73952021-08-04 19:25:54 +02003139 if (b == NULL)
Bram Moolenaar39211cb2021-04-18 15:48:04 +02003140 {
3141 if (in_vim9script())
3142 emsg(_(e_cannot_add_to_null_blob));
Bram Moolenaar39211cb2021-04-18 15:48:04 +02003143 }
Sean Dewar80d73952021-08-04 19:25:54 +02003144 else if (!value_check_lock(b->bv_lock,
3145 (char_u *)N_("insert() argument"), TRUE))
Bram Moolenaar08c308a2019-09-04 17:48:15 +02003146 {
Sean Dewar80d73952021-08-04 19:25:54 +02003147 int val, len;
3148 char_u *p;
3149
3150 len = blob_len(b);
3151 if (argvars[2].v_type != VAR_UNKNOWN)
Bram Moolenaar08c308a2019-09-04 17:48:15 +02003152 {
Sean Dewar80d73952021-08-04 19:25:54 +02003153 before = (long)tv_get_number_chk(&argvars[2], &error);
3154 if (error)
3155 return; // type error; errmsg already given
3156 if (before < 0 || before > len)
3157 {
3158 semsg(_(e_invarg2), tv_get_string(&argvars[2]));
3159 return;
3160 }
3161 }
3162 val = tv_get_number_chk(&argvars[1], &error);
3163 if (error)
3164 return;
3165 if (val < 0 || val > 255)
3166 {
3167 semsg(_(e_invarg2), tv_get_string(&argvars[1]));
Bram Moolenaar08c308a2019-09-04 17:48:15 +02003168 return;
3169 }
Bram Moolenaar08c308a2019-09-04 17:48:15 +02003170
Sean Dewar80d73952021-08-04 19:25:54 +02003171 if (ga_grow(&b->bv_ga, 1) == FAIL)
3172 return;
3173 p = (char_u *)b->bv_ga.ga_data;
3174 mch_memmove(p + before + 1, p + before, (size_t)len - before);
3175 *(p + before) = val;
3176 ++b->bv_ga.ga_len;
Bram Moolenaar08c308a2019-09-04 17:48:15 +02003177
Sean Dewar80d73952021-08-04 19:25:54 +02003178 copy_tv(&argvars[0], rettv);
3179 }
Bram Moolenaar08c308a2019-09-04 17:48:15 +02003180 }
3181 else if (argvars[0].v_type != VAR_LIST)
3182 semsg(_(e_listblobarg), "insert()");
Bram Moolenaar39211cb2021-04-18 15:48:04 +02003183 else
Bram Moolenaar08c308a2019-09-04 17:48:15 +02003184 {
Bram Moolenaar39211cb2021-04-18 15:48:04 +02003185 list_T *l = argvars[0].vval.v_list;
Bram Moolenaar08c308a2019-09-04 17:48:15 +02003186
Bram Moolenaar39211cb2021-04-18 15:48:04 +02003187 if (l == NULL)
Bram Moolenaar08c308a2019-09-04 17:48:15 +02003188 {
Bram Moolenaar39211cb2021-04-18 15:48:04 +02003189 if (in_vim9script())
3190 emsg(_(e_cannot_add_to_null_list));
Bram Moolenaar08c308a2019-09-04 17:48:15 +02003191 }
Bram Moolenaar39211cb2021-04-18 15:48:04 +02003192 else if (!value_check_lock(l->lv_lock,
3193 (char_u *)N_("insert() argument"), TRUE))
Bram Moolenaar08c308a2019-09-04 17:48:15 +02003194 {
Bram Moolenaar39211cb2021-04-18 15:48:04 +02003195 if (argvars[2].v_type != VAR_UNKNOWN)
3196 before = (long)tv_get_number_chk(&argvars[2], &error);
3197 if (error)
3198 return; // type error; errmsg already given
3199
3200 if (before == l->lv_len)
3201 item = NULL;
3202 else
3203 {
3204 item = list_find(l, before);
3205 if (item == NULL)
3206 {
3207 semsg(_(e_listidx), before);
3208 l = NULL;
3209 }
3210 }
3211 if (l != NULL)
3212 {
3213 (void)list_insert_tv(l, &argvars[1], item);
3214 copy_tv(&argvars[0], rettv);
3215 }
Bram Moolenaar08c308a2019-09-04 17:48:15 +02003216 }
3217 }
3218}
3219
3220/*
3221 * "remove()" function
3222 */
3223 void
3224f_remove(typval_T *argvars, typval_T *rettv)
3225{
3226 char_u *arg_errmsg = (char_u *)N_("remove() argument");
3227
Yegappan Lakshmanan0ad871d2021-07-23 20:37:56 +02003228 if (in_vim9script()
3229 && (check_for_list_or_dict_or_blob_arg(argvars, 0) == FAIL
3230 || ((argvars[0].v_type == VAR_LIST
3231 || argvars[0].v_type == VAR_BLOB)
3232 && (check_for_number_arg(argvars, 1) == FAIL
3233 || check_for_opt_number_arg(argvars, 2) == FAIL))
3234 || (argvars[0].v_type == VAR_DICT
3235 && check_for_string_or_number_arg(argvars, 1) == FAIL)))
3236 return;
3237
Bram Moolenaar08c308a2019-09-04 17:48:15 +02003238 if (argvars[0].v_type == VAR_DICT)
3239 dict_remove(argvars, rettv, arg_errmsg);
3240 else if (argvars[0].v_type == VAR_BLOB)
Sean Dewar80d73952021-08-04 19:25:54 +02003241 blob_remove(argvars, rettv, arg_errmsg);
Bram Moolenaar08c308a2019-09-04 17:48:15 +02003242 else if (argvars[0].v_type == VAR_LIST)
3243 list_remove(argvars, rettv, arg_errmsg);
3244 else
3245 semsg(_(e_listdictblobarg), "remove()");
3246}
3247
3248/*
3249 * "reverse({list})" function
3250 */
3251 void
3252f_reverse(typval_T *argvars, typval_T *rettv)
3253{
3254 list_T *l;
3255 listitem_T *li, *ni;
3256
Yegappan Lakshmanan0ad871d2021-07-23 20:37:56 +02003257 if (in_vim9script() && check_for_list_or_blob_arg(argvars, 0) == FAIL)
3258 return;
3259
Bram Moolenaar08c308a2019-09-04 17:48:15 +02003260 if (argvars[0].v_type == VAR_BLOB)
3261 {
3262 blob_T *b = argvars[0].vval.v_blob;
3263 int i, len = blob_len(b);
3264
3265 for (i = 0; i < len / 2; i++)
3266 {
3267 int tmp = blob_get(b, i);
3268
3269 blob_set(b, i, blob_get(b, len - i - 1));
3270 blob_set(b, len - i - 1, tmp);
3271 }
3272 rettv_blob_set(rettv, b);
3273 return;
3274 }
3275
3276 if (argvars[0].v_type != VAR_LIST)
3277 semsg(_(e_listblobarg), "reverse()");
Bram Moolenaaref982572021-08-12 19:27:57 +02003278 else
3279 {
3280 l = argvars[0].vval.v_list;
3281 rettv_list_set(rettv, l);
3282 if (l != NULL
Bram Moolenaara187c432020-09-16 21:08:28 +02003283 && !value_check_lock(l->lv_lock,
Bram Moolenaar08c308a2019-09-04 17:48:15 +02003284 (char_u *)N_("reverse() argument"), TRUE))
Bram Moolenaar89bfc822020-01-27 22:37:23 +01003285 {
Bram Moolenaaref982572021-08-12 19:27:57 +02003286 if (l->lv_first == &range_list_item)
3287 {
3288 varnumber_T new_start = l->lv_u.nonmat.lv_start
Bram Moolenaar0ff6aad2020-01-29 21:27:21 +01003289 + (l->lv_len - 1) * l->lv_u.nonmat.lv_stride;
Bram Moolenaaref982572021-08-12 19:27:57 +02003290 l->lv_u.nonmat.lv_end = new_start
Bram Moolenaar0ff6aad2020-01-29 21:27:21 +01003291 - (l->lv_u.nonmat.lv_end - l->lv_u.nonmat.lv_start);
Bram Moolenaaref982572021-08-12 19:27:57 +02003292 l->lv_u.nonmat.lv_start = new_start;
3293 l->lv_u.nonmat.lv_stride = -l->lv_u.nonmat.lv_stride;
3294 return;
3295 }
3296 li = l->lv_u.mat.lv_last;
3297 l->lv_first = l->lv_u.mat.lv_last = NULL;
3298 l->lv_len = 0;
3299 while (li != NULL)
3300 {
3301 ni = li->li_prev;
3302 list_append(l, li);
3303 li = ni;
3304 }
3305 l->lv_u.mat.lv_idx = l->lv_len - l->lv_u.mat.lv_idx - 1;
Bram Moolenaar89bfc822020-01-27 22:37:23 +01003306 }
Bram Moolenaar08c308a2019-09-04 17:48:15 +02003307 }
3308}
3309
Bram Moolenaar85629982020-06-01 18:39:20 +02003310/*
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00003311 * reduce() on a List
3312 */
3313 static void
3314reduce_list(
3315 typval_T *argvars,
3316 char_u *func_name,
3317 funcexe_T *funcexe,
3318 typval_T *rettv)
3319{
3320 list_T *l = argvars[0].vval.v_list;
3321 listitem_T *li = NULL;
3322 typval_T initial;
3323 typval_T argv[3];
3324 int r;
3325 int called_emsg_start = called_emsg;
3326 int prev_locked;
3327
3328 if (l != NULL)
3329 CHECK_LIST_MATERIALIZE(l);
3330 if (argvars[2].v_type == VAR_UNKNOWN)
3331 {
3332 if (l == NULL || l->lv_first == NULL)
3333 {
3334 semsg(_(e_reduceempty), "List");
3335 return;
3336 }
3337 initial = l->lv_first->li_tv;
3338 li = l->lv_first->li_next;
3339 }
3340 else
3341 {
3342 initial = argvars[2];
3343 if (l != NULL)
3344 li = l->lv_first;
3345 }
3346 copy_tv(&initial, rettv);
3347
3348 if (l == NULL)
3349 return;
3350
3351 prev_locked = l->lv_lock;
3352
3353 l->lv_lock = VAR_FIXED; // disallow the list changing here
3354 for ( ; li != NULL; li = li->li_next)
3355 {
3356 argv[0] = *rettv;
3357 argv[1] = li->li_tv;
3358 rettv->v_type = VAR_UNKNOWN;
3359 r = call_func(func_name, -1, rettv, 2, argv, funcexe);
3360 clear_tv(&argv[0]);
3361 if (r == FAIL || called_emsg != called_emsg_start)
3362 break;
3363 }
3364 l->lv_lock = prev_locked;
3365}
3366
3367/*
3368 * reduce() on a String
3369 */
3370 static void
3371reduce_string(
3372 typval_T *argvars,
3373 char_u *func_name,
3374 funcexe_T *funcexe,
3375 typval_T *rettv)
3376{
3377 char_u *p = tv_get_string(&argvars[0]);
3378 int len;
3379 typval_T argv[3];
3380 int r;
3381 int called_emsg_start = called_emsg;
3382
3383 if (argvars[2].v_type == VAR_UNKNOWN)
3384 {
3385 if (*p == NUL)
3386 {
3387 semsg(_(e_reduceempty), "String");
3388 return;
3389 }
3390 if (tv_get_first_char(p, rettv) == FAIL)
3391 return;
3392 p += STRLEN(rettv->vval.v_string);
3393 }
3394 else if (argvars[2].v_type != VAR_STRING)
3395 {
3396 semsg(_(e_string_expected_for_argument_nr), 3);
3397 return;
3398 }
3399 else
3400 copy_tv(&argvars[2], rettv);
3401
3402 for ( ; *p != NUL; p += len)
3403 {
3404 argv[0] = *rettv;
3405 if (tv_get_first_char(p, &argv[1]) == FAIL)
3406 break;
3407 len = (int)STRLEN(argv[1].vval.v_string);
3408 r = call_func(func_name, -1, rettv, 2, argv, funcexe);
3409 clear_tv(&argv[0]);
3410 clear_tv(&argv[1]);
3411 if (r == FAIL || called_emsg != called_emsg_start)
3412 return;
3413 }
3414}
3415
3416/*
3417 * reduce() on a Blob
3418 */
3419 static void
3420reduce_blob(
3421 typval_T *argvars,
3422 char_u *func_name,
3423 funcexe_T *funcexe,
3424 typval_T *rettv)
3425{
3426 blob_T *b = argvars[0].vval.v_blob;
3427 int called_emsg_start = called_emsg;
3428 int r;
3429 typval_T initial;
3430 typval_T argv[3];
3431 int i;
3432
3433 if (argvars[2].v_type == VAR_UNKNOWN)
3434 {
3435 if (b == NULL || b->bv_ga.ga_len == 0)
3436 {
3437 semsg(_(e_reduceempty), "Blob");
3438 return;
3439 }
3440 initial.v_type = VAR_NUMBER;
3441 initial.vval.v_number = blob_get(b, 0);
3442 i = 1;
3443 }
3444 else if (argvars[2].v_type != VAR_NUMBER)
3445 {
3446 emsg(_(e_number_expected));
3447 return;
3448 }
3449 else
3450 {
3451 initial = argvars[2];
3452 i = 0;
3453 }
3454
3455 copy_tv(&initial, rettv);
3456 if (b == NULL)
3457 return;
3458
3459 for ( ; i < b->bv_ga.ga_len; i++)
3460 {
3461 argv[0] = *rettv;
3462 argv[1].v_type = VAR_NUMBER;
3463 argv[1].vval.v_number = blob_get(b, i);
3464 r = call_func(func_name, -1, rettv, 2, argv, funcexe);
3465 clear_tv(&argv[0]);
3466 if (r == FAIL || called_emsg != called_emsg_start)
3467 return;
3468 }
3469}
3470
3471/*
Bram Moolenaar8e7d6222020-12-18 19:49:56 +01003472 * "reduce(list, { accumulator, element -> value } [, initial])" function
Bram Moolenaar85629982020-06-01 18:39:20 +02003473 */
3474 void
3475f_reduce(typval_T *argvars, typval_T *rettv)
3476{
Bram Moolenaar85629982020-06-01 18:39:20 +02003477 char_u *func_name;
3478 partial_T *partial = NULL;
3479 funcexe_T funcexe;
Bram Moolenaar85629982020-06-01 18:39:20 +02003480
rbtnn0ccb5842021-12-18 18:33:46 +00003481 if (in_vim9script()
3482 && check_for_string_or_list_or_blob_arg(argvars, 0) == FAIL)
Bram Moolenaar85629982020-06-01 18:39:20 +02003483 return;
rbtnn0ccb5842021-12-18 18:33:46 +00003484
3485 if (argvars[0].v_type != VAR_STRING
3486 && argvars[0].v_type != VAR_LIST
3487 && argvars[0].v_type != VAR_BLOB)
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00003488 {
rbtnn0ccb5842021-12-18 18:33:46 +00003489 semsg(_(e_string_list_or_blob_required), "reduce()");
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00003490 return;
3491 }
Bram Moolenaar85629982020-06-01 18:39:20 +02003492
3493 if (argvars[1].v_type == VAR_FUNC)
3494 func_name = argvars[1].vval.v_string;
3495 else if (argvars[1].v_type == VAR_PARTIAL)
3496 {
3497 partial = argvars[1].vval.v_partial;
3498 func_name = partial_name(partial);
3499 }
3500 else
3501 func_name = tv_get_string(&argvars[1]);
Bram Moolenaar0d90e722020-11-03 18:20:19 +01003502 if (func_name == NULL || *func_name == NUL)
3503 {
3504 emsg(_(e_missing_function_argument));
3505 return;
3506 }
Bram Moolenaar85629982020-06-01 18:39:20 +02003507
Bram Moolenaar851f86b2021-12-13 14:26:44 +00003508 CLEAR_FIELD(funcexe);
3509 funcexe.fe_evaluate = TRUE;
3510 funcexe.fe_partial = partial;
Bram Moolenaar85629982020-06-01 18:39:20 +02003511
3512 if (argvars[0].v_type == VAR_LIST)
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00003513 reduce_list(argvars, func_name, &funcexe, rettv);
rbtnn0ccb5842021-12-18 18:33:46 +00003514 else if (argvars[0].v_type == VAR_STRING)
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00003515 reduce_string(argvars, func_name, &funcexe, rettv);
Bram Moolenaar85629982020-06-01 18:39:20 +02003516 else
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00003517 reduce_blob(argvars, func_name, &funcexe, rettv);
Bram Moolenaar85629982020-06-01 18:39:20 +02003518}
3519
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02003520#endif // defined(FEAT_EVAL)