blob: 57a1a52746505a09e1358bd178dd12587efc10b9 [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/*
Yegappan Lakshmanand92813a2021-12-21 13:19:42 +00001990 * sort() List "l"
1991 */
1992 static void
1993do_sort(list_T *l, sortinfo_T *info)
1994{
1995 long len;
1996 sortItem_T *ptrs;
1997 long i = 0;
1998 listitem_T *li;
1999
2000 len = list_len(l);
2001
2002 // Make an array with each entry pointing to an item in the List.
2003 ptrs = ALLOC_MULT(sortItem_T, len);
2004 if (ptrs == NULL)
2005 return;
2006
2007 // sort(): ptrs will be the list to sort
2008 FOR_ALL_LIST_ITEMS(l, li)
2009 {
2010 ptrs[i].item = li;
2011 ptrs[i].idx = i;
2012 ++i;
2013 }
2014
2015 info->item_compare_func_err = FALSE;
2016 info->item_compare_keep_zero = FALSE;
2017 // test the compare function
2018 if ((info->item_compare_func != NULL
2019 || info->item_compare_partial != NULL)
2020 && item_compare2((void *)&ptrs[0], (void *)&ptrs[1])
2021 == ITEM_COMPARE_FAIL)
2022 emsg(_("E702: Sort compare function failed"));
2023 else
2024 {
2025 // Sort the array with item pointers.
2026 qsort((void *)ptrs, (size_t)len, sizeof(sortItem_T),
2027 info->item_compare_func == NULL
2028 && info->item_compare_partial == NULL
2029 ? item_compare : item_compare2);
2030
2031 if (!info->item_compare_func_err)
2032 {
2033 // Clear the List and append the items in sorted order.
2034 l->lv_first = l->lv_u.mat.lv_last
2035 = l->lv_u.mat.lv_idx_item = NULL;
2036 l->lv_len = 0;
2037 for (i = 0; i < len; ++i)
2038 list_append(l, ptrs[i].item);
2039 }
2040 }
2041
2042 vim_free(ptrs);
2043}
2044
2045/*
2046 * uniq() List "l"
2047 */
2048 static void
2049do_uniq(list_T *l, sortinfo_T *info)
2050{
2051 long len;
2052 sortItem_T *ptrs;
2053 long i = 0;
2054 listitem_T *li;
2055 int (*item_compare_func_ptr)(const void *, const void *);
2056
2057 len = list_len(l);
2058
2059 // Make an array with each entry pointing to an item in the List.
2060 ptrs = ALLOC_MULT(sortItem_T, len);
2061 if (ptrs == NULL)
2062 return;
2063
2064 // f_uniq(): ptrs will be a stack of items to remove
2065 info->item_compare_func_err = FALSE;
2066 info->item_compare_keep_zero = TRUE;
2067 item_compare_func_ptr = info->item_compare_func != NULL
2068 || info->item_compare_partial != NULL
2069 ? item_compare2 : item_compare;
2070
2071 for (li = l->lv_first; li != NULL && li->li_next != NULL;
2072 li = li->li_next)
2073 {
2074 if (item_compare_func_ptr((void *)&li, (void *)&li->li_next)
2075 == 0)
2076 ptrs[i++].item = li;
2077 if (info->item_compare_func_err)
2078 {
2079 emsg(_("E882: Uniq compare function failed"));
2080 break;
2081 }
2082 }
2083
2084 if (!info->item_compare_func_err)
2085 {
2086 while (--i >= 0)
2087 {
2088 li = ptrs[i].item->li_next;
2089 ptrs[i].item->li_next = li->li_next;
2090 if (li->li_next != NULL)
2091 li->li_next->li_prev = ptrs[i].item;
2092 else
2093 l->lv_u.mat.lv_last = ptrs[i].item;
2094 list_fix_watch(l, li);
2095 listitem_free(l, li);
2096 l->lv_len--;
2097 }
2098 }
2099
2100 vim_free(ptrs);
2101}
2102
2103/*
2104 * Parse the optional arguments to sort() and uniq() and return the values in
2105 * 'info'.
2106 */
2107 static int
2108parse_sort_uniq_args(typval_T *argvars, sortinfo_T *info)
2109{
2110 info->item_compare_ic = FALSE;
2111 info->item_compare_lc = FALSE;
2112 info->item_compare_numeric = FALSE;
2113 info->item_compare_numbers = FALSE;
2114#ifdef FEAT_FLOAT
2115 info->item_compare_float = FALSE;
2116#endif
2117 info->item_compare_func = NULL;
2118 info->item_compare_partial = NULL;
2119 info->item_compare_selfdict = NULL;
2120
2121 if (argvars[1].v_type == VAR_UNKNOWN)
2122 return OK;
2123
2124 // optional second argument: {func}
2125 if (argvars[1].v_type == VAR_FUNC)
2126 info->item_compare_func = argvars[1].vval.v_string;
2127 else if (argvars[1].v_type == VAR_PARTIAL)
2128 info->item_compare_partial = argvars[1].vval.v_partial;
2129 else
2130 {
2131 int error = FALSE;
2132 int nr = 0;
2133
2134 if (argvars[1].v_type == VAR_NUMBER)
2135 {
2136 nr = tv_get_number_chk(&argvars[1], &error);
2137 if (error)
2138 return FAIL;
2139 if (nr == 1)
2140 info->item_compare_ic = TRUE;
2141 }
2142 if (nr != 1)
2143 {
2144 if (argvars[1].v_type != VAR_NUMBER)
2145 info->item_compare_func = tv_get_string(&argvars[1]);
2146 else if (nr != 0)
2147 {
2148 emsg(_(e_invarg));
2149 return FAIL;
2150 }
2151 }
2152 if (info->item_compare_func != NULL)
2153 {
2154 if (*info->item_compare_func == NUL)
2155 {
2156 // empty string means default sort
2157 info->item_compare_func = NULL;
2158 }
2159 else if (STRCMP(info->item_compare_func, "n") == 0)
2160 {
2161 info->item_compare_func = NULL;
2162 info->item_compare_numeric = TRUE;
2163 }
2164 else if (STRCMP(info->item_compare_func, "N") == 0)
2165 {
2166 info->item_compare_func = NULL;
2167 info->item_compare_numbers = TRUE;
2168 }
2169#ifdef FEAT_FLOAT
2170 else if (STRCMP(info->item_compare_func, "f") == 0)
2171 {
2172 info->item_compare_func = NULL;
2173 info->item_compare_float = TRUE;
2174 }
2175#endif
2176 else if (STRCMP(info->item_compare_func, "i") == 0)
2177 {
2178 info->item_compare_func = NULL;
2179 info->item_compare_ic = TRUE;
2180 }
2181 else if (STRCMP(info->item_compare_func, "l") == 0)
2182 {
2183 info->item_compare_func = NULL;
2184 info->item_compare_lc = TRUE;
2185 }
2186 }
2187 }
2188
2189 if (argvars[2].v_type != VAR_UNKNOWN)
2190 {
2191 // optional third argument: {dict}
2192 if (argvars[2].v_type != VAR_DICT)
2193 {
2194 emsg(_(e_dictreq));
2195 return FAIL;
2196 }
2197 info->item_compare_selfdict = argvars[2].vval.v_dict;
2198 }
2199
2200 return OK;
2201}
2202
2203/*
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02002204 * "sort()" or "uniq()" function
2205 */
2206 static void
2207do_sort_uniq(typval_T *argvars, typval_T *rettv, int sort)
2208{
2209 list_T *l;
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02002210 sortinfo_T *old_sortinfo;
2211 sortinfo_T info;
2212 long len;
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02002213
Yegappan Lakshmanan0ad871d2021-07-23 20:37:56 +02002214 if (in_vim9script()
2215 && (check_for_list_arg(argvars, 0) == FAIL
2216 || (argvars[1].v_type != VAR_UNKNOWN
2217 && check_for_opt_dict_arg(argvars, 2) == FAIL)))
2218 return;
2219
Yegappan Lakshmanand92813a2021-12-21 13:19:42 +00002220 if (argvars[0].v_type != VAR_LIST)
2221 {
2222 semsg(_(e_listarg), sort ? "sort()" : "uniq()");
2223 return;
2224 }
2225
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002226 // Pointer to current info struct used in compare function. Save and
2227 // restore the current one for nested calls.
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02002228 old_sortinfo = sortinfo;
2229 sortinfo = &info;
2230
Yegappan Lakshmanand92813a2021-12-21 13:19:42 +00002231 l = argvars[0].vval.v_list;
2232 if (l != NULL && value_check_lock(l->lv_lock,
2233 (char_u *)(sort ? N_("sort() argument") : N_("uniq() argument")),
2234 TRUE))
2235 goto theend;
2236 rettv_list_set(rettv, l);
2237 if (l == NULL)
2238 goto theend;
2239 CHECK_LIST_MATERIALIZE(l);
2240
2241 len = list_len(l);
2242 if (len <= 1)
2243 goto theend; // short list sorts pretty quickly
2244
2245 if (parse_sort_uniq_args(argvars, &info) == FAIL)
2246 goto theend;
2247
2248 if (sort)
2249 do_sort(l, &info);
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02002250 else
Yegappan Lakshmanand92813a2021-12-21 13:19:42 +00002251 do_uniq(l, &info);
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02002252
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02002253theend:
2254 sortinfo = old_sortinfo;
2255}
2256
2257/*
2258 * "sort({list})" function
2259 */
2260 void
2261f_sort(typval_T *argvars, typval_T *rettv)
2262{
2263 do_sort_uniq(argvars, rettv, TRUE);
2264}
2265
2266/*
2267 * "uniq({list})" function
2268 */
2269 void
2270f_uniq(typval_T *argvars, typval_T *rettv)
2271{
2272 do_sort_uniq(argvars, rettv, FALSE);
2273}
2274
Bram Moolenaarea696852020-11-09 18:31:39 +01002275typedef enum {
2276 FILTERMAP_FILTER,
2277 FILTERMAP_MAP,
2278 FILTERMAP_MAPNEW
2279} filtermap_T;
2280
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02002281/*
2282 * Handle one item for map() and filter().
Bram Moolenaarea696852020-11-09 18:31:39 +01002283 * Sets v:val to "tv". Caller must set v:key.
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02002284 */
2285 static int
Bram Moolenaarea696852020-11-09 18:31:39 +01002286filter_map_one(
2287 typval_T *tv, // original value
2288 typval_T *expr, // callback
2289 filtermap_T filtermap,
2290 typval_T *newtv, // for map() and mapnew(): new value
2291 int *remp) // for filter(): remove flag
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02002292{
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02002293 typval_T argv[3];
2294 int retval = FAIL;
2295
2296 copy_tv(tv, get_vim_var_tv(VV_VAL));
2297 argv[0] = *get_vim_var_tv(VV_KEY);
2298 argv[1] = *get_vim_var_tv(VV_VAL);
Bram Moolenaarea696852020-11-09 18:31:39 +01002299 if (eval_expr_typval(expr, argv, 2, newtv) == FAIL)
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02002300 goto theend;
Bram Moolenaarea696852020-11-09 18:31:39 +01002301 if (filtermap == FILTERMAP_FILTER)
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02002302 {
2303 int error = FALSE;
2304
2305 // filter(): when expr is zero remove the item
Bram Moolenaar56acb092020-08-16 14:48:19 +02002306 if (in_vim9script())
Bram Moolenaarea696852020-11-09 18:31:39 +01002307 *remp = !tv2bool(newtv);
Bram Moolenaar56acb092020-08-16 14:48:19 +02002308 else
Bram Moolenaarea696852020-11-09 18:31:39 +01002309 *remp = (tv_get_number_chk(newtv, &error) == 0);
2310 clear_tv(newtv);
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02002311 // On type error, nothing has been removed; return FAIL to stop the
2312 // loop. The error message was given by tv_get_number_chk().
2313 if (error)
2314 goto theend;
2315 }
2316 retval = OK;
2317theend:
2318 clear_tv(get_vim_var_tv(VV_VAL));
2319 return retval;
2320}
2321
2322/*
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00002323 * Implementation of map() and filter() for a Dict.
2324 */
2325 static void
2326filter_map_dict(
2327 dict_T *d,
2328 filtermap_T filtermap,
2329 type_T *argtype,
2330 char *func_name,
2331 char_u *arg_errmsg,
2332 typval_T *expr,
2333 typval_T *rettv)
2334{
2335 int prev_lock;
2336 dict_T *d_ret = NULL;
2337 hashtab_T *ht;
2338 hashitem_T *hi;
2339 dictitem_T *di;
2340 int todo;
2341 int rem;
2342
2343 if (filtermap == FILTERMAP_MAPNEW)
2344 {
2345 rettv->v_type = VAR_DICT;
2346 rettv->vval.v_dict = NULL;
2347 }
2348 if (d == NULL
2349 || (filtermap == FILTERMAP_FILTER
2350 && value_check_lock(d->dv_lock, arg_errmsg, TRUE)))
2351 return;
2352
2353 prev_lock = d->dv_lock;
2354
2355 if (filtermap == FILTERMAP_MAPNEW)
2356 {
2357 if (rettv_dict_alloc(rettv) == FAIL)
2358 return;
2359 d_ret = rettv->vval.v_dict;
2360 }
2361
2362 if (filtermap != FILTERMAP_FILTER && d->dv_lock == 0)
2363 d->dv_lock = VAR_LOCKED;
2364 ht = &d->dv_hashtab;
2365 hash_lock(ht);
2366 todo = (int)ht->ht_used;
2367 for (hi = ht->ht_array; todo > 0; ++hi)
2368 {
2369 if (!HASHITEM_EMPTY(hi))
2370 {
2371 int r;
2372 typval_T newtv;
2373
2374 --todo;
2375 di = HI2DI(hi);
2376 if (filtermap == FILTERMAP_MAP
2377 && (value_check_lock(di->di_tv.v_lock,
2378 arg_errmsg, TRUE)
2379 || var_check_ro(di->di_flags,
2380 arg_errmsg, TRUE)))
2381 break;
2382 set_vim_var_string(VV_KEY, di->di_key, -1);
2383 newtv.v_type = VAR_UNKNOWN;
2384 r = filter_map_one(&di->di_tv, expr, filtermap,
2385 &newtv, &rem);
2386 clear_tv(get_vim_var_tv(VV_KEY));
2387 if (r == FAIL || did_emsg)
2388 {
2389 clear_tv(&newtv);
2390 break;
2391 }
2392 if (filtermap == FILTERMAP_MAP)
2393 {
2394 if (argtype != NULL && check_typval_arg_type(
2395 argtype->tt_member, &newtv,
2396 func_name, 0) == FAIL)
2397 {
2398 clear_tv(&newtv);
2399 break;
2400 }
2401 // map(): replace the dict item value
2402 clear_tv(&di->di_tv);
2403 newtv.v_lock = 0;
2404 di->di_tv = newtv;
2405 }
2406 else if (filtermap == FILTERMAP_MAPNEW)
2407 {
2408 // mapnew(): add the item value to the new dict
2409 r = dict_add_tv(d_ret, (char *)di->di_key, &newtv);
2410 clear_tv(&newtv);
2411 if (r == FAIL)
2412 break;
2413 }
2414 else if (filtermap == FILTERMAP_FILTER && rem)
2415 {
2416 // filter(false): remove the item from the dict
2417 if (var_check_fixed(di->di_flags, arg_errmsg, TRUE)
2418 || var_check_ro(di->di_flags, arg_errmsg, TRUE))
2419 break;
2420 dictitem_remove(d, di);
2421 }
2422 }
2423 }
2424 hash_unlock(ht);
2425 d->dv_lock = prev_lock;
2426}
2427
2428/*
2429 * Implementation of map() and filter() for a Blob.
2430 */
2431 static void
2432filter_map_blob(
2433 blob_T *blob_arg,
2434 filtermap_T filtermap,
2435 typval_T *expr,
2436 typval_T *rettv)
2437{
2438 blob_T *b;
2439 int i;
2440 typval_T tv;
2441 varnumber_T val;
2442 blob_T *b_ret;
2443 int idx = 0;
2444 int rem;
2445
2446 if (filtermap == FILTERMAP_MAPNEW)
2447 {
2448 rettv->v_type = VAR_BLOB;
2449 rettv->vval.v_blob = NULL;
2450 }
2451 if ((b = blob_arg) == NULL)
2452 return;
2453
2454 b_ret = b;
2455 if (filtermap == FILTERMAP_MAPNEW)
2456 {
2457 if (blob_copy(b, rettv) == FAIL)
2458 return;
2459 b_ret = rettv->vval.v_blob;
2460 }
2461
2462 // set_vim_var_nr() doesn't set the type
2463 set_vim_var_type(VV_KEY, VAR_NUMBER);
2464
2465 for (i = 0; i < b->bv_ga.ga_len; i++)
2466 {
2467 typval_T newtv;
2468
2469 tv.v_type = VAR_NUMBER;
2470 val = blob_get(b, i);
2471 tv.vval.v_number = val;
2472 set_vim_var_nr(VV_KEY, idx);
2473 if (filter_map_one(&tv, expr, filtermap, &newtv, &rem) == FAIL
2474 || did_emsg)
2475 break;
2476 if (newtv.v_type != VAR_NUMBER && newtv.v_type != VAR_BOOL)
2477 {
2478 clear_tv(&newtv);
2479 emsg(_(e_invalblob));
2480 break;
2481 }
2482 if (filtermap != FILTERMAP_FILTER)
2483 {
2484 if (newtv.vval.v_number != val)
2485 blob_set(b_ret, i, newtv.vval.v_number);
2486 }
2487 else if (rem)
2488 {
2489 char_u *p = (char_u *)blob_arg->bv_ga.ga_data;
2490
2491 mch_memmove(p + i, p + i + 1,
2492 (size_t)b->bv_ga.ga_len - i - 1);
2493 --b->bv_ga.ga_len;
2494 --i;
2495 }
2496 ++idx;
2497 }
2498}
2499
2500/*
2501 * Implementation of map() and filter() for a String.
2502 */
2503 static void
2504filter_map_string(
2505 char_u *str,
2506 filtermap_T filtermap,
2507 typval_T *expr,
2508 typval_T *rettv)
2509{
2510 char_u *p;
2511 typval_T tv;
2512 garray_T ga;
2513 int len = 0;
2514 int idx = 0;
2515 int rem;
2516
2517 rettv->v_type = VAR_STRING;
2518 rettv->vval.v_string = NULL;
2519
2520 // set_vim_var_nr() doesn't set the type
2521 set_vim_var_type(VV_KEY, VAR_NUMBER);
2522
2523 ga_init2(&ga, (int)sizeof(char), 80);
2524 for (p = str; *p != NUL; p += len)
2525 {
2526 typval_T newtv;
2527
2528 if (tv_get_first_char(p, &tv) == FAIL)
2529 break;
2530 len = (int)STRLEN(tv.vval.v_string);
2531
2532 set_vim_var_nr(VV_KEY, idx);
2533 if (filter_map_one(&tv, expr, filtermap, &newtv, &rem) == FAIL
2534 || did_emsg)
2535 break;
2536 if (did_emsg)
2537 {
2538 clear_tv(&newtv);
2539 clear_tv(&tv);
2540 break;
2541 }
2542 else if (filtermap != FILTERMAP_FILTER)
2543 {
2544 if (newtv.v_type != VAR_STRING)
2545 {
2546 clear_tv(&newtv);
2547 clear_tv(&tv);
2548 emsg(_(e_stringreq));
2549 break;
2550 }
2551 else
2552 ga_concat(&ga, newtv.vval.v_string);
2553 }
2554 else if (!rem)
2555 ga_concat(&ga, tv.vval.v_string);
2556
2557 clear_tv(&newtv);
2558 clear_tv(&tv);
2559
2560 ++idx;
2561 }
2562 ga_append(&ga, NUL);
2563 rettv->vval.v_string = ga.ga_data;
2564}
2565
2566/*
2567 * Implementation of map() and filter() for a List.
2568 */
2569 static void
2570filter_map_list(
2571 list_T *l,
2572 filtermap_T filtermap,
2573 type_T *argtype,
2574 char *func_name,
2575 char_u *arg_errmsg,
2576 typval_T *expr,
2577 typval_T *rettv)
2578{
2579 int prev_lock;
2580 list_T *l_ret = NULL;
2581 int idx = 0;
2582 int rem;
2583 listitem_T *li, *nli;
2584
2585 if (filtermap == FILTERMAP_MAPNEW)
2586 {
2587 rettv->v_type = VAR_LIST;
2588 rettv->vval.v_list = NULL;
2589 }
2590 if (l == NULL
2591 || (filtermap == FILTERMAP_FILTER
2592 && value_check_lock(l->lv_lock, arg_errmsg, TRUE)))
2593 return;
2594
2595 prev_lock = l->lv_lock;
2596
2597 if (filtermap == FILTERMAP_MAPNEW)
2598 {
2599 if (rettv_list_alloc(rettv) == FAIL)
2600 return;
2601 l_ret = rettv->vval.v_list;
2602 }
2603 // set_vim_var_nr() doesn't set the type
2604 set_vim_var_type(VV_KEY, VAR_NUMBER);
2605
2606 if (filtermap != FILTERMAP_FILTER && l->lv_lock == 0)
2607 l->lv_lock = VAR_LOCKED;
2608
2609 if (l->lv_first == &range_list_item)
2610 {
2611 varnumber_T val = l->lv_u.nonmat.lv_start;
2612 int len = l->lv_len;
2613 int stride = l->lv_u.nonmat.lv_stride;
2614
2615 // List from range(): loop over the numbers
2616 if (filtermap != FILTERMAP_MAPNEW)
2617 {
2618 l->lv_first = NULL;
2619 l->lv_u.mat.lv_last = NULL;
2620 l->lv_len = 0;
2621 l->lv_u.mat.lv_idx_item = NULL;
2622 }
2623
2624 for (idx = 0; idx < len; ++idx)
2625 {
2626 typval_T tv;
2627 typval_T newtv;
2628
2629 tv.v_type = VAR_NUMBER;
2630 tv.v_lock = 0;
2631 tv.vval.v_number = val;
2632 set_vim_var_nr(VV_KEY, idx);
2633 if (filter_map_one(&tv, expr, filtermap, &newtv, &rem)
2634 == FAIL)
2635 break;
2636 if (did_emsg)
2637 {
2638 clear_tv(&newtv);
2639 break;
2640 }
2641 if (filtermap != FILTERMAP_FILTER)
2642 {
2643 if (filtermap == FILTERMAP_MAP && argtype != NULL
2644 && check_typval_arg_type(
2645 argtype->tt_member, &newtv,
2646 func_name, 0) == FAIL)
2647 {
2648 clear_tv(&newtv);
2649 break;
2650 }
2651 // map(), mapnew(): always append the new value to the
2652 // list
2653 if (list_append_tv_move(filtermap == FILTERMAP_MAP
2654 ? l : l_ret, &newtv) == FAIL)
2655 break;
2656 }
2657 else if (!rem)
2658 {
2659 // filter(): append the list item value when not rem
2660 if (list_append_tv_move(l, &tv) == FAIL)
2661 break;
2662 }
2663
2664 val += stride;
2665 }
2666 }
2667 else
2668 {
2669 // Materialized list: loop over the items
2670 for (li = l->lv_first; li != NULL; li = nli)
2671 {
2672 typval_T newtv;
2673
2674 if (filtermap == FILTERMAP_MAP && value_check_lock(
2675 li->li_tv.v_lock, arg_errmsg, TRUE))
2676 break;
2677 nli = li->li_next;
2678 set_vim_var_nr(VV_KEY, idx);
2679 if (filter_map_one(&li->li_tv, expr, filtermap,
2680 &newtv, &rem) == FAIL)
2681 break;
2682 if (did_emsg)
2683 {
2684 clear_tv(&newtv);
2685 break;
2686 }
2687 if (filtermap == FILTERMAP_MAP)
2688 {
2689 if (argtype != NULL && check_typval_arg_type(
2690 argtype->tt_member, &newtv, func_name, 0) == FAIL)
2691 {
2692 clear_tv(&newtv);
2693 break;
2694 }
2695 // map(): replace the list item value
2696 clear_tv(&li->li_tv);
2697 newtv.v_lock = 0;
2698 li->li_tv = newtv;
2699 }
2700 else if (filtermap == FILTERMAP_MAPNEW)
2701 {
2702 // mapnew(): append the list item value
2703 if (list_append_tv_move(l_ret, &newtv) == FAIL)
2704 break;
2705 }
2706 else if (filtermap == FILTERMAP_FILTER && rem)
2707 listitem_remove(l, li);
2708 ++idx;
2709 }
2710 }
2711
2712 l->lv_lock = prev_lock;
2713}
2714
2715/*
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02002716 * Implementation of map() and filter().
2717 */
2718 static void
Bram Moolenaarea696852020-11-09 18:31:39 +01002719filter_map(typval_T *argvars, typval_T *rettv, filtermap_T filtermap)
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02002720{
2721 typval_T *expr;
Bram Moolenaar7a3fe3e2021-07-22 14:58:47 +02002722 char *func_name = filtermap == FILTERMAP_MAP ? "map()"
Bram Moolenaarea696852020-11-09 18:31:39 +01002723 : filtermap == FILTERMAP_MAPNEW ? "mapnew()"
Bram Moolenaar7a3fe3e2021-07-22 14:58:47 +02002724 : "filter()";
Bram Moolenaarea696852020-11-09 18:31:39 +01002725 char_u *arg_errmsg = (char_u *)(filtermap == FILTERMAP_MAP
2726 ? N_("map() argument")
2727 : filtermap == FILTERMAP_MAPNEW
2728 ? N_("mapnew() argument")
2729 : N_("filter() argument"));
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02002730 int save_did_emsg;
Bram Moolenaar70250fb2021-01-16 19:01:53 +01002731 type_T *type = NULL;
2732 garray_T type_list;
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02002733
Bram Moolenaarea696852020-11-09 18:31:39 +01002734 // map() and filter() return the first argument, also on failure.
Bram Moolenaar2d877592021-12-16 08:21:09 +00002735 if (filtermap != FILTERMAP_MAPNEW && argvars[0].v_type != VAR_STRING)
Bram Moolenaarea696852020-11-09 18:31:39 +01002736 copy_tv(&argvars[0], rettv);
Yegappan Lakshmanan0ad871d2021-07-23 20:37:56 +02002737
2738 if (in_vim9script()
Bram Moolenaar2d877592021-12-16 08:21:09 +00002739 && (check_for_list_or_dict_or_blob_or_string_arg(argvars, 0)
2740 == FAIL))
Yegappan Lakshmanan0ad871d2021-07-23 20:37:56 +02002741 return;
2742
Bram Moolenaar70250fb2021-01-16 19:01:53 +01002743 if (filtermap == FILTERMAP_MAP && in_vim9script())
2744 {
2745 // Check that map() does not change the type of the dict.
2746 ga_init2(&type_list, sizeof(type_T *), 10);
Bram Moolenaarf2253962021-04-13 20:53:13 +02002747 type = typval2type(argvars, get_copyID(), &type_list, TRUE);
Bram Moolenaar70250fb2021-01-16 19:01:53 +01002748 }
Bram Moolenaarffdf8ad2020-10-15 22:29:17 +02002749
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00002750 if (argvars[0].v_type != VAR_BLOB
2751 && argvars[0].v_type != VAR_LIST
2752 && argvars[0].v_type != VAR_DICT
2753 && argvars[0].v_type != VAR_STRING)
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02002754 {
rbtnnc479ce02021-12-15 19:14:54 +00002755 semsg(_(e_argument_of_str_must_be_list_string_dictionary_or_blob),
2756 func_name);
Bram Moolenaar70250fb2021-01-16 19:01:53 +01002757 goto theend;
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02002758 }
2759
2760 expr = &argvars[1];
2761 // On type errors, the preceding call has already displayed an error
2762 // message. Avoid a misleading error message for an empty string that
2763 // was not passed as argument.
2764 if (expr->v_type != VAR_UNKNOWN)
2765 {
2766 typval_T save_val;
2767 typval_T save_key;
2768
2769 prepare_vimvar(VV_VAL, &save_val);
2770 prepare_vimvar(VV_KEY, &save_key);
2771
2772 // We reset "did_emsg" to be able to detect whether an error
2773 // occurred during evaluation of the expression.
2774 save_did_emsg = did_emsg;
2775 did_emsg = FALSE;
2776
2777 if (argvars[0].v_type == VAR_DICT)
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00002778 filter_map_dict(argvars[0].vval.v_dict, filtermap, type, func_name,
2779 arg_errmsg, expr, rettv);
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02002780 else if (argvars[0].v_type == VAR_BLOB)
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00002781 filter_map_blob(argvars[0].vval.v_blob, filtermap, expr, rettv);
rbtnnc479ce02021-12-15 19:14:54 +00002782 else if (argvars[0].v_type == VAR_STRING)
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00002783 filter_map_string(tv_get_string(&argvars[0]), filtermap, expr,
2784 rettv);
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02002785 else // argvars[0].v_type == VAR_LIST
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00002786 filter_map_list(argvars[0].vval.v_list, filtermap, type, func_name,
2787 arg_errmsg, expr, rettv);
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02002788
2789 restore_vimvar(VV_KEY, &save_key);
2790 restore_vimvar(VV_VAL, &save_val);
2791
2792 did_emsg |= save_did_emsg;
2793 }
Bram Moolenaar70250fb2021-01-16 19:01:53 +01002794
2795theend:
2796 if (type != NULL)
2797 clear_type_list(&type_list);
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02002798}
2799
2800/*
2801 * "filter()" function
2802 */
2803 void
2804f_filter(typval_T *argvars, typval_T *rettv)
2805{
Bram Moolenaarea696852020-11-09 18:31:39 +01002806 filter_map(argvars, rettv, FILTERMAP_FILTER);
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02002807}
2808
2809/*
2810 * "map()" function
2811 */
2812 void
2813f_map(typval_T *argvars, typval_T *rettv)
2814{
Bram Moolenaarea696852020-11-09 18:31:39 +01002815 filter_map(argvars, rettv, FILTERMAP_MAP);
2816}
2817
2818/*
2819 * "mapnew()" function
2820 */
2821 void
2822f_mapnew(typval_T *argvars, typval_T *rettv)
2823{
2824 filter_map(argvars, rettv, FILTERMAP_MAPNEW);
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02002825}
2826
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002827/*
2828 * "add(list, item)" function
2829 */
2830 void
2831f_add(typval_T *argvars, typval_T *rettv)
2832{
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002833 rettv->vval.v_number = 1; // Default: Failed
Yegappan Lakshmanan0ad871d2021-07-23 20:37:56 +02002834
2835 if (in_vim9script()
2836 && (check_for_list_or_blob_arg(argvars, 0) == FAIL
2837 || (argvars[0].v_type == VAR_BLOB
2838 && check_for_number_arg(argvars, 1) == FAIL)))
2839 return;
2840
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002841 if (argvars[0].v_type == VAR_LIST)
2842 {
Bram Moolenaarb7c21af2021-04-18 14:12:31 +02002843 list_T *l = argvars[0].vval.v_list;
2844
2845 if (l == NULL)
2846 {
2847 if (in_vim9script())
2848 emsg(_(e_cannot_add_to_null_list));
2849 }
2850 else if (!value_check_lock(l->lv_lock,
2851 (char_u *)N_("add() argument"), TRUE)
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002852 && list_append_tv(l, &argvars[1]) == OK)
Bram Moolenaarb7c21af2021-04-18 14:12:31 +02002853 {
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002854 copy_tv(&argvars[0], rettv);
Bram Moolenaarb7c21af2021-04-18 14:12:31 +02002855 }
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002856 }
2857 else if (argvars[0].v_type == VAR_BLOB)
2858 {
Bram Moolenaarb7c21af2021-04-18 14:12:31 +02002859 blob_T *b = argvars[0].vval.v_blob;
2860
2861 if (b == NULL)
2862 {
2863 if (in_vim9script())
2864 emsg(_(e_cannot_add_to_null_blob));
2865 }
2866 else if (!value_check_lock(b->bv_lock,
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002867 (char_u *)N_("add() argument"), TRUE))
2868 {
2869 int error = FALSE;
2870 varnumber_T n = tv_get_number_chk(&argvars[1], &error);
2871
2872 if (!error)
2873 {
2874 ga_append(&b->bv_ga, (int)n);
2875 copy_tv(&argvars[0], rettv);
2876 }
2877 }
2878 }
2879 else
2880 emsg(_(e_listblobreq));
2881}
2882
2883/*
Yegappan Lakshmanand92813a2021-12-21 13:19:42 +00002884 * Count the number of times "needle" occurs in string "haystack". Case is
2885 * ignored if "ic" is TRUE.
2886 */
2887 static long
2888count_string(char_u *haystack, char_u *needle, int ic)
2889{
2890 long n = 0;
2891 char_u *p = haystack;
2892 char_u *next;
2893
2894 if (p == NULL || needle == NULL || *needle == NUL)
2895 return 0;
2896
2897 if (ic)
2898 {
2899 size_t len = STRLEN(needle);
2900
2901 while (*p != NUL)
2902 {
2903 if (MB_STRNICMP(p, needle, len) == 0)
2904 {
2905 ++n;
2906 p += len;
2907 }
2908 else
2909 MB_PTR_ADV(p);
2910 }
2911 }
2912 else
2913 while ((next = (char_u *)strstr((char *)p, (char *)needle)) != NULL)
2914 {
2915 ++n;
2916 p = next + STRLEN(needle);
2917 }
2918
2919 return n;
2920}
2921
2922/*
2923 * Count the number of times item "needle" occurs in List "l" starting at index
2924 * "idx". Case is ignored if "ic" is TRUE.
2925 */
2926 static long
2927count_list(list_T *l, typval_T *needle, long idx, int ic)
2928{
2929 long n = 0;
2930 listitem_T *li;
2931
2932 if (l == NULL)
2933 return 0;
2934
2935 CHECK_LIST_MATERIALIZE(l);
2936
2937 if (list_len(l) == 0)
2938 return 0;
2939
2940 li = list_find(l, idx);
2941 if (li == NULL)
2942 {
2943 semsg(_(e_listidx), idx);
2944 return 0;
2945 }
2946
2947 for ( ; li != NULL; li = li->li_next)
2948 if (tv_equal(&li->li_tv, needle, ic, FALSE))
2949 ++n;
2950
2951 return n;
2952}
2953
2954/*
2955 * Count the number of times item "needle" occurs in Dict "d". Case is ignored
2956 * if "ic" is TRUE.
2957 */
2958 static long
2959count_dict(dict_T *d, typval_T *needle, int ic)
2960{
2961 int todo;
2962 hashitem_T *hi;
2963 long n = 0;
2964
2965 if (d == NULL)
2966 return 0;
2967
2968 todo = (int)d->dv_hashtab.ht_used;
2969 for (hi = d->dv_hashtab.ht_array; todo > 0; ++hi)
2970 {
2971 if (!HASHITEM_EMPTY(hi))
2972 {
2973 --todo;
2974 if (tv_equal(&HI2DI(hi)->di_tv, needle, ic, FALSE))
2975 ++n;
2976 }
2977 }
2978
2979 return n;
2980}
2981
2982/*
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002983 * "count()" function
2984 */
2985 void
2986f_count(typval_T *argvars, typval_T *rettv)
2987{
2988 long n = 0;
2989 int ic = FALSE;
2990 int error = FALSE;
2991
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +02002992 if (in_vim9script()
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02002993 && (check_for_string_or_list_or_dict_arg(argvars, 0) == FAIL
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +02002994 || check_for_opt_bool_arg(argvars, 2) == FAIL
2995 || (argvars[2].v_type != VAR_UNKNOWN
2996 && check_for_opt_number_arg(argvars, 3) == FAIL)))
2997 return;
2998
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002999 if (argvars[2].v_type != VAR_UNKNOWN)
Bram Moolenaar119f5572020-09-02 21:31:22 +02003000 ic = (int)tv_get_bool_chk(&argvars[2], &error);
Bram Moolenaar08c308a2019-09-04 17:48:15 +02003001
Yegappan Lakshmanand92813a2021-12-21 13:19:42 +00003002 if (!error && argvars[0].v_type == VAR_STRING)
3003 n = count_string(argvars[0].vval.v_string,
3004 tv_get_string_chk(&argvars[1]), ic);
3005 else if (!error && argvars[0].v_type == VAR_LIST)
Bram Moolenaar08c308a2019-09-04 17:48:15 +02003006 {
Yegappan Lakshmanand92813a2021-12-21 13:19:42 +00003007 long idx = 0;
Bram Moolenaar08c308a2019-09-04 17:48:15 +02003008
Yegappan Lakshmanand92813a2021-12-21 13:19:42 +00003009 if (argvars[2].v_type != VAR_UNKNOWN
3010 && argvars[3].v_type != VAR_UNKNOWN)
3011 idx = (long)tv_get_number_chk(&argvars[3], &error);
3012 if (!error)
3013 n = count_list(argvars[0].vval.v_list, &argvars[1], idx, ic);
Bram Moolenaar08c308a2019-09-04 17:48:15 +02003014 }
Yegappan Lakshmanand92813a2021-12-21 13:19:42 +00003015 else if (!error && argvars[0].v_type == VAR_DICT)
Bram Moolenaar08c308a2019-09-04 17:48:15 +02003016 {
Yegappan Lakshmanand92813a2021-12-21 13:19:42 +00003017 if (argvars[2].v_type != VAR_UNKNOWN
3018 && argvars[3].v_type != VAR_UNKNOWN)
3019 emsg(_(e_invarg));
3020 else
3021 n = count_dict(argvars[0].vval.v_dict, &argvars[1], ic);
Bram Moolenaar08c308a2019-09-04 17:48:15 +02003022 }
3023 else
3024 semsg(_(e_listdictarg), "count()");
3025 rettv->vval.v_number = n;
3026}
3027
3028/*
Yegappan Lakshmanand92813a2021-12-21 13:19:42 +00003029 * extend() a List. Append List argvars[1] to List argvars[0] before index
3030 * argvars[3] and return the resulting list in "rettv". "is_new" is TRUE for
3031 * extendnew().
3032 */
3033 static void
3034extend_list(
3035 typval_T *argvars,
3036 type_T *type,
3037 char *func_name,
3038 char_u *arg_errmsg,
3039 int is_new,
3040 typval_T *rettv)
3041{
3042 list_T *l1, *l2;
3043 listitem_T *item;
3044 long before;
3045 int error = FALSE;
3046
3047 l1 = argvars[0].vval.v_list;
3048 if (l1 == NULL)
3049 {
3050 emsg(_(e_cannot_extend_null_list));
3051 return;
3052 }
3053 l2 = argvars[1].vval.v_list;
3054 if ((is_new || !value_check_lock(l1->lv_lock, arg_errmsg, TRUE))
3055 && l2 != NULL)
3056 {
3057 if (is_new)
3058 {
3059 l1 = list_copy(l1, FALSE, get_copyID());
3060 if (l1 == NULL)
3061 return;
3062 }
3063
3064 if (argvars[2].v_type != VAR_UNKNOWN)
3065 {
3066 before = (long)tv_get_number_chk(&argvars[2], &error);
3067 if (error)
3068 return; // type error; errmsg already given
3069
3070 if (before == l1->lv_len)
3071 item = NULL;
3072 else
3073 {
3074 item = list_find(l1, before);
3075 if (item == NULL)
3076 {
3077 semsg(_(e_listidx), before);
3078 return;
3079 }
3080 }
3081 }
3082 else
3083 item = NULL;
3084 if (type != NULL && check_typval_arg_type(
3085 type, &argvars[1], func_name, 2) == FAIL)
3086 return;
3087 list_extend(l1, l2, item);
3088
3089 if (is_new)
3090 {
3091 rettv->v_type = VAR_LIST;
3092 rettv->vval.v_list = l1;
3093 rettv->v_lock = FALSE;
3094 }
3095 else
3096 copy_tv(&argvars[0], rettv);
3097 }
3098}
3099
3100/*
3101 * extend() a Dict. Append Dict argvars[1] to Dict argvars[0] and return the
3102 * resulting Dict in "rettv". "is_new" is TRUE for extendnew().
3103 */
3104 static void
3105extend_dict(
3106 typval_T *argvars,
3107 type_T *type,
3108 char *func_name,
3109 char_u *arg_errmsg,
3110 int is_new,
3111 typval_T *rettv)
3112{
3113 dict_T *d1, *d2;
3114 char_u *action;
3115 int i;
3116
3117 d1 = argvars[0].vval.v_dict;
3118 if (d1 == NULL)
3119 {
3120 emsg(_(e_cannot_extend_null_dict));
3121 return;
3122 }
3123 d2 = argvars[1].vval.v_dict;
3124 if ((is_new || !value_check_lock(d1->dv_lock, arg_errmsg, TRUE))
3125 && d2 != NULL)
3126 {
3127 if (is_new)
3128 {
3129 d1 = dict_copy(d1, FALSE, get_copyID());
3130 if (d1 == NULL)
3131 return;
3132 }
3133
3134 // Check the third argument.
3135 if (argvars[2].v_type != VAR_UNKNOWN)
3136 {
3137 static char *(av[]) = {"keep", "force", "error"};
3138
3139 action = tv_get_string_chk(&argvars[2]);
3140 if (action == NULL)
3141 return;
3142 for (i = 0; i < 3; ++i)
3143 if (STRCMP(action, av[i]) == 0)
3144 break;
3145 if (i == 3)
3146 {
3147 semsg(_(e_invarg2), action);
3148 return;
3149 }
3150 }
3151 else
3152 action = (char_u *)"force";
3153
3154 if (type != NULL && check_typval_arg_type(type, &argvars[1],
3155 func_name, 2) == FAIL)
3156 return;
3157 dict_extend(d1, d2, action, func_name);
3158
3159 if (is_new)
3160 {
3161 rettv->v_type = VAR_DICT;
3162 rettv->vval.v_dict = d1;
3163 rettv->v_lock = FALSE;
3164 }
3165 else
3166 copy_tv(&argvars[0], rettv);
3167 }
3168}
3169
3170/*
Bram Moolenaarb0e6b512021-01-12 20:23:40 +01003171 * "extend()" or "extendnew()" function. "is_new" is TRUE for extendnew().
Bram Moolenaar08c308a2019-09-04 17:48:15 +02003172 */
Bram Moolenaarb0e6b512021-01-12 20:23:40 +01003173 static void
3174extend(typval_T *argvars, typval_T *rettv, char_u *arg_errmsg, int is_new)
Bram Moolenaar08c308a2019-09-04 17:48:15 +02003175{
Bram Moolenaarc03f5c62021-01-31 17:48:30 +01003176 type_T *type = NULL;
3177 garray_T type_list;
Bram Moolenaar7a3fe3e2021-07-22 14:58:47 +02003178 char *func_name = is_new ? "extendnew()" : "extend()";
Bram Moolenaarc03f5c62021-01-31 17:48:30 +01003179
3180 if (!is_new && in_vim9script())
3181 {
3182 // Check that map() does not change the type of the dict.
3183 ga_init2(&type_list, sizeof(type_T *), 10);
Bram Moolenaarf2253962021-04-13 20:53:13 +02003184 type = typval2type(argvars, get_copyID(), &type_list, TRUE);
Bram Moolenaarc03f5c62021-01-31 17:48:30 +01003185 }
3186
Bram Moolenaar08c308a2019-09-04 17:48:15 +02003187 if (argvars[0].v_type == VAR_LIST && argvars[1].v_type == VAR_LIST)
Yegappan Lakshmanand92813a2021-12-21 13:19:42 +00003188 extend_list(argvars, type, func_name, arg_errmsg, is_new, rettv);
Bram Moolenaar08c308a2019-09-04 17:48:15 +02003189 else if (argvars[0].v_type == VAR_DICT && argvars[1].v_type == VAR_DICT)
Yegappan Lakshmanand92813a2021-12-21 13:19:42 +00003190 extend_dict(argvars, type, func_name, arg_errmsg, is_new, rettv);
Bram Moolenaar08c308a2019-09-04 17:48:15 +02003191 else
Bram Moolenaar7a3fe3e2021-07-22 14:58:47 +02003192 semsg(_(e_listdictarg), func_name);
Bram Moolenaarc03f5c62021-01-31 17:48:30 +01003193
Bram Moolenaarc03f5c62021-01-31 17:48:30 +01003194 if (type != NULL)
3195 clear_type_list(&type_list);
Bram Moolenaarb0e6b512021-01-12 20:23:40 +01003196}
3197
3198/*
3199 * "extend(list, list [, idx])" function
3200 * "extend(dict, dict [, action])" function
3201 */
3202 void
3203f_extend(typval_T *argvars, typval_T *rettv)
3204{
3205 char_u *errmsg = (char_u *)N_("extend() argument");
3206
3207 extend(argvars, rettv, errmsg, FALSE);
3208}
3209
3210/*
3211 * "extendnew(list, list [, idx])" function
3212 * "extendnew(dict, dict [, action])" function
3213 */
3214 void
3215f_extendnew(typval_T *argvars, typval_T *rettv)
3216{
3217 char_u *errmsg = (char_u *)N_("extendnew() argument");
3218
3219 extend(argvars, rettv, errmsg, TRUE);
Bram Moolenaar08c308a2019-09-04 17:48:15 +02003220}
3221
3222/*
3223 * "insert()" function
3224 */
3225 void
3226f_insert(typval_T *argvars, typval_T *rettv)
3227{
3228 long before = 0;
3229 listitem_T *item;
Bram Moolenaar08c308a2019-09-04 17:48:15 +02003230 int error = FALSE;
3231
Yegappan Lakshmanan0ad871d2021-07-23 20:37:56 +02003232 if (in_vim9script()
3233 && (check_for_list_or_blob_arg(argvars, 0) == FAIL
3234 || (argvars[0].v_type == VAR_BLOB
3235 && check_for_number_arg(argvars, 1) == FAIL)
3236 || check_for_opt_number_arg(argvars, 2) == FAIL))
3237 return;
3238
Bram Moolenaar08c308a2019-09-04 17:48:15 +02003239 if (argvars[0].v_type == VAR_BLOB)
3240 {
Sean Dewar80d73952021-08-04 19:25:54 +02003241 blob_T *b = argvars[0].vval.v_blob;
Bram Moolenaar08c308a2019-09-04 17:48:15 +02003242
Sean Dewar80d73952021-08-04 19:25:54 +02003243 if (b == NULL)
Bram Moolenaar39211cb2021-04-18 15:48:04 +02003244 {
3245 if (in_vim9script())
3246 emsg(_(e_cannot_add_to_null_blob));
Bram Moolenaar39211cb2021-04-18 15:48:04 +02003247 }
Sean Dewar80d73952021-08-04 19:25:54 +02003248 else if (!value_check_lock(b->bv_lock,
3249 (char_u *)N_("insert() argument"), TRUE))
Bram Moolenaar08c308a2019-09-04 17:48:15 +02003250 {
Sean Dewar80d73952021-08-04 19:25:54 +02003251 int val, len;
3252 char_u *p;
3253
3254 len = blob_len(b);
3255 if (argvars[2].v_type != VAR_UNKNOWN)
Bram Moolenaar08c308a2019-09-04 17:48:15 +02003256 {
Sean Dewar80d73952021-08-04 19:25:54 +02003257 before = (long)tv_get_number_chk(&argvars[2], &error);
3258 if (error)
3259 return; // type error; errmsg already given
3260 if (before < 0 || before > len)
3261 {
3262 semsg(_(e_invarg2), tv_get_string(&argvars[2]));
3263 return;
3264 }
3265 }
3266 val = tv_get_number_chk(&argvars[1], &error);
3267 if (error)
3268 return;
3269 if (val < 0 || val > 255)
3270 {
3271 semsg(_(e_invarg2), tv_get_string(&argvars[1]));
Bram Moolenaar08c308a2019-09-04 17:48:15 +02003272 return;
3273 }
Bram Moolenaar08c308a2019-09-04 17:48:15 +02003274
Sean Dewar80d73952021-08-04 19:25:54 +02003275 if (ga_grow(&b->bv_ga, 1) == FAIL)
3276 return;
3277 p = (char_u *)b->bv_ga.ga_data;
3278 mch_memmove(p + before + 1, p + before, (size_t)len - before);
3279 *(p + before) = val;
3280 ++b->bv_ga.ga_len;
Bram Moolenaar08c308a2019-09-04 17:48:15 +02003281
Sean Dewar80d73952021-08-04 19:25:54 +02003282 copy_tv(&argvars[0], rettv);
3283 }
Bram Moolenaar08c308a2019-09-04 17:48:15 +02003284 }
3285 else if (argvars[0].v_type != VAR_LIST)
3286 semsg(_(e_listblobarg), "insert()");
Bram Moolenaar39211cb2021-04-18 15:48:04 +02003287 else
Bram Moolenaar08c308a2019-09-04 17:48:15 +02003288 {
Bram Moolenaar39211cb2021-04-18 15:48:04 +02003289 list_T *l = argvars[0].vval.v_list;
Bram Moolenaar08c308a2019-09-04 17:48:15 +02003290
Bram Moolenaar39211cb2021-04-18 15:48:04 +02003291 if (l == NULL)
Bram Moolenaar08c308a2019-09-04 17:48:15 +02003292 {
Bram Moolenaar39211cb2021-04-18 15:48:04 +02003293 if (in_vim9script())
3294 emsg(_(e_cannot_add_to_null_list));
Bram Moolenaar08c308a2019-09-04 17:48:15 +02003295 }
Bram Moolenaar39211cb2021-04-18 15:48:04 +02003296 else if (!value_check_lock(l->lv_lock,
3297 (char_u *)N_("insert() argument"), TRUE))
Bram Moolenaar08c308a2019-09-04 17:48:15 +02003298 {
Bram Moolenaar39211cb2021-04-18 15:48:04 +02003299 if (argvars[2].v_type != VAR_UNKNOWN)
3300 before = (long)tv_get_number_chk(&argvars[2], &error);
3301 if (error)
3302 return; // type error; errmsg already given
3303
3304 if (before == l->lv_len)
3305 item = NULL;
3306 else
3307 {
3308 item = list_find(l, before);
3309 if (item == NULL)
3310 {
3311 semsg(_(e_listidx), before);
3312 l = NULL;
3313 }
3314 }
3315 if (l != NULL)
3316 {
3317 (void)list_insert_tv(l, &argvars[1], item);
3318 copy_tv(&argvars[0], rettv);
3319 }
Bram Moolenaar08c308a2019-09-04 17:48:15 +02003320 }
3321 }
3322}
3323
3324/*
3325 * "remove()" function
3326 */
3327 void
3328f_remove(typval_T *argvars, typval_T *rettv)
3329{
3330 char_u *arg_errmsg = (char_u *)N_("remove() argument");
3331
Yegappan Lakshmanan0ad871d2021-07-23 20:37:56 +02003332 if (in_vim9script()
3333 && (check_for_list_or_dict_or_blob_arg(argvars, 0) == FAIL
3334 || ((argvars[0].v_type == VAR_LIST
3335 || argvars[0].v_type == VAR_BLOB)
3336 && (check_for_number_arg(argvars, 1) == FAIL
3337 || check_for_opt_number_arg(argvars, 2) == FAIL))
3338 || (argvars[0].v_type == VAR_DICT
3339 && check_for_string_or_number_arg(argvars, 1) == FAIL)))
3340 return;
3341
Bram Moolenaar08c308a2019-09-04 17:48:15 +02003342 if (argvars[0].v_type == VAR_DICT)
3343 dict_remove(argvars, rettv, arg_errmsg);
3344 else if (argvars[0].v_type == VAR_BLOB)
Sean Dewar80d73952021-08-04 19:25:54 +02003345 blob_remove(argvars, rettv, arg_errmsg);
Bram Moolenaar08c308a2019-09-04 17:48:15 +02003346 else if (argvars[0].v_type == VAR_LIST)
3347 list_remove(argvars, rettv, arg_errmsg);
3348 else
3349 semsg(_(e_listdictblobarg), "remove()");
3350}
3351
3352/*
3353 * "reverse({list})" function
3354 */
3355 void
3356f_reverse(typval_T *argvars, typval_T *rettv)
3357{
3358 list_T *l;
3359 listitem_T *li, *ni;
3360
Yegappan Lakshmanan0ad871d2021-07-23 20:37:56 +02003361 if (in_vim9script() && check_for_list_or_blob_arg(argvars, 0) == FAIL)
3362 return;
3363
Bram Moolenaar08c308a2019-09-04 17:48:15 +02003364 if (argvars[0].v_type == VAR_BLOB)
3365 {
3366 blob_T *b = argvars[0].vval.v_blob;
3367 int i, len = blob_len(b);
3368
3369 for (i = 0; i < len / 2; i++)
3370 {
3371 int tmp = blob_get(b, i);
3372
3373 blob_set(b, i, blob_get(b, len - i - 1));
3374 blob_set(b, len - i - 1, tmp);
3375 }
3376 rettv_blob_set(rettv, b);
3377 return;
3378 }
3379
3380 if (argvars[0].v_type != VAR_LIST)
3381 semsg(_(e_listblobarg), "reverse()");
Bram Moolenaaref982572021-08-12 19:27:57 +02003382 else
3383 {
3384 l = argvars[0].vval.v_list;
3385 rettv_list_set(rettv, l);
3386 if (l != NULL
Bram Moolenaara187c432020-09-16 21:08:28 +02003387 && !value_check_lock(l->lv_lock,
Bram Moolenaar08c308a2019-09-04 17:48:15 +02003388 (char_u *)N_("reverse() argument"), TRUE))
Bram Moolenaar89bfc822020-01-27 22:37:23 +01003389 {
Bram Moolenaaref982572021-08-12 19:27:57 +02003390 if (l->lv_first == &range_list_item)
3391 {
3392 varnumber_T new_start = l->lv_u.nonmat.lv_start
Bram Moolenaar0ff6aad2020-01-29 21:27:21 +01003393 + (l->lv_len - 1) * l->lv_u.nonmat.lv_stride;
Bram Moolenaaref982572021-08-12 19:27:57 +02003394 l->lv_u.nonmat.lv_end = new_start
Bram Moolenaar0ff6aad2020-01-29 21:27:21 +01003395 - (l->lv_u.nonmat.lv_end - l->lv_u.nonmat.lv_start);
Bram Moolenaaref982572021-08-12 19:27:57 +02003396 l->lv_u.nonmat.lv_start = new_start;
3397 l->lv_u.nonmat.lv_stride = -l->lv_u.nonmat.lv_stride;
3398 return;
3399 }
3400 li = l->lv_u.mat.lv_last;
3401 l->lv_first = l->lv_u.mat.lv_last = NULL;
3402 l->lv_len = 0;
3403 while (li != NULL)
3404 {
3405 ni = li->li_prev;
3406 list_append(l, li);
3407 li = ni;
3408 }
3409 l->lv_u.mat.lv_idx = l->lv_len - l->lv_u.mat.lv_idx - 1;
Bram Moolenaar89bfc822020-01-27 22:37:23 +01003410 }
Bram Moolenaar08c308a2019-09-04 17:48:15 +02003411 }
3412}
3413
Bram Moolenaar85629982020-06-01 18:39:20 +02003414/*
Yegappan Lakshmanand92813a2021-12-21 13:19:42 +00003415 * reduce() List argvars[0] using the function 'funcname' with arguments in
3416 * 'funcexe' starting with the initial value argvars[2] and return the result
3417 * in 'rettv'.
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00003418 */
3419 static void
3420reduce_list(
3421 typval_T *argvars,
3422 char_u *func_name,
3423 funcexe_T *funcexe,
3424 typval_T *rettv)
3425{
3426 list_T *l = argvars[0].vval.v_list;
3427 listitem_T *li = NULL;
3428 typval_T initial;
3429 typval_T argv[3];
3430 int r;
3431 int called_emsg_start = called_emsg;
3432 int prev_locked;
3433
3434 if (l != NULL)
3435 CHECK_LIST_MATERIALIZE(l);
3436 if (argvars[2].v_type == VAR_UNKNOWN)
3437 {
3438 if (l == NULL || l->lv_first == NULL)
3439 {
3440 semsg(_(e_reduceempty), "List");
3441 return;
3442 }
3443 initial = l->lv_first->li_tv;
3444 li = l->lv_first->li_next;
3445 }
3446 else
3447 {
3448 initial = argvars[2];
3449 if (l != NULL)
3450 li = l->lv_first;
3451 }
3452 copy_tv(&initial, rettv);
3453
3454 if (l == NULL)
3455 return;
3456
3457 prev_locked = l->lv_lock;
3458
3459 l->lv_lock = VAR_FIXED; // disallow the list changing here
3460 for ( ; li != NULL; li = li->li_next)
3461 {
3462 argv[0] = *rettv;
3463 argv[1] = li->li_tv;
3464 rettv->v_type = VAR_UNKNOWN;
3465 r = call_func(func_name, -1, rettv, 2, argv, funcexe);
3466 clear_tv(&argv[0]);
3467 if (r == FAIL || called_emsg != called_emsg_start)
3468 break;
3469 }
3470 l->lv_lock = prev_locked;
3471}
3472
3473/*
Yegappan Lakshmanand92813a2021-12-21 13:19:42 +00003474 * reduce() String argvars[0] using the function 'funcname' with arguments in
3475 * 'funcexe' starting with the initial value argvars[2] and return the result
3476 * in 'rettv'.
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00003477 */
3478 static void
3479reduce_string(
3480 typval_T *argvars,
3481 char_u *func_name,
3482 funcexe_T *funcexe,
3483 typval_T *rettv)
3484{
3485 char_u *p = tv_get_string(&argvars[0]);
3486 int len;
3487 typval_T argv[3];
3488 int r;
3489 int called_emsg_start = called_emsg;
3490
3491 if (argvars[2].v_type == VAR_UNKNOWN)
3492 {
3493 if (*p == NUL)
3494 {
3495 semsg(_(e_reduceempty), "String");
3496 return;
3497 }
3498 if (tv_get_first_char(p, rettv) == FAIL)
3499 return;
3500 p += STRLEN(rettv->vval.v_string);
3501 }
3502 else if (argvars[2].v_type != VAR_STRING)
3503 {
3504 semsg(_(e_string_expected_for_argument_nr), 3);
3505 return;
3506 }
3507 else
3508 copy_tv(&argvars[2], rettv);
3509
3510 for ( ; *p != NUL; p += len)
3511 {
3512 argv[0] = *rettv;
3513 if (tv_get_first_char(p, &argv[1]) == FAIL)
3514 break;
3515 len = (int)STRLEN(argv[1].vval.v_string);
3516 r = call_func(func_name, -1, rettv, 2, argv, funcexe);
3517 clear_tv(&argv[0]);
3518 clear_tv(&argv[1]);
3519 if (r == FAIL || called_emsg != called_emsg_start)
3520 return;
3521 }
3522}
3523
3524/*
Yegappan Lakshmanand92813a2021-12-21 13:19:42 +00003525 * reduce() Blob argvars[0] using the function 'funcname' with arguments in
3526 * 'funcexe' starting with the initial value argvars[2] and return the result
3527 * in 'rettv'.
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00003528 */
3529 static void
3530reduce_blob(
3531 typval_T *argvars,
3532 char_u *func_name,
3533 funcexe_T *funcexe,
3534 typval_T *rettv)
3535{
3536 blob_T *b = argvars[0].vval.v_blob;
3537 int called_emsg_start = called_emsg;
3538 int r;
3539 typval_T initial;
3540 typval_T argv[3];
3541 int i;
3542
3543 if (argvars[2].v_type == VAR_UNKNOWN)
3544 {
3545 if (b == NULL || b->bv_ga.ga_len == 0)
3546 {
3547 semsg(_(e_reduceempty), "Blob");
3548 return;
3549 }
3550 initial.v_type = VAR_NUMBER;
3551 initial.vval.v_number = blob_get(b, 0);
3552 i = 1;
3553 }
3554 else if (argvars[2].v_type != VAR_NUMBER)
3555 {
3556 emsg(_(e_number_expected));
3557 return;
3558 }
3559 else
3560 {
3561 initial = argvars[2];
3562 i = 0;
3563 }
3564
3565 copy_tv(&initial, rettv);
3566 if (b == NULL)
3567 return;
3568
3569 for ( ; i < b->bv_ga.ga_len; i++)
3570 {
3571 argv[0] = *rettv;
3572 argv[1].v_type = VAR_NUMBER;
3573 argv[1].vval.v_number = blob_get(b, i);
3574 r = call_func(func_name, -1, rettv, 2, argv, funcexe);
3575 clear_tv(&argv[0]);
3576 if (r == FAIL || called_emsg != called_emsg_start)
3577 return;
3578 }
3579}
3580
3581/*
Bram Moolenaar8e7d6222020-12-18 19:49:56 +01003582 * "reduce(list, { accumulator, element -> value } [, initial])" function
Yegappan Lakshmanand92813a2021-12-21 13:19:42 +00003583 * "reduce(blob, { accumulator, element -> value } [, initial])"
3584 * "reduce(string, { accumulator, element -> value } [, initial])"
Bram Moolenaar85629982020-06-01 18:39:20 +02003585 */
3586 void
3587f_reduce(typval_T *argvars, typval_T *rettv)
3588{
Bram Moolenaar85629982020-06-01 18:39:20 +02003589 char_u *func_name;
3590 partial_T *partial = NULL;
3591 funcexe_T funcexe;
Bram Moolenaar85629982020-06-01 18:39:20 +02003592
rbtnn0ccb5842021-12-18 18:33:46 +00003593 if (in_vim9script()
3594 && check_for_string_or_list_or_blob_arg(argvars, 0) == FAIL)
Bram Moolenaar85629982020-06-01 18:39:20 +02003595 return;
rbtnn0ccb5842021-12-18 18:33:46 +00003596
3597 if (argvars[0].v_type != VAR_STRING
3598 && argvars[0].v_type != VAR_LIST
3599 && argvars[0].v_type != VAR_BLOB)
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00003600 {
rbtnn0ccb5842021-12-18 18:33:46 +00003601 semsg(_(e_string_list_or_blob_required), "reduce()");
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00003602 return;
3603 }
Bram Moolenaar85629982020-06-01 18:39:20 +02003604
3605 if (argvars[1].v_type == VAR_FUNC)
3606 func_name = argvars[1].vval.v_string;
3607 else if (argvars[1].v_type == VAR_PARTIAL)
3608 {
3609 partial = argvars[1].vval.v_partial;
3610 func_name = partial_name(partial);
3611 }
3612 else
3613 func_name = tv_get_string(&argvars[1]);
Bram Moolenaar0d90e722020-11-03 18:20:19 +01003614 if (func_name == NULL || *func_name == NUL)
3615 {
3616 emsg(_(e_missing_function_argument));
3617 return;
3618 }
Bram Moolenaar85629982020-06-01 18:39:20 +02003619
Bram Moolenaar851f86b2021-12-13 14:26:44 +00003620 CLEAR_FIELD(funcexe);
3621 funcexe.fe_evaluate = TRUE;
3622 funcexe.fe_partial = partial;
Bram Moolenaar85629982020-06-01 18:39:20 +02003623
3624 if (argvars[0].v_type == VAR_LIST)
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00003625 reduce_list(argvars, func_name, &funcexe, rettv);
rbtnn0ccb5842021-12-18 18:33:46 +00003626 else if (argvars[0].v_type == VAR_STRING)
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00003627 reduce_string(argvars, func_name, &funcexe, rettv);
Bram Moolenaar85629982020-06-01 18:39:20 +02003628 else
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00003629 reduce_blob(argvars, func_name, &funcexe, rettv);
Bram Moolenaar85629982020-06-01 18:39:20 +02003630}
3631
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02003632#endif // defined(FEAT_EVAL)