blob: 991be8b01fa3935f8806a0dee7c8ef1a9e853f07 [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 Moolenaar4ba37b52019-12-04 21:57:43 +010018// List heads for garbage collection.
19static list_T *first_list = NULL; // list of all lists
Bram Moolenaarda861d62016-07-17 15:46:27 +020020
Bram Moolenaar00d253e2020-04-06 22:13:01 +020021#define FOR_ALL_WATCHERS(l, lw) \
22 for ((lw) = (l)->lv_watch; (lw) != NULL; (lw) = (lw)->lw_next)
23
Bram Moolenaarbdff0122020-04-05 18:56:05 +020024static void list_free_item(list_T *l, listitem_T *item);
25
Bram Moolenaarda861d62016-07-17 15:46:27 +020026/*
27 * Add a watcher to a list.
28 */
29 void
30list_add_watch(list_T *l, listwatch_T *lw)
31{
32 lw->lw_next = l->lv_watch;
33 l->lv_watch = lw;
34}
35
36/*
37 * Remove a watcher from a list.
38 * No warning when it isn't found...
39 */
40 void
41list_rem_watch(list_T *l, listwatch_T *lwrem)
42{
43 listwatch_T *lw, **lwp;
44
45 lwp = &l->lv_watch;
Bram Moolenaar00d253e2020-04-06 22:13:01 +020046 FOR_ALL_WATCHERS(l, lw)
Bram Moolenaarda861d62016-07-17 15:46:27 +020047 {
48 if (lw == lwrem)
49 {
50 *lwp = lw->lw_next;
51 break;
52 }
53 lwp = &lw->lw_next;
54 }
55}
56
57/*
58 * Just before removing an item from a list: advance watchers to the next
59 * item.
60 */
Bram Moolenaar5843f5f2019-08-20 20:13:45 +020061 static void
Bram Moolenaarda861d62016-07-17 15:46:27 +020062list_fix_watch(list_T *l, listitem_T *item)
63{
64 listwatch_T *lw;
65
Bram Moolenaar00d253e2020-04-06 22:13:01 +020066 FOR_ALL_WATCHERS(l, lw)
Bram Moolenaarda861d62016-07-17 15:46:27 +020067 if (lw->lw_item == item)
68 lw->lw_item = item->li_next;
69}
70
Bram Moolenaar8a7d6542020-01-26 15:56:19 +010071 static void
72list_init(list_T *l)
73{
74 // Prepend the list to the list of lists for garbage collection.
75 if (first_list != NULL)
76 first_list->lv_used_prev = l;
77 l->lv_used_prev = NULL;
78 l->lv_used_next = first_list;
79 first_list = l;
80}
81
Bram Moolenaarda861d62016-07-17 15:46:27 +020082/*
83 * Allocate an empty header for a list.
84 * Caller should take care of the reference count.
85 */
86 list_T *
87list_alloc(void)
88{
89 list_T *l;
90
Bram Moolenaarc799fe22019-05-28 23:08:19 +020091 l = ALLOC_CLEAR_ONE(list_T);
Bram Moolenaarda861d62016-07-17 15:46:27 +020092 if (l != NULL)
Bram Moolenaar8a7d6542020-01-26 15:56:19 +010093 list_init(l);
Bram Moolenaarda861d62016-07-17 15:46:27 +020094 return l;
95}
96
97/*
Bram Moolenaarf49cc602018-11-11 15:21:05 +010098 * list_alloc() with an ID for alloc_fail().
99 */
100 list_T *
101list_alloc_id(alloc_id_T id UNUSED)
102{
103#ifdef FEAT_EVAL
Bram Moolenaar51e14382019-05-25 20:21:28 +0200104 if (alloc_fail_id == id && alloc_does_fail(sizeof(list_T)))
Bram Moolenaarf49cc602018-11-11 15:21:05 +0100105 return NULL;
106#endif
107 return (list_alloc());
108}
109
110/*
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100111 * Allocate space for a list, plus "count" items.
Bram Moolenaar24fe33a2022-11-24 00:09:02 +0000112 * This uses one allocation for efficiency.
113 * The reference count is not set.
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100114 * 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));
Bram Moolenaar24fe33a2022-11-24 00:09:02 +0000122 if (l == NULL)
123 return NULL;
124
125 list_init(l);
126
Yegappan Lakshmanane8575982023-01-14 12:32:28 +0000127 if (count <= 0)
128 return l;
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100129
Yegappan Lakshmanane8575982023-01-14 12:32:28 +0000130 listitem_T *li = (listitem_T *)(l + 1);
131 int i;
132
133 l->lv_len = count;
134 l->lv_with_items = count;
135 l->lv_first = li;
136 l->lv_u.mat.lv_last = li + count - 1;
137 for (i = 0; i < count; ++i)
138 {
139 if (i == 0)
140 li->li_prev = NULL;
141 else
142 li->li_prev = li - 1;
143 if (i == count - 1)
144 li->li_next = NULL;
145 else
146 li->li_next = li + 1;
147 ++li;
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100148 }
Bram Moolenaar24fe33a2022-11-24 00:09:02 +0000149
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100150 return l;
151}
152
153/*
154 * Set item "idx" for a list previously allocated with list_alloc_with_items().
155 * The contents of "tv" is moved into the list item.
156 * Each item must be set exactly once.
157 */
158 void
159list_set_item(list_T *l, int idx, typval_T *tv)
160{
161 listitem_T *li = (listitem_T *)(l + 1) + idx;
162
163 li->li_tv = *tv;
164}
165
166/*
Bram Moolenaarda861d62016-07-17 15:46:27 +0200167 * Allocate an empty list for a return value, with reference count set.
168 * Returns OK or FAIL.
169 */
170 int
171rettv_list_alloc(typval_T *rettv)
172{
173 list_T *l = list_alloc();
174
175 if (l == NULL)
176 return FAIL;
177
Bram Moolenaarda861d62016-07-17 15:46:27 +0200178 rettv->v_lock = 0;
Bram Moolenaar45cf6e92017-04-30 20:25:19 +0200179 rettv_list_set(rettv, l);
Bram Moolenaarda861d62016-07-17 15:46:27 +0200180 return OK;
181}
182
183/*
Bram Moolenaar162b7142018-12-21 15:17:36 +0100184 * Same as rettv_list_alloc() but uses an allocation id for testing.
185 */
186 int
187rettv_list_alloc_id(typval_T *rettv, alloc_id_T id UNUSED)
188{
189#ifdef FEAT_EVAL
Bram Moolenaar51e14382019-05-25 20:21:28 +0200190 if (alloc_fail_id == id && alloc_does_fail(sizeof(list_T)))
Bram Moolenaar162b7142018-12-21 15:17:36 +0100191 return FAIL;
192#endif
193 return rettv_list_alloc(rettv);
194}
195
196
197/*
Bram Moolenaare809a4e2019-07-04 17:35:05 +0200198 * Set a list as the return value. Increments the reference count.
Bram Moolenaar45cf6e92017-04-30 20:25:19 +0200199 */
200 void
201rettv_list_set(typval_T *rettv, list_T *l)
202{
203 rettv->v_type = VAR_LIST;
204 rettv->vval.v_list = l;
205 if (l != NULL)
206 ++l->lv_refcount;
207}
208
209/*
Bram Moolenaarda861d62016-07-17 15:46:27 +0200210 * Unreference a list: decrement the reference count and free it when it
211 * becomes zero.
212 */
213 void
214list_unref(list_T *l)
215{
216 if (l != NULL && --l->lv_refcount <= 0)
217 list_free(l);
218}
219
220/*
221 * Free a list, including all non-container items it points to.
222 * Ignores the reference count.
223 */
224 static void
225list_free_contents(list_T *l)
226{
227 listitem_T *item;
228
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100229 if (l->lv_first != &range_list_item)
230 for (item = l->lv_first; item != NULL; item = l->lv_first)
231 {
232 // Remove the item before deleting it.
233 l->lv_first = item->li_next;
234 clear_tv(&item->li_tv);
235 list_free_item(l, item);
236 }
Bram Moolenaarda861d62016-07-17 15:46:27 +0200237}
238
239/*
240 * Go through the list of lists and free items without the copyID.
241 * But don't free a list that has a watcher (used in a for loop), these
242 * are not referenced anywhere.
243 */
244 int
245list_free_nonref(int copyID)
246{
247 list_T *ll;
248 int did_free = FALSE;
249
250 for (ll = first_list; ll != NULL; ll = ll->lv_used_next)
251 if ((ll->lv_copyID & COPYID_MASK) != (copyID & COPYID_MASK)
252 && ll->lv_watch == NULL)
253 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100254 // Free the List and ordinary items it contains, but don't recurse
255 // into Lists and Dictionaries, they will be in the list of dicts
256 // or list of lists.
Bram Moolenaarda861d62016-07-17 15:46:27 +0200257 list_free_contents(ll);
258 did_free = TRUE;
259 }
260 return did_free;
261}
262
263 static void
264list_free_list(list_T *l)
265{
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100266 // Remove the list from the list of lists for garbage collection.
Bram Moolenaarda861d62016-07-17 15:46:27 +0200267 if (l->lv_used_prev == NULL)
268 first_list = l->lv_used_next;
269 else
270 l->lv_used_prev->lv_used_next = l->lv_used_next;
271 if (l->lv_used_next != NULL)
272 l->lv_used_next->lv_used_prev = l->lv_used_prev;
273
Bram Moolenaaraa210a32021-01-02 15:41:03 +0100274 free_type(l->lv_type);
Bram Moolenaarda861d62016-07-17 15:46:27 +0200275 vim_free(l);
276}
277
278 void
279list_free_items(int copyID)
280{
281 list_T *ll, *ll_next;
282
283 for (ll = first_list; ll != NULL; ll = ll_next)
284 {
285 ll_next = ll->lv_used_next;
286 if ((ll->lv_copyID & COPYID_MASK) != (copyID & COPYID_MASK)
287 && ll->lv_watch == NULL)
288 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100289 // Free the List and ordinary items it contains, but don't recurse
290 // into Lists and Dictionaries, they will be in the list of dicts
291 // or list of lists.
Bram Moolenaarda861d62016-07-17 15:46:27 +0200292 list_free_list(ll);
293 }
294 }
295}
296
297 void
298list_free(list_T *l)
299{
Yegappan Lakshmanane8575982023-01-14 12:32:28 +0000300 if (in_free_unref_items)
301 return;
302
303 list_free_contents(l);
304 list_free_list(l);
Bram Moolenaarda861d62016-07-17 15:46:27 +0200305}
306
307/*
308 * Allocate a list item.
309 * It is not initialized, don't forget to set v_lock.
310 */
311 listitem_T *
312listitem_alloc(void)
313{
Bram Moolenaarc799fe22019-05-28 23:08:19 +0200314 return ALLOC_ONE(listitem_T);
Bram Moolenaarda861d62016-07-17 15:46:27 +0200315}
316
317/*
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100318 * Free a list item, unless it was allocated together with the list itself.
319 * Does not clear the value. Does not notify watchers.
Bram Moolenaarda861d62016-07-17 15:46:27 +0200320 */
Bram Moolenaarbdff0122020-04-05 18:56:05 +0200321 static void
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100322list_free_item(list_T *l, listitem_T *item)
323{
324 if (l->lv_with_items == 0 || item < (listitem_T *)l
325 || item >= (listitem_T *)(l + 1) + l->lv_with_items)
326 vim_free(item);
327}
328
329/*
330 * Free a list item, unless it was allocated together with the list itself.
331 * Also clears the value. Does not notify watchers.
332 */
333 void
334listitem_free(list_T *l, listitem_T *item)
Bram Moolenaarda861d62016-07-17 15:46:27 +0200335{
336 clear_tv(&item->li_tv);
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100337 list_free_item(l, item);
Bram Moolenaarda861d62016-07-17 15:46:27 +0200338}
339
340/*
341 * Remove a list item from a List and free it. Also clears the value.
342 */
343 void
344listitem_remove(list_T *l, listitem_T *item)
345{
346 vimlist_remove(l, item, item);
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100347 listitem_free(l, item);
Bram Moolenaarda861d62016-07-17 15:46:27 +0200348}
349
350/*
351 * Get the number of items in a list.
352 */
353 long
354list_len(list_T *l)
355{
356 if (l == NULL)
357 return 0L;
358 return l->lv_len;
359}
360
361/*
362 * Return TRUE when two lists have exactly the same values.
363 */
364 int
365list_equal(
366 list_T *l1,
367 list_T *l2,
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100368 int ic, // ignore case for strings
369 int recursive) // TRUE when used recursively
Bram Moolenaarda861d62016-07-17 15:46:27 +0200370{
371 listitem_T *item1, *item2;
372
Bram Moolenaarda861d62016-07-17 15:46:27 +0200373 if (l1 == l2)
374 return TRUE;
375 if (list_len(l1) != list_len(l2))
376 return FALSE;
Bram Moolenaar7b293c72020-04-09 21:33:22 +0200377 if (list_len(l1) == 0)
378 // empty and NULL list are considered equal
379 return TRUE;
380 if (l1 == NULL || l2 == NULL)
381 return FALSE;
Bram Moolenaarda861d62016-07-17 15:46:27 +0200382
Bram Moolenaar7e9f3512020-05-13 22:44:22 +0200383 CHECK_LIST_MATERIALIZE(l1);
384 CHECK_LIST_MATERIALIZE(l2);
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100385
Bram Moolenaarda861d62016-07-17 15:46:27 +0200386 for (item1 = l1->lv_first, item2 = l2->lv_first;
387 item1 != NULL && item2 != NULL;
388 item1 = item1->li_next, item2 = item2->li_next)
389 if (!tv_equal(&item1->li_tv, &item2->li_tv, ic, recursive))
390 return FALSE;
391 return item1 == NULL && item2 == NULL;
392}
393
394/*
395 * Locate item with index "n" in list "l" and return it.
396 * A negative index is counted from the end; -1 is the last item.
397 * Returns NULL when "n" is out of range.
398 */
399 listitem_T *
400list_find(list_T *l, long n)
401{
402 listitem_T *item;
403 long idx;
404
405 if (l == NULL)
406 return NULL;
407
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100408 // Negative index is relative to the end.
Bram Moolenaarda861d62016-07-17 15:46:27 +0200409 if (n < 0)
410 n = l->lv_len + n;
411
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100412 // Check for index out of range.
Bram Moolenaarda861d62016-07-17 15:46:27 +0200413 if (n < 0 || n >= l->lv_len)
414 return NULL;
415
Bram Moolenaar7e9f3512020-05-13 22:44:22 +0200416 CHECK_LIST_MATERIALIZE(l);
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100417
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100418 // When there is a cached index may start search from there.
Bram Moolenaar0ff6aad2020-01-29 21:27:21 +0100419 if (l->lv_u.mat.lv_idx_item != NULL)
Bram Moolenaarda861d62016-07-17 15:46:27 +0200420 {
Bram Moolenaar0ff6aad2020-01-29 21:27:21 +0100421 if (n < l->lv_u.mat.lv_idx / 2)
Bram Moolenaarda861d62016-07-17 15:46:27 +0200422 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100423 // closest to the start of the list
Bram Moolenaarda861d62016-07-17 15:46:27 +0200424 item = l->lv_first;
425 idx = 0;
426 }
Bram Moolenaar0ff6aad2020-01-29 21:27:21 +0100427 else if (n > (l->lv_u.mat.lv_idx + l->lv_len) / 2)
Bram Moolenaarda861d62016-07-17 15:46:27 +0200428 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100429 // closest to the end of the list
Bram Moolenaar0ff6aad2020-01-29 21:27:21 +0100430 item = l->lv_u.mat.lv_last;
Bram Moolenaarda861d62016-07-17 15:46:27 +0200431 idx = l->lv_len - 1;
432 }
433 else
434 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100435 // closest to the cached index
Bram Moolenaar0ff6aad2020-01-29 21:27:21 +0100436 item = l->lv_u.mat.lv_idx_item;
437 idx = l->lv_u.mat.lv_idx;
Bram Moolenaarda861d62016-07-17 15:46:27 +0200438 }
439 }
440 else
441 {
442 if (n < l->lv_len / 2)
443 {
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 }
448 else
449 {
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 }
455
456 while (n > idx)
457 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100458 // search forward
Bram Moolenaarda861d62016-07-17 15:46:27 +0200459 item = item->li_next;
460 ++idx;
461 }
462 while (n < idx)
463 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100464 // search backward
Bram Moolenaarda861d62016-07-17 15:46:27 +0200465 item = item->li_prev;
466 --idx;
467 }
468
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100469 // cache the used index
Bram Moolenaar0ff6aad2020-01-29 21:27:21 +0100470 l->lv_u.mat.lv_idx = idx;
471 l->lv_u.mat.lv_idx_item = item;
Bram Moolenaarda861d62016-07-17 15:46:27 +0200472
473 return item;
474}
475
476/*
477 * Get list item "l[idx]" as a number.
478 */
479 long
480list_find_nr(
481 list_T *l,
482 long idx,
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100483 int *errorp) // set to TRUE when something wrong
Bram Moolenaarda861d62016-07-17 15:46:27 +0200484{
485 listitem_T *li;
486
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100487 if (l != NULL && l->lv_first == &range_list_item)
488 {
489 long n = idx;
490
491 // not materialized range() list: compute the value.
492 // Negative index is relative to the end.
493 if (n < 0)
494 n = l->lv_len + n;
495
496 // Check for index out of range.
497 if (n < 0 || n >= l->lv_len)
498 {
499 if (errorp != NULL)
500 *errorp = TRUE;
501 return -1L;
502 }
503
Bram Moolenaar0ff6aad2020-01-29 21:27:21 +0100504 return l->lv_u.nonmat.lv_start + n * l->lv_u.nonmat.lv_stride;
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100505 }
506
Bram Moolenaarda861d62016-07-17 15:46:27 +0200507 li = list_find(l, idx);
508 if (li == NULL)
509 {
510 if (errorp != NULL)
511 *errorp = TRUE;
512 return -1L;
513 }
Bram Moolenaard155d7a2018-12-21 16:04:21 +0100514 return (long)tv_get_number_chk(&li->li_tv, errorp);
Bram Moolenaarda861d62016-07-17 15:46:27 +0200515}
516
517/*
518 * Get list item "l[idx - 1]" as a string. Returns NULL for failure.
519 */
520 char_u *
521list_find_str(list_T *l, long idx)
522{
523 listitem_T *li;
524
525 li = list_find(l, idx - 1);
526 if (li == NULL)
527 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +0000528 semsg(_(e_list_index_out_of_range_nr), idx);
Bram Moolenaarda861d62016-07-17 15:46:27 +0200529 return NULL;
530 }
Bram Moolenaard155d7a2018-12-21 16:04:21 +0100531 return tv_get_string(&li->li_tv);
Bram Moolenaarda861d62016-07-17 15:46:27 +0200532}
533
534/*
Bram Moolenaar5b5ae292021-02-20 17:04:02 +0100535 * Like list_find() but when a negative index is used that is not found use
536 * zero and set "idx" to zero. Used for first index of a range.
537 */
538 listitem_T *
539list_find_index(list_T *l, long *idx)
540{
541 listitem_T *li = list_find(l, *idx);
542
Yegappan Lakshmanane8575982023-01-14 12:32:28 +0000543 if (li != NULL)
544 return li;
545
546 if (*idx < 0)
Bram Moolenaar5b5ae292021-02-20 17:04:02 +0100547 {
Yegappan Lakshmanane8575982023-01-14 12:32:28 +0000548 *idx = 0;
549 li = list_find(l, *idx);
Bram Moolenaar5b5ae292021-02-20 17:04:02 +0100550 }
551 return li;
552}
553
554/*
Bram Moolenaarda861d62016-07-17 15:46:27 +0200555 * Locate "item" list "l" and return its index.
556 * Returns -1 when "item" is not in the list.
557 */
558 long
559list_idx_of_item(list_T *l, listitem_T *item)
560{
561 long idx = 0;
562 listitem_T *li;
563
564 if (l == NULL)
565 return -1;
Bram Moolenaar7e9f3512020-05-13 22:44:22 +0200566 CHECK_LIST_MATERIALIZE(l);
Bram Moolenaarda861d62016-07-17 15:46:27 +0200567 idx = 0;
568 for (li = l->lv_first; li != NULL && li != item; li = li->li_next)
569 ++idx;
570 if (li == NULL)
571 return -1;
572 return idx;
573}
574
575/*
576 * Append item "item" to the end of list "l".
577 */
578 void
579list_append(list_T *l, listitem_T *item)
580{
Bram Moolenaar7e9f3512020-05-13 22:44:22 +0200581 CHECK_LIST_MATERIALIZE(l);
Bram Moolenaar0ff6aad2020-01-29 21:27:21 +0100582 if (l->lv_u.mat.lv_last == NULL)
Bram Moolenaarda861d62016-07-17 15:46:27 +0200583 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100584 // empty list
Bram Moolenaarda861d62016-07-17 15:46:27 +0200585 l->lv_first = item;
Bram Moolenaarda861d62016-07-17 15:46:27 +0200586 item->li_prev = NULL;
587 }
588 else
589 {
Bram Moolenaar0ff6aad2020-01-29 21:27:21 +0100590 l->lv_u.mat.lv_last->li_next = item;
591 item->li_prev = l->lv_u.mat.lv_last;
Bram Moolenaarda861d62016-07-17 15:46:27 +0200592 }
Bram Moolenaar35c807d2022-01-27 16:36:29 +0000593 l->lv_u.mat.lv_last = item;
Bram Moolenaarda861d62016-07-17 15:46:27 +0200594 ++l->lv_len;
595 item->li_next = NULL;
596}
597
598/*
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100599 * Append typval_T "tv" to the end of list "l". "tv" is copied.
Bram Moolenaarf32f0992021-07-08 20:53:40 +0200600 * Return FAIL when out of memory or the type is wrong.
Bram Moolenaarda861d62016-07-17 15:46:27 +0200601 */
602 int
603list_append_tv(list_T *l, typval_T *tv)
604{
Bram Moolenaar90fba562021-07-09 19:17:55 +0200605 listitem_T *li;
Bram Moolenaarda861d62016-07-17 15:46:27 +0200606
Bram Moolenaarf32f0992021-07-08 20:53:40 +0200607 if (l->lv_type != NULL && l->lv_type->tt_member != NULL
Bram Moolenaar7a3fe3e2021-07-22 14:58:47 +0200608 && check_typval_arg_type(l->lv_type->tt_member, tv,
609 NULL, 0) == FAIL)
Bram Moolenaarf32f0992021-07-08 20:53:40 +0200610 return FAIL;
Bram Moolenaar90fba562021-07-09 19:17:55 +0200611 li = listitem_alloc();
Bram Moolenaarda861d62016-07-17 15:46:27 +0200612 if (li == NULL)
613 return FAIL;
614 copy_tv(tv, &li->li_tv);
615 list_append(l, li);
616 return OK;
617}
618
619/*
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100620 * As list_append_tv() but move the value instead of copying it.
621 * Return FAIL when out of memory.
622 */
Yegappan Lakshmanan8ee52af2021-08-09 19:59:06 +0200623 static int
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100624list_append_tv_move(list_T *l, typval_T *tv)
625{
626 listitem_T *li = listitem_alloc();
627
628 if (li == NULL)
629 return FAIL;
630 li->li_tv = *tv;
631 list_append(l, li);
632 return OK;
633}
634
635/*
Bram Moolenaarda861d62016-07-17 15:46:27 +0200636 * Add a dictionary to a list. Used by getqflist().
637 * Return FAIL when out of memory.
638 */
639 int
640list_append_dict(list_T *list, dict_T *dict)
641{
642 listitem_T *li = listitem_alloc();
643
644 if (li == NULL)
645 return FAIL;
646 li->li_tv.v_type = VAR_DICT;
647 li->li_tv.v_lock = 0;
648 li->li_tv.vval.v_dict = dict;
649 list_append(list, li);
650 ++dict->dv_refcount;
651 return OK;
652}
653
654/*
Bram Moolenaar4f505882018-02-10 21:06:32 +0100655 * Append list2 to list1.
656 * Return FAIL when out of memory.
657 */
658 int
Bram Moolenaar6f8d2ac2018-07-25 19:49:45 +0200659list_append_list(list_T *list1, list_T *list2)
Bram Moolenaar4f505882018-02-10 21:06:32 +0100660{
661 listitem_T *li = listitem_alloc();
662
663 if (li == NULL)
664 return FAIL;
665 li->li_tv.v_type = VAR_LIST;
666 li->li_tv.v_lock = 0;
667 li->li_tv.vval.v_list = list2;
668 list_append(list1, li);
669 ++list2->lv_refcount;
670 return OK;
671}
672
673/*
Bram Moolenaarda861d62016-07-17 15:46:27 +0200674 * Make a copy of "str" and append it as an item to list "l".
675 * When "len" >= 0 use "str[len]".
676 * Returns FAIL when out of memory.
677 */
678 int
679list_append_string(list_T *l, char_u *str, int len)
680{
681 listitem_T *li = listitem_alloc();
682
683 if (li == NULL)
684 return FAIL;
685 list_append(l, li);
686 li->li_tv.v_type = VAR_STRING;
687 li->li_tv.v_lock = 0;
688 if (str == NULL)
689 li->li_tv.vval.v_string = NULL;
690 else if ((li->li_tv.vval.v_string = (len >= 0 ? vim_strnsave(str, len)
691 : vim_strsave(str))) == NULL)
692 return FAIL;
693 return OK;
694}
695
696/*
697 * Append "n" to list "l".
698 * Returns FAIL when out of memory.
699 */
700 int
701list_append_number(list_T *l, varnumber_T n)
702{
703 listitem_T *li;
704
705 li = listitem_alloc();
706 if (li == NULL)
707 return FAIL;
708 li->li_tv.v_type = VAR_NUMBER;
709 li->li_tv.v_lock = 0;
710 li->li_tv.vval.v_number = n;
711 list_append(l, li);
712 return OK;
713}
714
715/*
716 * Insert typval_T "tv" in list "l" before "item".
717 * If "item" is NULL append at the end.
Bram Moolenaaraa210a32021-01-02 15:41:03 +0100718 * Return FAIL when out of memory or the type is wrong.
Bram Moolenaarda861d62016-07-17 15:46:27 +0200719 */
720 int
721list_insert_tv(list_T *l, typval_T *tv, listitem_T *item)
722{
Bram Moolenaaraa210a32021-01-02 15:41:03 +0100723 listitem_T *ni;
Bram Moolenaarda861d62016-07-17 15:46:27 +0200724
Bram Moolenaaraa210a32021-01-02 15:41:03 +0100725 if (l->lv_type != NULL && l->lv_type->tt_member != NULL
Bram Moolenaar7a3fe3e2021-07-22 14:58:47 +0200726 && check_typval_arg_type(l->lv_type->tt_member, tv,
727 NULL, 0) == FAIL)
Bram Moolenaaraa210a32021-01-02 15:41:03 +0100728 return FAIL;
729 ni = listitem_alloc();
Bram Moolenaarda861d62016-07-17 15:46:27 +0200730 if (ni == NULL)
731 return FAIL;
732 copy_tv(tv, &ni->li_tv);
733 list_insert(l, ni, item);
734 return OK;
735}
736
737 void
738list_insert(list_T *l, listitem_T *ni, listitem_T *item)
739{
Bram Moolenaar7e9f3512020-05-13 22:44:22 +0200740 CHECK_LIST_MATERIALIZE(l);
Bram Moolenaarda861d62016-07-17 15:46:27 +0200741 if (item == NULL)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100742 // Append new item at end of list.
Bram Moolenaarda861d62016-07-17 15:46:27 +0200743 list_append(l, ni);
744 else
745 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100746 // Insert new item before existing item.
Bram Moolenaarda861d62016-07-17 15:46:27 +0200747 ni->li_prev = item->li_prev;
748 ni->li_next = item;
749 if (item->li_prev == NULL)
750 {
751 l->lv_first = ni;
Bram Moolenaar0ff6aad2020-01-29 21:27:21 +0100752 ++l->lv_u.mat.lv_idx;
Bram Moolenaarda861d62016-07-17 15:46:27 +0200753 }
754 else
755 {
756 item->li_prev->li_next = ni;
Bram Moolenaar0ff6aad2020-01-29 21:27:21 +0100757 l->lv_u.mat.lv_idx_item = NULL;
Bram Moolenaarda861d62016-07-17 15:46:27 +0200758 }
759 item->li_prev = ni;
760 ++l->lv_len;
761 }
762}
763
764/*
Bram Moolenaar4f0884d2021-08-11 21:49:23 +0200765 * Get the list item in "l" with index "n1". "n1" is adjusted if needed.
Bram Moolenaar22ebd172022-04-01 15:26:58 +0100766 * In Vim9, it is at the end of the list, add an item if "can_append" is TRUE.
Bram Moolenaar4f0884d2021-08-11 21:49:23 +0200767 * Return NULL if there is no such item.
768 */
769 listitem_T *
Bram Moolenaar22ebd172022-04-01 15:26:58 +0100770check_range_index_one(list_T *l, long *n1, int can_append, int quiet)
Bram Moolenaar4f0884d2021-08-11 21:49:23 +0200771{
Bram Moolenaar22ebd172022-04-01 15:26:58 +0100772 long orig_n1 = *n1;
773 listitem_T *li = list_find_index(l, n1);
Bram Moolenaar4f0884d2021-08-11 21:49:23 +0200774
Yegappan Lakshmanane8575982023-01-14 12:32:28 +0000775 if (li != NULL)
776 return li;
777
778 // Vim9: Allow for adding an item at the end.
779 if (can_append && in_vim9script()
780 && *n1 == l->lv_len && l->lv_lock == 0)
781 {
782 list_append_number(l, 0);
783 li = list_find_index(l, n1);
784 }
Bram Moolenaar4f0884d2021-08-11 21:49:23 +0200785 if (li == NULL)
786 {
Yegappan Lakshmanane8575982023-01-14 12:32:28 +0000787 if (!quiet)
788 semsg(_(e_list_index_out_of_range_nr), orig_n1);
789 return NULL;
Bram Moolenaar4f0884d2021-08-11 21:49:23 +0200790 }
791 return li;
792}
793
794/*
795 * Check that "n2" can be used as the second index in a range of list "l".
796 * If "n1" or "n2" is negative it is changed to the positive index.
797 * "li1" is the item for item "n1".
798 * Return OK or FAIL.
799 */
800 int
801check_range_index_two(
802 list_T *l,
803 long *n1,
804 listitem_T *li1,
805 long *n2,
806 int quiet)
807{
808 if (*n2 < 0)
809 {
810 listitem_T *ni = list_find(l, *n2);
811
812 if (ni == NULL)
813 {
814 if (!quiet)
Bram Moolenaar460ae5d2022-01-01 14:19:49 +0000815 semsg(_(e_list_index_out_of_range_nr), *n2);
Bram Moolenaar4f0884d2021-08-11 21:49:23 +0200816 return FAIL;
817 }
818 *n2 = list_idx_of_item(l, ni);
819 }
820
821 // Check that n2 isn't before n1.
822 if (*n1 < 0)
823 *n1 = list_idx_of_item(l, li1);
824 if (*n2 < *n1)
825 {
826 if (!quiet)
Bram Moolenaar460ae5d2022-01-01 14:19:49 +0000827 semsg(_(e_list_index_out_of_range_nr), *n2);
Bram Moolenaar4f0884d2021-08-11 21:49:23 +0200828 return FAIL;
829 }
830 return OK;
831}
832
833/*
834 * Assign values from list "src" into a range of "dest".
835 * "idx1_arg" is the index of the first item in "dest" to be replaced.
836 * "idx2" is the index of last item to be replaced, but when "empty_idx2" is
837 * TRUE then replace all items after "idx1".
838 * "op" is the operator, normally "=" but can be "+=" and the like.
839 * "varname" is used for error messages.
840 * Returns OK or FAIL.
841 */
842 int
843list_assign_range(
844 list_T *dest,
845 list_T *src,
846 long idx1_arg,
847 long idx2,
848 int empty_idx2,
849 char_u *op,
850 char_u *varname)
851{
852 listitem_T *src_li;
853 listitem_T *dest_li;
854 long idx1 = idx1_arg;
855 listitem_T *first_li = list_find_index(dest, &idx1);
856 long idx;
Bram Moolenaar89071cb2021-08-13 18:20:09 +0200857 type_T *member_type = NULL;
Bram Moolenaar4f0884d2021-08-11 21:49:23 +0200858
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +0000859 // Check whether any of the list items is locked before making any changes.
Bram Moolenaar4f0884d2021-08-11 21:49:23 +0200860 idx = idx1;
861 dest_li = first_li;
862 for (src_li = src->lv_first; src_li != NULL && dest_li != NULL; )
863 {
864 if (value_check_lock(dest_li->li_tv.v_lock, varname, FALSE))
865 return FAIL;
866 src_li = src_li->li_next;
867 if (src_li == NULL || (!empty_idx2 && idx2 == idx))
868 break;
869 dest_li = dest_li->li_next;
870 ++idx;
871 }
872
Bram Moolenaar89071cb2021-08-13 18:20:09 +0200873 if (in_vim9script() && dest->lv_type != NULL
874 && dest->lv_type->tt_member != NULL)
875 member_type = dest->lv_type->tt_member;
876
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +0000877 // Assign the List values to the list items.
Bram Moolenaar4f0884d2021-08-11 21:49:23 +0200878 idx = idx1;
879 dest_li = first_li;
880 for (src_li = src->lv_first; src_li != NULL; )
881 {
882 if (op != NULL && *op != '=')
883 tv_op(&dest_li->li_tv, &src_li->li_tv, op);
884 else
885 {
Bram Moolenaar89071cb2021-08-13 18:20:09 +0200886 if (member_type != NULL
887 && check_typval_arg_type(member_type, &src_li->li_tv,
888 NULL, 0) == FAIL)
889 return FAIL;
Bram Moolenaar4f0884d2021-08-11 21:49:23 +0200890 clear_tv(&dest_li->li_tv);
891 copy_tv(&src_li->li_tv, &dest_li->li_tv);
892 }
893 src_li = src_li->li_next;
894 if (src_li == NULL || (!empty_idx2 && idx2 == idx))
895 break;
896 if (dest_li->li_next == NULL)
897 {
898 // Need to add an empty item.
899 if (list_append_number(dest, 0) == FAIL)
900 {
901 src_li = NULL;
902 break;
903 }
904 }
905 dest_li = dest_li->li_next;
906 ++idx;
907 }
908 if (src_li != NULL)
909 {
910 emsg(_(e_list_value_has_more_items_than_targets));
911 return FAIL;
912 }
913 if (empty_idx2
914 ? (dest_li != NULL && dest_li->li_next != NULL)
915 : idx != idx2)
916 {
917 emsg(_(e_list_value_does_not_have_enough_items));
918 return FAIL;
919 }
920 return OK;
921}
922
923/*
Bram Moolenaaracf7d732022-03-25 19:50:57 +0000924 * Flatten up to "maxitems" in "list", starting at "first" to depth "maxdepth".
925 * When "first" is NULL use the first item.
Bram Moolenaar077a1e62020-06-08 20:50:43 +0200926 * It does nothing if "maxdepth" is 0.
927 * Returns FAIL when out of memory.
928 */
Bram Moolenaar3b690062021-02-01 20:14:51 +0100929 static void
Bram Moolenaaracf7d732022-03-25 19:50:57 +0000930list_flatten(list_T *list, listitem_T *first, long maxitems, long maxdepth)
Bram Moolenaar077a1e62020-06-08 20:50:43 +0200931{
932 listitem_T *item;
Bram Moolenaaracf7d732022-03-25 19:50:57 +0000933 int done = 0;
Bram Moolenaar077a1e62020-06-08 20:50:43 +0200934
935 if (maxdepth == 0)
Bram Moolenaar3b690062021-02-01 20:14:51 +0100936 return;
Bram Moolenaar077a1e62020-06-08 20:50:43 +0200937 CHECK_LIST_MATERIALIZE(list);
Bram Moolenaaracf7d732022-03-25 19:50:57 +0000938 if (first == NULL)
939 item = list->lv_first;
940 else
941 item = first;
Bram Moolenaar077a1e62020-06-08 20:50:43 +0200942
Bram Moolenaaracf7d732022-03-25 19:50:57 +0000943 while (item != NULL && done < maxitems)
Bram Moolenaar077a1e62020-06-08 20:50:43 +0200944 {
Bram Moolenaaracf7d732022-03-25 19:50:57 +0000945 listitem_T *next = item->li_next;
946
Bram Moolenaar077a1e62020-06-08 20:50:43 +0200947 fast_breakcheck();
948 if (got_int)
Bram Moolenaar3b690062021-02-01 20:14:51 +0100949 return;
Bram Moolenaar077a1e62020-06-08 20:50:43 +0200950
951 if (item->li_tv.v_type == VAR_LIST)
952 {
Bram Moolenaaracf7d732022-03-25 19:50:57 +0000953 list_T *itemlist = item->li_tv.vval.v_list;
Bram Moolenaar077a1e62020-06-08 20:50:43 +0200954
955 vimlist_remove(list, item, item);
Bram Moolenaaracf7d732022-03-25 19:50:57 +0000956 if (list_extend(list, itemlist, next) == FAIL)
Bram Moolenaarb3bf33a2021-09-11 20:20:38 +0200957 {
958 list_free_item(list, item);
Bram Moolenaar3b690062021-02-01 20:14:51 +0100959 return;
Bram Moolenaarb3bf33a2021-09-11 20:20:38 +0200960 }
Bram Moolenaar077a1e62020-06-08 20:50:43 +0200961
Bram Moolenaaracf7d732022-03-25 19:50:57 +0000962 if (maxdepth > 0)
963 list_flatten(list, item->li_prev == NULL
964 ? list->lv_first : item->li_prev->li_next,
965 itemlist->lv_len, maxdepth - 1);
Bram Moolenaarf3980dc2022-03-26 16:42:23 +0000966 clear_tv(&item->li_tv);
Bram Moolenaarc6c1ec42022-03-26 10:50:11 +0000967 list_free_item(list, item);
Bram Moolenaaracf7d732022-03-25 19:50:57 +0000968 }
Bram Moolenaar077a1e62020-06-08 20:50:43 +0200969
Bram Moolenaaracf7d732022-03-25 19:50:57 +0000970 ++done;
971 item = next;
Bram Moolenaar077a1e62020-06-08 20:50:43 +0200972 }
Bram Moolenaar077a1e62020-06-08 20:50:43 +0200973}
974
975/*
Bram Moolenaar3b690062021-02-01 20:14:51 +0100976 * "flatten()" and "flattennew()" functions
Bram Moolenaar077a1e62020-06-08 20:50:43 +0200977 */
Bram Moolenaar3b690062021-02-01 20:14:51 +0100978 static void
979flatten_common(typval_T *argvars, typval_T *rettv, int make_copy)
Bram Moolenaar077a1e62020-06-08 20:50:43 +0200980{
981 list_T *l;
982 long maxdepth;
983 int error = FALSE;
984
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +0200985 if (in_vim9script()
986 && (check_for_list_arg(argvars, 0) == FAIL
987 || check_for_opt_number_arg(argvars, 1) == FAIL))
988 return;
989
Bram Moolenaar077a1e62020-06-08 20:50:43 +0200990 if (argvars[0].v_type != VAR_LIST)
991 {
Bram Moolenaar3a846e62022-01-01 16:21:00 +0000992 semsg(_(e_argument_of_str_must_be_list), "flatten()");
Bram Moolenaar077a1e62020-06-08 20:50:43 +0200993 return;
994 }
995
996 if (argvars[1].v_type == VAR_UNKNOWN)
997 maxdepth = 999999;
998 else
999 {
1000 maxdepth = (long)tv_get_number_chk(&argvars[1], &error);
1001 if (error)
1002 return;
1003 if (maxdepth < 0)
1004 {
Bram Moolenaard82a47d2022-01-05 20:24:39 +00001005 emsg(_(e_maxdepth_must_be_non_negative_number));
Bram Moolenaar077a1e62020-06-08 20:50:43 +02001006 return;
1007 }
1008 }
1009
1010 l = argvars[0].vval.v_list;
Bram Moolenaar3b690062021-02-01 20:14:51 +01001011 rettv->v_type = VAR_LIST;
1012 rettv->vval.v_list = l;
1013 if (l == NULL)
1014 return;
1015
1016 if (make_copy)
1017 {
Bram Moolenaarc6c1ec42022-03-26 10:50:11 +00001018 l = list_copy(l, FALSE, TRUE, get_copyID());
Bram Moolenaar3b690062021-02-01 20:14:51 +01001019 rettv->vval.v_list = l;
1020 if (l == NULL)
1021 return;
Bram Moolenaarb3bf33a2021-09-11 20:20:38 +02001022 // The type will change.
1023 free_type(l->lv_type);
1024 l->lv_type = NULL;
Bram Moolenaar3b690062021-02-01 20:14:51 +01001025 }
1026 else
1027 {
1028 if (value_check_lock(l->lv_lock,
1029 (char_u *)N_("flatten() argument"), TRUE))
1030 return;
1031 ++l->lv_refcount;
1032 }
1033
Bram Moolenaaracf7d732022-03-25 19:50:57 +00001034 list_flatten(l, NULL, l->lv_len, maxdepth);
Bram Moolenaar3b690062021-02-01 20:14:51 +01001035}
1036
1037/*
1038 * "flatten(list[, {maxdepth}])" function
1039 */
1040 void
1041f_flatten(typval_T *argvars, typval_T *rettv)
1042{
1043 if (in_vim9script())
1044 emsg(_(e_cannot_use_flatten_in_vim9_script));
1045 else
1046 flatten_common(argvars, rettv, FALSE);
1047}
1048
1049/*
1050 * "flattennew(list[, {maxdepth}])" function
1051 */
1052 void
1053f_flattennew(typval_T *argvars, typval_T *rettv)
1054{
1055 flatten_common(argvars, rettv, TRUE);
Bram Moolenaar077a1e62020-06-08 20:50:43 +02001056}
1057
1058/*
Bram Moolenaar976f8592022-08-30 14:34:52 +01001059 * "items(list)" function
1060 * Caller must have already checked that argvars[0] is a List.
1061 */
1062 void
1063list2items(typval_T *argvars, typval_T *rettv)
1064{
1065 list_T *l = argvars[0].vval.v_list;
1066 listitem_T *li;
1067 varnumber_T idx;
1068
1069 if (rettv_list_alloc(rettv) == FAIL)
1070 return;
Bram Moolenaar976f8592022-08-30 14:34:52 +01001071 if (l == NULL)
Bram Moolenaar3e518a82022-08-30 17:45:33 +01001072 return; // null list behaves like an empty list
Bram Moolenaar976f8592022-08-30 14:34:52 +01001073
1074 // TODO: would be more efficient to not materialize the argument
1075 CHECK_LIST_MATERIALIZE(l);
1076 for (idx = 0, li = l->lv_first; li != NULL; li = li->li_next, ++idx)
1077 {
1078 list_T *l2 = list_alloc();
1079
1080 if (l2 == NULL)
1081 break;
Bram Moolenaar9ba61942022-08-31 11:25:06 +01001082 if (list_append_list(rettv->vval.v_list, l2) == FAIL)
1083 {
1084 vim_free(l2);
1085 break;
1086 }
1087 if (list_append_number(l2, idx) == FAIL
Bram Moolenaar976f8592022-08-30 14:34:52 +01001088 || list_append_tv(l2, &li->li_tv) == FAIL)
1089 break;
1090 }
1091}
1092
1093/*
Bram Moolenaar3e518a82022-08-30 17:45:33 +01001094 * "items(string)" function
1095 * Caller must have already checked that argvars[0] is a String.
1096 */
1097 void
1098string2items(typval_T *argvars, typval_T *rettv)
1099{
1100 char_u *p = argvars[0].vval.v_string;
1101 varnumber_T idx;
1102
1103 if (rettv_list_alloc(rettv) == FAIL)
1104 return;
1105 if (p == NULL)
1106 return; // null string behaves like an empty string
1107
1108 for (idx = 0; *p != NUL; ++idx)
1109 {
1110 int len = mb_ptr2len(p);
1111 list_T *l2;
1112
1113 if (len == 0)
1114 break;
1115 l2 = list_alloc();
1116 if (l2 == NULL)
1117 break;
Bram Moolenaar9ba61942022-08-31 11:25:06 +01001118 if (list_append_list(rettv->vval.v_list, l2) == FAIL)
1119 {
1120 vim_free(l2);
1121 break;
1122 }
1123 if (list_append_number(l2, idx) == FAIL
Bram Moolenaar3e518a82022-08-30 17:45:33 +01001124 || list_append_string(l2, p, len) == FAIL)
1125 break;
1126 p += len;
1127 }
1128}
1129
1130/*
Bram Moolenaar1a739232020-10-10 15:37:58 +02001131 * Extend "l1" with "l2". "l1" must not be NULL.
Bram Moolenaarda861d62016-07-17 15:46:27 +02001132 * If "bef" is NULL append at the end, otherwise insert before this item.
1133 * Returns FAIL when out of memory.
1134 */
1135 int
1136list_extend(list_T *l1, list_T *l2, listitem_T *bef)
1137{
1138 listitem_T *item;
Bram Moolenaar1a739232020-10-10 15:37:58 +02001139 int todo;
Bram Moolenaardcae51f2021-04-08 20:10:10 +02001140 listitem_T *bef_prev;
Bram Moolenaarda861d62016-07-17 15:46:27 +02001141
Bram Moolenaar1a739232020-10-10 15:37:58 +02001142 // NULL list is equivalent to an empty list: nothing to do.
1143 if (l2 == NULL || l2->lv_len == 0)
1144 return OK;
1145
1146 todo = l2->lv_len;
Bram Moolenaar7e9f3512020-05-13 22:44:22 +02001147 CHECK_LIST_MATERIALIZE(l1);
1148 CHECK_LIST_MATERIALIZE(l2);
Bram Moolenaar8a7d6542020-01-26 15:56:19 +01001149
Bram Moolenaardcae51f2021-04-08 20:10:10 +02001150 // When exending a list with itself, at some point we run into the item
1151 // that was before "bef" and need to skip over the already inserted items
1152 // to "bef".
1153 bef_prev = bef == NULL ? NULL : bef->li_prev;
1154
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001155 // We also quit the loop when we have inserted the original item count of
1156 // the list, avoid a hang when we extend a list with itself.
Bram Moolenaardcae51f2021-04-08 20:10:10 +02001157 for (item = l2->lv_first; item != NULL && --todo >= 0;
1158 item = item == bef_prev ? bef : item->li_next)
Bram Moolenaarda861d62016-07-17 15:46:27 +02001159 if (list_insert_tv(l1, &item->li_tv, bef) == FAIL)
1160 return FAIL;
1161 return OK;
1162}
1163
1164/*
1165 * Concatenate lists "l1" and "l2" into a new list, stored in "tv".
1166 * Return FAIL when out of memory.
1167 */
1168 int
1169list_concat(list_T *l1, list_T *l2, typval_T *tv)
1170{
1171 list_T *l;
1172
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001173 // make a copy of the first list.
Bram Moolenaar1a739232020-10-10 15:37:58 +02001174 if (l1 == NULL)
1175 l = list_alloc();
1176 else
Bram Moolenaar381692b2022-02-02 20:01:27 +00001177 l = list_copy(l1, FALSE, TRUE, 0);
Bram Moolenaarda861d62016-07-17 15:46:27 +02001178 if (l == NULL)
1179 return FAIL;
1180 tv->v_type = VAR_LIST;
Bram Moolenaar9681f712020-11-21 13:58:50 +01001181 tv->v_lock = 0;
Bram Moolenaarda861d62016-07-17 15:46:27 +02001182 tv->vval.v_list = l;
Bram Moolenaar1a739232020-10-10 15:37:58 +02001183 if (l1 == NULL)
1184 ++l->lv_refcount;
Bram Moolenaarda861d62016-07-17 15:46:27 +02001185
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001186 // append all items from the second list
Bram Moolenaarda861d62016-07-17 15:46:27 +02001187 return list_extend(l, l2, NULL);
1188}
1189
Bram Moolenaar9af78762020-06-16 11:34:42 +02001190 list_T *
1191list_slice(list_T *ol, long n1, long n2)
1192{
1193 listitem_T *item;
1194 list_T *l = list_alloc();
1195
1196 if (l == NULL)
1197 return NULL;
1198 for (item = list_find(ol, n1); n1 <= n2; ++n1)
1199 {
1200 if (list_append_tv(l, &item->li_tv) == FAIL)
1201 {
1202 list_free(l);
1203 return NULL;
1204 }
1205 item = item->li_next;
1206 }
1207 return l;
1208}
1209
Bram Moolenaared591872020-08-15 22:14:53 +02001210 int
1211list_slice_or_index(
1212 list_T *list,
1213 int range,
Bram Moolenaar6601b622021-01-13 21:47:15 +01001214 varnumber_T n1_arg,
1215 varnumber_T n2_arg,
1216 int exclusive,
Bram Moolenaared591872020-08-15 22:14:53 +02001217 typval_T *rettv,
1218 int verbose)
1219{
1220 long len = list_len(list);
Bram Moolenaar6601b622021-01-13 21:47:15 +01001221 varnumber_T n1 = n1_arg;
1222 varnumber_T n2 = n2_arg;
Bram Moolenaared591872020-08-15 22:14:53 +02001223 typval_T var1;
1224
1225 if (n1 < 0)
1226 n1 = len + n1;
1227 if (n1 < 0 || n1 >= len)
1228 {
Bram Moolenaar92f05f22021-08-12 21:12:56 +02001229 // For a range we allow invalid values and for legacy script return an
1230 // empty list, for Vim9 script start at the first item.
1231 // A list index out of range is an error.
Bram Moolenaared591872020-08-15 22:14:53 +02001232 if (!range)
1233 {
1234 if (verbose)
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00001235 semsg(_(e_list_index_out_of_range_nr), (long)n1_arg);
Bram Moolenaared591872020-08-15 22:14:53 +02001236 return FAIL;
1237 }
Bram Moolenaar92f05f22021-08-12 21:12:56 +02001238 if (in_vim9script())
1239 n1 = n1 < 0 ? 0 : len;
1240 else
1241 n1 = len;
Bram Moolenaared591872020-08-15 22:14:53 +02001242 }
1243 if (range)
1244 {
1245 list_T *l;
1246
1247 if (n2 < 0)
1248 n2 = len + n2;
1249 else if (n2 >= len)
Bram Moolenaar6601b622021-01-13 21:47:15 +01001250 n2 = len - (exclusive ? 0 : 1);
1251 if (exclusive)
1252 --n2;
Bram Moolenaared591872020-08-15 22:14:53 +02001253 if (n2 < 0 || n2 + 1 < n1)
1254 n2 = -1;
1255 l = list_slice(list, n1, n2);
1256 if (l == NULL)
1257 return FAIL;
1258 clear_tv(rettv);
1259 rettv_list_set(rettv, l);
1260 }
1261 else
1262 {
1263 // copy the item to "var1" to avoid that freeing the list makes it
1264 // invalid.
1265 copy_tv(&list_find(list, n1)->li_tv, &var1);
1266 clear_tv(rettv);
1267 *rettv = var1;
1268 }
1269 return OK;
1270}
1271
Bram Moolenaarda861d62016-07-17 15:46:27 +02001272/*
1273 * Make a copy of list "orig". Shallow if "deep" is FALSE.
1274 * The refcount of the new list is set to 1.
Bram Moolenaar381692b2022-02-02 20:01:27 +00001275 * See item_copy() for "top" and "copyID".
Bram Moolenaarda861d62016-07-17 15:46:27 +02001276 * Returns NULL when out of memory.
1277 */
1278 list_T *
Bram Moolenaar381692b2022-02-02 20:01:27 +00001279list_copy(list_T *orig, int deep, int top, int copyID)
Bram Moolenaarda861d62016-07-17 15:46:27 +02001280{
1281 list_T *copy;
1282 listitem_T *item;
1283 listitem_T *ni;
1284
1285 if (orig == NULL)
1286 return NULL;
1287
1288 copy = list_alloc();
Yegappan Lakshmanane8575982023-01-14 12:32:28 +00001289 if (copy == NULL)
1290 return NULL;
1291
1292 if (orig->lv_type == NULL || top || deep)
1293 copy->lv_type = NULL;
1294 else
1295 copy->lv_type = alloc_type(orig->lv_type);
1296 if (copyID != 0)
Bram Moolenaarda861d62016-07-17 15:46:27 +02001297 {
Yegappan Lakshmanane8575982023-01-14 12:32:28 +00001298 // Do this before adding the items, because one of the items may
1299 // refer back to this list.
1300 orig->lv_copyID = copyID;
1301 orig->lv_copylist = copy;
1302 }
1303 CHECK_LIST_MATERIALIZE(orig);
1304 for (item = orig->lv_first; item != NULL && !got_int;
1305 item = item->li_next)
1306 {
1307 ni = listitem_alloc();
1308 if (ni == NULL)
1309 break;
1310 if (deep)
Bram Moolenaarda861d62016-07-17 15:46:27 +02001311 {
Yegappan Lakshmanane8575982023-01-14 12:32:28 +00001312 if (item_copy(&item->li_tv, &ni->li_tv,
1313 deep, FALSE, copyID) == FAIL)
Bram Moolenaarda861d62016-07-17 15:46:27 +02001314 {
Yegappan Lakshmanane8575982023-01-14 12:32:28 +00001315 vim_free(ni);
1316 break;
Bram Moolenaarda861d62016-07-17 15:46:27 +02001317 }
Bram Moolenaarda861d62016-07-17 15:46:27 +02001318 }
Yegappan Lakshmanane8575982023-01-14 12:32:28 +00001319 else
1320 copy_tv(&item->li_tv, &ni->li_tv);
1321 list_append(copy, ni);
1322 }
1323 ++copy->lv_refcount;
1324 if (item != NULL)
1325 {
1326 list_unref(copy);
1327 copy = NULL;
Bram Moolenaarda861d62016-07-17 15:46:27 +02001328 }
1329
1330 return copy;
1331}
1332
1333/*
1334 * Remove items "item" to "item2" from list "l".
1335 * Does not free the listitem or the value!
1336 * This used to be called list_remove, but that conflicts with a Sun header
1337 * file.
1338 */
1339 void
1340vimlist_remove(list_T *l, listitem_T *item, listitem_T *item2)
1341{
1342 listitem_T *ip;
1343
Bram Moolenaar7e9f3512020-05-13 22:44:22 +02001344 CHECK_LIST_MATERIALIZE(l);
Bram Moolenaar8a7d6542020-01-26 15:56:19 +01001345
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001346 // notify watchers
Bram Moolenaarda861d62016-07-17 15:46:27 +02001347 for (ip = item; ip != NULL; ip = ip->li_next)
1348 {
1349 --l->lv_len;
1350 list_fix_watch(l, ip);
1351 if (ip == item2)
1352 break;
1353 }
1354
1355 if (item2->li_next == NULL)
Bram Moolenaar0ff6aad2020-01-29 21:27:21 +01001356 l->lv_u.mat.lv_last = item->li_prev;
Bram Moolenaarda861d62016-07-17 15:46:27 +02001357 else
1358 item2->li_next->li_prev = item->li_prev;
1359 if (item->li_prev == NULL)
1360 l->lv_first = item2->li_next;
1361 else
1362 item->li_prev->li_next = item2->li_next;
Bram Moolenaar0ff6aad2020-01-29 21:27:21 +01001363 l->lv_u.mat.lv_idx_item = NULL;
Bram Moolenaarda861d62016-07-17 15:46:27 +02001364}
1365
1366/*
1367 * Return an allocated string with the string representation of a list.
1368 * May return NULL.
1369 */
1370 char_u *
1371list2string(typval_T *tv, int copyID, int restore_copyID)
1372{
1373 garray_T ga;
1374
1375 if (tv->vval.v_list == NULL)
1376 return NULL;
Bram Moolenaar04935fb2022-01-08 16:19:22 +00001377 ga_init2(&ga, sizeof(char), 80);
Bram Moolenaarda861d62016-07-17 15:46:27 +02001378 ga_append(&ga, '[');
Bram Moolenaar7e9f3512020-05-13 22:44:22 +02001379 CHECK_LIST_MATERIALIZE(tv->vval.v_list);
Bram Moolenaarda861d62016-07-17 15:46:27 +02001380 if (list_join(&ga, tv->vval.v_list, (char_u *)", ",
1381 FALSE, restore_copyID, copyID) == FAIL)
1382 {
1383 vim_free(ga.ga_data);
1384 return NULL;
1385 }
1386 ga_append(&ga, ']');
1387 ga_append(&ga, NUL);
1388 return (char_u *)ga.ga_data;
1389}
1390
1391typedef struct join_S {
1392 char_u *s;
1393 char_u *tofree;
1394} join_T;
1395
1396 static int
1397list_join_inner(
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001398 garray_T *gap, // to store the result in
Bram Moolenaarda861d62016-07-17 15:46:27 +02001399 list_T *l,
1400 char_u *sep,
1401 int echo_style,
1402 int restore_copyID,
1403 int copyID,
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001404 garray_T *join_gap) // to keep each list item string
Bram Moolenaarda861d62016-07-17 15:46:27 +02001405{
1406 int i;
1407 join_T *p;
1408 int len;
1409 int sumlen = 0;
1410 int first = TRUE;
1411 char_u *tofree;
1412 char_u numbuf[NUMBUFLEN];
1413 listitem_T *item;
1414 char_u *s;
1415
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001416 // Stringify each item in the list.
Bram Moolenaar7e9f3512020-05-13 22:44:22 +02001417 CHECK_LIST_MATERIALIZE(l);
Bram Moolenaarda861d62016-07-17 15:46:27 +02001418 for (item = l->lv_first; item != NULL && !got_int; item = item->li_next)
1419 {
1420 s = echo_string_core(&item->li_tv, &tofree, numbuf, copyID,
Bram Moolenaar35422f42017-08-05 16:33:56 +02001421 echo_style, restore_copyID, !echo_style);
Bram Moolenaarda861d62016-07-17 15:46:27 +02001422 if (s == NULL)
1423 return FAIL;
1424
1425 len = (int)STRLEN(s);
1426 sumlen += len;
1427
1428 (void)ga_grow(join_gap, 1);
1429 p = ((join_T *)join_gap->ga_data) + (join_gap->ga_len++);
1430 if (tofree != NULL || s != numbuf)
1431 {
1432 p->s = s;
1433 p->tofree = tofree;
1434 }
1435 else
1436 {
1437 p->s = vim_strnsave(s, len);
1438 p->tofree = p->s;
1439 }
1440
1441 line_breakcheck();
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001442 if (did_echo_string_emsg) // recursion error, bail out
Bram Moolenaarda861d62016-07-17 15:46:27 +02001443 break;
1444 }
1445
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001446 // Allocate result buffer with its total size, avoid re-allocation and
1447 // multiple copy operations. Add 2 for a tailing ']' and NUL.
Bram Moolenaarda861d62016-07-17 15:46:27 +02001448 if (join_gap->ga_len >= 2)
1449 sumlen += (int)STRLEN(sep) * (join_gap->ga_len - 1);
1450 if (ga_grow(gap, sumlen + 2) == FAIL)
1451 return FAIL;
1452
1453 for (i = 0; i < join_gap->ga_len && !got_int; ++i)
1454 {
1455 if (first)
1456 first = FALSE;
1457 else
1458 ga_concat(gap, sep);
1459 p = ((join_T *)join_gap->ga_data) + i;
1460
1461 if (p->s != NULL)
1462 ga_concat(gap, p->s);
1463 line_breakcheck();
1464 }
1465
1466 return OK;
1467}
1468
1469/*
1470 * Join list "l" into a string in "*gap", using separator "sep".
1471 * When "echo_style" is TRUE use String as echoed, otherwise as inside a List.
1472 * Return FAIL or OK.
1473 */
1474 int
1475list_join(
1476 garray_T *gap,
1477 list_T *l,
1478 char_u *sep,
1479 int echo_style,
1480 int restore_copyID,
1481 int copyID)
1482{
1483 garray_T join_ga;
1484 int retval;
1485 join_T *p;
1486 int i;
1487
1488 if (l->lv_len < 1)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001489 return OK; // nothing to do
Bram Moolenaar04935fb2022-01-08 16:19:22 +00001490 ga_init2(&join_ga, sizeof(join_T), l->lv_len);
Bram Moolenaarda861d62016-07-17 15:46:27 +02001491 retval = list_join_inner(gap, l, sep, echo_style, restore_copyID,
1492 copyID, &join_ga);
1493
Yegappan Lakshmanane8575982023-01-14 12:32:28 +00001494 if (join_ga.ga_data == NULL)
1495 return retval;
1496
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001497 // Dispose each item in join_ga.
Yegappan Lakshmanane8575982023-01-14 12:32:28 +00001498 p = (join_T *)join_ga.ga_data;
1499 for (i = 0; i < join_ga.ga_len; ++i)
Bram Moolenaarda861d62016-07-17 15:46:27 +02001500 {
Yegappan Lakshmanane8575982023-01-14 12:32:28 +00001501 vim_free(p->tofree);
1502 ++p;
Bram Moolenaarda861d62016-07-17 15:46:27 +02001503 }
Yegappan Lakshmanane8575982023-01-14 12:32:28 +00001504 ga_clear(&join_ga);
Bram Moolenaarda861d62016-07-17 15:46:27 +02001505
1506 return retval;
1507}
1508
1509/*
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001510 * "join()" function
1511 */
1512 void
1513f_join(typval_T *argvars, typval_T *rettv)
1514{
1515 garray_T ga;
1516 char_u *sep;
1517
Yegappan Lakshmananca195cc2022-06-14 13:42:26 +01001518 rettv->v_type = VAR_STRING;
1519
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +02001520 if (in_vim9script()
1521 && (check_for_list_arg(argvars, 0) == FAIL
1522 || check_for_opt_string_arg(argvars, 1) == FAIL))
1523 return;
1524
Bram Moolenaard83392a2022-09-01 12:22:46 +01001525 if (check_for_list_arg(argvars, 0) == FAIL)
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001526 return;
Yegappan Lakshmananca195cc2022-06-14 13:42:26 +01001527
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001528 if (argvars[0].vval.v_list == NULL)
1529 return;
Bram Moolenaaref982572021-08-12 19:27:57 +02001530
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001531 if (argvars[1].v_type == VAR_UNKNOWN)
1532 sep = (char_u *)" ";
1533 else
1534 sep = tv_get_string_chk(&argvars[1]);
1535
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001536 if (sep != NULL)
1537 {
Bram Moolenaar04935fb2022-01-08 16:19:22 +00001538 ga_init2(&ga, sizeof(char), 80);
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001539 list_join(&ga, argvars[0].vval.v_list, sep, TRUE, FALSE, 0);
1540 ga_append(&ga, NUL);
1541 rettv->vval.v_string = (char_u *)ga.ga_data;
1542 }
1543 else
1544 rettv->vval.v_string = NULL;
1545}
1546
1547/*
Bram Moolenaarda861d62016-07-17 15:46:27 +02001548 * Allocate a variable for a List and fill it from "*arg".
Bram Moolenaar71478202020-06-26 22:46:27 +02001549 * "*arg" points to the "[".
Bram Moolenaarda861d62016-07-17 15:46:27 +02001550 * Return OK or FAIL.
1551 */
1552 int
Bram Moolenaar9a78e6d2020-07-01 18:29:55 +02001553eval_list(char_u **arg, typval_T *rettv, evalarg_T *evalarg, int do_error)
Bram Moolenaarda861d62016-07-17 15:46:27 +02001554{
Bram Moolenaar71478202020-06-26 22:46:27 +02001555 int evaluate = evalarg == NULL ? FALSE
1556 : evalarg->eval_flags & EVAL_EVALUATE;
Bram Moolenaarda861d62016-07-17 15:46:27 +02001557 list_T *l = NULL;
1558 typval_T tv;
1559 listitem_T *item;
Bram Moolenaareb6880b2020-07-12 17:07:05 +02001560 int vim9script = in_vim9script();
Bram Moolenaar71478202020-06-26 22:46:27 +02001561 int had_comma;
Bram Moolenaarda861d62016-07-17 15:46:27 +02001562
1563 if (evaluate)
1564 {
1565 l = list_alloc();
1566 if (l == NULL)
1567 return FAIL;
1568 }
1569
Bram Moolenaar962d7212020-07-04 14:15:00 +02001570 *arg = skipwhite_and_linebreak(*arg + 1, evalarg);
Bram Moolenaarda861d62016-07-17 15:46:27 +02001571 while (**arg != ']' && **arg != NUL)
1572 {
Bram Moolenaar71478202020-06-26 22:46:27 +02001573 if (eval1(arg, &tv, evalarg) == FAIL) // recursive!
Bram Moolenaarda861d62016-07-17 15:46:27 +02001574 goto failret;
1575 if (evaluate)
1576 {
1577 item = listitem_alloc();
1578 if (item != NULL)
1579 {
1580 item->li_tv = tv;
1581 item->li_tv.v_lock = 0;
1582 list_append(l, item);
1583 }
1584 else
1585 clear_tv(&tv);
1586 }
Bram Moolenaar4d4d1cd2020-07-30 22:14:33 +02001587 // Legacy Vim script allowed a space before the comma.
1588 if (!vim9script)
1589 *arg = skipwhite(*arg);
Bram Moolenaarda861d62016-07-17 15:46:27 +02001590
Bram Moolenaare6e03172020-06-27 16:36:05 +02001591 // the comma must come after the value
Bram Moolenaar71478202020-06-26 22:46:27 +02001592 had_comma = **arg == ',';
1593 if (had_comma)
Bram Moolenaare6e03172020-06-27 16:36:05 +02001594 {
Bram Moolenaar659bb222020-11-12 20:16:39 +01001595 if (vim9script && !IS_WHITE_OR_NUL((*arg)[1]) && (*arg)[1] != ']')
Bram Moolenaare6e03172020-06-27 16:36:05 +02001596 {
Bram Moolenaarc3fc75d2021-02-07 15:28:09 +01001597 semsg(_(e_white_space_required_after_str_str), ",", *arg);
Bram Moolenaare6e03172020-06-27 16:36:05 +02001598 goto failret;
1599 }
Bram Moolenaar71478202020-06-26 22:46:27 +02001600 *arg = skipwhite(*arg + 1);
Bram Moolenaare6e03172020-06-27 16:36:05 +02001601 }
Bram Moolenaar71478202020-06-26 22:46:27 +02001602
Bram Moolenaare6b53242020-07-01 17:28:33 +02001603 // The "]" can be on the next line. But a double quoted string may
1604 // follow, not a comment.
Bram Moolenaar962d7212020-07-04 14:15:00 +02001605 *arg = skipwhite_and_linebreak(*arg, evalarg);
Bram Moolenaarda861d62016-07-17 15:46:27 +02001606 if (**arg == ']')
1607 break;
Bram Moolenaar71478202020-06-26 22:46:27 +02001608
1609 if (!had_comma)
Bram Moolenaarda861d62016-07-17 15:46:27 +02001610 {
Bram Moolenaar8a7d6542020-01-26 15:56:19 +01001611 if (do_error)
Bram Moolenaardb199212020-08-12 18:01:53 +02001612 {
1613 if (**arg == ',')
Bram Moolenaarba98fb52021-02-07 18:06:29 +01001614 semsg(_(e_no_white_space_allowed_before_str_str),
1615 ",", *arg);
Bram Moolenaardb199212020-08-12 18:01:53 +02001616 else
Bram Moolenaara6f79292022-01-04 21:30:47 +00001617 semsg(_(e_missing_comma_in_list_str), *arg);
Bram Moolenaardb199212020-08-12 18:01:53 +02001618 }
Bram Moolenaarda861d62016-07-17 15:46:27 +02001619 goto failret;
1620 }
Bram Moolenaarda861d62016-07-17 15:46:27 +02001621 }
1622
1623 if (**arg != ']')
1624 {
Bram Moolenaar8a7d6542020-01-26 15:56:19 +01001625 if (do_error)
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00001626 semsg(_(e_missing_end_of_list_rsb_str), *arg);
Bram Moolenaarda861d62016-07-17 15:46:27 +02001627failret:
1628 if (evaluate)
1629 list_free(l);
1630 return FAIL;
1631 }
1632
Bram Moolenaar9d489562020-07-30 20:08:50 +02001633 *arg += 1;
Bram Moolenaarda861d62016-07-17 15:46:27 +02001634 if (evaluate)
Bram Moolenaar45cf6e92017-04-30 20:25:19 +02001635 rettv_list_set(rettv, l);
Bram Moolenaarda861d62016-07-17 15:46:27 +02001636
1637 return OK;
1638}
1639
Bram Moolenaar73dad1e2016-07-17 22:13:49 +02001640/*
Bram Moolenaarcaa55b62017-01-10 13:51:09 +01001641 * Write "list" of strings to file "fd".
Bram Moolenaar73dad1e2016-07-17 22:13:49 +02001642 */
1643 int
1644write_list(FILE *fd, list_T *list, int binary)
1645{
1646 listitem_T *li;
1647 int c;
1648 int ret = OK;
1649 char_u *s;
1650
Bram Moolenaar7e9f3512020-05-13 22:44:22 +02001651 CHECK_LIST_MATERIALIZE(list);
Bram Moolenaaraeea7212020-04-02 18:50:46 +02001652 FOR_ALL_LIST_ITEMS(list, li)
Bram Moolenaar73dad1e2016-07-17 22:13:49 +02001653 {
Bram Moolenaard155d7a2018-12-21 16:04:21 +01001654 for (s = tv_get_string(&li->li_tv); *s != NUL; ++s)
Bram Moolenaar73dad1e2016-07-17 22:13:49 +02001655 {
1656 if (*s == '\n')
1657 c = putc(NUL, fd);
1658 else
1659 c = putc(*s, fd);
1660 if (c == EOF)
1661 {
1662 ret = FAIL;
1663 break;
1664 }
1665 }
1666 if (!binary || li->li_next != NULL)
1667 if (putc('\n', fd) == EOF)
1668 {
1669 ret = FAIL;
1670 break;
1671 }
1672 if (ret == FAIL)
1673 {
Bram Moolenaar40bcec12021-12-05 22:19:27 +00001674 emsg(_(e_error_while_writing));
Bram Moolenaar73dad1e2016-07-17 22:13:49 +02001675 break;
1676 }
1677 }
1678 return ret;
1679}
1680
Bram Moolenaardf48fb42016-07-22 21:50:18 +02001681/*
1682 * Initialize a static list with 10 items.
1683 */
1684 void
1685init_static_list(staticList10_T *sl)
1686{
1687 list_T *l = &sl->sl_list;
1688 int i;
1689
Yegappan Lakshmanan960dcbd2023-03-07 17:45:11 +00001690 CLEAR_POINTER(sl);
Bram Moolenaardf48fb42016-07-22 21:50:18 +02001691 l->lv_first = &sl->sl_items[0];
Bram Moolenaar0ff6aad2020-01-29 21:27:21 +01001692 l->lv_u.mat.lv_last = &sl->sl_items[9];
Bram Moolenaardf48fb42016-07-22 21:50:18 +02001693 l->lv_refcount = DO_NOT_FREE_CNT;
1694 l->lv_lock = VAR_FIXED;
1695 sl->sl_list.lv_len = 10;
1696
1697 for (i = 0; i < 10; ++i)
1698 {
1699 listitem_T *li = &sl->sl_items[i];
1700
1701 if (i == 0)
1702 li->li_prev = NULL;
1703 else
1704 li->li_prev = li - 1;
1705 if (i == 9)
1706 li->li_next = NULL;
1707 else
1708 li->li_next = li + 1;
1709 }
1710}
1711
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001712/*
1713 * "list2str()" function
1714 */
1715 void
1716f_list2str(typval_T *argvars, typval_T *rettv)
1717{
1718 list_T *l;
1719 listitem_T *li;
1720 garray_T ga;
1721 int utf8 = FALSE;
1722
1723 rettv->v_type = VAR_STRING;
1724 rettv->vval.v_string = NULL;
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +02001725
1726 if (in_vim9script()
1727 && (check_for_list_arg(argvars, 0) == FAIL
1728 || check_for_opt_bool_arg(argvars, 1) == FAIL))
1729 return;
1730
Bram Moolenaard83392a2022-09-01 12:22:46 +01001731 if (check_for_list_arg(argvars, 0) == FAIL)
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001732 return;
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001733
1734 l = argvars[0].vval.v_list;
1735 if (l == NULL)
1736 return; // empty list results in empty string
1737
1738 if (argvars[1].v_type != VAR_UNKNOWN)
Bram Moolenaara48f7862020-09-05 20:16:57 +02001739 utf8 = (int)tv_get_bool_chk(&argvars[1], NULL);
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001740
Bram Moolenaar7e9f3512020-05-13 22:44:22 +02001741 CHECK_LIST_MATERIALIZE(l);
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001742 ga_init2(&ga, 1, 80);
1743 if (has_mbyte || utf8)
1744 {
1745 char_u buf[MB_MAXBYTES + 1];
1746 int (*char2bytes)(int, char_u *);
1747
1748 if (utf8 || enc_utf8)
1749 char2bytes = utf_char2bytes;
1750 else
1751 char2bytes = mb_char2bytes;
1752
Bram Moolenaaraeea7212020-04-02 18:50:46 +02001753 FOR_ALL_LIST_ITEMS(l, li)
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001754 {
1755 buf[(*char2bytes)(tv_get_number(&li->li_tv), buf)] = NUL;
1756 ga_concat(&ga, buf);
1757 }
1758 ga_append(&ga, NUL);
1759 }
1760 else if (ga_grow(&ga, list_len(l) + 1) == OK)
1761 {
Bram Moolenaaraeea7212020-04-02 18:50:46 +02001762 FOR_ALL_LIST_ITEMS(l, li)
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001763 ga_append(&ga, tv_get_number(&li->li_tv));
1764 ga_append(&ga, NUL);
1765 }
1766
1767 rettv->v_type = VAR_STRING;
1768 rettv->vval.v_string = ga.ga_data;
1769}
1770
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00001771/*
1772 * Remove item argvars[1] from List argvars[0]. If argvars[2] is supplied, then
1773 * remove the range of items from argvars[1] to argvars[2] (inclusive).
1774 */
Bram Moolenaarbdff0122020-04-05 18:56:05 +02001775 static void
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001776list_remove(typval_T *argvars, typval_T *rettv, char_u *arg_errmsg)
1777{
1778 list_T *l;
1779 listitem_T *item, *item2;
1780 listitem_T *li;
1781 int error = FALSE;
Bram Moolenaar239f8d92021-01-17 13:21:20 +01001782 long idx;
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00001783 long end;
1784 int cnt = 0;
1785 list_T *rl;
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001786
1787 if ((l = argvars[0].vval.v_list) == NULL
Bram Moolenaara187c432020-09-16 21:08:28 +02001788 || value_check_lock(l->lv_lock, arg_errmsg, TRUE))
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001789 return;
1790
1791 idx = (long)tv_get_number_chk(&argvars[1], &error);
1792 if (error)
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00001793 return; // type error: do nothing, errmsg already given
1794
1795 if ((item = list_find(l, idx)) == NULL)
1796 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00001797 semsg(_(e_list_index_out_of_range_nr), idx);
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00001798 return;
1799 }
1800
1801 if (argvars[2].v_type == VAR_UNKNOWN)
1802 {
1803 // Remove one item, return its value.
1804 vimlist_remove(l, item, item);
1805 *rettv = item->li_tv;
1806 list_free_item(l, item);
1807 return;
1808 }
1809
1810 // Remove range of items, return list with values.
1811 end = (long)tv_get_number_chk(&argvars[2], &error);
1812 if (error)
1813 return; // type error: do nothing
1814
1815 if ((item2 = list_find(l, end)) == NULL)
1816 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00001817 semsg(_(e_list_index_out_of_range_nr), end);
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00001818 return;
1819 }
1820
1821 for (li = item; li != NULL; li = li->li_next)
1822 {
1823 ++cnt;
1824 if (li == item2)
1825 break;
1826 }
1827 if (li == NULL) // didn't find "item2" after "item"
1828 {
1829 emsg(_(e_invalid_range));
1830 return;
1831 }
1832
1833 vimlist_remove(l, item, item2);
Bram Moolenaar93a10962022-06-16 11:42:09 +01001834 if (rettv_list_alloc(rettv) == FAIL)
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00001835 return;
1836
1837 rl = rettv->vval.v_list;
1838
1839 if (l->lv_with_items > 0)
1840 {
1841 // need to copy the list items and move the value
1842 while (item != NULL)
1843 {
1844 li = listitem_alloc();
1845 if (li == NULL)
1846 return;
1847 li->li_tv = item->li_tv;
1848 init_tv(&item->li_tv);
1849 list_append(rl, li);
1850 if (item == item2)
1851 break;
1852 item = item->li_next;
1853 }
1854 }
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001855 else
1856 {
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00001857 rl->lv_first = item;
1858 rl->lv_u.mat.lv_last = item2;
1859 item->li_prev = NULL;
1860 item2->li_next = NULL;
1861 rl->lv_len = cnt;
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001862 }
1863}
1864
1865static int item_compare(const void *s1, const void *s2);
1866static int item_compare2(const void *s1, const void *s2);
1867
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001868// struct used in the array that's given to qsort()
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001869typedef struct
1870{
1871 listitem_T *item;
1872 int idx;
1873} sortItem_T;
1874
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001875// struct storing information about current sort
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001876typedef struct
1877{
1878 int item_compare_ic;
Bram Moolenaar55e29612020-11-01 13:57:44 +01001879 int item_compare_lc;
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001880 int item_compare_numeric;
1881 int item_compare_numbers;
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001882 int item_compare_float;
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001883 char_u *item_compare_func;
1884 partial_T *item_compare_partial;
1885 dict_T *item_compare_selfdict;
1886 int item_compare_func_err;
1887 int item_compare_keep_zero;
1888} sortinfo_T;
1889static sortinfo_T *sortinfo = NULL;
1890#define ITEM_COMPARE_FAIL 999
1891
1892/*
1893 * Compare functions for f_sort() and f_uniq() below.
1894 */
1895 static int
1896item_compare(const void *s1, const void *s2)
1897{
1898 sortItem_T *si1, *si2;
1899 typval_T *tv1, *tv2;
1900 char_u *p1, *p2;
1901 char_u *tofree1 = NULL, *tofree2 = NULL;
1902 int res;
1903 char_u numbuf1[NUMBUFLEN];
1904 char_u numbuf2[NUMBUFLEN];
1905
1906 si1 = (sortItem_T *)s1;
1907 si2 = (sortItem_T *)s2;
1908 tv1 = &si1->item->li_tv;
1909 tv2 = &si2->item->li_tv;
1910
1911 if (sortinfo->item_compare_numbers)
1912 {
Bram Moolenaarbe19d782023-03-09 22:06:49 +00001913 varnumber_T v1 = tv_to_number(tv1);
1914 varnumber_T v2 = tv_to_number(tv2);
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001915
1916 return v1 == v2 ? 0 : v1 > v2 ? 1 : -1;
1917 }
1918
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001919 if (sortinfo->item_compare_float)
1920 {
1921 float_T v1 = tv_get_float(tv1);
1922 float_T v2 = tv_get_float(tv2);
1923
1924 return v1 == v2 ? 0 : v1 > v2 ? 1 : -1;
1925 }
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001926
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001927 // tv2string() puts quotes around a string and allocates memory. Don't do
1928 // that for string variables. Use a single quote when comparing with a
1929 // non-string to do what the docs promise.
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001930 if (tv1->v_type == VAR_STRING)
1931 {
1932 if (tv2->v_type != VAR_STRING || sortinfo->item_compare_numeric)
1933 p1 = (char_u *)"'";
1934 else
1935 p1 = tv1->vval.v_string;
1936 }
1937 else
1938 p1 = tv2string(tv1, &tofree1, numbuf1, 0);
1939 if (tv2->v_type == VAR_STRING)
1940 {
1941 if (tv1->v_type != VAR_STRING || sortinfo->item_compare_numeric)
1942 p2 = (char_u *)"'";
1943 else
1944 p2 = tv2->vval.v_string;
1945 }
1946 else
1947 p2 = tv2string(tv2, &tofree2, numbuf2, 0);
1948 if (p1 == NULL)
1949 p1 = (char_u *)"";
1950 if (p2 == NULL)
1951 p2 = (char_u *)"";
1952 if (!sortinfo->item_compare_numeric)
1953 {
Bram Moolenaar55e29612020-11-01 13:57:44 +01001954 if (sortinfo->item_compare_lc)
1955 res = strcoll((char *)p1, (char *)p2);
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001956 else
Bram Moolenaar55e29612020-11-01 13:57:44 +01001957 res = sortinfo->item_compare_ic ? STRICMP(p1, p2): STRCMP(p1, p2);
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001958 }
1959 else
1960 {
1961 double n1, n2;
1962 n1 = strtod((char *)p1, (char **)&p1);
1963 n2 = strtod((char *)p2, (char **)&p2);
1964 res = n1 == n2 ? 0 : n1 > n2 ? 1 : -1;
1965 }
1966
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001967 // When the result would be zero, compare the item indexes. Makes the
1968 // sort stable.
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001969 if (res == 0 && !sortinfo->item_compare_keep_zero)
1970 res = si1->idx > si2->idx ? 1 : -1;
1971
1972 vim_free(tofree1);
1973 vim_free(tofree2);
1974 return res;
1975}
1976
1977 static int
1978item_compare2(const void *s1, const void *s2)
1979{
1980 sortItem_T *si1, *si2;
1981 int res;
1982 typval_T rettv;
1983 typval_T argv[3];
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001984 char_u *func_name;
1985 partial_T *partial = sortinfo->item_compare_partial;
Bram Moolenaarc6538bc2019-08-03 18:17:11 +02001986 funcexe_T funcexe;
Bram Moolenaar23018f22021-12-27 11:54:37 +00001987 int did_emsg_before = did_emsg;
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001988
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001989 // shortcut after failure in previous call; compare all items equal
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001990 if (sortinfo->item_compare_func_err)
1991 return 0;
1992
1993 si1 = (sortItem_T *)s1;
1994 si2 = (sortItem_T *)s2;
1995
1996 if (partial == NULL)
1997 func_name = sortinfo->item_compare_func;
1998 else
1999 func_name = partial_name(partial);
2000
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002001 // Copy the values. This is needed to be able to set v_lock to VAR_FIXED
2002 // in the copy without changing the original list items.
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02002003 copy_tv(&si1->item->li_tv, &argv[0]);
2004 copy_tv(&si2->item->li_tv, &argv[1]);
2005
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002006 rettv.v_type = VAR_UNKNOWN; // clear_tv() uses this
Bram Moolenaara80faa82020-04-12 19:37:17 +02002007 CLEAR_FIELD(funcexe);
Bram Moolenaar851f86b2021-12-13 14:26:44 +00002008 funcexe.fe_evaluate = TRUE;
2009 funcexe.fe_partial = partial;
2010 funcexe.fe_selfdict = sortinfo->item_compare_selfdict;
Bram Moolenaarc6538bc2019-08-03 18:17:11 +02002011 res = call_func(func_name, -1, &rettv, 2, argv, &funcexe);
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02002012 clear_tv(&argv[0]);
2013 clear_tv(&argv[1]);
2014
Bram Moolenaar23018f22021-12-27 11:54:37 +00002015 if (res == FAIL || did_emsg > did_emsg_before)
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02002016 res = ITEM_COMPARE_FAIL;
2017 else
Yasuhiro Matsumotoc04f6232021-09-19 17:01:39 +02002018 {
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02002019 res = (int)tv_get_number_chk(&rettv, &sortinfo->item_compare_func_err);
Yasuhiro Matsumotoc04f6232021-09-19 17:01:39 +02002020 if (res > 0)
2021 res = 1;
2022 else if (res < 0)
2023 res = -1;
2024 }
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02002025 if (sortinfo->item_compare_func_err)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002026 res = ITEM_COMPARE_FAIL; // return value has wrong type
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02002027 clear_tv(&rettv);
2028
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002029 // When the result would be zero, compare the pointers themselves. Makes
2030 // the sort stable.
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02002031 if (res == 0 && !sortinfo->item_compare_keep_zero)
2032 res = si1->idx > si2->idx ? 1 : -1;
2033
2034 return res;
2035}
2036
2037/*
Yegappan Lakshmanand92813a2021-12-21 13:19:42 +00002038 * sort() List "l"
2039 */
2040 static void
2041do_sort(list_T *l, sortinfo_T *info)
2042{
2043 long len;
2044 sortItem_T *ptrs;
2045 long i = 0;
2046 listitem_T *li;
2047
2048 len = list_len(l);
2049
2050 // Make an array with each entry pointing to an item in the List.
2051 ptrs = ALLOC_MULT(sortItem_T, len);
2052 if (ptrs == NULL)
2053 return;
2054
2055 // sort(): ptrs will be the list to sort
2056 FOR_ALL_LIST_ITEMS(l, li)
2057 {
2058 ptrs[i].item = li;
2059 ptrs[i].idx = i;
2060 ++i;
2061 }
2062
2063 info->item_compare_func_err = FALSE;
2064 info->item_compare_keep_zero = FALSE;
2065 // test the compare function
2066 if ((info->item_compare_func != NULL
2067 || info->item_compare_partial != NULL)
2068 && item_compare2((void *)&ptrs[0], (void *)&ptrs[1])
2069 == ITEM_COMPARE_FAIL)
Bram Moolenaara6f79292022-01-04 21:30:47 +00002070 emsg(_(e_sort_compare_function_failed));
Yegappan Lakshmanand92813a2021-12-21 13:19:42 +00002071 else
2072 {
2073 // Sort the array with item pointers.
2074 qsort((void *)ptrs, (size_t)len, sizeof(sortItem_T),
2075 info->item_compare_func == NULL
2076 && info->item_compare_partial == NULL
2077 ? item_compare : item_compare2);
2078
2079 if (!info->item_compare_func_err)
2080 {
2081 // Clear the List and append the items in sorted order.
2082 l->lv_first = l->lv_u.mat.lv_last
2083 = l->lv_u.mat.lv_idx_item = NULL;
2084 l->lv_len = 0;
2085 for (i = 0; i < len; ++i)
2086 list_append(l, ptrs[i].item);
2087 }
2088 }
2089
2090 vim_free(ptrs);
2091}
2092
2093/*
2094 * uniq() List "l"
2095 */
2096 static void
2097do_uniq(list_T *l, sortinfo_T *info)
2098{
2099 long len;
2100 sortItem_T *ptrs;
2101 long i = 0;
2102 listitem_T *li;
2103 int (*item_compare_func_ptr)(const void *, const void *);
2104
2105 len = list_len(l);
2106
2107 // Make an array with each entry pointing to an item in the List.
2108 ptrs = ALLOC_MULT(sortItem_T, len);
2109 if (ptrs == NULL)
2110 return;
2111
2112 // f_uniq(): ptrs will be a stack of items to remove
2113 info->item_compare_func_err = FALSE;
2114 info->item_compare_keep_zero = TRUE;
2115 item_compare_func_ptr = info->item_compare_func != NULL
2116 || info->item_compare_partial != NULL
2117 ? item_compare2 : item_compare;
2118
2119 for (li = l->lv_first; li != NULL && li->li_next != NULL;
2120 li = li->li_next)
2121 {
2122 if (item_compare_func_ptr((void *)&li, (void *)&li->li_next)
2123 == 0)
2124 ptrs[i++].item = li;
2125 if (info->item_compare_func_err)
2126 {
Bram Moolenaard82a47d2022-01-05 20:24:39 +00002127 emsg(_(e_uniq_compare_function_failed));
Yegappan Lakshmanand92813a2021-12-21 13:19:42 +00002128 break;
2129 }
2130 }
2131
2132 if (!info->item_compare_func_err)
2133 {
2134 while (--i >= 0)
2135 {
2136 li = ptrs[i].item->li_next;
2137 ptrs[i].item->li_next = li->li_next;
2138 if (li->li_next != NULL)
2139 li->li_next->li_prev = ptrs[i].item;
2140 else
2141 l->lv_u.mat.lv_last = ptrs[i].item;
2142 list_fix_watch(l, li);
2143 listitem_free(l, li);
2144 l->lv_len--;
2145 }
2146 }
2147
2148 vim_free(ptrs);
2149}
2150
2151/*
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00002152 * Parse the optional arguments supplied to the sort() or uniq() function and
2153 * return the values in "info".
Yegappan Lakshmanand92813a2021-12-21 13:19:42 +00002154 */
2155 static int
2156parse_sort_uniq_args(typval_T *argvars, sortinfo_T *info)
2157{
2158 info->item_compare_ic = FALSE;
2159 info->item_compare_lc = FALSE;
2160 info->item_compare_numeric = FALSE;
2161 info->item_compare_numbers = FALSE;
Yegappan Lakshmanand92813a2021-12-21 13:19:42 +00002162 info->item_compare_float = FALSE;
Yegappan Lakshmanand92813a2021-12-21 13:19:42 +00002163 info->item_compare_func = NULL;
2164 info->item_compare_partial = NULL;
2165 info->item_compare_selfdict = NULL;
2166
2167 if (argvars[1].v_type == VAR_UNKNOWN)
2168 return OK;
2169
2170 // optional second argument: {func}
2171 if (argvars[1].v_type == VAR_FUNC)
2172 info->item_compare_func = argvars[1].vval.v_string;
2173 else if (argvars[1].v_type == VAR_PARTIAL)
2174 info->item_compare_partial = argvars[1].vval.v_partial;
2175 else
2176 {
2177 int error = FALSE;
2178 int nr = 0;
2179
2180 if (argvars[1].v_type == VAR_NUMBER)
2181 {
2182 nr = tv_get_number_chk(&argvars[1], &error);
2183 if (error)
2184 return FAIL;
2185 if (nr == 1)
2186 info->item_compare_ic = TRUE;
2187 }
2188 if (nr != 1)
2189 {
2190 if (argvars[1].v_type != VAR_NUMBER)
2191 info->item_compare_func = tv_get_string(&argvars[1]);
2192 else if (nr != 0)
2193 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00002194 emsg(_(e_invalid_argument));
Yegappan Lakshmanand92813a2021-12-21 13:19:42 +00002195 return FAIL;
2196 }
2197 }
2198 if (info->item_compare_func != NULL)
2199 {
2200 if (*info->item_compare_func == NUL)
2201 {
2202 // empty string means default sort
2203 info->item_compare_func = NULL;
2204 }
2205 else if (STRCMP(info->item_compare_func, "n") == 0)
2206 {
2207 info->item_compare_func = NULL;
2208 info->item_compare_numeric = TRUE;
2209 }
2210 else if (STRCMP(info->item_compare_func, "N") == 0)
2211 {
2212 info->item_compare_func = NULL;
2213 info->item_compare_numbers = TRUE;
2214 }
Yegappan Lakshmanand92813a2021-12-21 13:19:42 +00002215 else if (STRCMP(info->item_compare_func, "f") == 0)
2216 {
2217 info->item_compare_func = NULL;
2218 info->item_compare_float = TRUE;
2219 }
Yegappan Lakshmanand92813a2021-12-21 13:19:42 +00002220 else if (STRCMP(info->item_compare_func, "i") == 0)
2221 {
2222 info->item_compare_func = NULL;
2223 info->item_compare_ic = TRUE;
2224 }
2225 else if (STRCMP(info->item_compare_func, "l") == 0)
2226 {
2227 info->item_compare_func = NULL;
2228 info->item_compare_lc = TRUE;
2229 }
2230 }
2231 }
2232
2233 if (argvars[2].v_type != VAR_UNKNOWN)
2234 {
2235 // optional third argument: {dict}
Yegappan Lakshmanan04c4c572022-08-30 19:48:24 +01002236 if (check_for_dict_arg(argvars, 2) == FAIL)
Yegappan Lakshmanand92813a2021-12-21 13:19:42 +00002237 return FAIL;
Yegappan Lakshmanand92813a2021-12-21 13:19:42 +00002238 info->item_compare_selfdict = argvars[2].vval.v_dict;
2239 }
2240
2241 return OK;
2242}
2243
2244/*
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02002245 * "sort()" or "uniq()" function
2246 */
2247 static void
2248do_sort_uniq(typval_T *argvars, typval_T *rettv, int sort)
2249{
2250 list_T *l;
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02002251 sortinfo_T *old_sortinfo;
2252 sortinfo_T info;
2253 long len;
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02002254
Yegappan Lakshmanan0ad871d2021-07-23 20:37:56 +02002255 if (in_vim9script()
2256 && (check_for_list_arg(argvars, 0) == FAIL
2257 || (argvars[1].v_type != VAR_UNKNOWN
Bram Moolenaar2007dd42022-02-23 13:17:47 +00002258 && (check_for_string_or_func_arg(argvars, 1) == FAIL
2259 || check_for_opt_dict_arg(argvars, 2) == FAIL))))
Yegappan Lakshmanan0ad871d2021-07-23 20:37:56 +02002260 return;
2261
Yegappan Lakshmanand92813a2021-12-21 13:19:42 +00002262 if (argvars[0].v_type != VAR_LIST)
2263 {
Bram Moolenaar3a846e62022-01-01 16:21:00 +00002264 semsg(_(e_argument_of_str_must_be_list), sort ? "sort()" : "uniq()");
Yegappan Lakshmanand92813a2021-12-21 13:19:42 +00002265 return;
2266 }
2267
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002268 // Pointer to current info struct used in compare function. Save and
2269 // restore the current one for nested calls.
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02002270 old_sortinfo = sortinfo;
2271 sortinfo = &info;
2272
Yegappan Lakshmanand92813a2021-12-21 13:19:42 +00002273 l = argvars[0].vval.v_list;
2274 if (l != NULL && value_check_lock(l->lv_lock,
2275 (char_u *)(sort ? N_("sort() argument") : N_("uniq() argument")),
2276 TRUE))
2277 goto theend;
2278 rettv_list_set(rettv, l);
2279 if (l == NULL)
2280 goto theend;
2281 CHECK_LIST_MATERIALIZE(l);
2282
2283 len = list_len(l);
2284 if (len <= 1)
2285 goto theend; // short list sorts pretty quickly
2286
2287 if (parse_sort_uniq_args(argvars, &info) == FAIL)
2288 goto theend;
2289
2290 if (sort)
2291 do_sort(l, &info);
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02002292 else
Yegappan Lakshmanand92813a2021-12-21 13:19:42 +00002293 do_uniq(l, &info);
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02002294
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02002295theend:
2296 sortinfo = old_sortinfo;
2297}
2298
2299/*
2300 * "sort({list})" function
2301 */
2302 void
2303f_sort(typval_T *argvars, typval_T *rettv)
2304{
2305 do_sort_uniq(argvars, rettv, TRUE);
2306}
2307
2308/*
2309 * "uniq({list})" function
2310 */
2311 void
2312f_uniq(typval_T *argvars, typval_T *rettv)
2313{
2314 do_sort_uniq(argvars, rettv, FALSE);
2315}
2316
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02002317/*
2318 * Handle one item for map() and filter().
Bram Moolenaarea696852020-11-09 18:31:39 +01002319 * Sets v:val to "tv". Caller must set v:key.
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02002320 */
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00002321 int
Bram Moolenaarea696852020-11-09 18:31:39 +01002322filter_map_one(
2323 typval_T *tv, // original value
2324 typval_T *expr, // callback
2325 filtermap_T filtermap,
Bram Moolenaar82418262022-09-28 16:16:15 +01002326 funccall_T *fc, // from eval_expr_get_funccal()
Bram Moolenaarea696852020-11-09 18:31:39 +01002327 typval_T *newtv, // for map() and mapnew(): new value
2328 int *remp) // for filter(): remove flag
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02002329{
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02002330 typval_T argv[3];
2331 int retval = FAIL;
2332
2333 copy_tv(tv, get_vim_var_tv(VV_VAL));
2334 argv[0] = *get_vim_var_tv(VV_KEY);
2335 argv[1] = *get_vim_var_tv(VV_VAL);
Bram Moolenaar82418262022-09-28 16:16:15 +01002336 if (eval_expr_typval(expr, argv, 2, fc, newtv) == FAIL)
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02002337 goto theend;
Bram Moolenaarea696852020-11-09 18:31:39 +01002338 if (filtermap == FILTERMAP_FILTER)
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02002339 {
2340 int error = FALSE;
2341
2342 // filter(): when expr is zero remove the item
Bram Moolenaar56acb092020-08-16 14:48:19 +02002343 if (in_vim9script())
Bram Moolenaar18024052021-12-25 21:43:28 +00002344 *remp = !tv_get_bool_chk(newtv, &error);
Bram Moolenaar56acb092020-08-16 14:48:19 +02002345 else
Bram Moolenaarea696852020-11-09 18:31:39 +01002346 *remp = (tv_get_number_chk(newtv, &error) == 0);
2347 clear_tv(newtv);
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02002348 // On type error, nothing has been removed; return FAIL to stop the
2349 // loop. The error message was given by tv_get_number_chk().
2350 if (error)
2351 goto theend;
2352 }
2353 retval = OK;
2354theend:
2355 clear_tv(get_vim_var_tv(VV_VAL));
2356 return retval;
2357}
2358
2359/*
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00002360 * Implementation of map() and filter() for a List. Apply "expr" to every item
2361 * in List "l" and return the result in "rettv".
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00002362 */
2363 static void
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00002364list_filter_map(
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00002365 list_T *l,
2366 filtermap_T filtermap,
2367 type_T *argtype,
2368 char *func_name,
2369 char_u *arg_errmsg,
2370 typval_T *expr,
2371 typval_T *rettv)
2372{
2373 int prev_lock;
2374 list_T *l_ret = NULL;
2375 int idx = 0;
2376 int rem;
2377 listitem_T *li, *nli;
Bram Moolenaar82418262022-09-28 16:16:15 +01002378 typval_T newtv;
2379 funccall_T *fc;
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00002380
2381 if (filtermap == FILTERMAP_MAPNEW)
2382 {
2383 rettv->v_type = VAR_LIST;
2384 rettv->vval.v_list = NULL;
2385 }
Bram Moolenaar52df40e2022-09-28 13:22:59 +01002386 if (l == NULL || (filtermap == FILTERMAP_FILTER
2387 && value_check_lock(l->lv_lock, arg_errmsg, TRUE)))
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00002388 return;
2389
2390 prev_lock = l->lv_lock;
2391
2392 if (filtermap == FILTERMAP_MAPNEW)
2393 {
2394 if (rettv_list_alloc(rettv) == FAIL)
2395 return;
2396 l_ret = rettv->vval.v_list;
2397 }
2398 // set_vim_var_nr() doesn't set the type
2399 set_vim_var_type(VV_KEY, VAR_NUMBER);
2400
2401 if (filtermap != FILTERMAP_FILTER && l->lv_lock == 0)
2402 l->lv_lock = VAR_LOCKED;
2403
Bram Moolenaar82418262022-09-28 16:16:15 +01002404 // Create one funccal_T for all eval_expr_typval() calls.
2405 fc = eval_expr_get_funccal(expr, &newtv);
2406
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00002407 if (l->lv_first == &range_list_item)
2408 {
2409 varnumber_T val = l->lv_u.nonmat.lv_start;
2410 int len = l->lv_len;
2411 int stride = l->lv_u.nonmat.lv_stride;
2412
2413 // List from range(): loop over the numbers
2414 if (filtermap != FILTERMAP_MAPNEW)
2415 {
2416 l->lv_first = NULL;
2417 l->lv_u.mat.lv_last = NULL;
2418 l->lv_len = 0;
2419 l->lv_u.mat.lv_idx_item = NULL;
2420 }
2421
2422 for (idx = 0; idx < len; ++idx)
2423 {
2424 typval_T tv;
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00002425
2426 tv.v_type = VAR_NUMBER;
2427 tv.v_lock = 0;
2428 tv.vval.v_number = val;
2429 set_vim_var_nr(VV_KEY, idx);
Bram Moolenaar82418262022-09-28 16:16:15 +01002430 if (filter_map_one(&tv, expr, filtermap, fc, &newtv, &rem) == FAIL)
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00002431 break;
2432 if (did_emsg)
2433 {
2434 clear_tv(&newtv);
2435 break;
2436 }
2437 if (filtermap != FILTERMAP_FILTER)
2438 {
2439 if (filtermap == FILTERMAP_MAP && argtype != NULL
2440 && check_typval_arg_type(
2441 argtype->tt_member, &newtv,
2442 func_name, 0) == FAIL)
2443 {
2444 clear_tv(&newtv);
2445 break;
2446 }
2447 // map(), mapnew(): always append the new value to the
2448 // list
2449 if (list_append_tv_move(filtermap == FILTERMAP_MAP
2450 ? l : l_ret, &newtv) == FAIL)
2451 break;
2452 }
2453 else if (!rem)
2454 {
2455 // filter(): append the list item value when not rem
2456 if (list_append_tv_move(l, &tv) == FAIL)
2457 break;
2458 }
2459
2460 val += stride;
2461 }
2462 }
2463 else
2464 {
2465 // Materialized list: loop over the items
2466 for (li = l->lv_first; li != NULL; li = nli)
2467 {
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00002468 if (filtermap == FILTERMAP_MAP && value_check_lock(
2469 li->li_tv.v_lock, arg_errmsg, TRUE))
2470 break;
2471 nli = li->li_next;
2472 set_vim_var_nr(VV_KEY, idx);
Bram Moolenaar82418262022-09-28 16:16:15 +01002473 if (filter_map_one(&li->li_tv, expr, filtermap, fc,
2474 &newtv, &rem) == FAIL)
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00002475 break;
2476 if (did_emsg)
2477 {
2478 clear_tv(&newtv);
2479 break;
2480 }
2481 if (filtermap == FILTERMAP_MAP)
2482 {
2483 if (argtype != NULL && check_typval_arg_type(
2484 argtype->tt_member, &newtv, func_name, 0) == FAIL)
2485 {
2486 clear_tv(&newtv);
2487 break;
2488 }
2489 // map(): replace the list item value
2490 clear_tv(&li->li_tv);
2491 newtv.v_lock = 0;
2492 li->li_tv = newtv;
2493 }
2494 else if (filtermap == FILTERMAP_MAPNEW)
2495 {
2496 // mapnew(): append the list item value
2497 if (list_append_tv_move(l_ret, &newtv) == FAIL)
2498 break;
2499 }
2500 else if (filtermap == FILTERMAP_FILTER && rem)
2501 listitem_remove(l, li);
2502 ++idx;
2503 }
2504 }
2505
2506 l->lv_lock = prev_lock;
Bram Moolenaar82418262022-09-28 16:16:15 +01002507 if (fc != NULL)
2508 remove_funccal();
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00002509}
2510
2511/*
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02002512 * Implementation of map() and filter().
2513 */
2514 static void
Bram Moolenaarea696852020-11-09 18:31:39 +01002515filter_map(typval_T *argvars, typval_T *rettv, filtermap_T filtermap)
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02002516{
2517 typval_T *expr;
Bram Moolenaar7a3fe3e2021-07-22 14:58:47 +02002518 char *func_name = filtermap == FILTERMAP_MAP ? "map()"
Bram Moolenaarea696852020-11-09 18:31:39 +01002519 : filtermap == FILTERMAP_MAPNEW ? "mapnew()"
Bram Moolenaar7a3fe3e2021-07-22 14:58:47 +02002520 : "filter()";
Bram Moolenaarea696852020-11-09 18:31:39 +01002521 char_u *arg_errmsg = (char_u *)(filtermap == FILTERMAP_MAP
2522 ? N_("map() argument")
2523 : filtermap == FILTERMAP_MAPNEW
2524 ? N_("mapnew() argument")
2525 : N_("filter() argument"));
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02002526 int save_did_emsg;
Bram Moolenaar70250fb2021-01-16 19:01:53 +01002527 type_T *type = NULL;
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02002528
Bram Moolenaarea696852020-11-09 18:31:39 +01002529 // map() and filter() return the first argument, also on failure.
Bram Moolenaar2d877592021-12-16 08:21:09 +00002530 if (filtermap != FILTERMAP_MAPNEW && argvars[0].v_type != VAR_STRING)
Bram Moolenaarea696852020-11-09 18:31:39 +01002531 copy_tv(&argvars[0], rettv);
Yegappan Lakshmanan0ad871d2021-07-23 20:37:56 +02002532
2533 if (in_vim9script()
Bram Moolenaar2d877592021-12-16 08:21:09 +00002534 && (check_for_list_or_dict_or_blob_or_string_arg(argvars, 0)
2535 == FAIL))
Yegappan Lakshmanan0ad871d2021-07-23 20:37:56 +02002536 return;
2537
Bram Moolenaar70250fb2021-01-16 19:01:53 +01002538 if (filtermap == FILTERMAP_MAP && in_vim9script())
2539 {
Bram Moolenaar35c807d2022-01-27 16:36:29 +00002540 // Check that map() does not change the declared type of the list or
2541 // dict.
2542 if (argvars[0].v_type == VAR_DICT && argvars[0].vval.v_dict != NULL)
2543 type = argvars[0].vval.v_dict->dv_type;
2544 else if (argvars[0].v_type == VAR_LIST
2545 && argvars[0].vval.v_list != NULL)
2546 type = argvars[0].vval.v_list->lv_type;
Bram Moolenaar70250fb2021-01-16 19:01:53 +01002547 }
Bram Moolenaarffdf8ad2020-10-15 22:29:17 +02002548
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00002549 if (argvars[0].v_type != VAR_BLOB
2550 && argvars[0].v_type != VAR_LIST
2551 && argvars[0].v_type != VAR_DICT
2552 && argvars[0].v_type != VAR_STRING)
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02002553 {
rbtnnc479ce02021-12-15 19:14:54 +00002554 semsg(_(e_argument_of_str_must_be_list_string_dictionary_or_blob),
2555 func_name);
Bram Moolenaar98cd3032022-01-27 17:37:41 +00002556 return;
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02002557 }
2558
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02002559 // On type errors, the preceding call has already displayed an error
2560 // message. Avoid a misleading error message for an empty string that
2561 // was not passed as argument.
Bram Moolenaar35c807d2022-01-27 16:36:29 +00002562 expr = &argvars[1];
Yegappan Lakshmanane8575982023-01-14 12:32:28 +00002563 if (expr->v_type == VAR_UNKNOWN)
2564 return;
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02002565
Yegappan Lakshmanane8575982023-01-14 12:32:28 +00002566 typval_T save_val;
2567 typval_T save_key;
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02002568
Yegappan Lakshmanane8575982023-01-14 12:32:28 +00002569 prepare_vimvar(VV_VAL, &save_val);
2570 prepare_vimvar(VV_KEY, &save_key);
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02002571
Yegappan Lakshmanane8575982023-01-14 12:32:28 +00002572 // We reset "did_emsg" to be able to detect whether an error
2573 // occurred during evaluation of the expression.
2574 save_did_emsg = did_emsg;
2575 did_emsg = FALSE;
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02002576
Yegappan Lakshmanane8575982023-01-14 12:32:28 +00002577 if (argvars[0].v_type == VAR_DICT)
2578 dict_filter_map(argvars[0].vval.v_dict, filtermap, type, func_name,
2579 arg_errmsg, expr, rettv);
2580 else if (argvars[0].v_type == VAR_BLOB)
2581 blob_filter_map(argvars[0].vval.v_blob, filtermap, expr, rettv);
2582 else if (argvars[0].v_type == VAR_STRING)
2583 string_filter_map(tv_get_string(&argvars[0]), filtermap, expr,
2584 rettv);
2585 else // argvars[0].v_type == VAR_LIST
2586 list_filter_map(argvars[0].vval.v_list, filtermap, type, func_name,
2587 arg_errmsg, expr, rettv);
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02002588
Yegappan Lakshmanane8575982023-01-14 12:32:28 +00002589 restore_vimvar(VV_KEY, &save_key);
2590 restore_vimvar(VV_VAL, &save_val);
2591
2592 did_emsg |= save_did_emsg;
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02002593}
2594
2595/*
2596 * "filter()" function
2597 */
2598 void
2599f_filter(typval_T *argvars, typval_T *rettv)
2600{
Bram Moolenaarea696852020-11-09 18:31:39 +01002601 filter_map(argvars, rettv, FILTERMAP_FILTER);
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02002602}
2603
2604/*
2605 * "map()" function
2606 */
2607 void
2608f_map(typval_T *argvars, typval_T *rettv)
2609{
Bram Moolenaarea696852020-11-09 18:31:39 +01002610 filter_map(argvars, rettv, FILTERMAP_MAP);
2611}
2612
2613/*
2614 * "mapnew()" function
2615 */
2616 void
2617f_mapnew(typval_T *argvars, typval_T *rettv)
2618{
2619 filter_map(argvars, rettv, FILTERMAP_MAPNEW);
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02002620}
2621
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002622/*
2623 * "add(list, item)" function
2624 */
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00002625 static void
2626list_add(typval_T *argvars, typval_T *rettv)
2627{
2628 list_T *l = argvars[0].vval.v_list;
2629
2630 if (l == NULL)
2631 {
2632 if (in_vim9script())
2633 emsg(_(e_cannot_add_to_null_list));
2634 }
2635 else if (!value_check_lock(l->lv_lock,
2636 (char_u *)N_("add() argument"), TRUE)
2637 && list_append_tv(l, &argvars[1]) == OK)
2638 {
2639 copy_tv(&argvars[0], rettv);
2640 }
2641}
2642
2643/*
2644 * "add(object, item)" function
2645 */
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002646 void
2647f_add(typval_T *argvars, typval_T *rettv)
2648{
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002649 rettv->vval.v_number = 1; // Default: Failed
Yegappan Lakshmanan0ad871d2021-07-23 20:37:56 +02002650
2651 if (in_vim9script()
2652 && (check_for_list_or_blob_arg(argvars, 0) == FAIL
2653 || (argvars[0].v_type == VAR_BLOB
2654 && check_for_number_arg(argvars, 1) == FAIL)))
2655 return;
2656
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002657 if (argvars[0].v_type == VAR_LIST)
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00002658 list_add(argvars, rettv);
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002659 else if (argvars[0].v_type == VAR_BLOB)
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00002660 blob_add(argvars, rettv);
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002661 else
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00002662 emsg(_(e_list_or_blob_required));
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002663}
2664
2665/*
Yegappan Lakshmanand92813a2021-12-21 13:19:42 +00002666 * Count the number of times item "needle" occurs in List "l" starting at index
2667 * "idx". Case is ignored if "ic" is TRUE.
2668 */
2669 static long
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00002670list_count(list_T *l, typval_T *needle, long idx, int ic)
Yegappan Lakshmanand92813a2021-12-21 13:19:42 +00002671{
2672 long n = 0;
2673 listitem_T *li;
2674
2675 if (l == NULL)
2676 return 0;
2677
2678 CHECK_LIST_MATERIALIZE(l);
2679
2680 if (list_len(l) == 0)
2681 return 0;
2682
2683 li = list_find(l, idx);
2684 if (li == NULL)
2685 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00002686 semsg(_(e_list_index_out_of_range_nr), idx);
Yegappan Lakshmanand92813a2021-12-21 13:19:42 +00002687 return 0;
2688 }
2689
2690 for ( ; li != NULL; li = li->li_next)
2691 if (tv_equal(&li->li_tv, needle, ic, FALSE))
2692 ++n;
2693
2694 return n;
2695}
2696
2697/*
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002698 * "count()" function
2699 */
2700 void
2701f_count(typval_T *argvars, typval_T *rettv)
2702{
2703 long n = 0;
2704 int ic = FALSE;
2705 int error = FALSE;
2706
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +02002707 if (in_vim9script()
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02002708 && (check_for_string_or_list_or_dict_arg(argvars, 0) == FAIL
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +02002709 || check_for_opt_bool_arg(argvars, 2) == FAIL
2710 || (argvars[2].v_type != VAR_UNKNOWN
2711 && check_for_opt_number_arg(argvars, 3) == FAIL)))
2712 return;
2713
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002714 if (argvars[2].v_type != VAR_UNKNOWN)
Bram Moolenaar119f5572020-09-02 21:31:22 +02002715 ic = (int)tv_get_bool_chk(&argvars[2], &error);
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002716
Yegappan Lakshmanand92813a2021-12-21 13:19:42 +00002717 if (!error && argvars[0].v_type == VAR_STRING)
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00002718 n = string_count(argvars[0].vval.v_string,
Yegappan Lakshmanand92813a2021-12-21 13:19:42 +00002719 tv_get_string_chk(&argvars[1]), ic);
2720 else if (!error && argvars[0].v_type == VAR_LIST)
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002721 {
Yegappan Lakshmanand92813a2021-12-21 13:19:42 +00002722 long idx = 0;
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002723
Yegappan Lakshmanand92813a2021-12-21 13:19:42 +00002724 if (argvars[2].v_type != VAR_UNKNOWN
2725 && argvars[3].v_type != VAR_UNKNOWN)
2726 idx = (long)tv_get_number_chk(&argvars[3], &error);
2727 if (!error)
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00002728 n = list_count(argvars[0].vval.v_list, &argvars[1], idx, ic);
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002729 }
Yegappan Lakshmanand92813a2021-12-21 13:19:42 +00002730 else if (!error && argvars[0].v_type == VAR_DICT)
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002731 {
Yegappan Lakshmanand92813a2021-12-21 13:19:42 +00002732 if (argvars[2].v_type != VAR_UNKNOWN
2733 && argvars[3].v_type != VAR_UNKNOWN)
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00002734 emsg(_(e_invalid_argument));
Yegappan Lakshmanand92813a2021-12-21 13:19:42 +00002735 else
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00002736 n = dict_count(argvars[0].vval.v_dict, &argvars[1], ic);
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002737 }
2738 else
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00002739 semsg(_(e_argument_of_str_must_be_list_or_dictionary), "count()");
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002740 rettv->vval.v_number = n;
2741}
2742
2743/*
Yegappan Lakshmanand92813a2021-12-21 13:19:42 +00002744 * extend() a List. Append List argvars[1] to List argvars[0] before index
2745 * argvars[3] and return the resulting list in "rettv". "is_new" is TRUE for
2746 * extendnew().
2747 */
2748 static void
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00002749list_extend_func(
Yegappan Lakshmanand92813a2021-12-21 13:19:42 +00002750 typval_T *argvars,
2751 type_T *type,
2752 char *func_name,
2753 char_u *arg_errmsg,
2754 int is_new,
2755 typval_T *rettv)
2756{
2757 list_T *l1, *l2;
2758 listitem_T *item;
2759 long before;
2760 int error = FALSE;
2761
2762 l1 = argvars[0].vval.v_list;
2763 if (l1 == NULL)
2764 {
2765 emsg(_(e_cannot_extend_null_list));
2766 return;
2767 }
2768 l2 = argvars[1].vval.v_list;
2769 if ((is_new || !value_check_lock(l1->lv_lock, arg_errmsg, TRUE))
Bram Moolenaar381692b2022-02-02 20:01:27 +00002770 && l2 != NULL)
Yegappan Lakshmanand92813a2021-12-21 13:19:42 +00002771 {
2772 if (is_new)
2773 {
Bram Moolenaar381692b2022-02-02 20:01:27 +00002774 l1 = list_copy(l1, FALSE, TRUE, get_copyID());
Yegappan Lakshmanand92813a2021-12-21 13:19:42 +00002775 if (l1 == NULL)
2776 return;
2777 }
2778
2779 if (argvars[2].v_type != VAR_UNKNOWN)
2780 {
2781 before = (long)tv_get_number_chk(&argvars[2], &error);
2782 if (error)
2783 return; // type error; errmsg already given
2784
2785 if (before == l1->lv_len)
2786 item = NULL;
2787 else
2788 {
2789 item = list_find(l1, before);
2790 if (item == NULL)
2791 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00002792 semsg(_(e_list_index_out_of_range_nr), before);
Yegappan Lakshmanand92813a2021-12-21 13:19:42 +00002793 return;
2794 }
2795 }
2796 }
2797 else
2798 item = NULL;
2799 if (type != NULL && check_typval_arg_type(
2800 type, &argvars[1], func_name, 2) == FAIL)
2801 return;
2802 list_extend(l1, l2, item);
2803
2804 if (is_new)
2805 {
2806 rettv->v_type = VAR_LIST;
2807 rettv->vval.v_list = l1;
2808 rettv->v_lock = FALSE;
2809 }
2810 else
2811 copy_tv(&argvars[0], rettv);
2812 }
2813}
2814
2815/*
Bram Moolenaarb0e6b512021-01-12 20:23:40 +01002816 * "extend()" or "extendnew()" function. "is_new" is TRUE for extendnew().
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002817 */
Bram Moolenaarb0e6b512021-01-12 20:23:40 +01002818 static void
2819extend(typval_T *argvars, typval_T *rettv, char_u *arg_errmsg, int is_new)
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002820{
Bram Moolenaarc03f5c62021-01-31 17:48:30 +01002821 type_T *type = NULL;
Bram Moolenaar7a3fe3e2021-07-22 14:58:47 +02002822 char *func_name = is_new ? "extendnew()" : "extend()";
Bram Moolenaarc03f5c62021-01-31 17:48:30 +01002823
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002824 if (argvars[0].v_type == VAR_LIST && argvars[1].v_type == VAR_LIST)
Bram Moolenaarad8f2482022-01-03 16:52:28 +00002825 {
2826 // Check that extend() does not change the type of the list if it was
2827 // declared.
2828 if (!is_new && in_vim9script() && argvars[0].vval.v_list != NULL)
2829 type = argvars[0].vval.v_list->lv_type;
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00002830 list_extend_func(argvars, type, func_name, arg_errmsg, is_new, rettv);
Bram Moolenaarad8f2482022-01-03 16:52:28 +00002831 }
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002832 else if (argvars[0].v_type == VAR_DICT && argvars[1].v_type == VAR_DICT)
Bram Moolenaarad8f2482022-01-03 16:52:28 +00002833 {
Bram Moolenaarbe19d782023-03-09 22:06:49 +00002834 // Check that extend() does not change the type of the dict if it was
Bram Moolenaarad8f2482022-01-03 16:52:28 +00002835 // declared.
2836 if (!is_new && in_vim9script() && argvars[0].vval.v_dict != NULL)
2837 type = argvars[0].vval.v_dict->dv_type;
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00002838 dict_extend_func(argvars, type, func_name, arg_errmsg, is_new, rettv);
Bram Moolenaarad8f2482022-01-03 16:52:28 +00002839 }
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002840 else
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00002841 semsg(_(e_argument_of_str_must_be_list_or_dictionary), func_name);
Bram Moolenaarb0e6b512021-01-12 20:23:40 +01002842}
2843
2844/*
2845 * "extend(list, list [, idx])" function
2846 * "extend(dict, dict [, action])" function
2847 */
2848 void
2849f_extend(typval_T *argvars, typval_T *rettv)
2850{
2851 char_u *errmsg = (char_u *)N_("extend() argument");
2852
2853 extend(argvars, rettv, errmsg, FALSE);
2854}
2855
2856/*
2857 * "extendnew(list, list [, idx])" function
2858 * "extendnew(dict, dict [, action])" function
2859 */
2860 void
2861f_extendnew(typval_T *argvars, typval_T *rettv)
2862{
2863 char_u *errmsg = (char_u *)N_("extendnew() argument");
2864
2865 extend(argvars, rettv, errmsg, TRUE);
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002866}
2867
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00002868 static void
2869list_insert_func(typval_T *argvars, typval_T *rettv)
2870{
2871 list_T *l = argvars[0].vval.v_list;
2872 long before = 0;
2873 listitem_T *item;
2874 int error = FALSE;
2875
2876 if (l == NULL)
2877 {
2878 if (in_vim9script())
2879 emsg(_(e_cannot_add_to_null_list));
2880 return;
2881 }
2882
2883 if (value_check_lock(l->lv_lock, (char_u *)N_("insert() argument"), TRUE))
2884 return;
2885
2886 if (argvars[2].v_type != VAR_UNKNOWN)
2887 before = (long)tv_get_number_chk(&argvars[2], &error);
2888 if (error)
2889 return; // type error; errmsg already given
2890
2891 if (before == l->lv_len)
2892 item = NULL;
2893 else
2894 {
2895 item = list_find(l, before);
2896 if (item == NULL)
2897 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00002898 semsg(_(e_list_index_out_of_range_nr), before);
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00002899 l = NULL;
2900 }
2901 }
2902 if (l != NULL)
2903 {
2904 (void)list_insert_tv(l, &argvars[1], item);
2905 copy_tv(&argvars[0], rettv);
2906 }
2907}
2908
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002909/*
2910 * "insert()" function
2911 */
2912 void
2913f_insert(typval_T *argvars, typval_T *rettv)
2914{
Yegappan Lakshmanan0ad871d2021-07-23 20:37:56 +02002915 if (in_vim9script()
2916 && (check_for_list_or_blob_arg(argvars, 0) == FAIL
2917 || (argvars[0].v_type == VAR_BLOB
2918 && check_for_number_arg(argvars, 1) == FAIL)
2919 || check_for_opt_number_arg(argvars, 2) == FAIL))
2920 return;
2921
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002922 if (argvars[0].v_type == VAR_BLOB)
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00002923 blob_insert_func(argvars, rettv);
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002924 else if (argvars[0].v_type != VAR_LIST)
Bram Moolenaard82a47d2022-01-05 20:24:39 +00002925 semsg(_(e_argument_of_str_must_be_list_or_blob), "insert()");
Bram Moolenaar39211cb2021-04-18 15:48:04 +02002926 else
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00002927 list_insert_func(argvars, rettv);
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002928}
2929
2930/*
2931 * "remove()" function
2932 */
2933 void
2934f_remove(typval_T *argvars, typval_T *rettv)
2935{
2936 char_u *arg_errmsg = (char_u *)N_("remove() argument");
2937
Yegappan Lakshmanan0ad871d2021-07-23 20:37:56 +02002938 if (in_vim9script()
2939 && (check_for_list_or_dict_or_blob_arg(argvars, 0) == FAIL
2940 || ((argvars[0].v_type == VAR_LIST
2941 || argvars[0].v_type == VAR_BLOB)
2942 && (check_for_number_arg(argvars, 1) == FAIL
2943 || check_for_opt_number_arg(argvars, 2) == FAIL))
2944 || (argvars[0].v_type == VAR_DICT
2945 && check_for_string_or_number_arg(argvars, 1) == FAIL)))
2946 return;
2947
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002948 if (argvars[0].v_type == VAR_DICT)
2949 dict_remove(argvars, rettv, arg_errmsg);
2950 else if (argvars[0].v_type == VAR_BLOB)
Sean Dewar80d73952021-08-04 19:25:54 +02002951 blob_remove(argvars, rettv, arg_errmsg);
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002952 else if (argvars[0].v_type == VAR_LIST)
2953 list_remove(argvars, rettv, arg_errmsg);
2954 else
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00002955 semsg(_(e_argument_of_str_must_be_list_dictionary_or_blob), "remove()");
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002956}
2957
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00002958 static void
2959list_reverse(list_T *l, typval_T *rettv)
2960{
2961 listitem_T *li, *ni;
2962
2963 rettv_list_set(rettv, l);
2964 if (l != NULL
2965 && !value_check_lock(l->lv_lock,
2966 (char_u *)N_("reverse() argument"), TRUE))
2967 {
2968 if (l->lv_first == &range_list_item)
2969 {
2970 varnumber_T new_start = l->lv_u.nonmat.lv_start
=?UTF-8?q?Dundar=20G=C3=B6c?=d5cec1f2022-01-29 15:19:23 +00002971 + ((varnumber_T)l->lv_len - 1) * l->lv_u.nonmat.lv_stride;
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00002972 l->lv_u.nonmat.lv_end = new_start
2973 - (l->lv_u.nonmat.lv_end - l->lv_u.nonmat.lv_start);
2974 l->lv_u.nonmat.lv_start = new_start;
2975 l->lv_u.nonmat.lv_stride = -l->lv_u.nonmat.lv_stride;
2976 return;
2977 }
2978 li = l->lv_u.mat.lv_last;
2979 l->lv_first = l->lv_u.mat.lv_last = NULL;
2980 l->lv_len = 0;
2981 while (li != NULL)
2982 {
2983 ni = li->li_prev;
2984 list_append(l, li);
2985 li = ni;
2986 }
2987 l->lv_u.mat.lv_idx = l->lv_len - l->lv_u.mat.lv_idx - 1;
2988 }
2989}
2990
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002991/*
2992 * "reverse({list})" function
2993 */
2994 void
2995f_reverse(typval_T *argvars, typval_T *rettv)
2996{
Yegappan Lakshmanan0ad871d2021-07-23 20:37:56 +02002997 if (in_vim9script() && check_for_list_or_blob_arg(argvars, 0) == FAIL)
2998 return;
2999
Bram Moolenaar08c308a2019-09-04 17:48:15 +02003000 if (argvars[0].v_type == VAR_BLOB)
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00003001 blob_reverse(argvars[0].vval.v_blob, rettv);
3002 else if (argvars[0].v_type != VAR_LIST)
Bram Moolenaard82a47d2022-01-05 20:24:39 +00003003 semsg(_(e_argument_of_str_must_be_list_or_blob), "reverse()");
Bram Moolenaaref982572021-08-12 19:27:57 +02003004 else
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00003005 list_reverse(argvars[0].vval.v_list, rettv);
Bram Moolenaar08c308a2019-09-04 17:48:15 +02003006}
3007
Bram Moolenaar85629982020-06-01 18:39:20 +02003008/*
Bram Moolenaarf1c60d42022-09-22 17:07:00 +01003009 * Implementation of reduce() for list "argvars[0]", using the function "expr"
3010 * starting with the optional initial value argvars[2] and return the result in
3011 * "rettv".
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00003012 */
3013 static void
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00003014list_reduce(
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00003015 typval_T *argvars,
Bram Moolenaarf1c60d42022-09-22 17:07:00 +01003016 typval_T *expr,
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00003017 typval_T *rettv)
3018{
3019 list_T *l = argvars[0].vval.v_list;
3020 listitem_T *li = NULL;
Bram Moolenaar52df40e2022-09-28 13:22:59 +01003021 int range_list;
3022 int range_idx = 0;
3023 varnumber_T range_val = 0;
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00003024 typval_T initial;
3025 typval_T argv[3];
3026 int r;
3027 int called_emsg_start = called_emsg;
3028 int prev_locked;
Bram Moolenaar82418262022-09-28 16:16:15 +01003029 funccall_T *fc;
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00003030
Bram Moolenaar52df40e2022-09-28 13:22:59 +01003031 // Using reduce on a range() uses "range_idx" and "range_val".
3032 range_list = l != NULL && l->lv_first == &range_list_item;
3033 if (range_list)
3034 range_val = l->lv_u.nonmat.lv_start;
3035
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00003036 if (argvars[2].v_type == VAR_UNKNOWN)
3037 {
Bram Moolenaar52df40e2022-09-28 13:22:59 +01003038 if (l == NULL || l->lv_len == 0)
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00003039 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00003040 semsg(_(e_reduce_of_an_empty_str_with_no_initial_value), "List");
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00003041 return;
3042 }
Bram Moolenaar52df40e2022-09-28 13:22:59 +01003043 if (range_list)
3044 {
3045 initial.v_type = VAR_NUMBER;
3046 initial.vval.v_number = range_val;
3047 range_val += l->lv_u.nonmat.lv_stride;
3048 range_idx = 1;
3049 }
3050 else
3051 {
3052 initial = l->lv_first->li_tv;
3053 li = l->lv_first->li_next;
3054 }
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00003055 }
3056 else
3057 {
3058 initial = argvars[2];
Bram Moolenaar52df40e2022-09-28 13:22:59 +01003059 if (l != NULL && !range_list)
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00003060 li = l->lv_first;
3061 }
3062 copy_tv(&initial, rettv);
3063
3064 if (l == NULL)
3065 return;
3066
Bram Moolenaar82418262022-09-28 16:16:15 +01003067 // Create one funccal_T for all eval_expr_typval() calls.
3068 fc = eval_expr_get_funccal(expr, rettv);
3069
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00003070 prev_locked = l->lv_lock;
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00003071 l->lv_lock = VAR_FIXED; // disallow the list changing here
Bram Moolenaar52df40e2022-09-28 13:22:59 +01003072
3073 while (range_list ? range_idx < l->lv_len : li != NULL)
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00003074 {
3075 argv[0] = *rettv;
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00003076 rettv->v_type = VAR_UNKNOWN;
Bram Moolenaarf1c60d42022-09-22 17:07:00 +01003077
Bram Moolenaar52df40e2022-09-28 13:22:59 +01003078 if (range_list)
3079 {
3080 argv[1].v_type = VAR_NUMBER;
3081 argv[1].vval.v_number = range_val;
3082 }
3083 else
3084 argv[1] = li->li_tv;
3085
Bram Moolenaar82418262022-09-28 16:16:15 +01003086 r = eval_expr_typval(expr, argv, 2, fc, rettv);
Bram Moolenaarf1c60d42022-09-22 17:07:00 +01003087
Bram Moolenaar1936c762022-09-28 15:19:10 +01003088 if (argv[0].v_type != VAR_NUMBER && argv[0].v_type != VAR_UNKNOWN)
3089 clear_tv(&argv[0]);
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00003090 if (r == FAIL || called_emsg != called_emsg_start)
3091 break;
Bram Moolenaar52df40e2022-09-28 13:22:59 +01003092
Bram Moolenaar1936c762022-09-28 15:19:10 +01003093 // advance to the next item
Bram Moolenaar52df40e2022-09-28 13:22:59 +01003094 if (range_list)
3095 {
3096 range_val += l->lv_u.nonmat.lv_stride;
3097 ++range_idx;
3098 }
3099 else
3100 li = li->li_next;
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00003101 }
Bram Moolenaar52df40e2022-09-28 13:22:59 +01003102
Bram Moolenaar82418262022-09-28 16:16:15 +01003103 if (fc != NULL)
3104 remove_funccal();
3105
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00003106 l->lv_lock = prev_locked;
3107}
3108
3109/*
Bram Moolenaar8e7d6222020-12-18 19:49:56 +01003110 * "reduce(list, { accumulator, element -> value } [, initial])" function
Yegappan Lakshmanand92813a2021-12-21 13:19:42 +00003111 * "reduce(blob, { accumulator, element -> value } [, initial])"
3112 * "reduce(string, { accumulator, element -> value } [, initial])"
Bram Moolenaar85629982020-06-01 18:39:20 +02003113 */
3114 void
3115f_reduce(typval_T *argvars, typval_T *rettv)
3116{
Bram Moolenaar85629982020-06-01 18:39:20 +02003117 char_u *func_name;
Bram Moolenaar85629982020-06-01 18:39:20 +02003118
rbtnn0ccb5842021-12-18 18:33:46 +00003119 if (in_vim9script()
3120 && check_for_string_or_list_or_blob_arg(argvars, 0) == FAIL)
Bram Moolenaar85629982020-06-01 18:39:20 +02003121 return;
rbtnn0ccb5842021-12-18 18:33:46 +00003122
3123 if (argvars[0].v_type != VAR_STRING
3124 && argvars[0].v_type != VAR_LIST
3125 && argvars[0].v_type != VAR_BLOB)
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00003126 {
rbtnn0ccb5842021-12-18 18:33:46 +00003127 semsg(_(e_string_list_or_blob_required), "reduce()");
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00003128 return;
3129 }
Bram Moolenaar85629982020-06-01 18:39:20 +02003130
3131 if (argvars[1].v_type == VAR_FUNC)
3132 func_name = argvars[1].vval.v_string;
3133 else if (argvars[1].v_type == VAR_PARTIAL)
Bram Moolenaarf1c60d42022-09-22 17:07:00 +01003134 func_name = partial_name(argvars[1].vval.v_partial);
Bram Moolenaar85629982020-06-01 18:39:20 +02003135 else
3136 func_name = tv_get_string(&argvars[1]);
Bram Moolenaar0d90e722020-11-03 18:20:19 +01003137 if (func_name == NULL || *func_name == NUL)
3138 {
3139 emsg(_(e_missing_function_argument));
3140 return;
3141 }
Bram Moolenaar85629982020-06-01 18:39:20 +02003142
Bram Moolenaar85629982020-06-01 18:39:20 +02003143 if (argvars[0].v_type == VAR_LIST)
Bram Moolenaarf1c60d42022-09-22 17:07:00 +01003144 list_reduce(argvars, &argvars[1], rettv);
rbtnn0ccb5842021-12-18 18:33:46 +00003145 else if (argvars[0].v_type == VAR_STRING)
Bram Moolenaarf1c60d42022-09-22 17:07:00 +01003146 string_reduce(argvars, &argvars[1], rettv);
Bram Moolenaar85629982020-06-01 18:39:20 +02003147 else
Bram Moolenaarf1c60d42022-09-22 17:07:00 +01003148 blob_reduce(argvars, &argvars[1], rettv);
Bram Moolenaar85629982020-06-01 18:39:20 +02003149}
3150
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02003151#endif // defined(FEAT_EVAL)