xref: /vim-8.2.3635/src/list.c (revision c04f6234)
1edf3f97aSBram Moolenaar /* vi:set ts=8 sts=4 sw=4 noet:
2da861d63SBram Moolenaar  *
3da861d63SBram Moolenaar  * VIM - Vi IMproved	by Bram Moolenaar
4da861d63SBram Moolenaar  *
5da861d63SBram Moolenaar  * Do ":help uganda"  in Vim to read copying and usage conditions.
6da861d63SBram Moolenaar  * Do ":help credits" in Vim to see a list of people who contributed.
7da861d63SBram Moolenaar  * See README.txt for an overview of the Vim source code.
8da861d63SBram Moolenaar  */
9da861d63SBram Moolenaar 
10da861d63SBram Moolenaar /*
1108c308aeSBram Moolenaar  * list.c: List support and container (List, Dict, Blob) functions.
12da861d63SBram Moolenaar  */
13da861d63SBram Moolenaar 
14da861d63SBram Moolenaar #include "vim.h"
15da861d63SBram Moolenaar 
16da861d63SBram Moolenaar #if defined(FEAT_EVAL) || defined(PROTO)
17da861d63SBram Moolenaar 
1808c308aeSBram Moolenaar static char *e_listblobarg = N_("E899: Argument of %s must be a List or Blob");
1908c308aeSBram Moolenaar 
204ba37b58SBram Moolenaar // List heads for garbage collection.
214ba37b58SBram Moolenaar static list_T		*first_list = NULL;	// list of all lists
22da861d63SBram Moolenaar 
2300d253e2SBram Moolenaar #define FOR_ALL_WATCHERS(l, lw) \
2400d253e2SBram Moolenaar     for ((lw) = (l)->lv_watch; (lw) != NULL; (lw) = (lw)->lw_next)
2500d253e2SBram Moolenaar 
26bdff012fSBram Moolenaar static void list_free_item(list_T *l, listitem_T *item);
27bdff012fSBram Moolenaar 
28da861d63SBram Moolenaar /*
29da861d63SBram Moolenaar  * Add a watcher to a list.
30da861d63SBram Moolenaar  */
31da861d63SBram Moolenaar     void
list_add_watch(list_T * l,listwatch_T * lw)32da861d63SBram Moolenaar list_add_watch(list_T *l, listwatch_T *lw)
33da861d63SBram Moolenaar {
34da861d63SBram Moolenaar     lw->lw_next = l->lv_watch;
35da861d63SBram Moolenaar     l->lv_watch = lw;
36da861d63SBram Moolenaar }
37da861d63SBram Moolenaar 
38da861d63SBram Moolenaar /*
39da861d63SBram Moolenaar  * Remove a watcher from a list.
40da861d63SBram Moolenaar  * No warning when it isn't found...
41da861d63SBram Moolenaar  */
42da861d63SBram Moolenaar     void
list_rem_watch(list_T * l,listwatch_T * lwrem)43da861d63SBram Moolenaar list_rem_watch(list_T *l, listwatch_T *lwrem)
44da861d63SBram Moolenaar {
45da861d63SBram Moolenaar     listwatch_T	*lw, **lwp;
46da861d63SBram Moolenaar 
47da861d63SBram Moolenaar     lwp = &l->lv_watch;
4800d253e2SBram Moolenaar     FOR_ALL_WATCHERS(l, lw)
49da861d63SBram Moolenaar     {
50da861d63SBram Moolenaar 	if (lw == lwrem)
51da861d63SBram Moolenaar 	{
52da861d63SBram Moolenaar 	    *lwp = lw->lw_next;
53da861d63SBram Moolenaar 	    break;
54da861d63SBram Moolenaar 	}
55da861d63SBram Moolenaar 	lwp = &lw->lw_next;
56da861d63SBram Moolenaar     }
57da861d63SBram Moolenaar }
58da861d63SBram Moolenaar 
59da861d63SBram Moolenaar /*
60da861d63SBram Moolenaar  * Just before removing an item from a list: advance watchers to the next
61da861d63SBram Moolenaar  * item.
62da861d63SBram Moolenaar  */
635843f5f3SBram Moolenaar     static void
list_fix_watch(list_T * l,listitem_T * item)64da861d63SBram Moolenaar list_fix_watch(list_T *l, listitem_T *item)
65da861d63SBram Moolenaar {
66da861d63SBram Moolenaar     listwatch_T	*lw;
67da861d63SBram Moolenaar 
6800d253e2SBram Moolenaar     FOR_ALL_WATCHERS(l, lw)
69da861d63SBram Moolenaar 	if (lw->lw_item == item)
70da861d63SBram Moolenaar 	    lw->lw_item = item->li_next;
71da861d63SBram Moolenaar }
72da861d63SBram Moolenaar 
738a7d6542SBram Moolenaar     static void
list_init(list_T * l)748a7d6542SBram Moolenaar list_init(list_T *l)
758a7d6542SBram Moolenaar {
768a7d6542SBram Moolenaar     // Prepend the list to the list of lists for garbage collection.
778a7d6542SBram Moolenaar     if (first_list != NULL)
788a7d6542SBram Moolenaar 	first_list->lv_used_prev = l;
798a7d6542SBram Moolenaar     l->lv_used_prev = NULL;
808a7d6542SBram Moolenaar     l->lv_used_next = first_list;
818a7d6542SBram Moolenaar     first_list = l;
828a7d6542SBram Moolenaar }
838a7d6542SBram Moolenaar 
84da861d63SBram Moolenaar /*
85da861d63SBram Moolenaar  * Allocate an empty header for a list.
86da861d63SBram Moolenaar  * Caller should take care of the reference count.
87da861d63SBram Moolenaar  */
88da861d63SBram Moolenaar     list_T *
list_alloc(void)89da861d63SBram Moolenaar list_alloc(void)
90da861d63SBram Moolenaar {
91da861d63SBram Moolenaar     list_T  *l;
92da861d63SBram Moolenaar 
93c799fe20SBram Moolenaar     l = ALLOC_CLEAR_ONE(list_T);
94da861d63SBram Moolenaar     if (l != NULL)
958a7d6542SBram Moolenaar 	list_init(l);
96da861d63SBram Moolenaar     return l;
97da861d63SBram Moolenaar }
98da861d63SBram Moolenaar 
99da861d63SBram Moolenaar /*
100f49cc60aSBram Moolenaar  * list_alloc() with an ID for alloc_fail().
101f49cc60aSBram Moolenaar  */
102f49cc60aSBram Moolenaar     list_T *
list_alloc_id(alloc_id_T id UNUSED)103f49cc60aSBram Moolenaar list_alloc_id(alloc_id_T id UNUSED)
104f49cc60aSBram Moolenaar {
105f49cc60aSBram Moolenaar #ifdef FEAT_EVAL
10651e14387SBram Moolenaar     if (alloc_fail_id == id && alloc_does_fail(sizeof(list_T)))
107f49cc60aSBram Moolenaar 	return NULL;
108f49cc60aSBram Moolenaar #endif
109f49cc60aSBram Moolenaar     return (list_alloc());
110f49cc60aSBram Moolenaar }
111f49cc60aSBram Moolenaar 
112f49cc60aSBram Moolenaar /*
1138a7d6542SBram Moolenaar  * Allocate space for a list, plus "count" items.
1148a7d6542SBram Moolenaar  * Next list_set_item() must be called for each item.
1158a7d6542SBram Moolenaar  */
1168a7d6542SBram Moolenaar     list_T *
list_alloc_with_items(int count)1178a7d6542SBram Moolenaar list_alloc_with_items(int count)
1188a7d6542SBram Moolenaar {
1198a7d6542SBram Moolenaar     list_T	*l;
1208a7d6542SBram Moolenaar 
1218a7d6542SBram Moolenaar     l = (list_T *)alloc_clear(sizeof(list_T) + count * sizeof(listitem_T));
1228a7d6542SBram Moolenaar     if (l != NULL)
1238a7d6542SBram Moolenaar     {
1248a7d6542SBram Moolenaar 	list_init(l);
1258a7d6542SBram Moolenaar 
1268a7d6542SBram Moolenaar 	if (count > 0)
1278a7d6542SBram Moolenaar 	{
1288a7d6542SBram Moolenaar 	    listitem_T	*li = (listitem_T *)(l + 1);
1298a7d6542SBram Moolenaar 	    int		i;
1308a7d6542SBram Moolenaar 
1318a7d6542SBram Moolenaar 	    l->lv_len = count;
1328a7d6542SBram Moolenaar 	    l->lv_with_items = count;
1338a7d6542SBram Moolenaar 	    l->lv_first = li;
1340ff6aad3SBram Moolenaar 	    l->lv_u.mat.lv_last = li + count - 1;
1358a7d6542SBram Moolenaar 	    for (i = 0; i < count; ++i)
1368a7d6542SBram Moolenaar 	    {
1378a7d6542SBram Moolenaar 		if (i == 0)
1388a7d6542SBram Moolenaar 		    li->li_prev = NULL;
1398a7d6542SBram Moolenaar 		else
1408a7d6542SBram Moolenaar 		    li->li_prev = li - 1;
1418a7d6542SBram Moolenaar 		if (i == count - 1)
1428a7d6542SBram Moolenaar 		    li->li_next = NULL;
1438a7d6542SBram Moolenaar 		else
1448a7d6542SBram Moolenaar 		    li->li_next = li + 1;
1458a7d6542SBram Moolenaar 		++li;
1468a7d6542SBram Moolenaar 	    }
1478a7d6542SBram Moolenaar 	}
1488a7d6542SBram Moolenaar     }
1498a7d6542SBram Moolenaar     return l;
1508a7d6542SBram Moolenaar }
1518a7d6542SBram Moolenaar 
1528a7d6542SBram Moolenaar /*
1538a7d6542SBram Moolenaar  * Set item "idx" for a list previously allocated with list_alloc_with_items().
1548a7d6542SBram Moolenaar  * The contents of "tv" is moved into the list item.
1558a7d6542SBram Moolenaar  * Each item must be set exactly once.
1568a7d6542SBram Moolenaar  */
1578a7d6542SBram Moolenaar     void
list_set_item(list_T * l,int idx,typval_T * tv)1588a7d6542SBram Moolenaar list_set_item(list_T *l, int idx, typval_T *tv)
1598a7d6542SBram Moolenaar {
1608a7d6542SBram Moolenaar     listitem_T	*li = (listitem_T *)(l + 1) + idx;
1618a7d6542SBram Moolenaar 
1628a7d6542SBram Moolenaar     li->li_tv = *tv;
1638a7d6542SBram Moolenaar }
1648a7d6542SBram Moolenaar 
1658a7d6542SBram Moolenaar /*
166da861d63SBram Moolenaar  * Allocate an empty list for a return value, with reference count set.
167da861d63SBram Moolenaar  * Returns OK or FAIL.
168da861d63SBram Moolenaar  */
169da861d63SBram Moolenaar     int
rettv_list_alloc(typval_T * rettv)170da861d63SBram Moolenaar rettv_list_alloc(typval_T *rettv)
171da861d63SBram Moolenaar {
172da861d63SBram Moolenaar     list_T	*l = list_alloc();
173da861d63SBram Moolenaar 
174da861d63SBram Moolenaar     if (l == NULL)
175da861d63SBram Moolenaar 	return FAIL;
176da861d63SBram Moolenaar 
177da861d63SBram Moolenaar     rettv->v_lock = 0;
17845cf6e91SBram Moolenaar     rettv_list_set(rettv, l);
179da861d63SBram Moolenaar     return OK;
180da861d63SBram Moolenaar }
181da861d63SBram Moolenaar 
182da861d63SBram Moolenaar /*
183162b7147SBram Moolenaar  * Same as rettv_list_alloc() but uses an allocation id for testing.
184162b7147SBram Moolenaar  */
185162b7147SBram Moolenaar     int
rettv_list_alloc_id(typval_T * rettv,alloc_id_T id UNUSED)186162b7147SBram Moolenaar rettv_list_alloc_id(typval_T *rettv, alloc_id_T id UNUSED)
187162b7147SBram Moolenaar {
188162b7147SBram Moolenaar #ifdef FEAT_EVAL
18951e14387SBram Moolenaar     if (alloc_fail_id == id && alloc_does_fail(sizeof(list_T)))
190162b7147SBram Moolenaar 	return FAIL;
191162b7147SBram Moolenaar #endif
192162b7147SBram Moolenaar     return rettv_list_alloc(rettv);
193162b7147SBram Moolenaar }
194162b7147SBram Moolenaar 
195162b7147SBram Moolenaar 
196162b7147SBram Moolenaar /*
197e809a4edSBram Moolenaar  * Set a list as the return value.  Increments the reference count.
19845cf6e91SBram Moolenaar  */
19945cf6e91SBram Moolenaar     void
rettv_list_set(typval_T * rettv,list_T * l)20045cf6e91SBram Moolenaar rettv_list_set(typval_T *rettv, list_T *l)
20145cf6e91SBram Moolenaar {
20245cf6e91SBram Moolenaar     rettv->v_type = VAR_LIST;
20345cf6e91SBram Moolenaar     rettv->vval.v_list = l;
20445cf6e91SBram Moolenaar     if (l != NULL)
20545cf6e91SBram Moolenaar 	++l->lv_refcount;
20645cf6e91SBram Moolenaar }
20745cf6e91SBram Moolenaar 
20845cf6e91SBram Moolenaar /*
209da861d63SBram Moolenaar  * Unreference a list: decrement the reference count and free it when it
210da861d63SBram Moolenaar  * becomes zero.
211da861d63SBram Moolenaar  */
212da861d63SBram Moolenaar     void
list_unref(list_T * l)213da861d63SBram Moolenaar list_unref(list_T *l)
214da861d63SBram Moolenaar {
215da861d63SBram Moolenaar     if (l != NULL && --l->lv_refcount <= 0)
216da861d63SBram Moolenaar 	list_free(l);
217da861d63SBram Moolenaar }
218da861d63SBram Moolenaar 
219da861d63SBram Moolenaar /*
220da861d63SBram Moolenaar  * Free a list, including all non-container items it points to.
221da861d63SBram Moolenaar  * Ignores the reference count.
222da861d63SBram Moolenaar  */
223da861d63SBram Moolenaar     static void
list_free_contents(list_T * l)224da861d63SBram Moolenaar list_free_contents(list_T *l)
225da861d63SBram Moolenaar {
226da861d63SBram Moolenaar     listitem_T *item;
227da861d63SBram Moolenaar 
2288a7d6542SBram Moolenaar     if (l->lv_first != &range_list_item)
229da861d63SBram Moolenaar 	for (item = l->lv_first; item != NULL; item = l->lv_first)
230da861d63SBram Moolenaar 	{
2314ba37b58SBram Moolenaar 	    // Remove the item before deleting it.
232da861d63SBram Moolenaar 	    l->lv_first = item->li_next;
233da861d63SBram Moolenaar 	    clear_tv(&item->li_tv);
2348a7d6542SBram Moolenaar 	    list_free_item(l, item);
235da861d63SBram Moolenaar 	}
236da861d63SBram Moolenaar }
237da861d63SBram Moolenaar 
238da861d63SBram Moolenaar /*
239da861d63SBram Moolenaar  * Go through the list of lists and free items without the copyID.
240da861d63SBram Moolenaar  * But don't free a list that has a watcher (used in a for loop), these
241da861d63SBram Moolenaar  * are not referenced anywhere.
242da861d63SBram Moolenaar  */
243da861d63SBram Moolenaar     int
list_free_nonref(int copyID)244da861d63SBram Moolenaar list_free_nonref(int copyID)
245da861d63SBram Moolenaar {
246da861d63SBram Moolenaar     list_T	*ll;
247da861d63SBram Moolenaar     int		did_free = FALSE;
248da861d63SBram Moolenaar 
249da861d63SBram Moolenaar     for (ll = first_list; ll != NULL; ll = ll->lv_used_next)
250da861d63SBram Moolenaar 	if ((ll->lv_copyID & COPYID_MASK) != (copyID & COPYID_MASK)
251da861d63SBram Moolenaar 						      && ll->lv_watch == NULL)
252da861d63SBram Moolenaar 	{
2534ba37b58SBram Moolenaar 	    // Free the List and ordinary items it contains, but don't recurse
2544ba37b58SBram Moolenaar 	    // into Lists and Dictionaries, they will be in the list of dicts
2554ba37b58SBram Moolenaar 	    // or list of lists.
256da861d63SBram Moolenaar 	    list_free_contents(ll);
257da861d63SBram Moolenaar 	    did_free = TRUE;
258da861d63SBram Moolenaar 	}
259da861d63SBram Moolenaar     return did_free;
260da861d63SBram Moolenaar }
261da861d63SBram Moolenaar 
262da861d63SBram Moolenaar     static void
list_free_list(list_T * l)263da861d63SBram Moolenaar list_free_list(list_T  *l)
264da861d63SBram Moolenaar {
2654ba37b58SBram Moolenaar     // Remove the list from the list of lists for garbage collection.
266da861d63SBram Moolenaar     if (l->lv_used_prev == NULL)
267da861d63SBram Moolenaar 	first_list = l->lv_used_next;
268da861d63SBram Moolenaar     else
269da861d63SBram Moolenaar 	l->lv_used_prev->lv_used_next = l->lv_used_next;
270da861d63SBram Moolenaar     if (l->lv_used_next != NULL)
271da861d63SBram Moolenaar 	l->lv_used_next->lv_used_prev = l->lv_used_prev;
272da861d63SBram Moolenaar 
273aa210a3aSBram Moolenaar     free_type(l->lv_type);
274da861d63SBram Moolenaar     vim_free(l);
275da861d63SBram Moolenaar }
276da861d63SBram Moolenaar 
277da861d63SBram Moolenaar     void
list_free_items(int copyID)278da861d63SBram Moolenaar list_free_items(int copyID)
279da861d63SBram Moolenaar {
280da861d63SBram Moolenaar     list_T	*ll, *ll_next;
281da861d63SBram Moolenaar 
282da861d63SBram Moolenaar     for (ll = first_list; ll != NULL; ll = ll_next)
283da861d63SBram Moolenaar     {
284da861d63SBram Moolenaar 	ll_next = ll->lv_used_next;
285da861d63SBram Moolenaar 	if ((ll->lv_copyID & COPYID_MASK) != (copyID & COPYID_MASK)
286da861d63SBram Moolenaar 						      && ll->lv_watch == NULL)
287da861d63SBram Moolenaar 	{
2884ba37b58SBram Moolenaar 	    // Free the List and ordinary items it contains, but don't recurse
2894ba37b58SBram Moolenaar 	    // into Lists and Dictionaries, they will be in the list of dicts
2904ba37b58SBram Moolenaar 	    // or list of lists.
291da861d63SBram Moolenaar 	    list_free_list(ll);
292da861d63SBram Moolenaar 	}
293da861d63SBram Moolenaar     }
294da861d63SBram Moolenaar }
295da861d63SBram Moolenaar 
296da861d63SBram Moolenaar     void
list_free(list_T * l)297da861d63SBram Moolenaar list_free(list_T *l)
298da861d63SBram Moolenaar {
299da861d63SBram Moolenaar     if (!in_free_unref_items)
300da861d63SBram Moolenaar     {
301da861d63SBram Moolenaar 	list_free_contents(l);
302da861d63SBram Moolenaar 	list_free_list(l);
303da861d63SBram Moolenaar     }
304da861d63SBram Moolenaar }
305da861d63SBram Moolenaar 
306da861d63SBram Moolenaar /*
307da861d63SBram Moolenaar  * Allocate a list item.
308da861d63SBram Moolenaar  * It is not initialized, don't forget to set v_lock.
309da861d63SBram Moolenaar  */
310da861d63SBram Moolenaar     listitem_T *
listitem_alloc(void)311da861d63SBram Moolenaar listitem_alloc(void)
312da861d63SBram Moolenaar {
313c799fe20SBram Moolenaar     return ALLOC_ONE(listitem_T);
314da861d63SBram Moolenaar }
315da861d63SBram Moolenaar 
316da861d63SBram Moolenaar /*
3178a7d6542SBram Moolenaar  * Free a list item, unless it was allocated together with the list itself.
3188a7d6542SBram Moolenaar  * Does not clear the value.  Does not notify watchers.
319da861d63SBram Moolenaar  */
320bdff012fSBram Moolenaar     static void
list_free_item(list_T * l,listitem_T * item)3218a7d6542SBram Moolenaar list_free_item(list_T *l, listitem_T *item)
3228a7d6542SBram Moolenaar {
3238a7d6542SBram Moolenaar     if (l->lv_with_items == 0 || item < (listitem_T *)l
3248a7d6542SBram Moolenaar 			   || item >= (listitem_T *)(l + 1) + l->lv_with_items)
3258a7d6542SBram Moolenaar 	vim_free(item);
3268a7d6542SBram Moolenaar }
3278a7d6542SBram Moolenaar 
3288a7d6542SBram Moolenaar /*
3298a7d6542SBram Moolenaar  * Free a list item, unless it was allocated together with the list itself.
3308a7d6542SBram Moolenaar  * Also clears the value.  Does not notify watchers.
3318a7d6542SBram Moolenaar  */
3328a7d6542SBram Moolenaar     void
listitem_free(list_T * l,listitem_T * item)3338a7d6542SBram Moolenaar listitem_free(list_T *l, listitem_T *item)
334da861d63SBram Moolenaar {
335da861d63SBram Moolenaar     clear_tv(&item->li_tv);
3368a7d6542SBram Moolenaar     list_free_item(l, item);
337da861d63SBram Moolenaar }
338da861d63SBram Moolenaar 
339da861d63SBram Moolenaar /*
340da861d63SBram Moolenaar  * Remove a list item from a List and free it.  Also clears the value.
341da861d63SBram Moolenaar  */
342da861d63SBram Moolenaar     void
listitem_remove(list_T * l,listitem_T * item)343da861d63SBram Moolenaar listitem_remove(list_T *l, listitem_T *item)
344da861d63SBram Moolenaar {
345da861d63SBram Moolenaar     vimlist_remove(l, item, item);
3468a7d6542SBram Moolenaar     listitem_free(l, item);
347da861d63SBram Moolenaar }
348da861d63SBram Moolenaar 
349da861d63SBram Moolenaar /*
350da861d63SBram Moolenaar  * Get the number of items in a list.
351da861d63SBram Moolenaar  */
352da861d63SBram Moolenaar     long
list_len(list_T * l)353da861d63SBram Moolenaar list_len(list_T *l)
354da861d63SBram Moolenaar {
355da861d63SBram Moolenaar     if (l == NULL)
356da861d63SBram Moolenaar 	return 0L;
357da861d63SBram Moolenaar     return l->lv_len;
358da861d63SBram Moolenaar }
359da861d63SBram Moolenaar 
360da861d63SBram Moolenaar /*
361da861d63SBram Moolenaar  * Return TRUE when two lists have exactly the same values.
362da861d63SBram Moolenaar  */
363da861d63SBram Moolenaar     int
list_equal(list_T * l1,list_T * l2,int ic,int recursive)364da861d63SBram Moolenaar list_equal(
365da861d63SBram Moolenaar     list_T	*l1,
366da861d63SBram Moolenaar     list_T	*l2,
3674ba37b58SBram Moolenaar     int		ic,	// ignore case for strings
3684ba37b58SBram Moolenaar     int		recursive)  // TRUE when used recursively
369da861d63SBram Moolenaar {
370da861d63SBram Moolenaar     listitem_T	*item1, *item2;
371da861d63SBram Moolenaar 
372da861d63SBram Moolenaar     if (l1 == l2)
373da861d63SBram Moolenaar 	return TRUE;
374da861d63SBram Moolenaar     if (list_len(l1) != list_len(l2))
375da861d63SBram Moolenaar 	return FALSE;
3767b293c73SBram Moolenaar     if (list_len(l1) == 0)
3777b293c73SBram Moolenaar 	// empty and NULL list are considered equal
3787b293c73SBram Moolenaar 	return TRUE;
3797b293c73SBram Moolenaar     if (l1 == NULL || l2 == NULL)
3807b293c73SBram Moolenaar 	return FALSE;
381da861d63SBram Moolenaar 
3827e9f351bSBram Moolenaar     CHECK_LIST_MATERIALIZE(l1);
3837e9f351bSBram Moolenaar     CHECK_LIST_MATERIALIZE(l2);
3848a7d6542SBram Moolenaar 
385da861d63SBram Moolenaar     for (item1 = l1->lv_first, item2 = l2->lv_first;
386da861d63SBram Moolenaar 	    item1 != NULL && item2 != NULL;
387da861d63SBram Moolenaar 			       item1 = item1->li_next, item2 = item2->li_next)
388da861d63SBram Moolenaar 	if (!tv_equal(&item1->li_tv, &item2->li_tv, ic, recursive))
389da861d63SBram Moolenaar 	    return FALSE;
390da861d63SBram Moolenaar     return item1 == NULL && item2 == NULL;
391da861d63SBram Moolenaar }
392da861d63SBram Moolenaar 
393da861d63SBram Moolenaar /*
394da861d63SBram Moolenaar  * Locate item with index "n" in list "l" and return it.
395da861d63SBram Moolenaar  * A negative index is counted from the end; -1 is the last item.
396da861d63SBram Moolenaar  * Returns NULL when "n" is out of range.
397da861d63SBram Moolenaar  */
398da861d63SBram Moolenaar     listitem_T *
list_find(list_T * l,long n)399da861d63SBram Moolenaar list_find(list_T *l, long n)
400da861d63SBram Moolenaar {
401da861d63SBram Moolenaar     listitem_T	*item;
402da861d63SBram Moolenaar     long	idx;
403da861d63SBram Moolenaar 
404da861d63SBram Moolenaar     if (l == NULL)
405da861d63SBram Moolenaar 	return NULL;
406da861d63SBram Moolenaar 
4074ba37b58SBram Moolenaar     // Negative index is relative to the end.
408da861d63SBram Moolenaar     if (n < 0)
409da861d63SBram Moolenaar 	n = l->lv_len + n;
410da861d63SBram Moolenaar 
4114ba37b58SBram Moolenaar     // Check for index out of range.
412da861d63SBram Moolenaar     if (n < 0 || n >= l->lv_len)
413da861d63SBram Moolenaar 	return NULL;
414da861d63SBram Moolenaar 
4157e9f351bSBram Moolenaar     CHECK_LIST_MATERIALIZE(l);
4168a7d6542SBram Moolenaar 
4174ba37b58SBram Moolenaar     // When there is a cached index may start search from there.
4180ff6aad3SBram Moolenaar     if (l->lv_u.mat.lv_idx_item != NULL)
419da861d63SBram Moolenaar     {
4200ff6aad3SBram Moolenaar 	if (n < l->lv_u.mat.lv_idx / 2)
421da861d63SBram Moolenaar 	{
4224ba37b58SBram Moolenaar 	    // closest to the start of the list
423da861d63SBram Moolenaar 	    item = l->lv_first;
424da861d63SBram Moolenaar 	    idx = 0;
425da861d63SBram Moolenaar 	}
4260ff6aad3SBram Moolenaar 	else if (n > (l->lv_u.mat.lv_idx + l->lv_len) / 2)
427da861d63SBram Moolenaar 	{
4284ba37b58SBram Moolenaar 	    // closest to the end of the list
4290ff6aad3SBram Moolenaar 	    item = l->lv_u.mat.lv_last;
430da861d63SBram Moolenaar 	    idx = l->lv_len - 1;
431da861d63SBram Moolenaar 	}
432da861d63SBram Moolenaar 	else
433da861d63SBram Moolenaar 	{
4344ba37b58SBram Moolenaar 	    // closest to the cached index
4350ff6aad3SBram Moolenaar 	    item = l->lv_u.mat.lv_idx_item;
4360ff6aad3SBram Moolenaar 	    idx = l->lv_u.mat.lv_idx;
437da861d63SBram Moolenaar 	}
438da861d63SBram Moolenaar     }
439da861d63SBram Moolenaar     else
440da861d63SBram Moolenaar     {
441da861d63SBram Moolenaar 	if (n < l->lv_len / 2)
442da861d63SBram Moolenaar 	{
4434ba37b58SBram Moolenaar 	    // closest to the start of the list
444da861d63SBram Moolenaar 	    item = l->lv_first;
445da861d63SBram Moolenaar 	    idx = 0;
446da861d63SBram Moolenaar 	}
447da861d63SBram Moolenaar 	else
448da861d63SBram Moolenaar 	{
4494ba37b58SBram Moolenaar 	    // closest to the end of the list
4500ff6aad3SBram Moolenaar 	    item = l->lv_u.mat.lv_last;
451da861d63SBram Moolenaar 	    idx = l->lv_len - 1;
452da861d63SBram Moolenaar 	}
453da861d63SBram Moolenaar     }
454da861d63SBram Moolenaar 
455da861d63SBram Moolenaar     while (n > idx)
456da861d63SBram Moolenaar     {
4574ba37b58SBram Moolenaar 	// search forward
458da861d63SBram Moolenaar 	item = item->li_next;
459da861d63SBram Moolenaar 	++idx;
460da861d63SBram Moolenaar     }
461da861d63SBram Moolenaar     while (n < idx)
462da861d63SBram Moolenaar     {
4634ba37b58SBram Moolenaar 	// search backward
464da861d63SBram Moolenaar 	item = item->li_prev;
465da861d63SBram Moolenaar 	--idx;
466da861d63SBram Moolenaar     }
467da861d63SBram Moolenaar 
4684ba37b58SBram Moolenaar     // cache the used index
4690ff6aad3SBram Moolenaar     l->lv_u.mat.lv_idx = idx;
4700ff6aad3SBram Moolenaar     l->lv_u.mat.lv_idx_item = item;
471da861d63SBram Moolenaar 
472da861d63SBram Moolenaar     return item;
473da861d63SBram Moolenaar }
474da861d63SBram Moolenaar 
475da861d63SBram Moolenaar /*
476da861d63SBram Moolenaar  * Get list item "l[idx]" as a number.
477da861d63SBram Moolenaar  */
478da861d63SBram Moolenaar     long
list_find_nr(list_T * l,long idx,int * errorp)479da861d63SBram Moolenaar list_find_nr(
480da861d63SBram Moolenaar     list_T	*l,
481da861d63SBram Moolenaar     long	idx,
4824ba37b58SBram Moolenaar     int		*errorp)	// set to TRUE when something wrong
483da861d63SBram Moolenaar {
484da861d63SBram Moolenaar     listitem_T	*li;
485da861d63SBram Moolenaar 
4868a7d6542SBram Moolenaar     if (l != NULL && l->lv_first == &range_list_item)
4878a7d6542SBram Moolenaar     {
4888a7d6542SBram Moolenaar 	long	    n = idx;
4898a7d6542SBram Moolenaar 
4908a7d6542SBram Moolenaar 	// not materialized range() list: compute the value.
4918a7d6542SBram Moolenaar 	// Negative index is relative to the end.
4928a7d6542SBram Moolenaar 	if (n < 0)
4938a7d6542SBram Moolenaar 	    n = l->lv_len + n;
4948a7d6542SBram Moolenaar 
4958a7d6542SBram Moolenaar 	// Check for index out of range.
4968a7d6542SBram Moolenaar 	if (n < 0 || n >= l->lv_len)
4978a7d6542SBram Moolenaar 	{
4988a7d6542SBram Moolenaar 	    if (errorp != NULL)
4998a7d6542SBram Moolenaar 		*errorp = TRUE;
5008a7d6542SBram Moolenaar 	    return -1L;
5018a7d6542SBram Moolenaar 	}
5028a7d6542SBram Moolenaar 
5030ff6aad3SBram Moolenaar 	return l->lv_u.nonmat.lv_start + n * l->lv_u.nonmat.lv_stride;
5048a7d6542SBram Moolenaar     }
5058a7d6542SBram Moolenaar 
506da861d63SBram Moolenaar     li = list_find(l, idx);
507da861d63SBram Moolenaar     if (li == NULL)
508da861d63SBram Moolenaar     {
509da861d63SBram Moolenaar 	if (errorp != NULL)
510da861d63SBram Moolenaar 	    *errorp = TRUE;
511da861d63SBram Moolenaar 	return -1L;
512da861d63SBram Moolenaar     }
513d155d7a8SBram Moolenaar     return (long)tv_get_number_chk(&li->li_tv, errorp);
514da861d63SBram Moolenaar }
515da861d63SBram Moolenaar 
516da861d63SBram Moolenaar /*
517da861d63SBram Moolenaar  * Get list item "l[idx - 1]" as a string.  Returns NULL for failure.
518da861d63SBram Moolenaar  */
519da861d63SBram Moolenaar     char_u *
list_find_str(list_T * l,long idx)520da861d63SBram Moolenaar list_find_str(list_T *l, long idx)
521da861d63SBram Moolenaar {
522da861d63SBram Moolenaar     listitem_T	*li;
523da861d63SBram Moolenaar 
524da861d63SBram Moolenaar     li = list_find(l, idx - 1);
525da861d63SBram Moolenaar     if (li == NULL)
526da861d63SBram Moolenaar     {
527f9e3e09fSBram Moolenaar 	semsg(_(e_listidx), idx);
528da861d63SBram Moolenaar 	return NULL;
529da861d63SBram Moolenaar     }
530d155d7a8SBram Moolenaar     return tv_get_string(&li->li_tv);
531da861d63SBram Moolenaar }
532da861d63SBram Moolenaar 
533da861d63SBram Moolenaar /*
5345b5ae29bSBram Moolenaar  * Like list_find() but when a negative index is used that is not found use
5355b5ae29bSBram Moolenaar  * zero and set "idx" to zero.  Used for first index of a range.
5365b5ae29bSBram Moolenaar  */
5375b5ae29bSBram Moolenaar     listitem_T *
list_find_index(list_T * l,long * idx)5385b5ae29bSBram Moolenaar list_find_index(list_T *l, long *idx)
5395b5ae29bSBram Moolenaar {
5405b5ae29bSBram Moolenaar     listitem_T *li = list_find(l, *idx);
5415b5ae29bSBram Moolenaar 
5425b5ae29bSBram Moolenaar     if (li == NULL)
5435b5ae29bSBram Moolenaar     {
5445b5ae29bSBram Moolenaar 	if (*idx < 0)
5455b5ae29bSBram Moolenaar 	{
5465b5ae29bSBram Moolenaar 	    *idx = 0;
5475b5ae29bSBram Moolenaar 	    li = list_find(l, *idx);
5485b5ae29bSBram Moolenaar 	}
5495b5ae29bSBram Moolenaar     }
5505b5ae29bSBram Moolenaar     return li;
5515b5ae29bSBram Moolenaar }
5525b5ae29bSBram Moolenaar 
5535b5ae29bSBram Moolenaar /*
554da861d63SBram Moolenaar  * Locate "item" list "l" and return its index.
555da861d63SBram Moolenaar  * Returns -1 when "item" is not in the list.
556da861d63SBram Moolenaar  */
557da861d63SBram Moolenaar     long
list_idx_of_item(list_T * l,listitem_T * item)558da861d63SBram Moolenaar list_idx_of_item(list_T *l, listitem_T *item)
559da861d63SBram Moolenaar {
560da861d63SBram Moolenaar     long	idx = 0;
561da861d63SBram Moolenaar     listitem_T	*li;
562da861d63SBram Moolenaar 
563da861d63SBram Moolenaar     if (l == NULL)
564da861d63SBram Moolenaar 	return -1;
5657e9f351bSBram Moolenaar     CHECK_LIST_MATERIALIZE(l);
566da861d63SBram Moolenaar     idx = 0;
567da861d63SBram Moolenaar     for (li = l->lv_first; li != NULL && li != item; li = li->li_next)
568da861d63SBram Moolenaar 	++idx;
569da861d63SBram Moolenaar     if (li == NULL)
570da861d63SBram Moolenaar 	return -1;
571da861d63SBram Moolenaar     return idx;
572da861d63SBram Moolenaar }
573da861d63SBram Moolenaar 
574da861d63SBram Moolenaar /*
575da861d63SBram Moolenaar  * Append item "item" to the end of list "l".
576da861d63SBram Moolenaar  */
577da861d63SBram Moolenaar     void
list_append(list_T * l,listitem_T * item)578da861d63SBram Moolenaar list_append(list_T *l, listitem_T *item)
579da861d63SBram Moolenaar {
5807e9f351bSBram Moolenaar     CHECK_LIST_MATERIALIZE(l);
5810ff6aad3SBram Moolenaar     if (l->lv_u.mat.lv_last == NULL)
582da861d63SBram Moolenaar     {
5834ba37b58SBram Moolenaar 	// empty list
584da861d63SBram Moolenaar 	l->lv_first = item;
5850ff6aad3SBram Moolenaar 	l->lv_u.mat.lv_last = item;
586da861d63SBram Moolenaar 	item->li_prev = NULL;
587da861d63SBram Moolenaar     }
588da861d63SBram Moolenaar     else
589da861d63SBram Moolenaar     {
5900ff6aad3SBram Moolenaar 	l->lv_u.mat.lv_last->li_next = item;
5910ff6aad3SBram Moolenaar 	item->li_prev = l->lv_u.mat.lv_last;
5920ff6aad3SBram Moolenaar 	l->lv_u.mat.lv_last = item;
593da861d63SBram Moolenaar     }
594da861d63SBram Moolenaar     ++l->lv_len;
595da861d63SBram Moolenaar     item->li_next = NULL;
596da861d63SBram Moolenaar }
597da861d63SBram Moolenaar 
598da861d63SBram Moolenaar /*
5998a7d6542SBram Moolenaar  * Append typval_T "tv" to the end of list "l".  "tv" is copied.
600f32f0997SBram Moolenaar  * Return FAIL when out of memory or the type is wrong.
601da861d63SBram Moolenaar  */
602da861d63SBram Moolenaar     int
list_append_tv(list_T * l,typval_T * tv)603da861d63SBram Moolenaar list_append_tv(list_T *l, typval_T *tv)
604da861d63SBram Moolenaar {
60590fba562SBram Moolenaar     listitem_T	*li;
606da861d63SBram Moolenaar 
607f32f0997SBram Moolenaar     if (l->lv_type != NULL && l->lv_type->tt_member != NULL
6087a3fe3e1SBram Moolenaar 		&& check_typval_arg_type(l->lv_type->tt_member, tv,
6097a3fe3e1SBram Moolenaar 							      NULL, 0) == FAIL)
610f32f0997SBram Moolenaar 	return FAIL;
61190fba562SBram Moolenaar     li = listitem_alloc();
612da861d63SBram Moolenaar     if (li == NULL)
613da861d63SBram Moolenaar 	return FAIL;
614da861d63SBram Moolenaar     copy_tv(tv, &li->li_tv);
615da861d63SBram Moolenaar     list_append(l, li);
616da861d63SBram Moolenaar     return OK;
617da861d63SBram Moolenaar }
618da861d63SBram Moolenaar 
619da861d63SBram Moolenaar /*
6208a7d6542SBram Moolenaar  * As list_append_tv() but move the value instead of copying it.
6218a7d6542SBram Moolenaar  * Return FAIL when out of memory.
6228a7d6542SBram Moolenaar  */
6238ee52affSYegappan Lakshmanan     static int
list_append_tv_move(list_T * l,typval_T * tv)6248a7d6542SBram Moolenaar list_append_tv_move(list_T *l, typval_T *tv)
6258a7d6542SBram Moolenaar {
6268a7d6542SBram Moolenaar     listitem_T	*li = listitem_alloc();
6278a7d6542SBram Moolenaar 
6288a7d6542SBram Moolenaar     if (li == NULL)
6298a7d6542SBram Moolenaar 	return FAIL;
6308a7d6542SBram Moolenaar     li->li_tv = *tv;
6318a7d6542SBram Moolenaar     list_append(l, li);
6328a7d6542SBram Moolenaar     return OK;
6338a7d6542SBram Moolenaar }
6348a7d6542SBram Moolenaar 
6358a7d6542SBram Moolenaar /*
636da861d63SBram Moolenaar  * Add a dictionary to a list.  Used by getqflist().
637da861d63SBram Moolenaar  * Return FAIL when out of memory.
638da861d63SBram Moolenaar  */
639da861d63SBram Moolenaar     int
list_append_dict(list_T * list,dict_T * dict)640da861d63SBram Moolenaar list_append_dict(list_T *list, dict_T *dict)
641da861d63SBram Moolenaar {
642da861d63SBram Moolenaar     listitem_T	*li = listitem_alloc();
643da861d63SBram Moolenaar 
644da861d63SBram Moolenaar     if (li == NULL)
645da861d63SBram Moolenaar 	return FAIL;
646da861d63SBram Moolenaar     li->li_tv.v_type = VAR_DICT;
647da861d63SBram Moolenaar     li->li_tv.v_lock = 0;
648da861d63SBram Moolenaar     li->li_tv.vval.v_dict = dict;
649da861d63SBram Moolenaar     list_append(list, li);
650da861d63SBram Moolenaar     ++dict->dv_refcount;
651da861d63SBram Moolenaar     return OK;
652da861d63SBram Moolenaar }
653da861d63SBram Moolenaar 
654da861d63SBram Moolenaar /*
6554f50588bSBram Moolenaar  * Append list2 to list1.
6564f50588bSBram Moolenaar  * Return FAIL when out of memory.
6574f50588bSBram Moolenaar  */
6584f50588bSBram Moolenaar     int
list_append_list(list_T * list1,list_T * list2)6596f8d2ac6SBram Moolenaar list_append_list(list_T *list1, list_T *list2)
6604f50588bSBram Moolenaar {
6614f50588bSBram Moolenaar     listitem_T	*li = listitem_alloc();
6624f50588bSBram Moolenaar 
6634f50588bSBram Moolenaar     if (li == NULL)
6644f50588bSBram Moolenaar 	return FAIL;
6654f50588bSBram Moolenaar     li->li_tv.v_type = VAR_LIST;
6664f50588bSBram Moolenaar     li->li_tv.v_lock = 0;
6674f50588bSBram Moolenaar     li->li_tv.vval.v_list = list2;
6684f50588bSBram Moolenaar     list_append(list1, li);
6694f50588bSBram Moolenaar     ++list2->lv_refcount;
6704f50588bSBram Moolenaar     return OK;
6714f50588bSBram Moolenaar }
6724f50588bSBram Moolenaar 
6734f50588bSBram Moolenaar /*
674da861d63SBram Moolenaar  * Make a copy of "str" and append it as an item to list "l".
675da861d63SBram Moolenaar  * When "len" >= 0 use "str[len]".
676da861d63SBram Moolenaar  * Returns FAIL when out of memory.
677da861d63SBram Moolenaar  */
678da861d63SBram Moolenaar     int
list_append_string(list_T * l,char_u * str,int len)679da861d63SBram Moolenaar list_append_string(list_T *l, char_u *str, int len)
680da861d63SBram Moolenaar {
681da861d63SBram Moolenaar     listitem_T *li = listitem_alloc();
682da861d63SBram Moolenaar 
683da861d63SBram Moolenaar     if (li == NULL)
684da861d63SBram Moolenaar 	return FAIL;
685da861d63SBram Moolenaar     list_append(l, li);
686da861d63SBram Moolenaar     li->li_tv.v_type = VAR_STRING;
687da861d63SBram Moolenaar     li->li_tv.v_lock = 0;
688da861d63SBram Moolenaar     if (str == NULL)
689da861d63SBram Moolenaar 	li->li_tv.vval.v_string = NULL;
690da861d63SBram Moolenaar     else if ((li->li_tv.vval.v_string = (len >= 0 ? vim_strnsave(str, len)
691da861d63SBram Moolenaar 						 : vim_strsave(str))) == NULL)
692da861d63SBram Moolenaar 	return FAIL;
693da861d63SBram Moolenaar     return OK;
694da861d63SBram Moolenaar }
695da861d63SBram Moolenaar 
696da861d63SBram Moolenaar /*
697da861d63SBram Moolenaar  * Append "n" to list "l".
698da861d63SBram Moolenaar  * Returns FAIL when out of memory.
699da861d63SBram Moolenaar  */
700da861d63SBram Moolenaar     int
list_append_number(list_T * l,varnumber_T n)701da861d63SBram Moolenaar list_append_number(list_T *l, varnumber_T n)
702da861d63SBram Moolenaar {
703da861d63SBram Moolenaar     listitem_T	*li;
704da861d63SBram Moolenaar 
705da861d63SBram Moolenaar     li = listitem_alloc();
706da861d63SBram Moolenaar     if (li == NULL)
707da861d63SBram Moolenaar 	return FAIL;
708da861d63SBram Moolenaar     li->li_tv.v_type = VAR_NUMBER;
709da861d63SBram Moolenaar     li->li_tv.v_lock = 0;
710da861d63SBram Moolenaar     li->li_tv.vval.v_number = n;
711da861d63SBram Moolenaar     list_append(l, li);
712da861d63SBram Moolenaar     return OK;
713da861d63SBram Moolenaar }
714da861d63SBram Moolenaar 
715da861d63SBram Moolenaar /*
716da861d63SBram Moolenaar  * Insert typval_T "tv" in list "l" before "item".
717da861d63SBram Moolenaar  * If "item" is NULL append at the end.
718aa210a3aSBram Moolenaar  * Return FAIL when out of memory or the type is wrong.
719da861d63SBram Moolenaar  */
720da861d63SBram Moolenaar     int
list_insert_tv(list_T * l,typval_T * tv,listitem_T * item)721da861d63SBram Moolenaar list_insert_tv(list_T *l, typval_T *tv, listitem_T *item)
722da861d63SBram Moolenaar {
723aa210a3aSBram Moolenaar     listitem_T	*ni;
724da861d63SBram Moolenaar 
725aa210a3aSBram Moolenaar     if (l->lv_type != NULL && l->lv_type->tt_member != NULL
7267a3fe3e1SBram Moolenaar 		&& check_typval_arg_type(l->lv_type->tt_member, tv,
7277a3fe3e1SBram Moolenaar 							      NULL, 0) == FAIL)
728aa210a3aSBram Moolenaar 	return FAIL;
729aa210a3aSBram Moolenaar     ni = listitem_alloc();
730da861d63SBram Moolenaar     if (ni == NULL)
731da861d63SBram Moolenaar 	return FAIL;
732da861d63SBram Moolenaar     copy_tv(tv, &ni->li_tv);
733da861d63SBram Moolenaar     list_insert(l, ni, item);
734da861d63SBram Moolenaar     return OK;
735da861d63SBram Moolenaar }
736da861d63SBram Moolenaar 
737da861d63SBram Moolenaar     void
list_insert(list_T * l,listitem_T * ni,listitem_T * item)738da861d63SBram Moolenaar list_insert(list_T *l, listitem_T *ni, listitem_T *item)
739da861d63SBram Moolenaar {
7407e9f351bSBram Moolenaar     CHECK_LIST_MATERIALIZE(l);
741da861d63SBram Moolenaar     if (item == NULL)
7424ba37b58SBram Moolenaar 	// Append new item at end of list.
743da861d63SBram Moolenaar 	list_append(l, ni);
744da861d63SBram Moolenaar     else
745da861d63SBram Moolenaar     {
7464ba37b58SBram Moolenaar 	// Insert new item before existing item.
747da861d63SBram Moolenaar 	ni->li_prev = item->li_prev;
748da861d63SBram Moolenaar 	ni->li_next = item;
749da861d63SBram Moolenaar 	if (item->li_prev == NULL)
750da861d63SBram Moolenaar 	{
751da861d63SBram Moolenaar 	    l->lv_first = ni;
7520ff6aad3SBram Moolenaar 	    ++l->lv_u.mat.lv_idx;
753da861d63SBram Moolenaar 	}
754da861d63SBram Moolenaar 	else
755da861d63SBram Moolenaar 	{
756da861d63SBram Moolenaar 	    item->li_prev->li_next = ni;
7570ff6aad3SBram Moolenaar 	    l->lv_u.mat.lv_idx_item = NULL;
758da861d63SBram Moolenaar 	}
759da861d63SBram Moolenaar 	item->li_prev = ni;
760da861d63SBram Moolenaar 	++l->lv_len;
761da861d63SBram Moolenaar     }
762da861d63SBram Moolenaar }
763da861d63SBram Moolenaar 
764da861d63SBram Moolenaar /*
7654f0884d6SBram Moolenaar  * Get the list item in "l" with index "n1".  "n1" is adjusted if needed.
7664f0884d6SBram Moolenaar  * In Vim9, it is at the end of the list, add an item.
7674f0884d6SBram Moolenaar  * Return NULL if there is no such item.
7684f0884d6SBram Moolenaar  */
7694f0884d6SBram Moolenaar     listitem_T *
check_range_index_one(list_T * l,long * n1,int quiet)7704f0884d6SBram Moolenaar check_range_index_one(list_T *l, long *n1, int quiet)
7714f0884d6SBram Moolenaar {
7724f0884d6SBram Moolenaar     listitem_T *li = list_find_index(l, n1);
7734f0884d6SBram Moolenaar 
7744f0884d6SBram Moolenaar     if (li == NULL)
7754f0884d6SBram Moolenaar     {
7764f0884d6SBram Moolenaar 	// Vim9: Allow for adding an item at the end.
7774f0884d6SBram Moolenaar 	if (in_vim9script() && *n1 == l->lv_len && l->lv_lock == 0)
7784f0884d6SBram Moolenaar 	{
7794f0884d6SBram Moolenaar 	    list_append_number(l, 0);
7804f0884d6SBram Moolenaar 	    li = list_find_index(l, n1);
7814f0884d6SBram Moolenaar 	}
7824f0884d6SBram Moolenaar 	if (li == NULL)
7834f0884d6SBram Moolenaar 	{
7844f0884d6SBram Moolenaar 	    if (!quiet)
7854f0884d6SBram Moolenaar 		semsg(_(e_listidx), *n1);
7864f0884d6SBram Moolenaar 	    return NULL;
7874f0884d6SBram Moolenaar 	}
7884f0884d6SBram Moolenaar     }
7894f0884d6SBram Moolenaar     return li;
7904f0884d6SBram Moolenaar }
7914f0884d6SBram Moolenaar 
7924f0884d6SBram Moolenaar /*
7934f0884d6SBram Moolenaar  * Check that "n2" can be used as the second index in a range of list "l".
7944f0884d6SBram Moolenaar  * If "n1" or "n2" is negative it is changed to the positive index.
7954f0884d6SBram Moolenaar  * "li1" is the item for item "n1".
7964f0884d6SBram Moolenaar  * Return OK or FAIL.
7974f0884d6SBram Moolenaar  */
7984f0884d6SBram Moolenaar     int
check_range_index_two(list_T * l,long * n1,listitem_T * li1,long * n2,int quiet)7994f0884d6SBram Moolenaar check_range_index_two(
8004f0884d6SBram Moolenaar 	list_T	    *l,
8014f0884d6SBram Moolenaar 	long	    *n1,
8024f0884d6SBram Moolenaar 	listitem_T  *li1,
8034f0884d6SBram Moolenaar 	long	    *n2,
8044f0884d6SBram Moolenaar 	int	    quiet)
8054f0884d6SBram Moolenaar {
8064f0884d6SBram Moolenaar     if (*n2 < 0)
8074f0884d6SBram Moolenaar     {
8084f0884d6SBram Moolenaar 	listitem_T	*ni = list_find(l, *n2);
8094f0884d6SBram Moolenaar 
8104f0884d6SBram Moolenaar 	if (ni == NULL)
8114f0884d6SBram Moolenaar 	{
8124f0884d6SBram Moolenaar 	    if (!quiet)
8134f0884d6SBram Moolenaar 		semsg(_(e_listidx), *n2);
8144f0884d6SBram Moolenaar 	    return FAIL;
8154f0884d6SBram Moolenaar 	}
8164f0884d6SBram Moolenaar 	*n2 = list_idx_of_item(l, ni);
8174f0884d6SBram Moolenaar     }
8184f0884d6SBram Moolenaar 
8194f0884d6SBram Moolenaar     // Check that n2 isn't before n1.
8204f0884d6SBram Moolenaar     if (*n1 < 0)
8214f0884d6SBram Moolenaar 	*n1 = list_idx_of_item(l, li1);
8224f0884d6SBram Moolenaar     if (*n2 < *n1)
8234f0884d6SBram Moolenaar     {
8244f0884d6SBram Moolenaar 	if (!quiet)
8254f0884d6SBram Moolenaar 	    semsg(_(e_listidx), *n2);
8264f0884d6SBram Moolenaar 	return FAIL;
8274f0884d6SBram Moolenaar     }
8284f0884d6SBram Moolenaar     return OK;
8294f0884d6SBram Moolenaar }
8304f0884d6SBram Moolenaar 
8314f0884d6SBram Moolenaar /*
8324f0884d6SBram Moolenaar  * Assign values from list "src" into a range of "dest".
8334f0884d6SBram Moolenaar  * "idx1_arg" is the index of the first item in "dest" to be replaced.
8344f0884d6SBram Moolenaar  * "idx2" is the index of last item to be replaced, but when "empty_idx2" is
8354f0884d6SBram Moolenaar  * TRUE then replace all items after "idx1".
8364f0884d6SBram Moolenaar  * "op" is the operator, normally "=" but can be "+=" and the like.
8374f0884d6SBram Moolenaar  * "varname" is used for error messages.
8384f0884d6SBram Moolenaar  * Returns OK or FAIL.
8394f0884d6SBram Moolenaar  */
8404f0884d6SBram Moolenaar     int
list_assign_range(list_T * dest,list_T * src,long idx1_arg,long idx2,int empty_idx2,char_u * op,char_u * varname)8414f0884d6SBram Moolenaar list_assign_range(
8424f0884d6SBram Moolenaar 	list_T	    *dest,
8434f0884d6SBram Moolenaar 	list_T	    *src,
8444f0884d6SBram Moolenaar 	long	    idx1_arg,
8454f0884d6SBram Moolenaar 	long	    idx2,
8464f0884d6SBram Moolenaar 	int	    empty_idx2,
8474f0884d6SBram Moolenaar 	char_u	    *op,
8484f0884d6SBram Moolenaar 	char_u	    *varname)
8494f0884d6SBram Moolenaar {
8504f0884d6SBram Moolenaar     listitem_T	*src_li;
8514f0884d6SBram Moolenaar     listitem_T	*dest_li;
8524f0884d6SBram Moolenaar     long	idx1 = idx1_arg;
8534f0884d6SBram Moolenaar     listitem_T	*first_li = list_find_index(dest, &idx1);
8544f0884d6SBram Moolenaar     long	idx;
85589071cb6SBram Moolenaar     type_T	*member_type = NULL;
8564f0884d6SBram Moolenaar 
8574f0884d6SBram Moolenaar     /*
8584f0884d6SBram Moolenaar      * Check whether any of the list items is locked before making any changes.
8594f0884d6SBram Moolenaar      */
8604f0884d6SBram Moolenaar     idx = idx1;
8614f0884d6SBram Moolenaar     dest_li = first_li;
8624f0884d6SBram Moolenaar     for (src_li = src->lv_first; src_li != NULL && dest_li != NULL; )
8634f0884d6SBram Moolenaar     {
8644f0884d6SBram Moolenaar 	if (value_check_lock(dest_li->li_tv.v_lock, varname, FALSE))
8654f0884d6SBram Moolenaar 	    return FAIL;
8664f0884d6SBram Moolenaar 	src_li = src_li->li_next;
8674f0884d6SBram Moolenaar 	if (src_li == NULL || (!empty_idx2 && idx2 == idx))
8684f0884d6SBram Moolenaar 	    break;
8694f0884d6SBram Moolenaar 	dest_li = dest_li->li_next;
8704f0884d6SBram Moolenaar 	++idx;
8714f0884d6SBram Moolenaar     }
8724f0884d6SBram Moolenaar 
87389071cb6SBram Moolenaar     if (in_vim9script() && dest->lv_type != NULL
87489071cb6SBram Moolenaar 					   && dest->lv_type->tt_member != NULL)
87589071cb6SBram Moolenaar 	member_type = dest->lv_type->tt_member;
87689071cb6SBram Moolenaar 
8774f0884d6SBram Moolenaar     /*
8784f0884d6SBram Moolenaar      * Assign the List values to the list items.
8794f0884d6SBram Moolenaar      */
8804f0884d6SBram Moolenaar     idx = idx1;
8814f0884d6SBram Moolenaar     dest_li = first_li;
8824f0884d6SBram Moolenaar     for (src_li = src->lv_first; src_li != NULL; )
8834f0884d6SBram Moolenaar     {
8844f0884d6SBram Moolenaar 	if (op != NULL && *op != '=')
8854f0884d6SBram Moolenaar 	    tv_op(&dest_li->li_tv, &src_li->li_tv, op);
8864f0884d6SBram Moolenaar 	else
8874f0884d6SBram Moolenaar 	{
88889071cb6SBram Moolenaar 	    if (member_type != NULL
88989071cb6SBram Moolenaar 		    && check_typval_arg_type(member_type, &src_li->li_tv,
89089071cb6SBram Moolenaar 							      NULL, 0) == FAIL)
89189071cb6SBram Moolenaar 		return FAIL;
8924f0884d6SBram Moolenaar 	    clear_tv(&dest_li->li_tv);
8934f0884d6SBram Moolenaar 	    copy_tv(&src_li->li_tv, &dest_li->li_tv);
8944f0884d6SBram Moolenaar 	}
8954f0884d6SBram Moolenaar 	src_li = src_li->li_next;
8964f0884d6SBram Moolenaar 	if (src_li == NULL || (!empty_idx2 && idx2 == idx))
8974f0884d6SBram Moolenaar 	    break;
8984f0884d6SBram Moolenaar 	if (dest_li->li_next == NULL)
8994f0884d6SBram Moolenaar 	{
9004f0884d6SBram Moolenaar 	    // Need to add an empty item.
9014f0884d6SBram Moolenaar 	    if (list_append_number(dest, 0) == FAIL)
9024f0884d6SBram Moolenaar 	    {
9034f0884d6SBram Moolenaar 		src_li = NULL;
9044f0884d6SBram Moolenaar 		break;
9054f0884d6SBram Moolenaar 	    }
9064f0884d6SBram Moolenaar 	}
9074f0884d6SBram Moolenaar 	dest_li = dest_li->li_next;
9084f0884d6SBram Moolenaar 	++idx;
9094f0884d6SBram Moolenaar     }
9104f0884d6SBram Moolenaar     if (src_li != NULL)
9114f0884d6SBram Moolenaar     {
9124f0884d6SBram Moolenaar 	emsg(_(e_list_value_has_more_items_than_targets));
9134f0884d6SBram Moolenaar 	return FAIL;
9144f0884d6SBram Moolenaar     }
9154f0884d6SBram Moolenaar     if (empty_idx2
9164f0884d6SBram Moolenaar 	    ? (dest_li != NULL && dest_li->li_next != NULL)
9174f0884d6SBram Moolenaar 	    : idx != idx2)
9184f0884d6SBram Moolenaar     {
9194f0884d6SBram Moolenaar 	emsg(_(e_list_value_does_not_have_enough_items));
9204f0884d6SBram Moolenaar 	return FAIL;
9214f0884d6SBram Moolenaar     }
9224f0884d6SBram Moolenaar     return OK;
9234f0884d6SBram Moolenaar }
9244f0884d6SBram Moolenaar 
9254f0884d6SBram Moolenaar /*
926077a1e67SBram Moolenaar  * Flatten "list" to depth "maxdepth".
927077a1e67SBram Moolenaar  * It does nothing if "maxdepth" is 0.
928077a1e67SBram Moolenaar  * Returns FAIL when out of memory.
929077a1e67SBram Moolenaar  */
9303b690069SBram Moolenaar     static void
list_flatten(list_T * list,long maxdepth)931077a1e67SBram Moolenaar list_flatten(list_T *list, long maxdepth)
932077a1e67SBram Moolenaar {
933077a1e67SBram Moolenaar     listitem_T	*item;
934dcf59c37SBram Moolenaar     listitem_T	*tofree;
935077a1e67SBram Moolenaar     int		n;
936077a1e67SBram Moolenaar 
937077a1e67SBram Moolenaar     if (maxdepth == 0)
9383b690069SBram Moolenaar 	return;
939077a1e67SBram Moolenaar     CHECK_LIST_MATERIALIZE(list);
940077a1e67SBram Moolenaar 
941077a1e67SBram Moolenaar     n = 0;
942077a1e67SBram Moolenaar     item = list->lv_first;
943077a1e67SBram Moolenaar     while (item != NULL)
944077a1e67SBram Moolenaar     {
945077a1e67SBram Moolenaar 	fast_breakcheck();
946077a1e67SBram Moolenaar 	if (got_int)
9473b690069SBram Moolenaar 	    return;
948077a1e67SBram Moolenaar 
949077a1e67SBram Moolenaar 	if (item->li_tv.v_type == VAR_LIST)
950077a1e67SBram Moolenaar 	{
951077a1e67SBram Moolenaar 	    listitem_T *next = item->li_next;
952077a1e67SBram Moolenaar 
953077a1e67SBram Moolenaar 	    vimlist_remove(list, item, item);
954077a1e67SBram Moolenaar 	    if (list_extend(list, item->li_tv.vval.v_list, next) == FAIL)
955b3bf33a7SBram Moolenaar 	    {
956b3bf33a7SBram Moolenaar 		list_free_item(list, item);
9573b690069SBram Moolenaar 		return;
958b3bf33a7SBram Moolenaar 	    }
959dcf59c37SBram Moolenaar 	    clear_tv(&item->li_tv);
960dcf59c37SBram Moolenaar 	    tofree = item;
961077a1e67SBram Moolenaar 
962077a1e67SBram Moolenaar 	    if (item->li_prev == NULL)
963077a1e67SBram Moolenaar 		item = list->lv_first;
964077a1e67SBram Moolenaar 	    else
965077a1e67SBram Moolenaar 		item = item->li_prev->li_next;
966dcf59c37SBram Moolenaar 	    list_free_item(list, tofree);
967077a1e67SBram Moolenaar 
968077a1e67SBram Moolenaar 	    if (++n >= maxdepth)
969077a1e67SBram Moolenaar 	    {
970077a1e67SBram Moolenaar 		n = 0;
971077a1e67SBram Moolenaar 		item = next;
972077a1e67SBram Moolenaar 	    }
973077a1e67SBram Moolenaar 	}
974077a1e67SBram Moolenaar 	else
975077a1e67SBram Moolenaar 	{
976077a1e67SBram Moolenaar 	    n = 0;
977077a1e67SBram Moolenaar 	    item = item->li_next;
978077a1e67SBram Moolenaar 	}
979077a1e67SBram Moolenaar     }
980077a1e67SBram Moolenaar }
981077a1e67SBram Moolenaar 
982077a1e67SBram Moolenaar /*
9833b690069SBram Moolenaar  * "flatten()" and "flattennew()" functions
984077a1e67SBram Moolenaar  */
9853b690069SBram Moolenaar     static void
flatten_common(typval_T * argvars,typval_T * rettv,int make_copy)9863b690069SBram Moolenaar flatten_common(typval_T *argvars, typval_T *rettv, int make_copy)
987077a1e67SBram Moolenaar {
988077a1e67SBram Moolenaar     list_T  *l;
989077a1e67SBram Moolenaar     long    maxdepth;
990077a1e67SBram Moolenaar     int	    error = FALSE;
991077a1e67SBram Moolenaar 
99283494b4aSYegappan Lakshmanan     if (in_vim9script()
99383494b4aSYegappan Lakshmanan 	    && (check_for_list_arg(argvars, 0) == FAIL
99483494b4aSYegappan Lakshmanan 		|| check_for_opt_number_arg(argvars, 1) == FAIL))
99583494b4aSYegappan Lakshmanan 	return;
99683494b4aSYegappan Lakshmanan 
997077a1e67SBram Moolenaar     if (argvars[0].v_type != VAR_LIST)
998077a1e67SBram Moolenaar     {
999077a1e67SBram Moolenaar 	semsg(_(e_listarg), "flatten()");
1000077a1e67SBram Moolenaar 	return;
1001077a1e67SBram Moolenaar     }
1002077a1e67SBram Moolenaar 
1003077a1e67SBram Moolenaar     if (argvars[1].v_type == VAR_UNKNOWN)
1004077a1e67SBram Moolenaar 	maxdepth = 999999;
1005077a1e67SBram Moolenaar     else
1006077a1e67SBram Moolenaar     {
1007077a1e67SBram Moolenaar 	maxdepth = (long)tv_get_number_chk(&argvars[1], &error);
1008077a1e67SBram Moolenaar 	if (error)
1009077a1e67SBram Moolenaar 	    return;
1010077a1e67SBram Moolenaar 	if (maxdepth < 0)
1011077a1e67SBram Moolenaar 	{
1012077a1e67SBram Moolenaar 	    emsg(_("E900: maxdepth must be non-negative number"));
1013077a1e67SBram Moolenaar 	    return;
1014077a1e67SBram Moolenaar 	}
1015077a1e67SBram Moolenaar     }
1016077a1e67SBram Moolenaar 
1017077a1e67SBram Moolenaar     l = argvars[0].vval.v_list;
10183b690069SBram Moolenaar     rettv->v_type = VAR_LIST;
10193b690069SBram Moolenaar     rettv->vval.v_list = l;
10203b690069SBram Moolenaar     if (l == NULL)
10213b690069SBram Moolenaar 	return;
10223b690069SBram Moolenaar 
10233b690069SBram Moolenaar     if (make_copy)
10243b690069SBram Moolenaar     {
10253b690069SBram Moolenaar 	l = list_copy(l, TRUE, get_copyID());
10263b690069SBram Moolenaar 	rettv->vval.v_list = l;
10273b690069SBram Moolenaar 	if (l == NULL)
10283b690069SBram Moolenaar 	    return;
1029b3bf33a7SBram Moolenaar 	// The type will change.
1030b3bf33a7SBram Moolenaar 	free_type(l->lv_type);
1031b3bf33a7SBram Moolenaar 	l->lv_type = NULL;
10323b690069SBram Moolenaar     }
10333b690069SBram Moolenaar     else
10343b690069SBram Moolenaar     {
10353b690069SBram Moolenaar 	if (value_check_lock(l->lv_lock,
10363b690069SBram Moolenaar 				     (char_u *)N_("flatten() argument"), TRUE))
10373b690069SBram Moolenaar 	    return;
10383b690069SBram Moolenaar 	++l->lv_refcount;
10393b690069SBram Moolenaar     }
10403b690069SBram Moolenaar 
10413b690069SBram Moolenaar     list_flatten(l, maxdepth);
10423b690069SBram Moolenaar }
10433b690069SBram Moolenaar 
10443b690069SBram Moolenaar /*
10453b690069SBram Moolenaar  * "flatten(list[, {maxdepth}])" function
10463b690069SBram Moolenaar  */
10473b690069SBram Moolenaar     void
f_flatten(typval_T * argvars,typval_T * rettv)10483b690069SBram Moolenaar f_flatten(typval_T *argvars, typval_T *rettv)
10493b690069SBram Moolenaar {
10503b690069SBram Moolenaar     if (in_vim9script())
10513b690069SBram Moolenaar 	emsg(_(e_cannot_use_flatten_in_vim9_script));
10523b690069SBram Moolenaar     else
10533b690069SBram Moolenaar 	flatten_common(argvars, rettv, FALSE);
10543b690069SBram Moolenaar }
10553b690069SBram Moolenaar 
10563b690069SBram Moolenaar /*
10573b690069SBram Moolenaar  * "flattennew(list[, {maxdepth}])" function
10583b690069SBram Moolenaar  */
10593b690069SBram Moolenaar     void
f_flattennew(typval_T * argvars,typval_T * rettv)10603b690069SBram Moolenaar f_flattennew(typval_T *argvars, typval_T *rettv)
10613b690069SBram Moolenaar {
10623b690069SBram Moolenaar     flatten_common(argvars, rettv, TRUE);
1063077a1e67SBram Moolenaar }
1064077a1e67SBram Moolenaar 
1065077a1e67SBram Moolenaar /*
10661a739237SBram Moolenaar  * Extend "l1" with "l2".  "l1" must not be NULL.
1067da861d63SBram Moolenaar  * If "bef" is NULL append at the end, otherwise insert before this item.
1068da861d63SBram Moolenaar  * Returns FAIL when out of memory.
1069da861d63SBram Moolenaar  */
1070da861d63SBram Moolenaar     int
list_extend(list_T * l1,list_T * l2,listitem_T * bef)1071da861d63SBram Moolenaar list_extend(list_T *l1, list_T *l2, listitem_T *bef)
1072da861d63SBram Moolenaar {
1073da861d63SBram Moolenaar     listitem_T	*item;
10741a739237SBram Moolenaar     int		todo;
1075dcae51faSBram Moolenaar     listitem_T	*bef_prev;
1076da861d63SBram Moolenaar 
10771a739237SBram Moolenaar     // NULL list is equivalent to an empty list: nothing to do.
10781a739237SBram Moolenaar     if (l2 == NULL || l2->lv_len == 0)
10791a739237SBram Moolenaar 	return OK;
10801a739237SBram Moolenaar 
10811a739237SBram Moolenaar     todo = l2->lv_len;
10827e9f351bSBram Moolenaar     CHECK_LIST_MATERIALIZE(l1);
10837e9f351bSBram Moolenaar     CHECK_LIST_MATERIALIZE(l2);
10848a7d6542SBram Moolenaar 
1085dcae51faSBram Moolenaar     // When exending a list with itself, at some point we run into the item
1086dcae51faSBram Moolenaar     // that was before "bef" and need to skip over the already inserted items
1087dcae51faSBram Moolenaar     // to "bef".
1088dcae51faSBram Moolenaar     bef_prev = bef == NULL ? NULL : bef->li_prev;
1089dcae51faSBram Moolenaar 
10904ba37b58SBram Moolenaar     // We also quit the loop when we have inserted the original item count of
10914ba37b58SBram Moolenaar     // the list, avoid a hang when we extend a list with itself.
1092dcae51faSBram Moolenaar     for (item = l2->lv_first; item != NULL && --todo >= 0;
1093dcae51faSBram Moolenaar 				 item = item == bef_prev ? bef : item->li_next)
1094da861d63SBram Moolenaar 	if (list_insert_tv(l1, &item->li_tv, bef) == FAIL)
1095da861d63SBram Moolenaar 	    return FAIL;
1096da861d63SBram Moolenaar     return OK;
1097da861d63SBram Moolenaar }
1098da861d63SBram Moolenaar 
1099da861d63SBram Moolenaar /*
1100da861d63SBram Moolenaar  * Concatenate lists "l1" and "l2" into a new list, stored in "tv".
1101da861d63SBram Moolenaar  * Return FAIL when out of memory.
1102da861d63SBram Moolenaar  */
1103da861d63SBram Moolenaar     int
list_concat(list_T * l1,list_T * l2,typval_T * tv)1104da861d63SBram Moolenaar list_concat(list_T *l1, list_T *l2, typval_T *tv)
1105da861d63SBram Moolenaar {
1106da861d63SBram Moolenaar     list_T	*l;
1107da861d63SBram Moolenaar 
11084ba37b58SBram Moolenaar     // make a copy of the first list.
11091a739237SBram Moolenaar     if (l1 == NULL)
11101a739237SBram Moolenaar 	l = list_alloc();
11111a739237SBram Moolenaar     else
1112da861d63SBram Moolenaar 	l = list_copy(l1, FALSE, 0);
1113da861d63SBram Moolenaar     if (l == NULL)
1114da861d63SBram Moolenaar 	return FAIL;
1115da861d63SBram Moolenaar     tv->v_type = VAR_LIST;
11169681f713SBram Moolenaar     tv->v_lock = 0;
1117da861d63SBram Moolenaar     tv->vval.v_list = l;
11181a739237SBram Moolenaar     if (l1 == NULL)
11191a739237SBram Moolenaar 	++l->lv_refcount;
1120da861d63SBram Moolenaar 
11214ba37b58SBram Moolenaar     // append all items from the second list
1122da861d63SBram Moolenaar     return list_extend(l, l2, NULL);
1123da861d63SBram Moolenaar }
1124da861d63SBram Moolenaar 
11259af78769SBram Moolenaar     list_T *
list_slice(list_T * ol,long n1,long n2)11269af78769SBram Moolenaar list_slice(list_T *ol, long n1, long n2)
11279af78769SBram Moolenaar {
11289af78769SBram Moolenaar     listitem_T	*item;
11299af78769SBram Moolenaar     list_T	*l = list_alloc();
11309af78769SBram Moolenaar 
11319af78769SBram Moolenaar     if (l == NULL)
11329af78769SBram Moolenaar 	return NULL;
11339af78769SBram Moolenaar     for (item = list_find(ol, n1); n1 <= n2; ++n1)
11349af78769SBram Moolenaar     {
11359af78769SBram Moolenaar 	if (list_append_tv(l, &item->li_tv) == FAIL)
11369af78769SBram Moolenaar 	{
11379af78769SBram Moolenaar 	    list_free(l);
11389af78769SBram Moolenaar 	    return NULL;
11399af78769SBram Moolenaar 	}
11409af78769SBram Moolenaar 	item = item->li_next;
11419af78769SBram Moolenaar     }
11429af78769SBram Moolenaar     return l;
11439af78769SBram Moolenaar }
11449af78769SBram Moolenaar 
1145ed591877SBram Moolenaar     int
list_slice_or_index(list_T * list,int range,varnumber_T n1_arg,varnumber_T n2_arg,int exclusive,typval_T * rettv,int verbose)1146ed591877SBram Moolenaar list_slice_or_index(
1147ed591877SBram Moolenaar 	    list_T	*list,
1148ed591877SBram Moolenaar 	    int		range,
11496601b629SBram Moolenaar 	    varnumber_T	n1_arg,
11506601b629SBram Moolenaar 	    varnumber_T	n2_arg,
11516601b629SBram Moolenaar 	    int		exclusive,
1152ed591877SBram Moolenaar 	    typval_T	*rettv,
1153ed591877SBram Moolenaar 	    int		verbose)
1154ed591877SBram Moolenaar {
1155ed591877SBram Moolenaar     long	len = list_len(list);
11566601b629SBram Moolenaar     varnumber_T	n1 = n1_arg;
11576601b629SBram Moolenaar     varnumber_T	n2 = n2_arg;
1158ed591877SBram Moolenaar     typval_T	var1;
1159ed591877SBram Moolenaar 
1160ed591877SBram Moolenaar     if (n1 < 0)
1161ed591877SBram Moolenaar 	n1 = len + n1;
1162ed591877SBram Moolenaar     if (n1 < 0 || n1 >= len)
1163ed591877SBram Moolenaar     {
116492f05f21SBram Moolenaar 	// For a range we allow invalid values and for legacy script return an
116592f05f21SBram Moolenaar 	// empty list, for Vim9 script start at the first item.
116692f05f21SBram Moolenaar 	// A list index out of range is an error.
1167ed591877SBram Moolenaar 	if (!range)
1168ed591877SBram Moolenaar 	{
1169ed591877SBram Moolenaar 	    if (verbose)
1170239f8d93SBram Moolenaar 		semsg(_(e_listidx), (long)n1_arg);
1171ed591877SBram Moolenaar 	    return FAIL;
1172ed591877SBram Moolenaar 	}
117392f05f21SBram Moolenaar 	if (in_vim9script())
1174cc673e74SBram Moolenaar 	    n1 = n1 < 0 ? 0 : len;
117592f05f21SBram Moolenaar 	else
117692f05f21SBram Moolenaar 	    n1 = len;
1177ed591877SBram Moolenaar     }
1178ed591877SBram Moolenaar     if (range)
1179ed591877SBram Moolenaar     {
1180ed591877SBram Moolenaar 	list_T	*l;
1181ed591877SBram Moolenaar 
1182ed591877SBram Moolenaar 	if (n2 < 0)
1183ed591877SBram Moolenaar 	    n2 = len + n2;
1184ed591877SBram Moolenaar 	else if (n2 >= len)
11856601b629SBram Moolenaar 	    n2 = len - (exclusive ? 0 : 1);
11866601b629SBram Moolenaar 	if (exclusive)
11876601b629SBram Moolenaar 	    --n2;
1188ed591877SBram Moolenaar 	if (n2 < 0 || n2 + 1 < n1)
1189ed591877SBram Moolenaar 	    n2 = -1;
1190ed591877SBram Moolenaar 	l = list_slice(list, n1, n2);
1191ed591877SBram Moolenaar 	if (l == NULL)
1192ed591877SBram Moolenaar 	    return FAIL;
1193ed591877SBram Moolenaar 	clear_tv(rettv);
1194ed591877SBram Moolenaar 	rettv_list_set(rettv, l);
1195ed591877SBram Moolenaar     }
1196ed591877SBram Moolenaar     else
1197ed591877SBram Moolenaar     {
1198ed591877SBram Moolenaar 	// copy the item to "var1" to avoid that freeing the list makes it
1199ed591877SBram Moolenaar 	// invalid.
1200ed591877SBram Moolenaar 	copy_tv(&list_find(list, n1)->li_tv, &var1);
1201ed591877SBram Moolenaar 	clear_tv(rettv);
1202ed591877SBram Moolenaar 	*rettv = var1;
1203ed591877SBram Moolenaar     }
1204ed591877SBram Moolenaar     return OK;
1205ed591877SBram Moolenaar }
1206ed591877SBram Moolenaar 
1207da861d63SBram Moolenaar /*
1208da861d63SBram Moolenaar  * Make a copy of list "orig".  Shallow if "deep" is FALSE.
1209da861d63SBram Moolenaar  * The refcount of the new list is set to 1.
1210da861d63SBram Moolenaar  * See item_copy() for "copyID".
1211da861d63SBram Moolenaar  * Returns NULL when out of memory.
1212da861d63SBram Moolenaar  */
1213da861d63SBram Moolenaar     list_T *
list_copy(list_T * orig,int deep,int copyID)1214da861d63SBram Moolenaar list_copy(list_T *orig, int deep, int copyID)
1215da861d63SBram Moolenaar {
1216da861d63SBram Moolenaar     list_T	*copy;
1217da861d63SBram Moolenaar     listitem_T	*item;
1218da861d63SBram Moolenaar     listitem_T	*ni;
1219da861d63SBram Moolenaar 
1220da861d63SBram Moolenaar     if (orig == NULL)
1221da861d63SBram Moolenaar 	return NULL;
1222da861d63SBram Moolenaar 
1223da861d63SBram Moolenaar     copy = list_alloc();
1224da861d63SBram Moolenaar     if (copy != NULL)
1225da861d63SBram Moolenaar     {
1226b3bf33a7SBram Moolenaar 	copy->lv_type = alloc_type(orig->lv_type);
1227da861d63SBram Moolenaar 	if (copyID != 0)
1228da861d63SBram Moolenaar 	{
12294ba37b58SBram Moolenaar 	    // Do this before adding the items, because one of the items may
12304ba37b58SBram Moolenaar 	    // refer back to this list.
1231da861d63SBram Moolenaar 	    orig->lv_copyID = copyID;
1232da861d63SBram Moolenaar 	    orig->lv_copylist = copy;
1233da861d63SBram Moolenaar 	}
12347e9f351bSBram Moolenaar 	CHECK_LIST_MATERIALIZE(orig);
1235da861d63SBram Moolenaar 	for (item = orig->lv_first; item != NULL && !got_int;
1236da861d63SBram Moolenaar 							 item = item->li_next)
1237da861d63SBram Moolenaar 	{
1238da861d63SBram Moolenaar 	    ni = listitem_alloc();
1239da861d63SBram Moolenaar 	    if (ni == NULL)
1240da861d63SBram Moolenaar 		break;
1241da861d63SBram Moolenaar 	    if (deep)
1242da861d63SBram Moolenaar 	    {
1243da861d63SBram Moolenaar 		if (item_copy(&item->li_tv, &ni->li_tv, deep, copyID) == FAIL)
1244da861d63SBram Moolenaar 		{
1245da861d63SBram Moolenaar 		    vim_free(ni);
1246da861d63SBram Moolenaar 		    break;
1247da861d63SBram Moolenaar 		}
1248da861d63SBram Moolenaar 	    }
1249da861d63SBram Moolenaar 	    else
1250da861d63SBram Moolenaar 		copy_tv(&item->li_tv, &ni->li_tv);
1251da861d63SBram Moolenaar 	    list_append(copy, ni);
1252da861d63SBram Moolenaar 	}
1253da861d63SBram Moolenaar 	++copy->lv_refcount;
1254da861d63SBram Moolenaar 	if (item != NULL)
1255da861d63SBram Moolenaar 	{
1256da861d63SBram Moolenaar 	    list_unref(copy);
1257da861d63SBram Moolenaar 	    copy = NULL;
1258da861d63SBram Moolenaar 	}
1259da861d63SBram Moolenaar     }
1260da861d63SBram Moolenaar 
1261da861d63SBram Moolenaar     return copy;
1262da861d63SBram Moolenaar }
1263da861d63SBram Moolenaar 
1264da861d63SBram Moolenaar /*
1265da861d63SBram Moolenaar  * Remove items "item" to "item2" from list "l".
1266da861d63SBram Moolenaar  * Does not free the listitem or the value!
1267da861d63SBram Moolenaar  * This used to be called list_remove, but that conflicts with a Sun header
1268da861d63SBram Moolenaar  * file.
1269da861d63SBram Moolenaar  */
1270da861d63SBram Moolenaar     void
vimlist_remove(list_T * l,listitem_T * item,listitem_T * item2)1271da861d63SBram Moolenaar vimlist_remove(list_T *l, listitem_T *item, listitem_T *item2)
1272da861d63SBram Moolenaar {
1273da861d63SBram Moolenaar     listitem_T	*ip;
1274da861d63SBram Moolenaar 
12757e9f351bSBram Moolenaar     CHECK_LIST_MATERIALIZE(l);
12768a7d6542SBram Moolenaar 
12774ba37b58SBram Moolenaar     // notify watchers
1278da861d63SBram Moolenaar     for (ip = item; ip != NULL; ip = ip->li_next)
1279da861d63SBram Moolenaar     {
1280da861d63SBram Moolenaar 	--l->lv_len;
1281da861d63SBram Moolenaar 	list_fix_watch(l, ip);
1282da861d63SBram Moolenaar 	if (ip == item2)
1283da861d63SBram Moolenaar 	    break;
1284da861d63SBram Moolenaar     }
1285da861d63SBram Moolenaar 
1286da861d63SBram Moolenaar     if (item2->li_next == NULL)
12870ff6aad3SBram Moolenaar 	l->lv_u.mat.lv_last = item->li_prev;
1288da861d63SBram Moolenaar     else
1289da861d63SBram Moolenaar 	item2->li_next->li_prev = item->li_prev;
1290da861d63SBram Moolenaar     if (item->li_prev == NULL)
1291da861d63SBram Moolenaar 	l->lv_first = item2->li_next;
1292da861d63SBram Moolenaar     else
1293da861d63SBram Moolenaar 	item->li_prev->li_next = item2->li_next;
12940ff6aad3SBram Moolenaar     l->lv_u.mat.lv_idx_item = NULL;
1295da861d63SBram Moolenaar }
1296da861d63SBram Moolenaar 
1297da861d63SBram Moolenaar /*
1298da861d63SBram Moolenaar  * Return an allocated string with the string representation of a list.
1299da861d63SBram Moolenaar  * May return NULL.
1300da861d63SBram Moolenaar  */
1301da861d63SBram Moolenaar     char_u *
list2string(typval_T * tv,int copyID,int restore_copyID)1302da861d63SBram Moolenaar list2string(typval_T *tv, int copyID, int restore_copyID)
1303da861d63SBram Moolenaar {
1304da861d63SBram Moolenaar     garray_T	ga;
1305da861d63SBram Moolenaar 
1306da861d63SBram Moolenaar     if (tv->vval.v_list == NULL)
1307da861d63SBram Moolenaar 	return NULL;
1308da861d63SBram Moolenaar     ga_init2(&ga, (int)sizeof(char), 80);
1309da861d63SBram Moolenaar     ga_append(&ga, '[');
13107e9f351bSBram Moolenaar     CHECK_LIST_MATERIALIZE(tv->vval.v_list);
1311da861d63SBram Moolenaar     if (list_join(&ga, tv->vval.v_list, (char_u *)", ",
1312da861d63SBram Moolenaar 				       FALSE, restore_copyID, copyID) == FAIL)
1313da861d63SBram Moolenaar     {
1314da861d63SBram Moolenaar 	vim_free(ga.ga_data);
1315da861d63SBram Moolenaar 	return NULL;
1316da861d63SBram Moolenaar     }
1317da861d63SBram Moolenaar     ga_append(&ga, ']');
1318da861d63SBram Moolenaar     ga_append(&ga, NUL);
1319da861d63SBram Moolenaar     return (char_u *)ga.ga_data;
1320da861d63SBram Moolenaar }
1321da861d63SBram Moolenaar 
1322da861d63SBram Moolenaar typedef struct join_S {
1323da861d63SBram Moolenaar     char_u	*s;
1324da861d63SBram Moolenaar     char_u	*tofree;
1325da861d63SBram Moolenaar } join_T;
1326da861d63SBram Moolenaar 
1327da861d63SBram Moolenaar     static int
list_join_inner(garray_T * gap,list_T * l,char_u * sep,int echo_style,int restore_copyID,int copyID,garray_T * join_gap)1328da861d63SBram Moolenaar list_join_inner(
13294ba37b58SBram Moolenaar     garray_T	*gap,		// to store the result in
1330da861d63SBram Moolenaar     list_T	*l,
1331da861d63SBram Moolenaar     char_u	*sep,
1332da861d63SBram Moolenaar     int		echo_style,
1333da861d63SBram Moolenaar     int		restore_copyID,
1334da861d63SBram Moolenaar     int		copyID,
13354ba37b58SBram Moolenaar     garray_T	*join_gap)	// to keep each list item string
1336da861d63SBram Moolenaar {
1337da861d63SBram Moolenaar     int		i;
1338da861d63SBram Moolenaar     join_T	*p;
1339da861d63SBram Moolenaar     int		len;
1340da861d63SBram Moolenaar     int		sumlen = 0;
1341da861d63SBram Moolenaar     int		first = TRUE;
1342da861d63SBram Moolenaar     char_u	*tofree;
1343da861d63SBram Moolenaar     char_u	numbuf[NUMBUFLEN];
1344da861d63SBram Moolenaar     listitem_T	*item;
1345da861d63SBram Moolenaar     char_u	*s;
1346da861d63SBram Moolenaar 
13474ba37b58SBram Moolenaar     // Stringify each item in the list.
13487e9f351bSBram Moolenaar     CHECK_LIST_MATERIALIZE(l);
1349da861d63SBram Moolenaar     for (item = l->lv_first; item != NULL && !got_int; item = item->li_next)
1350da861d63SBram Moolenaar     {
1351da861d63SBram Moolenaar 	s = echo_string_core(&item->li_tv, &tofree, numbuf, copyID,
135235422f45SBram Moolenaar 				      echo_style, restore_copyID, !echo_style);
1353da861d63SBram Moolenaar 	if (s == NULL)
1354da861d63SBram Moolenaar 	    return FAIL;
1355da861d63SBram Moolenaar 
1356da861d63SBram Moolenaar 	len = (int)STRLEN(s);
1357da861d63SBram Moolenaar 	sumlen += len;
1358da861d63SBram Moolenaar 
1359da861d63SBram Moolenaar 	(void)ga_grow(join_gap, 1);
1360da861d63SBram Moolenaar 	p = ((join_T *)join_gap->ga_data) + (join_gap->ga_len++);
1361da861d63SBram Moolenaar 	if (tofree != NULL || s != numbuf)
1362da861d63SBram Moolenaar 	{
1363da861d63SBram Moolenaar 	    p->s = s;
1364da861d63SBram Moolenaar 	    p->tofree = tofree;
1365da861d63SBram Moolenaar 	}
1366da861d63SBram Moolenaar 	else
1367da861d63SBram Moolenaar 	{
1368da861d63SBram Moolenaar 	    p->s = vim_strnsave(s, len);
1369da861d63SBram Moolenaar 	    p->tofree = p->s;
1370da861d63SBram Moolenaar 	}
1371da861d63SBram Moolenaar 
1372da861d63SBram Moolenaar 	line_breakcheck();
13734ba37b58SBram Moolenaar 	if (did_echo_string_emsg)  // recursion error, bail out
1374da861d63SBram Moolenaar 	    break;
1375da861d63SBram Moolenaar     }
1376da861d63SBram Moolenaar 
13774ba37b58SBram Moolenaar     // Allocate result buffer with its total size, avoid re-allocation and
13784ba37b58SBram Moolenaar     // multiple copy operations.  Add 2 for a tailing ']' and NUL.
1379da861d63SBram Moolenaar     if (join_gap->ga_len >= 2)
1380da861d63SBram Moolenaar 	sumlen += (int)STRLEN(sep) * (join_gap->ga_len - 1);
1381da861d63SBram Moolenaar     if (ga_grow(gap, sumlen + 2) == FAIL)
1382da861d63SBram Moolenaar 	return FAIL;
1383da861d63SBram Moolenaar 
1384da861d63SBram Moolenaar     for (i = 0; i < join_gap->ga_len && !got_int; ++i)
1385da861d63SBram Moolenaar     {
1386da861d63SBram Moolenaar 	if (first)
1387da861d63SBram Moolenaar 	    first = FALSE;
1388da861d63SBram Moolenaar 	else
1389da861d63SBram Moolenaar 	    ga_concat(gap, sep);
1390da861d63SBram Moolenaar 	p = ((join_T *)join_gap->ga_data) + i;
1391da861d63SBram Moolenaar 
1392da861d63SBram Moolenaar 	if (p->s != NULL)
1393da861d63SBram Moolenaar 	    ga_concat(gap, p->s);
1394da861d63SBram Moolenaar 	line_breakcheck();
1395da861d63SBram Moolenaar     }
1396da861d63SBram Moolenaar 
1397da861d63SBram Moolenaar     return OK;
1398da861d63SBram Moolenaar }
1399da861d63SBram Moolenaar 
1400da861d63SBram Moolenaar /*
1401da861d63SBram Moolenaar  * Join list "l" into a string in "*gap", using separator "sep".
1402da861d63SBram Moolenaar  * When "echo_style" is TRUE use String as echoed, otherwise as inside a List.
1403da861d63SBram Moolenaar  * Return FAIL or OK.
1404da861d63SBram Moolenaar  */
1405da861d63SBram Moolenaar     int
list_join(garray_T * gap,list_T * l,char_u * sep,int echo_style,int restore_copyID,int copyID)1406da861d63SBram Moolenaar list_join(
1407da861d63SBram Moolenaar     garray_T	*gap,
1408da861d63SBram Moolenaar     list_T	*l,
1409da861d63SBram Moolenaar     char_u	*sep,
1410da861d63SBram Moolenaar     int		echo_style,
1411da861d63SBram Moolenaar     int		restore_copyID,
1412da861d63SBram Moolenaar     int		copyID)
1413da861d63SBram Moolenaar {
1414da861d63SBram Moolenaar     garray_T	join_ga;
1415da861d63SBram Moolenaar     int		retval;
1416da861d63SBram Moolenaar     join_T	*p;
1417da861d63SBram Moolenaar     int		i;
1418da861d63SBram Moolenaar 
1419da861d63SBram Moolenaar     if (l->lv_len < 1)
14204ba37b58SBram Moolenaar 	return OK; // nothing to do
1421da861d63SBram Moolenaar     ga_init2(&join_ga, (int)sizeof(join_T), l->lv_len);
1422da861d63SBram Moolenaar     retval = list_join_inner(gap, l, sep, echo_style, restore_copyID,
1423da861d63SBram Moolenaar 							    copyID, &join_ga);
1424da861d63SBram Moolenaar 
14254ba37b58SBram Moolenaar     // Dispose each item in join_ga.
1426da861d63SBram Moolenaar     if (join_ga.ga_data != NULL)
1427da861d63SBram Moolenaar     {
1428da861d63SBram Moolenaar 	p = (join_T *)join_ga.ga_data;
1429da861d63SBram Moolenaar 	for (i = 0; i < join_ga.ga_len; ++i)
1430da861d63SBram Moolenaar 	{
1431da861d63SBram Moolenaar 	    vim_free(p->tofree);
1432da861d63SBram Moolenaar 	    ++p;
1433da861d63SBram Moolenaar 	}
1434da861d63SBram Moolenaar 	ga_clear(&join_ga);
1435da861d63SBram Moolenaar     }
1436da861d63SBram Moolenaar 
1437da861d63SBram Moolenaar     return retval;
1438da861d63SBram Moolenaar }
1439da861d63SBram Moolenaar 
1440da861d63SBram Moolenaar /*
14419f9fe37fSBram Moolenaar  * "join()" function
14429f9fe37fSBram Moolenaar  */
14439f9fe37fSBram Moolenaar     void
f_join(typval_T * argvars,typval_T * rettv)14449f9fe37fSBram Moolenaar f_join(typval_T *argvars, typval_T *rettv)
14459f9fe37fSBram Moolenaar {
14469f9fe37fSBram Moolenaar     garray_T	ga;
14479f9fe37fSBram Moolenaar     char_u	*sep;
14489f9fe37fSBram Moolenaar 
144983494b4aSYegappan Lakshmanan     if (in_vim9script()
145083494b4aSYegappan Lakshmanan 	    && (check_for_list_arg(argvars, 0) == FAIL
145183494b4aSYegappan Lakshmanan 		|| check_for_opt_string_arg(argvars, 1) == FAIL))
145283494b4aSYegappan Lakshmanan 	return;
145383494b4aSYegappan Lakshmanan 
14549f9fe37fSBram Moolenaar     if (argvars[0].v_type != VAR_LIST)
14559f9fe37fSBram Moolenaar     {
14569f9fe37fSBram Moolenaar 	emsg(_(e_listreq));
14579f9fe37fSBram Moolenaar 	return;
14589f9fe37fSBram Moolenaar     }
1459ef982575SBram Moolenaar     rettv->v_type = VAR_STRING;
14609f9fe37fSBram Moolenaar     if (argvars[0].vval.v_list == NULL)
14619f9fe37fSBram Moolenaar 	return;
1462ef982575SBram Moolenaar 
14639f9fe37fSBram Moolenaar     if (argvars[1].v_type == VAR_UNKNOWN)
14649f9fe37fSBram Moolenaar 	sep = (char_u *)" ";
14659f9fe37fSBram Moolenaar     else
14669f9fe37fSBram Moolenaar 	sep = tv_get_string_chk(&argvars[1]);
14679f9fe37fSBram Moolenaar 
14689f9fe37fSBram Moolenaar     if (sep != NULL)
14699f9fe37fSBram Moolenaar     {
14709f9fe37fSBram Moolenaar 	ga_init2(&ga, (int)sizeof(char), 80);
14719f9fe37fSBram Moolenaar 	list_join(&ga, argvars[0].vval.v_list, sep, TRUE, FALSE, 0);
14729f9fe37fSBram Moolenaar 	ga_append(&ga, NUL);
14739f9fe37fSBram Moolenaar 	rettv->vval.v_string = (char_u *)ga.ga_data;
14749f9fe37fSBram Moolenaar     }
14759f9fe37fSBram Moolenaar     else
14769f9fe37fSBram Moolenaar 	rettv->vval.v_string = NULL;
14779f9fe37fSBram Moolenaar }
14789f9fe37fSBram Moolenaar 
14799f9fe37fSBram Moolenaar /*
1480da861d63SBram Moolenaar  * Allocate a variable for a List and fill it from "*arg".
14817147820cSBram Moolenaar  * "*arg" points to the "[".
1482da861d63SBram Moolenaar  * Return OK or FAIL.
1483da861d63SBram Moolenaar  */
1484da861d63SBram Moolenaar     int
eval_list(char_u ** arg,typval_T * rettv,evalarg_T * evalarg,int do_error)14859a78e6dfSBram Moolenaar eval_list(char_u **arg, typval_T *rettv, evalarg_T *evalarg, int do_error)
1486da861d63SBram Moolenaar {
14877147820cSBram Moolenaar     int		evaluate = evalarg == NULL ? FALSE
14887147820cSBram Moolenaar 					 : evalarg->eval_flags & EVAL_EVALUATE;
1489da861d63SBram Moolenaar     list_T	*l = NULL;
1490da861d63SBram Moolenaar     typval_T	tv;
1491da861d63SBram Moolenaar     listitem_T	*item;
1492eb6880b6SBram Moolenaar     int		vim9script = in_vim9script();
14937147820cSBram Moolenaar     int		had_comma;
1494da861d63SBram Moolenaar 
1495da861d63SBram Moolenaar     if (evaluate)
1496da861d63SBram Moolenaar     {
1497da861d63SBram Moolenaar 	l = list_alloc();
1498da861d63SBram Moolenaar 	if (l == NULL)
1499da861d63SBram Moolenaar 	    return FAIL;
1500da861d63SBram Moolenaar     }
1501da861d63SBram Moolenaar 
1502962d7213SBram Moolenaar     *arg = skipwhite_and_linebreak(*arg + 1, evalarg);
1503da861d63SBram Moolenaar     while (**arg != ']' && **arg != NUL)
1504da861d63SBram Moolenaar     {
15057147820cSBram Moolenaar 	if (eval1(arg, &tv, evalarg) == FAIL)	// recursive!
1506da861d63SBram Moolenaar 	    goto failret;
1507da861d63SBram Moolenaar 	if (evaluate)
1508da861d63SBram Moolenaar 	{
1509da861d63SBram Moolenaar 	    item = listitem_alloc();
1510da861d63SBram Moolenaar 	    if (item != NULL)
1511da861d63SBram Moolenaar 	    {
1512da861d63SBram Moolenaar 		item->li_tv = tv;
1513da861d63SBram Moolenaar 		item->li_tv.v_lock = 0;
1514da861d63SBram Moolenaar 		list_append(l, item);
1515da861d63SBram Moolenaar 	    }
1516da861d63SBram Moolenaar 	    else
1517da861d63SBram Moolenaar 		clear_tv(&tv);
1518da861d63SBram Moolenaar 	}
15194d4d1cd5SBram Moolenaar 	// Legacy Vim script allowed a space before the comma.
15204d4d1cd5SBram Moolenaar 	if (!vim9script)
15214d4d1cd5SBram Moolenaar 	    *arg = skipwhite(*arg);
1522da861d63SBram Moolenaar 
1523e6e03173SBram Moolenaar 	// the comma must come after the value
15247147820cSBram Moolenaar 	had_comma = **arg == ',';
15257147820cSBram Moolenaar 	if (had_comma)
1526e6e03173SBram Moolenaar 	{
1527659bb227SBram Moolenaar 	    if (vim9script && !IS_WHITE_OR_NUL((*arg)[1]) && (*arg)[1] != ']')
1528e6e03173SBram Moolenaar 	    {
1529c3fc75dbSBram Moolenaar 		semsg(_(e_white_space_required_after_str_str), ",", *arg);
1530e6e03173SBram Moolenaar 		goto failret;
1531e6e03173SBram Moolenaar 	    }
15327147820cSBram Moolenaar 	    *arg = skipwhite(*arg + 1);
1533e6e03173SBram Moolenaar 	}
15347147820cSBram Moolenaar 
1535e6b5324eSBram Moolenaar 	// The "]" can be on the next line.  But a double quoted string may
1536e6b5324eSBram Moolenaar 	// follow, not a comment.
1537962d7213SBram Moolenaar 	*arg = skipwhite_and_linebreak(*arg, evalarg);
1538da861d63SBram Moolenaar 	if (**arg == ']')
1539da861d63SBram Moolenaar 	    break;
15407147820cSBram Moolenaar 
15417147820cSBram Moolenaar 	if (!had_comma)
1542da861d63SBram Moolenaar 	{
15438a7d6542SBram Moolenaar 	    if (do_error)
1544db199216SBram Moolenaar 	    {
1545db199216SBram Moolenaar 		if (**arg == ',')
1546ba98fb54SBram Moolenaar 		    semsg(_(e_no_white_space_allowed_before_str_str),
1547ba98fb54SBram Moolenaar 								    ",", *arg);
1548db199216SBram Moolenaar 		else
1549f9e3e09fSBram Moolenaar 		    semsg(_("E696: Missing comma in List: %s"), *arg);
1550db199216SBram Moolenaar 	    }
1551da861d63SBram Moolenaar 	    goto failret;
1552da861d63SBram Moolenaar 	}
1553da861d63SBram Moolenaar     }
1554da861d63SBram Moolenaar 
1555da861d63SBram Moolenaar     if (**arg != ']')
1556da861d63SBram Moolenaar     {
15578a7d6542SBram Moolenaar 	if (do_error)
1558ee619e5bSBram Moolenaar 	    semsg(_(e_list_end), *arg);
1559da861d63SBram Moolenaar failret:
1560da861d63SBram Moolenaar 	if (evaluate)
1561da861d63SBram Moolenaar 	    list_free(l);
1562da861d63SBram Moolenaar 	return FAIL;
1563da861d63SBram Moolenaar     }
1564da861d63SBram Moolenaar 
15659d489566SBram Moolenaar     *arg += 1;
1566da861d63SBram Moolenaar     if (evaluate)
156745cf6e91SBram Moolenaar 	rettv_list_set(rettv, l);
1568da861d63SBram Moolenaar 
1569da861d63SBram Moolenaar     return OK;
1570da861d63SBram Moolenaar }
1571da861d63SBram Moolenaar 
157273dad1e6SBram Moolenaar /*
1573caa55b65SBram Moolenaar  * Write "list" of strings to file "fd".
157473dad1e6SBram Moolenaar  */
157573dad1e6SBram Moolenaar     int
write_list(FILE * fd,list_T * list,int binary)157673dad1e6SBram Moolenaar write_list(FILE *fd, list_T *list, int binary)
157773dad1e6SBram Moolenaar {
157873dad1e6SBram Moolenaar     listitem_T	*li;
157973dad1e6SBram Moolenaar     int		c;
158073dad1e6SBram Moolenaar     int		ret = OK;
158173dad1e6SBram Moolenaar     char_u	*s;
158273dad1e6SBram Moolenaar 
15837e9f351bSBram Moolenaar     CHECK_LIST_MATERIALIZE(list);
1584aeea7215SBram Moolenaar     FOR_ALL_LIST_ITEMS(list, li)
158573dad1e6SBram Moolenaar     {
1586d155d7a8SBram Moolenaar 	for (s = tv_get_string(&li->li_tv); *s != NUL; ++s)
158773dad1e6SBram Moolenaar 	{
158873dad1e6SBram Moolenaar 	    if (*s == '\n')
158973dad1e6SBram Moolenaar 		c = putc(NUL, fd);
159073dad1e6SBram Moolenaar 	    else
159173dad1e6SBram Moolenaar 		c = putc(*s, fd);
159273dad1e6SBram Moolenaar 	    if (c == EOF)
159373dad1e6SBram Moolenaar 	    {
159473dad1e6SBram Moolenaar 		ret = FAIL;
159573dad1e6SBram Moolenaar 		break;
159673dad1e6SBram Moolenaar 	    }
159773dad1e6SBram Moolenaar 	}
159873dad1e6SBram Moolenaar 	if (!binary || li->li_next != NULL)
159973dad1e6SBram Moolenaar 	    if (putc('\n', fd) == EOF)
160073dad1e6SBram Moolenaar 	    {
160173dad1e6SBram Moolenaar 		ret = FAIL;
160273dad1e6SBram Moolenaar 		break;
160373dad1e6SBram Moolenaar 	    }
160473dad1e6SBram Moolenaar 	if (ret == FAIL)
160573dad1e6SBram Moolenaar 	{
1606f9e3e09fSBram Moolenaar 	    emsg(_(e_write));
160773dad1e6SBram Moolenaar 	    break;
160873dad1e6SBram Moolenaar 	}
160973dad1e6SBram Moolenaar     }
161073dad1e6SBram Moolenaar     return ret;
161173dad1e6SBram Moolenaar }
161273dad1e6SBram Moolenaar 
1613df48fb45SBram Moolenaar /*
1614df48fb45SBram Moolenaar  * Initialize a static list with 10 items.
1615df48fb45SBram Moolenaar  */
1616df48fb45SBram Moolenaar     void
init_static_list(staticList10_T * sl)1617df48fb45SBram Moolenaar init_static_list(staticList10_T *sl)
1618df48fb45SBram Moolenaar {
1619df48fb45SBram Moolenaar     list_T  *l = &sl->sl_list;
1620df48fb45SBram Moolenaar     int	    i;
1621df48fb45SBram Moolenaar 
1622df48fb45SBram Moolenaar     memset(sl, 0, sizeof(staticList10_T));
1623df48fb45SBram Moolenaar     l->lv_first = &sl->sl_items[0];
16240ff6aad3SBram Moolenaar     l->lv_u.mat.lv_last = &sl->sl_items[9];
1625df48fb45SBram Moolenaar     l->lv_refcount = DO_NOT_FREE_CNT;
1626df48fb45SBram Moolenaar     l->lv_lock = VAR_FIXED;
1627df48fb45SBram Moolenaar     sl->sl_list.lv_len = 10;
1628df48fb45SBram Moolenaar 
1629df48fb45SBram Moolenaar     for (i = 0; i < 10; ++i)
1630df48fb45SBram Moolenaar     {
1631df48fb45SBram Moolenaar 	listitem_T *li = &sl->sl_items[i];
1632df48fb45SBram Moolenaar 
1633df48fb45SBram Moolenaar 	if (i == 0)
1634df48fb45SBram Moolenaar 	    li->li_prev = NULL;
1635df48fb45SBram Moolenaar 	else
1636df48fb45SBram Moolenaar 	    li->li_prev = li - 1;
1637df48fb45SBram Moolenaar 	if (i == 9)
1638df48fb45SBram Moolenaar 	    li->li_next = NULL;
1639df48fb45SBram Moolenaar 	else
1640df48fb45SBram Moolenaar 	    li->li_next = li + 1;
1641df48fb45SBram Moolenaar     }
1642df48fb45SBram Moolenaar }
1643df48fb45SBram Moolenaar 
16449f9fe37fSBram Moolenaar /*
16459f9fe37fSBram Moolenaar  * "list2str()" function
16469f9fe37fSBram Moolenaar  */
16479f9fe37fSBram Moolenaar     void
f_list2str(typval_T * argvars,typval_T * rettv)16489f9fe37fSBram Moolenaar f_list2str(typval_T *argvars, typval_T *rettv)
16499f9fe37fSBram Moolenaar {
16509f9fe37fSBram Moolenaar     list_T	*l;
16519f9fe37fSBram Moolenaar     listitem_T	*li;
16529f9fe37fSBram Moolenaar     garray_T	ga;
16539f9fe37fSBram Moolenaar     int		utf8 = FALSE;
16549f9fe37fSBram Moolenaar 
16559f9fe37fSBram Moolenaar     rettv->v_type = VAR_STRING;
16569f9fe37fSBram Moolenaar     rettv->vval.v_string = NULL;
165783494b4aSYegappan Lakshmanan 
165883494b4aSYegappan Lakshmanan     if (in_vim9script()
165983494b4aSYegappan Lakshmanan 	    && (check_for_list_arg(argvars, 0) == FAIL
166083494b4aSYegappan Lakshmanan 		|| check_for_opt_bool_arg(argvars, 1) == FAIL))
166183494b4aSYegappan Lakshmanan 	return;
166283494b4aSYegappan Lakshmanan 
16639f9fe37fSBram Moolenaar     if (argvars[0].v_type != VAR_LIST)
16649f9fe37fSBram Moolenaar     {
16659f9fe37fSBram Moolenaar 	emsg(_(e_invarg));
16669f9fe37fSBram Moolenaar 	return;
16679f9fe37fSBram Moolenaar     }
16689f9fe37fSBram Moolenaar 
16699f9fe37fSBram Moolenaar     l = argvars[0].vval.v_list;
16709f9fe37fSBram Moolenaar     if (l == NULL)
16719f9fe37fSBram Moolenaar 	return;  // empty list results in empty string
16729f9fe37fSBram Moolenaar 
16739f9fe37fSBram Moolenaar     if (argvars[1].v_type != VAR_UNKNOWN)
1674a48f7867SBram Moolenaar 	utf8 = (int)tv_get_bool_chk(&argvars[1], NULL);
16759f9fe37fSBram Moolenaar 
16767e9f351bSBram Moolenaar     CHECK_LIST_MATERIALIZE(l);
16779f9fe37fSBram Moolenaar     ga_init2(&ga, 1, 80);
16789f9fe37fSBram Moolenaar     if (has_mbyte || utf8)
16799f9fe37fSBram Moolenaar     {
16809f9fe37fSBram Moolenaar 	char_u	buf[MB_MAXBYTES + 1];
16819f9fe37fSBram Moolenaar 	int	(*char2bytes)(int, char_u *);
16829f9fe37fSBram Moolenaar 
16839f9fe37fSBram Moolenaar 	if (utf8 || enc_utf8)
16849f9fe37fSBram Moolenaar 	    char2bytes = utf_char2bytes;
16859f9fe37fSBram Moolenaar 	else
16869f9fe37fSBram Moolenaar 	    char2bytes = mb_char2bytes;
16879f9fe37fSBram Moolenaar 
1688aeea7215SBram Moolenaar 	FOR_ALL_LIST_ITEMS(l, li)
16899f9fe37fSBram Moolenaar 	{
16909f9fe37fSBram Moolenaar 	    buf[(*char2bytes)(tv_get_number(&li->li_tv), buf)] = NUL;
16919f9fe37fSBram Moolenaar 	    ga_concat(&ga, buf);
16929f9fe37fSBram Moolenaar 	}
16939f9fe37fSBram Moolenaar 	ga_append(&ga, NUL);
16949f9fe37fSBram Moolenaar     }
16959f9fe37fSBram Moolenaar     else if (ga_grow(&ga, list_len(l) + 1) == OK)
16969f9fe37fSBram Moolenaar     {
1697aeea7215SBram Moolenaar 	FOR_ALL_LIST_ITEMS(l, li)
16989f9fe37fSBram Moolenaar 	    ga_append(&ga, tv_get_number(&li->li_tv));
16999f9fe37fSBram Moolenaar 	ga_append(&ga, NUL);
17009f9fe37fSBram Moolenaar     }
17019f9fe37fSBram Moolenaar 
17029f9fe37fSBram Moolenaar     rettv->v_type = VAR_STRING;
17039f9fe37fSBram Moolenaar     rettv->vval.v_string = ga.ga_data;
17049f9fe37fSBram Moolenaar }
17059f9fe37fSBram Moolenaar 
1706bdff012fSBram Moolenaar     static void
list_remove(typval_T * argvars,typval_T * rettv,char_u * arg_errmsg)17079f9fe37fSBram Moolenaar list_remove(typval_T *argvars, typval_T *rettv, char_u *arg_errmsg)
17089f9fe37fSBram Moolenaar {
17099f9fe37fSBram Moolenaar     list_T	*l;
17109f9fe37fSBram Moolenaar     listitem_T	*item, *item2;
17119f9fe37fSBram Moolenaar     listitem_T	*li;
17129f9fe37fSBram Moolenaar     int		error = FALSE;
1713239f8d93SBram Moolenaar     long	idx;
17149f9fe37fSBram Moolenaar 
17159f9fe37fSBram Moolenaar     if ((l = argvars[0].vval.v_list) == NULL
1716a187c43cSBram Moolenaar 			     || value_check_lock(l->lv_lock, arg_errmsg, TRUE))
17179f9fe37fSBram Moolenaar 	return;
17189f9fe37fSBram Moolenaar 
17199f9fe37fSBram Moolenaar     idx = (long)tv_get_number_chk(&argvars[1], &error);
17209f9fe37fSBram Moolenaar     if (error)
17219f9fe37fSBram Moolenaar 	;		// type error: do nothing, errmsg already given
17229f9fe37fSBram Moolenaar     else if ((item = list_find(l, idx)) == NULL)
17239f9fe37fSBram Moolenaar 	semsg(_(e_listidx), idx);
17249f9fe37fSBram Moolenaar     else
17259f9fe37fSBram Moolenaar     {
17269f9fe37fSBram Moolenaar 	if (argvars[2].v_type == VAR_UNKNOWN)
17279f9fe37fSBram Moolenaar 	{
17284ba37b58SBram Moolenaar 	    // Remove one item, return its value.
17299f9fe37fSBram Moolenaar 	    vimlist_remove(l, item, item);
17309f9fe37fSBram Moolenaar 	    *rettv = item->li_tv;
17318a7d6542SBram Moolenaar 	    list_free_item(l, item);
17329f9fe37fSBram Moolenaar 	}
17339f9fe37fSBram Moolenaar 	else
17349f9fe37fSBram Moolenaar 	{
17359f9fe37fSBram Moolenaar 	    // Remove range of items, return list with values.
1736239f8d93SBram Moolenaar 	    long end = (long)tv_get_number_chk(&argvars[2], &error);
17379f9fe37fSBram Moolenaar 
17389f9fe37fSBram Moolenaar 	    if (error)
17399f9fe37fSBram Moolenaar 		;		// type error: do nothing
17409f9fe37fSBram Moolenaar 	    else if ((item2 = list_find(l, end)) == NULL)
17419f9fe37fSBram Moolenaar 		semsg(_(e_listidx), end);
17429f9fe37fSBram Moolenaar 	    else
17439f9fe37fSBram Moolenaar 	    {
17449f9fe37fSBram Moolenaar 		int	    cnt = 0;
17459f9fe37fSBram Moolenaar 
17469f9fe37fSBram Moolenaar 		for (li = item; li != NULL; li = li->li_next)
17479f9fe37fSBram Moolenaar 		{
17489f9fe37fSBram Moolenaar 		    ++cnt;
17499f9fe37fSBram Moolenaar 		    if (li == item2)
17509f9fe37fSBram Moolenaar 			break;
17519f9fe37fSBram Moolenaar 		}
17524ba37b58SBram Moolenaar 		if (li == NULL)  // didn't find "item2" after "item"
1753108010aaSBram Moolenaar 		    emsg(_(e_invalid_range));
17549f9fe37fSBram Moolenaar 		else
17559f9fe37fSBram Moolenaar 		{
17569f9fe37fSBram Moolenaar 		    vimlist_remove(l, item, item2);
17579f9fe37fSBram Moolenaar 		    if (rettv_list_alloc(rettv) == OK)
17589f9fe37fSBram Moolenaar 		    {
1759885971e2SBram Moolenaar 			list_T *rl = rettv->vval.v_list;
1760885971e2SBram Moolenaar 
1761885971e2SBram Moolenaar 			if (l->lv_with_items > 0)
1762885971e2SBram Moolenaar 			{
1763885971e2SBram Moolenaar 			    // need to copy the list items and move the value
1764885971e2SBram Moolenaar 			    while (item != NULL)
1765885971e2SBram Moolenaar 			    {
1766885971e2SBram Moolenaar 				li = listitem_alloc();
1767885971e2SBram Moolenaar 				if (li == NULL)
1768885971e2SBram Moolenaar 				    return;
1769885971e2SBram Moolenaar 				li->li_tv = item->li_tv;
1770885971e2SBram Moolenaar 				init_tv(&item->li_tv);
1771885971e2SBram Moolenaar 				list_append(rl, li);
1772885971e2SBram Moolenaar 				if (item == item2)
1773885971e2SBram Moolenaar 				    break;
1774885971e2SBram Moolenaar 				item = item->li_next;
1775885971e2SBram Moolenaar 			    }
1776885971e2SBram Moolenaar 			}
1777885971e2SBram Moolenaar 			else
1778885971e2SBram Moolenaar 			{
1779885971e2SBram Moolenaar 			    rl->lv_first = item;
1780885971e2SBram Moolenaar 			    rl->lv_u.mat.lv_last = item2;
17819f9fe37fSBram Moolenaar 			    item->li_prev = NULL;
17829f9fe37fSBram Moolenaar 			    item2->li_next = NULL;
1783885971e2SBram Moolenaar 			    rl->lv_len = cnt;
1784885971e2SBram Moolenaar 			}
17859f9fe37fSBram Moolenaar 		    }
17869f9fe37fSBram Moolenaar 		}
17879f9fe37fSBram Moolenaar 	    }
17889f9fe37fSBram Moolenaar 	}
17899f9fe37fSBram Moolenaar     }
17909f9fe37fSBram Moolenaar }
17919f9fe37fSBram Moolenaar 
17929f9fe37fSBram Moolenaar static int item_compare(const void *s1, const void *s2);
17939f9fe37fSBram Moolenaar static int item_compare2(const void *s1, const void *s2);
17949f9fe37fSBram Moolenaar 
17954ba37b58SBram Moolenaar // struct used in the array that's given to qsort()
17969f9fe37fSBram Moolenaar typedef struct
17979f9fe37fSBram Moolenaar {
17989f9fe37fSBram Moolenaar     listitem_T	*item;
17999f9fe37fSBram Moolenaar     int		idx;
18009f9fe37fSBram Moolenaar } sortItem_T;
18019f9fe37fSBram Moolenaar 
18024ba37b58SBram Moolenaar // struct storing information about current sort
18039f9fe37fSBram Moolenaar typedef struct
18049f9fe37fSBram Moolenaar {
18059f9fe37fSBram Moolenaar     int		item_compare_ic;
180655e29611SBram Moolenaar     int		item_compare_lc;
18079f9fe37fSBram Moolenaar     int		item_compare_numeric;
18089f9fe37fSBram Moolenaar     int		item_compare_numbers;
18099f9fe37fSBram Moolenaar #ifdef FEAT_FLOAT
18109f9fe37fSBram Moolenaar     int		item_compare_float;
18119f9fe37fSBram Moolenaar #endif
18129f9fe37fSBram Moolenaar     char_u	*item_compare_func;
18139f9fe37fSBram Moolenaar     partial_T	*item_compare_partial;
18149f9fe37fSBram Moolenaar     dict_T	*item_compare_selfdict;
18159f9fe37fSBram Moolenaar     int		item_compare_func_err;
18169f9fe37fSBram Moolenaar     int		item_compare_keep_zero;
18179f9fe37fSBram Moolenaar } sortinfo_T;
18189f9fe37fSBram Moolenaar static sortinfo_T	*sortinfo = NULL;
18199f9fe37fSBram Moolenaar #define ITEM_COMPARE_FAIL 999
18209f9fe37fSBram Moolenaar 
18219f9fe37fSBram Moolenaar /*
18229f9fe37fSBram Moolenaar  * Compare functions for f_sort() and f_uniq() below.
18239f9fe37fSBram Moolenaar  */
18249f9fe37fSBram Moolenaar     static int
item_compare(const void * s1,const void * s2)18259f9fe37fSBram Moolenaar item_compare(const void *s1, const void *s2)
18269f9fe37fSBram Moolenaar {
18279f9fe37fSBram Moolenaar     sortItem_T  *si1, *si2;
18289f9fe37fSBram Moolenaar     typval_T	*tv1, *tv2;
18299f9fe37fSBram Moolenaar     char_u	*p1, *p2;
18309f9fe37fSBram Moolenaar     char_u	*tofree1 = NULL, *tofree2 = NULL;
18319f9fe37fSBram Moolenaar     int		res;
18329f9fe37fSBram Moolenaar     char_u	numbuf1[NUMBUFLEN];
18339f9fe37fSBram Moolenaar     char_u	numbuf2[NUMBUFLEN];
18349f9fe37fSBram Moolenaar 
18359f9fe37fSBram Moolenaar     si1 = (sortItem_T *)s1;
18369f9fe37fSBram Moolenaar     si2 = (sortItem_T *)s2;
18379f9fe37fSBram Moolenaar     tv1 = &si1->item->li_tv;
18389f9fe37fSBram Moolenaar     tv2 = &si2->item->li_tv;
18399f9fe37fSBram Moolenaar 
18409f9fe37fSBram Moolenaar     if (sortinfo->item_compare_numbers)
18419f9fe37fSBram Moolenaar     {
18429f9fe37fSBram Moolenaar 	varnumber_T	v1 = tv_get_number(tv1);
18439f9fe37fSBram Moolenaar 	varnumber_T	v2 = tv_get_number(tv2);
18449f9fe37fSBram Moolenaar 
18459f9fe37fSBram Moolenaar 	return v1 == v2 ? 0 : v1 > v2 ? 1 : -1;
18469f9fe37fSBram Moolenaar     }
18479f9fe37fSBram Moolenaar 
18489f9fe37fSBram Moolenaar #ifdef FEAT_FLOAT
18499f9fe37fSBram Moolenaar     if (sortinfo->item_compare_float)
18509f9fe37fSBram Moolenaar     {
18519f9fe37fSBram Moolenaar 	float_T	v1 = tv_get_float(tv1);
18529f9fe37fSBram Moolenaar 	float_T	v2 = tv_get_float(tv2);
18539f9fe37fSBram Moolenaar 
18549f9fe37fSBram Moolenaar 	return v1 == v2 ? 0 : v1 > v2 ? 1 : -1;
18559f9fe37fSBram Moolenaar     }
18569f9fe37fSBram Moolenaar #endif
18579f9fe37fSBram Moolenaar 
18584ba37b58SBram Moolenaar     // tv2string() puts quotes around a string and allocates memory.  Don't do
18594ba37b58SBram Moolenaar     // that for string variables. Use a single quote when comparing with a
18604ba37b58SBram Moolenaar     // non-string to do what the docs promise.
18619f9fe37fSBram Moolenaar     if (tv1->v_type == VAR_STRING)
18629f9fe37fSBram Moolenaar     {
18639f9fe37fSBram Moolenaar 	if (tv2->v_type != VAR_STRING || sortinfo->item_compare_numeric)
18649f9fe37fSBram Moolenaar 	    p1 = (char_u *)"'";
18659f9fe37fSBram Moolenaar 	else
18669f9fe37fSBram Moolenaar 	    p1 = tv1->vval.v_string;
18679f9fe37fSBram Moolenaar     }
18689f9fe37fSBram Moolenaar     else
18699f9fe37fSBram Moolenaar 	p1 = tv2string(tv1, &tofree1, numbuf1, 0);
18709f9fe37fSBram Moolenaar     if (tv2->v_type == VAR_STRING)
18719f9fe37fSBram Moolenaar     {
18729f9fe37fSBram Moolenaar 	if (tv1->v_type != VAR_STRING || sortinfo->item_compare_numeric)
18739f9fe37fSBram Moolenaar 	    p2 = (char_u *)"'";
18749f9fe37fSBram Moolenaar 	else
18759f9fe37fSBram Moolenaar 	    p2 = tv2->vval.v_string;
18769f9fe37fSBram Moolenaar     }
18779f9fe37fSBram Moolenaar     else
18789f9fe37fSBram Moolenaar 	p2 = tv2string(tv2, &tofree2, numbuf2, 0);
18799f9fe37fSBram Moolenaar     if (p1 == NULL)
18809f9fe37fSBram Moolenaar 	p1 = (char_u *)"";
18819f9fe37fSBram Moolenaar     if (p2 == NULL)
18829f9fe37fSBram Moolenaar 	p2 = (char_u *)"";
18839f9fe37fSBram Moolenaar     if (!sortinfo->item_compare_numeric)
18849f9fe37fSBram Moolenaar     {
188555e29611SBram Moolenaar 	if (sortinfo->item_compare_lc)
188655e29611SBram Moolenaar 	    res = strcoll((char *)p1, (char *)p2);
18879f9fe37fSBram Moolenaar 	else
188855e29611SBram Moolenaar 	    res = sortinfo->item_compare_ic ? STRICMP(p1, p2): STRCMP(p1, p2);
18899f9fe37fSBram Moolenaar     }
18909f9fe37fSBram Moolenaar     else
18919f9fe37fSBram Moolenaar     {
18929f9fe37fSBram Moolenaar 	double n1, n2;
18939f9fe37fSBram Moolenaar 	n1 = strtod((char *)p1, (char **)&p1);
18949f9fe37fSBram Moolenaar 	n2 = strtod((char *)p2, (char **)&p2);
18959f9fe37fSBram Moolenaar 	res = n1 == n2 ? 0 : n1 > n2 ? 1 : -1;
18969f9fe37fSBram Moolenaar     }
18979f9fe37fSBram Moolenaar 
18984ba37b58SBram Moolenaar     // When the result would be zero, compare the item indexes.  Makes the
18994ba37b58SBram Moolenaar     // sort stable.
19009f9fe37fSBram Moolenaar     if (res == 0 && !sortinfo->item_compare_keep_zero)
19019f9fe37fSBram Moolenaar 	res = si1->idx > si2->idx ? 1 : -1;
19029f9fe37fSBram Moolenaar 
19039f9fe37fSBram Moolenaar     vim_free(tofree1);
19049f9fe37fSBram Moolenaar     vim_free(tofree2);
19059f9fe37fSBram Moolenaar     return res;
19069f9fe37fSBram Moolenaar }
19079f9fe37fSBram Moolenaar 
19089f9fe37fSBram Moolenaar     static int
item_compare2(const void * s1,const void * s2)19099f9fe37fSBram Moolenaar item_compare2(const void *s1, const void *s2)
19109f9fe37fSBram Moolenaar {
19119f9fe37fSBram Moolenaar     sortItem_T  *si1, *si2;
19129f9fe37fSBram Moolenaar     int		res;
19139f9fe37fSBram Moolenaar     typval_T	rettv;
19149f9fe37fSBram Moolenaar     typval_T	argv[3];
19159f9fe37fSBram Moolenaar     char_u	*func_name;
19169f9fe37fSBram Moolenaar     partial_T	*partial = sortinfo->item_compare_partial;
1917c6538bccSBram Moolenaar     funcexe_T	funcexe;
19189f9fe37fSBram Moolenaar 
19194ba37b58SBram Moolenaar     // shortcut after failure in previous call; compare all items equal
19209f9fe37fSBram Moolenaar     if (sortinfo->item_compare_func_err)
19219f9fe37fSBram Moolenaar 	return 0;
19229f9fe37fSBram Moolenaar 
19239f9fe37fSBram Moolenaar     si1 = (sortItem_T *)s1;
19249f9fe37fSBram Moolenaar     si2 = (sortItem_T *)s2;
19259f9fe37fSBram Moolenaar 
19269f9fe37fSBram Moolenaar     if (partial == NULL)
19279f9fe37fSBram Moolenaar 	func_name = sortinfo->item_compare_func;
19289f9fe37fSBram Moolenaar     else
19299f9fe37fSBram Moolenaar 	func_name = partial_name(partial);
19309f9fe37fSBram Moolenaar 
19314ba37b58SBram Moolenaar     // Copy the values.  This is needed to be able to set v_lock to VAR_FIXED
19324ba37b58SBram Moolenaar     // in the copy without changing the original list items.
19339f9fe37fSBram Moolenaar     copy_tv(&si1->item->li_tv, &argv[0]);
19349f9fe37fSBram Moolenaar     copy_tv(&si2->item->li_tv, &argv[1]);
19359f9fe37fSBram Moolenaar 
19364ba37b58SBram Moolenaar     rettv.v_type = VAR_UNKNOWN;		// clear_tv() uses this
1937a80faa89SBram Moolenaar     CLEAR_FIELD(funcexe);
1938c6538bccSBram Moolenaar     funcexe.evaluate = TRUE;
1939c6538bccSBram Moolenaar     funcexe.partial = partial;
1940c6538bccSBram Moolenaar     funcexe.selfdict = sortinfo->item_compare_selfdict;
1941c6538bccSBram Moolenaar     res = call_func(func_name, -1, &rettv, 2, argv, &funcexe);
19429f9fe37fSBram Moolenaar     clear_tv(&argv[0]);
19439f9fe37fSBram Moolenaar     clear_tv(&argv[1]);
19449f9fe37fSBram Moolenaar 
19459f9fe37fSBram Moolenaar     if (res == FAIL)
19469f9fe37fSBram Moolenaar 	res = ITEM_COMPARE_FAIL;
19479f9fe37fSBram Moolenaar     else
1948*c04f6234SYasuhiro Matsumoto     {
19499f9fe37fSBram Moolenaar 	res = (int)tv_get_number_chk(&rettv, &sortinfo->item_compare_func_err);
1950*c04f6234SYasuhiro Matsumoto 	if (res > 0)
1951*c04f6234SYasuhiro Matsumoto 	    res = 1;
1952*c04f6234SYasuhiro Matsumoto 	else if (res < 0)
1953*c04f6234SYasuhiro Matsumoto 	    res = -1;
1954*c04f6234SYasuhiro Matsumoto     }
19559f9fe37fSBram Moolenaar     if (sortinfo->item_compare_func_err)
19564ba37b58SBram Moolenaar 	res = ITEM_COMPARE_FAIL;  // return value has wrong type
19579f9fe37fSBram Moolenaar     clear_tv(&rettv);
19589f9fe37fSBram Moolenaar 
19594ba37b58SBram Moolenaar     // When the result would be zero, compare the pointers themselves.  Makes
19604ba37b58SBram Moolenaar     // the sort stable.
19619f9fe37fSBram Moolenaar     if (res == 0 && !sortinfo->item_compare_keep_zero)
19629f9fe37fSBram Moolenaar 	res = si1->idx > si2->idx ? 1 : -1;
19639f9fe37fSBram Moolenaar 
19649f9fe37fSBram Moolenaar     return res;
19659f9fe37fSBram Moolenaar }
19669f9fe37fSBram Moolenaar 
19679f9fe37fSBram Moolenaar /*
19689f9fe37fSBram Moolenaar  * "sort()" or "uniq()" function
19699f9fe37fSBram Moolenaar  */
19709f9fe37fSBram Moolenaar     static void
do_sort_uniq(typval_T * argvars,typval_T * rettv,int sort)19719f9fe37fSBram Moolenaar do_sort_uniq(typval_T *argvars, typval_T *rettv, int sort)
19729f9fe37fSBram Moolenaar {
19739f9fe37fSBram Moolenaar     list_T	*l;
19749f9fe37fSBram Moolenaar     listitem_T	*li;
19759f9fe37fSBram Moolenaar     sortItem_T	*ptrs;
19769f9fe37fSBram Moolenaar     sortinfo_T	*old_sortinfo;
19779f9fe37fSBram Moolenaar     sortinfo_T	info;
19789f9fe37fSBram Moolenaar     long	len;
19799f9fe37fSBram Moolenaar     long	i;
19809f9fe37fSBram Moolenaar 
19810ad871dcSYegappan Lakshmanan     if (in_vim9script()
19820ad871dcSYegappan Lakshmanan 	    && (check_for_list_arg(argvars, 0) == FAIL
19830ad871dcSYegappan Lakshmanan 		|| (argvars[1].v_type != VAR_UNKNOWN
19840ad871dcSYegappan Lakshmanan 		    && check_for_opt_dict_arg(argvars, 2) == FAIL)))
19850ad871dcSYegappan Lakshmanan 	return;
19860ad871dcSYegappan Lakshmanan 
19874ba37b58SBram Moolenaar     // Pointer to current info struct used in compare function. Save and
19884ba37b58SBram Moolenaar     // restore the current one for nested calls.
19899f9fe37fSBram Moolenaar     old_sortinfo = sortinfo;
19909f9fe37fSBram Moolenaar     sortinfo = &info;
19919f9fe37fSBram Moolenaar 
19929f9fe37fSBram Moolenaar     if (argvars[0].v_type != VAR_LIST)
19939f9fe37fSBram Moolenaar 	semsg(_(e_listarg), sort ? "sort()" : "uniq()");
19949f9fe37fSBram Moolenaar     else
19959f9fe37fSBram Moolenaar     {
19969f9fe37fSBram Moolenaar 	l = argvars[0].vval.v_list;
1997ef982575SBram Moolenaar 	if (l != NULL && value_check_lock(l->lv_lock,
19989f9fe37fSBram Moolenaar 	     (char_u *)(sort ? N_("sort() argument") : N_("uniq() argument")),
19999f9fe37fSBram Moolenaar 									TRUE))
20009f9fe37fSBram Moolenaar 	    goto theend;
20019f9fe37fSBram Moolenaar 	rettv_list_set(rettv, l);
2002ef982575SBram Moolenaar 	if (l == NULL)
2003ef982575SBram Moolenaar 	    goto theend;
20047e9f351bSBram Moolenaar 	CHECK_LIST_MATERIALIZE(l);
20059f9fe37fSBram Moolenaar 
20069f9fe37fSBram Moolenaar 	len = list_len(l);
20079f9fe37fSBram Moolenaar 	if (len <= 1)
20084ba37b58SBram Moolenaar 	    goto theend;	// short list sorts pretty quickly
20099f9fe37fSBram Moolenaar 
20109f9fe37fSBram Moolenaar 	info.item_compare_ic = FALSE;
201155e29611SBram Moolenaar 	info.item_compare_lc = FALSE;
20129f9fe37fSBram Moolenaar 	info.item_compare_numeric = FALSE;
20139f9fe37fSBram Moolenaar 	info.item_compare_numbers = FALSE;
20149f9fe37fSBram Moolenaar #ifdef FEAT_FLOAT
20159f9fe37fSBram Moolenaar 	info.item_compare_float = FALSE;
20169f9fe37fSBram Moolenaar #endif
20179f9fe37fSBram Moolenaar 	info.item_compare_func = NULL;
20189f9fe37fSBram Moolenaar 	info.item_compare_partial = NULL;
20199f9fe37fSBram Moolenaar 	info.item_compare_selfdict = NULL;
20209f9fe37fSBram Moolenaar 	if (argvars[1].v_type != VAR_UNKNOWN)
20219f9fe37fSBram Moolenaar 	{
20224ba37b58SBram Moolenaar 	    // optional second argument: {func}
20239f9fe37fSBram Moolenaar 	    if (argvars[1].v_type == VAR_FUNC)
20249f9fe37fSBram Moolenaar 		info.item_compare_func = argvars[1].vval.v_string;
20259f9fe37fSBram Moolenaar 	    else if (argvars[1].v_type == VAR_PARTIAL)
20269f9fe37fSBram Moolenaar 		info.item_compare_partial = argvars[1].vval.v_partial;
20279f9fe37fSBram Moolenaar 	    else
20289f9fe37fSBram Moolenaar 	    {
20299f9fe37fSBram Moolenaar 		int	    error = FALSE;
203008e51f44SBram Moolenaar 		int	    nr = 0;
20319f9fe37fSBram Moolenaar 
203208e51f44SBram Moolenaar 		if (argvars[1].v_type == VAR_NUMBER)
203308e51f44SBram Moolenaar 		{
203408e51f44SBram Moolenaar 		    nr = tv_get_number_chk(&argvars[1], &error);
20359f9fe37fSBram Moolenaar 		    if (error)
20364ba37b58SBram Moolenaar 			goto theend;	// type error; errmsg already given
203708e51f44SBram Moolenaar 		    if (nr == 1)
20389f9fe37fSBram Moolenaar 			info.item_compare_ic = TRUE;
203908e51f44SBram Moolenaar 		}
204008e51f44SBram Moolenaar 		if (nr != 1)
204108e51f44SBram Moolenaar 		{
204208e51f44SBram Moolenaar 		    if (argvars[1].v_type != VAR_NUMBER)
20439f9fe37fSBram Moolenaar 			info.item_compare_func = tv_get_string(&argvars[1]);
204408e51f44SBram Moolenaar 		    else if (nr != 0)
20459f9fe37fSBram Moolenaar 		    {
20469f9fe37fSBram Moolenaar 			emsg(_(e_invarg));
20479f9fe37fSBram Moolenaar 			goto theend;
20489f9fe37fSBram Moolenaar 		    }
204908e51f44SBram Moolenaar 		}
20509f9fe37fSBram Moolenaar 		if (info.item_compare_func != NULL)
20519f9fe37fSBram Moolenaar 		{
20529f9fe37fSBram Moolenaar 		    if (*info.item_compare_func == NUL)
20539f9fe37fSBram Moolenaar 		    {
20544ba37b58SBram Moolenaar 			// empty string means default sort
20559f9fe37fSBram Moolenaar 			info.item_compare_func = NULL;
20569f9fe37fSBram Moolenaar 		    }
20579f9fe37fSBram Moolenaar 		    else if (STRCMP(info.item_compare_func, "n") == 0)
20589f9fe37fSBram Moolenaar 		    {
20599f9fe37fSBram Moolenaar 			info.item_compare_func = NULL;
20609f9fe37fSBram Moolenaar 			info.item_compare_numeric = TRUE;
20619f9fe37fSBram Moolenaar 		    }
20629f9fe37fSBram Moolenaar 		    else if (STRCMP(info.item_compare_func, "N") == 0)
20639f9fe37fSBram Moolenaar 		    {
20649f9fe37fSBram Moolenaar 			info.item_compare_func = NULL;
20659f9fe37fSBram Moolenaar 			info.item_compare_numbers = TRUE;
20669f9fe37fSBram Moolenaar 		    }
20679f9fe37fSBram Moolenaar #ifdef FEAT_FLOAT
20689f9fe37fSBram Moolenaar 		    else if (STRCMP(info.item_compare_func, "f") == 0)
20699f9fe37fSBram Moolenaar 		    {
20709f9fe37fSBram Moolenaar 			info.item_compare_func = NULL;
20719f9fe37fSBram Moolenaar 			info.item_compare_float = TRUE;
20729f9fe37fSBram Moolenaar 		    }
20739f9fe37fSBram Moolenaar #endif
20749f9fe37fSBram Moolenaar 		    else if (STRCMP(info.item_compare_func, "i") == 0)
20759f9fe37fSBram Moolenaar 		    {
20769f9fe37fSBram Moolenaar 			info.item_compare_func = NULL;
20779f9fe37fSBram Moolenaar 			info.item_compare_ic = TRUE;
20789f9fe37fSBram Moolenaar 		    }
207955e29611SBram Moolenaar 		    else if (STRCMP(info.item_compare_func, "l") == 0)
208055e29611SBram Moolenaar 		    {
208155e29611SBram Moolenaar 			info.item_compare_func = NULL;
208255e29611SBram Moolenaar 			info.item_compare_lc = TRUE;
208355e29611SBram Moolenaar 		    }
20849f9fe37fSBram Moolenaar 		}
20859f9fe37fSBram Moolenaar 	    }
20869f9fe37fSBram Moolenaar 
20879f9fe37fSBram Moolenaar 	    if (argvars[2].v_type != VAR_UNKNOWN)
20889f9fe37fSBram Moolenaar 	    {
20894ba37b58SBram Moolenaar 		// optional third argument: {dict}
20909f9fe37fSBram Moolenaar 		if (argvars[2].v_type != VAR_DICT)
20919f9fe37fSBram Moolenaar 		{
20929f9fe37fSBram Moolenaar 		    emsg(_(e_dictreq));
20939f9fe37fSBram Moolenaar 		    goto theend;
20949f9fe37fSBram Moolenaar 		}
20959f9fe37fSBram Moolenaar 		info.item_compare_selfdict = argvars[2].vval.v_dict;
20969f9fe37fSBram Moolenaar 	    }
20979f9fe37fSBram Moolenaar 	}
20989f9fe37fSBram Moolenaar 
20994ba37b58SBram Moolenaar 	// Make an array with each entry pointing to an item in the List.
21009f9fe37fSBram Moolenaar 	ptrs = ALLOC_MULT(sortItem_T, len);
21019f9fe37fSBram Moolenaar 	if (ptrs == NULL)
21029f9fe37fSBram Moolenaar 	    goto theend;
21039f9fe37fSBram Moolenaar 
21049f9fe37fSBram Moolenaar 	i = 0;
21059f9fe37fSBram Moolenaar 	if (sort)
21069f9fe37fSBram Moolenaar 	{
21074ba37b58SBram Moolenaar 	    // sort(): ptrs will be the list to sort
2108aeea7215SBram Moolenaar 	    FOR_ALL_LIST_ITEMS(l, li)
21099f9fe37fSBram Moolenaar 	    {
21109f9fe37fSBram Moolenaar 		ptrs[i].item = li;
21119f9fe37fSBram Moolenaar 		ptrs[i].idx = i;
21129f9fe37fSBram Moolenaar 		++i;
21139f9fe37fSBram Moolenaar 	    }
21149f9fe37fSBram Moolenaar 
21159f9fe37fSBram Moolenaar 	    info.item_compare_func_err = FALSE;
21169f9fe37fSBram Moolenaar 	    info.item_compare_keep_zero = FALSE;
21174ba37b58SBram Moolenaar 	    // test the compare function
21189f9fe37fSBram Moolenaar 	    if ((info.item_compare_func != NULL
21199f9fe37fSBram Moolenaar 					 || info.item_compare_partial != NULL)
21209f9fe37fSBram Moolenaar 		    && item_compare2((void *)&ptrs[0], (void *)&ptrs[1])
21219f9fe37fSBram Moolenaar 							 == ITEM_COMPARE_FAIL)
21229f9fe37fSBram Moolenaar 		emsg(_("E702: Sort compare function failed"));
21239f9fe37fSBram Moolenaar 	    else
21249f9fe37fSBram Moolenaar 	    {
21254ba37b58SBram Moolenaar 		// Sort the array with item pointers.
21269f9fe37fSBram Moolenaar 		qsort((void *)ptrs, (size_t)len, sizeof(sortItem_T),
21279f9fe37fSBram Moolenaar 		    info.item_compare_func == NULL
21289f9fe37fSBram Moolenaar 					  && info.item_compare_partial == NULL
21299f9fe37fSBram Moolenaar 					       ? item_compare : item_compare2);
21309f9fe37fSBram Moolenaar 
21319f9fe37fSBram Moolenaar 		if (!info.item_compare_func_err)
21329f9fe37fSBram Moolenaar 		{
21334ba37b58SBram Moolenaar 		    // Clear the List and append the items in sorted order.
21340ff6aad3SBram Moolenaar 		    l->lv_first = l->lv_u.mat.lv_last
21350ff6aad3SBram Moolenaar 					      = l->lv_u.mat.lv_idx_item = NULL;
21369f9fe37fSBram Moolenaar 		    l->lv_len = 0;
21379f9fe37fSBram Moolenaar 		    for (i = 0; i < len; ++i)
21389f9fe37fSBram Moolenaar 			list_append(l, ptrs[i].item);
21399f9fe37fSBram Moolenaar 		}
21409f9fe37fSBram Moolenaar 	    }
21419f9fe37fSBram Moolenaar 	}
21429f9fe37fSBram Moolenaar 	else
21439f9fe37fSBram Moolenaar 	{
21449f9fe37fSBram Moolenaar 	    int	(*item_compare_func_ptr)(const void *, const void *);
21459f9fe37fSBram Moolenaar 
21464ba37b58SBram Moolenaar 	    // f_uniq(): ptrs will be a stack of items to remove
21479f9fe37fSBram Moolenaar 	    info.item_compare_func_err = FALSE;
21489f9fe37fSBram Moolenaar 	    info.item_compare_keep_zero = TRUE;
21499f9fe37fSBram Moolenaar 	    item_compare_func_ptr = info.item_compare_func != NULL
21509f9fe37fSBram Moolenaar 					  || info.item_compare_partial != NULL
21519f9fe37fSBram Moolenaar 					       ? item_compare2 : item_compare;
21529f9fe37fSBram Moolenaar 
21539f9fe37fSBram Moolenaar 	    for (li = l->lv_first; li != NULL && li->li_next != NULL;
21549f9fe37fSBram Moolenaar 							     li = li->li_next)
21559f9fe37fSBram Moolenaar 	    {
21569f9fe37fSBram Moolenaar 		if (item_compare_func_ptr((void *)&li, (void *)&li->li_next)
21579f9fe37fSBram Moolenaar 									 == 0)
21589f9fe37fSBram Moolenaar 		    ptrs[i++].item = li;
21599f9fe37fSBram Moolenaar 		if (info.item_compare_func_err)
21609f9fe37fSBram Moolenaar 		{
21619f9fe37fSBram Moolenaar 		    emsg(_("E882: Uniq compare function failed"));
21629f9fe37fSBram Moolenaar 		    break;
21639f9fe37fSBram Moolenaar 		}
21649f9fe37fSBram Moolenaar 	    }
21659f9fe37fSBram Moolenaar 
21669f9fe37fSBram Moolenaar 	    if (!info.item_compare_func_err)
21679f9fe37fSBram Moolenaar 	    {
21689f9fe37fSBram Moolenaar 		while (--i >= 0)
21699f9fe37fSBram Moolenaar 		{
21709f9fe37fSBram Moolenaar 		    li = ptrs[i].item->li_next;
21719f9fe37fSBram Moolenaar 		    ptrs[i].item->li_next = li->li_next;
21729f9fe37fSBram Moolenaar 		    if (li->li_next != NULL)
21739f9fe37fSBram Moolenaar 			li->li_next->li_prev = ptrs[i].item;
21749f9fe37fSBram Moolenaar 		    else
21750ff6aad3SBram Moolenaar 			l->lv_u.mat.lv_last = ptrs[i].item;
21769f9fe37fSBram Moolenaar 		    list_fix_watch(l, li);
21778a7d6542SBram Moolenaar 		    listitem_free(l, li);
21789f9fe37fSBram Moolenaar 		    l->lv_len--;
21799f9fe37fSBram Moolenaar 		}
21809f9fe37fSBram Moolenaar 	    }
21819f9fe37fSBram Moolenaar 	}
21829f9fe37fSBram Moolenaar 
21839f9fe37fSBram Moolenaar 	vim_free(ptrs);
21849f9fe37fSBram Moolenaar     }
21859f9fe37fSBram Moolenaar theend:
21869f9fe37fSBram Moolenaar     sortinfo = old_sortinfo;
21879f9fe37fSBram Moolenaar }
21889f9fe37fSBram Moolenaar 
21899f9fe37fSBram Moolenaar /*
21909f9fe37fSBram Moolenaar  * "sort({list})" function
21919f9fe37fSBram Moolenaar  */
21929f9fe37fSBram Moolenaar     void
f_sort(typval_T * argvars,typval_T * rettv)21939f9fe37fSBram Moolenaar f_sort(typval_T *argvars, typval_T *rettv)
21949f9fe37fSBram Moolenaar {
21959f9fe37fSBram Moolenaar     do_sort_uniq(argvars, rettv, TRUE);
21969f9fe37fSBram Moolenaar }
21979f9fe37fSBram Moolenaar 
21989f9fe37fSBram Moolenaar /*
21999f9fe37fSBram Moolenaar  * "uniq({list})" function
22009f9fe37fSBram Moolenaar  */
22019f9fe37fSBram Moolenaar     void
f_uniq(typval_T * argvars,typval_T * rettv)22029f9fe37fSBram Moolenaar f_uniq(typval_T *argvars, typval_T *rettv)
22039f9fe37fSBram Moolenaar {
22049f9fe37fSBram Moolenaar     do_sort_uniq(argvars, rettv, FALSE);
22059f9fe37fSBram Moolenaar }
22069f9fe37fSBram Moolenaar 
2207ea696852SBram Moolenaar typedef enum {
2208ea696852SBram Moolenaar     FILTERMAP_FILTER,
2209ea696852SBram Moolenaar     FILTERMAP_MAP,
2210ea696852SBram Moolenaar     FILTERMAP_MAPNEW
2211ea696852SBram Moolenaar } filtermap_T;
2212ea696852SBram Moolenaar 
22131e1d3004SBram Moolenaar /*
22141e1d3004SBram Moolenaar  * Handle one item for map() and filter().
2215ea696852SBram Moolenaar  * Sets v:val to "tv".  Caller must set v:key.
22161e1d3004SBram Moolenaar  */
22171e1d3004SBram Moolenaar     static int
filter_map_one(typval_T * tv,typval_T * expr,filtermap_T filtermap,typval_T * newtv,int * remp)2218ea696852SBram Moolenaar filter_map_one(
2219ea696852SBram Moolenaar 	typval_T	*tv,	    // original value
2220ea696852SBram Moolenaar 	typval_T	*expr,	    // callback
2221ea696852SBram Moolenaar 	filtermap_T	filtermap,
2222ea696852SBram Moolenaar 	typval_T	*newtv,	    // for map() and mapnew(): new value
2223ea696852SBram Moolenaar 	int		*remp)	    // for filter(): remove flag
22241e1d3004SBram Moolenaar {
22251e1d3004SBram Moolenaar     typval_T	argv[3];
22261e1d3004SBram Moolenaar     int		retval = FAIL;
22271e1d3004SBram Moolenaar 
22281e1d3004SBram Moolenaar     copy_tv(tv, get_vim_var_tv(VV_VAL));
22291e1d3004SBram Moolenaar     argv[0] = *get_vim_var_tv(VV_KEY);
22301e1d3004SBram Moolenaar     argv[1] = *get_vim_var_tv(VV_VAL);
2231ea696852SBram Moolenaar     if (eval_expr_typval(expr, argv, 2, newtv) == FAIL)
22321e1d3004SBram Moolenaar 	goto theend;
2233ea696852SBram Moolenaar     if (filtermap == FILTERMAP_FILTER)
22341e1d3004SBram Moolenaar     {
22351e1d3004SBram Moolenaar 	int	    error = FALSE;
22361e1d3004SBram Moolenaar 
22371e1d3004SBram Moolenaar 	// filter(): when expr is zero remove the item
223856acb094SBram Moolenaar 	if (in_vim9script())
2239ea696852SBram Moolenaar 	    *remp = !tv2bool(newtv);
224056acb094SBram Moolenaar 	else
2241ea696852SBram Moolenaar 	    *remp = (tv_get_number_chk(newtv, &error) == 0);
2242ea696852SBram Moolenaar 	clear_tv(newtv);
22431e1d3004SBram Moolenaar 	// On type error, nothing has been removed; return FAIL to stop the
22441e1d3004SBram Moolenaar 	// loop.  The error message was given by tv_get_number_chk().
22451e1d3004SBram Moolenaar 	if (error)
22461e1d3004SBram Moolenaar 	    goto theend;
22471e1d3004SBram Moolenaar     }
22481e1d3004SBram Moolenaar     retval = OK;
22491e1d3004SBram Moolenaar theend:
22501e1d3004SBram Moolenaar     clear_tv(get_vim_var_tv(VV_VAL));
22511e1d3004SBram Moolenaar     return retval;
22521e1d3004SBram Moolenaar }
22531e1d3004SBram Moolenaar 
22541e1d3004SBram Moolenaar /*
22551e1d3004SBram Moolenaar  * Implementation of map() and filter().
22561e1d3004SBram Moolenaar  */
22571e1d3004SBram Moolenaar     static void
filter_map(typval_T * argvars,typval_T * rettv,filtermap_T filtermap)2258ea696852SBram Moolenaar filter_map(typval_T *argvars, typval_T *rettv, filtermap_T filtermap)
22591e1d3004SBram Moolenaar {
22601e1d3004SBram Moolenaar     typval_T	*expr;
22611e1d3004SBram Moolenaar     listitem_T	*li, *nli;
22621e1d3004SBram Moolenaar     list_T	*l = NULL;
22631e1d3004SBram Moolenaar     dictitem_T	*di;
22641e1d3004SBram Moolenaar     hashtab_T	*ht;
22651e1d3004SBram Moolenaar     hashitem_T	*hi;
22661e1d3004SBram Moolenaar     dict_T	*d = NULL;
22671e1d3004SBram Moolenaar     blob_T	*b = NULL;
22681e1d3004SBram Moolenaar     int		rem;
22691e1d3004SBram Moolenaar     int		todo;
22707a3fe3e1SBram Moolenaar     char	*func_name = filtermap == FILTERMAP_MAP ? "map()"
2271ea696852SBram Moolenaar 				  : filtermap == FILTERMAP_MAPNEW ? "mapnew()"
22727a3fe3e1SBram Moolenaar 				  : "filter()";
2273ea696852SBram Moolenaar     char_u	*arg_errmsg = (char_u *)(filtermap == FILTERMAP_MAP
2274ea696852SBram Moolenaar 							 ? N_("map() argument")
2275ea696852SBram Moolenaar 				       : filtermap == FILTERMAP_MAPNEW
2276ea696852SBram Moolenaar 						      ? N_("mapnew() argument")
22771e1d3004SBram Moolenaar 						    : N_("filter() argument"));
22781e1d3004SBram Moolenaar     int		save_did_emsg;
22791e1d3004SBram Moolenaar     int		idx = 0;
228070250fb4SBram Moolenaar     type_T	*type = NULL;
228170250fb4SBram Moolenaar     garray_T	type_list;
22821e1d3004SBram Moolenaar 
2283ea696852SBram Moolenaar     // map() and filter() return the first argument, also on failure.
2284ea696852SBram Moolenaar     if (filtermap != FILTERMAP_MAPNEW)
2285ffdf8adfSBram Moolenaar 	copy_tv(&argvars[0], rettv);
22860ad871dcSYegappan Lakshmanan 
22870ad871dcSYegappan Lakshmanan     if (in_vim9script()
22880ad871dcSYegappan Lakshmanan 	    && (check_for_list_or_dict_or_blob_arg(argvars, 0) == FAIL))
22890ad871dcSYegappan Lakshmanan 	return;
22900ad871dcSYegappan Lakshmanan 
229170250fb4SBram Moolenaar     if (filtermap == FILTERMAP_MAP && in_vim9script())
229270250fb4SBram Moolenaar     {
229370250fb4SBram Moolenaar 	// Check that map() does not change the type of the dict.
229470250fb4SBram Moolenaar 	ga_init2(&type_list, sizeof(type_T *), 10);
2295f2253963SBram Moolenaar 	type = typval2type(argvars, get_copyID(), &type_list, TRUE);
229670250fb4SBram Moolenaar     }
2297ffdf8adfSBram Moolenaar 
22981e1d3004SBram Moolenaar     if (argvars[0].v_type == VAR_BLOB)
22991e1d3004SBram Moolenaar     {
2300ea696852SBram Moolenaar 	if (filtermap == FILTERMAP_MAPNEW)
2301ea696852SBram Moolenaar 	{
2302ea696852SBram Moolenaar 	    rettv->v_type = VAR_BLOB;
2303ea696852SBram Moolenaar 	    rettv->vval.v_blob = NULL;
2304ea696852SBram Moolenaar 	}
23051e1d3004SBram Moolenaar 	if ((b = argvars[0].vval.v_blob) == NULL)
230670250fb4SBram Moolenaar 	    goto theend;
23071e1d3004SBram Moolenaar     }
23081e1d3004SBram Moolenaar     else if (argvars[0].v_type == VAR_LIST)
23091e1d3004SBram Moolenaar     {
2310ea696852SBram Moolenaar 	if (filtermap == FILTERMAP_MAPNEW)
2311ea696852SBram Moolenaar 	{
2312ea696852SBram Moolenaar 	    rettv->v_type = VAR_LIST;
2313ea696852SBram Moolenaar 	    rettv->vval.v_list = NULL;
2314ea696852SBram Moolenaar 	}
23151e1d3004SBram Moolenaar 	if ((l = argvars[0].vval.v_list) == NULL
2316ea696852SBram Moolenaar 	      || (filtermap == FILTERMAP_FILTER
2317ea696852SBram Moolenaar 			    && value_check_lock(l->lv_lock, arg_errmsg, TRUE)))
231870250fb4SBram Moolenaar 	    goto theend;
23191e1d3004SBram Moolenaar     }
23201e1d3004SBram Moolenaar     else if (argvars[0].v_type == VAR_DICT)
23211e1d3004SBram Moolenaar     {
2322ea696852SBram Moolenaar 	if (filtermap == FILTERMAP_MAPNEW)
2323ea696852SBram Moolenaar 	{
2324ea696852SBram Moolenaar 	    rettv->v_type = VAR_DICT;
2325ea696852SBram Moolenaar 	    rettv->vval.v_dict = NULL;
2326ea696852SBram Moolenaar 	}
23271e1d3004SBram Moolenaar 	if ((d = argvars[0].vval.v_dict) == NULL
2328ea696852SBram Moolenaar 	      || (filtermap == FILTERMAP_FILTER
2329ea696852SBram Moolenaar 			    && value_check_lock(d->dv_lock, arg_errmsg, TRUE)))
233070250fb4SBram Moolenaar 	    goto theend;
23311e1d3004SBram Moolenaar     }
23321e1d3004SBram Moolenaar     else
23331e1d3004SBram Moolenaar     {
23347a3fe3e1SBram Moolenaar 	semsg(_(e_listdictblobarg), func_name);
233570250fb4SBram Moolenaar 	goto theend;
23361e1d3004SBram Moolenaar     }
23371e1d3004SBram Moolenaar 
23381e1d3004SBram Moolenaar     expr = &argvars[1];
23391e1d3004SBram Moolenaar     // On type errors, the preceding call has already displayed an error
23401e1d3004SBram Moolenaar     // message.  Avoid a misleading error message for an empty string that
23411e1d3004SBram Moolenaar     // was not passed as argument.
23421e1d3004SBram Moolenaar     if (expr->v_type != VAR_UNKNOWN)
23431e1d3004SBram Moolenaar     {
23441e1d3004SBram Moolenaar 	typval_T	save_val;
23451e1d3004SBram Moolenaar 	typval_T	save_key;
23461e1d3004SBram Moolenaar 
23471e1d3004SBram Moolenaar 	prepare_vimvar(VV_VAL, &save_val);
23481e1d3004SBram Moolenaar 	prepare_vimvar(VV_KEY, &save_key);
23491e1d3004SBram Moolenaar 
23501e1d3004SBram Moolenaar 	// We reset "did_emsg" to be able to detect whether an error
23511e1d3004SBram Moolenaar 	// occurred during evaluation of the expression.
23521e1d3004SBram Moolenaar 	save_did_emsg = did_emsg;
23531e1d3004SBram Moolenaar 	did_emsg = FALSE;
23541e1d3004SBram Moolenaar 
23551e1d3004SBram Moolenaar 	if (argvars[0].v_type == VAR_DICT)
23561e1d3004SBram Moolenaar 	{
2357db661fb9SBram Moolenaar 	    int	    prev_lock = d->dv_lock;
2358ea696852SBram Moolenaar 	    dict_T  *d_ret = NULL;
2359db661fb9SBram Moolenaar 
2360ea696852SBram Moolenaar 	    if (filtermap == FILTERMAP_MAPNEW)
2361ea696852SBram Moolenaar 	    {
2362ea696852SBram Moolenaar 		if (rettv_dict_alloc(rettv) == FAIL)
236370250fb4SBram Moolenaar 		    goto theend;
2364ea696852SBram Moolenaar 		d_ret = rettv->vval.v_dict;
2365ea696852SBram Moolenaar 	    }
2366ea696852SBram Moolenaar 
2367ea696852SBram Moolenaar 	    if (filtermap != FILTERMAP_FILTER && d->dv_lock == 0)
2368db661fb9SBram Moolenaar 		d->dv_lock = VAR_LOCKED;
23691e1d3004SBram Moolenaar 	    ht = &d->dv_hashtab;
23701e1d3004SBram Moolenaar 	    hash_lock(ht);
23711e1d3004SBram Moolenaar 	    todo = (int)ht->ht_used;
23721e1d3004SBram Moolenaar 	    for (hi = ht->ht_array; todo > 0; ++hi)
23731e1d3004SBram Moolenaar 	    {
23741e1d3004SBram Moolenaar 		if (!HASHITEM_EMPTY(hi))
23751e1d3004SBram Moolenaar 		{
23761e1d3004SBram Moolenaar 		    int		r;
2377ea696852SBram Moolenaar 		    typval_T	newtv;
23781e1d3004SBram Moolenaar 
23791e1d3004SBram Moolenaar 		    --todo;
23801e1d3004SBram Moolenaar 		    di = HI2DI(hi);
238157cf4973SBram Moolenaar 		    if (filtermap == FILTERMAP_MAP
2382ea696852SBram Moolenaar 					 && (value_check_lock(di->di_tv.v_lock,
23831e1d3004SBram Moolenaar 							   arg_errmsg, TRUE)
23841e1d3004SBram Moolenaar 				|| var_check_ro(di->di_flags,
23851e1d3004SBram Moolenaar 							   arg_errmsg, TRUE)))
23861e1d3004SBram Moolenaar 			break;
23871e1d3004SBram Moolenaar 		    set_vim_var_string(VV_KEY, di->di_key, -1);
2388027c4ab4SBram Moolenaar 		    newtv.v_type = VAR_UNKNOWN;
2389ea696852SBram Moolenaar 		    r = filter_map_one(&di->di_tv, expr, filtermap,
2390ea696852SBram Moolenaar 								 &newtv, &rem);
23911e1d3004SBram Moolenaar 		    clear_tv(get_vim_var_tv(VV_KEY));
23921e1d3004SBram Moolenaar 		    if (r == FAIL || did_emsg)
23931e1d3004SBram Moolenaar 		    {
2394ea696852SBram Moolenaar 			clear_tv(&newtv);
2395ea696852SBram Moolenaar 			break;
2396ea696852SBram Moolenaar 		    }
2397ea696852SBram Moolenaar 		    if (filtermap == FILTERMAP_MAP)
2398ea696852SBram Moolenaar 		    {
2399f785aa13SBram Moolenaar 			if (type != NULL && check_typval_arg_type(
24007a3fe3e1SBram Moolenaar 				     type->tt_member, &newtv,
24017a3fe3e1SBram Moolenaar 							 func_name, 0) == FAIL)
240270250fb4SBram Moolenaar 			{
240370250fb4SBram Moolenaar 			    clear_tv(&newtv);
240470250fb4SBram Moolenaar 			    break;
240570250fb4SBram Moolenaar 			}
2406ea696852SBram Moolenaar 			// map(): replace the dict item value
2407ea696852SBram Moolenaar 			clear_tv(&di->di_tv);
2408ea696852SBram Moolenaar 			newtv.v_lock = 0;
2409ea696852SBram Moolenaar 			di->di_tv = newtv;
2410ea696852SBram Moolenaar 		    }
2411ea696852SBram Moolenaar 		    else if (filtermap == FILTERMAP_MAPNEW)
2412ea696852SBram Moolenaar 		    {
2413ea696852SBram Moolenaar 			// mapnew(): add the item value to the new dict
2414ea696852SBram Moolenaar 			r = dict_add_tv(d_ret, (char *)di->di_key, &newtv);
2415ea696852SBram Moolenaar 			clear_tv(&newtv);
2416ea696852SBram Moolenaar 			if (r == FAIL)
2417ea696852SBram Moolenaar 			    break;
2418ea696852SBram Moolenaar 		    }
2419ea696852SBram Moolenaar 		    else if (filtermap == FILTERMAP_FILTER && rem)
2420ea696852SBram Moolenaar 		    {
2421ea696852SBram Moolenaar 			// filter(false): remove the item from the dict
24221e1d3004SBram Moolenaar 			if (var_check_fixed(di->di_flags, arg_errmsg, TRUE)
24231e1d3004SBram Moolenaar 			    || var_check_ro(di->di_flags, arg_errmsg, TRUE))
24241e1d3004SBram Moolenaar 			    break;
24251e1d3004SBram Moolenaar 			dictitem_remove(d, di);
24261e1d3004SBram Moolenaar 		    }
24271e1d3004SBram Moolenaar 		}
24281e1d3004SBram Moolenaar 	    }
24291e1d3004SBram Moolenaar 	    hash_unlock(ht);
2430db661fb9SBram Moolenaar 	    d->dv_lock = prev_lock;
24311e1d3004SBram Moolenaar 	}
24321e1d3004SBram Moolenaar 	else if (argvars[0].v_type == VAR_BLOB)
24331e1d3004SBram Moolenaar 	{
24341e1d3004SBram Moolenaar 	    int		i;
24351e1d3004SBram Moolenaar 	    typval_T	tv;
243649c57ce5SBram Moolenaar 	    varnumber_T	val;
2437ea696852SBram Moolenaar 	    blob_T	*b_ret = b;
2438ea696852SBram Moolenaar 
2439ea696852SBram Moolenaar 	    if (filtermap == FILTERMAP_MAPNEW)
2440ea696852SBram Moolenaar 	    {
2441ea696852SBram Moolenaar 		if (blob_copy(b, rettv) == FAIL)
244270250fb4SBram Moolenaar 		    goto theend;
2443ea696852SBram Moolenaar 		b_ret = rettv->vval.v_blob;
2444ea696852SBram Moolenaar 	    }
24451e1d3004SBram Moolenaar 
24461e1d3004SBram Moolenaar 	    // set_vim_var_nr() doesn't set the type
24471e1d3004SBram Moolenaar 	    set_vim_var_type(VV_KEY, VAR_NUMBER);
24481e1d3004SBram Moolenaar 
24491e1d3004SBram Moolenaar 	    for (i = 0; i < b->bv_ga.ga_len; i++)
24501e1d3004SBram Moolenaar 	    {
2451ea696852SBram Moolenaar 		typval_T newtv;
2452ea696852SBram Moolenaar 
24531e1d3004SBram Moolenaar 		tv.v_type = VAR_NUMBER;
245449c57ce5SBram Moolenaar 		val = blob_get(b, i);
245549c57ce5SBram Moolenaar 		tv.vval.v_number = val;
24561e1d3004SBram Moolenaar 		set_vim_var_nr(VV_KEY, idx);
2457ea696852SBram Moolenaar 		if (filter_map_one(&tv, expr, filtermap, &newtv, &rem) == FAIL
2458ea696852SBram Moolenaar 								   || did_emsg)
24591e1d3004SBram Moolenaar 		    break;
246039211cbaSBram Moolenaar 		if (newtv.v_type != VAR_NUMBER && newtv.v_type != VAR_BOOL)
24611e1d3004SBram Moolenaar 		{
2462ea696852SBram Moolenaar 		    clear_tv(&newtv);
24631e1d3004SBram Moolenaar 		    emsg(_(e_invalblob));
24641e1d3004SBram Moolenaar 		    break;
24651e1d3004SBram Moolenaar 		}
2466ea696852SBram Moolenaar 		if (filtermap != FILTERMAP_FILTER)
246749c57ce5SBram Moolenaar 		{
2468ea696852SBram Moolenaar 		    if (newtv.vval.v_number != val)
2469ea696852SBram Moolenaar 			blob_set(b_ret, i, newtv.vval.v_number);
247049c57ce5SBram Moolenaar 		}
247149c57ce5SBram Moolenaar 		else if (rem)
24721e1d3004SBram Moolenaar 		{
24731e1d3004SBram Moolenaar 		    char_u *p = (char_u *)argvars[0].vval.v_blob->bv_ga.ga_data;
24741e1d3004SBram Moolenaar 
247549c57ce5SBram Moolenaar 		    mch_memmove(p + i, p + i + 1,
24761e1d3004SBram Moolenaar 					      (size_t)b->bv_ga.ga_len - i - 1);
24771e1d3004SBram Moolenaar 		    --b->bv_ga.ga_len;
24781e1d3004SBram Moolenaar 		    --i;
24791e1d3004SBram Moolenaar 		}
248049c57ce5SBram Moolenaar 		++idx;
24811e1d3004SBram Moolenaar 	    }
24821e1d3004SBram Moolenaar 	}
24831e1d3004SBram Moolenaar 	else // argvars[0].v_type == VAR_LIST
24841e1d3004SBram Moolenaar 	{
2485db661fb9SBram Moolenaar 	    int	    prev_lock = l->lv_lock;
2486ea696852SBram Moolenaar 	    list_T  *l_ret = NULL;
2487db661fb9SBram Moolenaar 
2488ea696852SBram Moolenaar 	    if (filtermap == FILTERMAP_MAPNEW)
2489ea696852SBram Moolenaar 	    {
2490ea696852SBram Moolenaar 		if (rettv_list_alloc(rettv) == FAIL)
249170250fb4SBram Moolenaar 		    goto theend;
2492ea696852SBram Moolenaar 		l_ret = rettv->vval.v_list;
2493ea696852SBram Moolenaar 	    }
24941e1d3004SBram Moolenaar 	    // set_vim_var_nr() doesn't set the type
24951e1d3004SBram Moolenaar 	    set_vim_var_type(VV_KEY, VAR_NUMBER);
24961e1d3004SBram Moolenaar 
2497ea696852SBram Moolenaar 	    if (filtermap != FILTERMAP_FILTER && l->lv_lock == 0)
2498db661fb9SBram Moolenaar 		l->lv_lock = VAR_LOCKED;
2499f8ca03bfSBram Moolenaar 
2500f8ca03bfSBram Moolenaar 	    if (l->lv_first == &range_list_item)
2501f8ca03bfSBram Moolenaar 	    {
2502f8ca03bfSBram Moolenaar 		varnumber_T	val = l->lv_u.nonmat.lv_start;
2503f8ca03bfSBram Moolenaar 		int		len = l->lv_len;
2504f8ca03bfSBram Moolenaar 		int		stride = l->lv_u.nonmat.lv_stride;
2505f8ca03bfSBram Moolenaar 
2506f8ca03bfSBram Moolenaar 		// List from range(): loop over the numbers
250775ab91ffSBram Moolenaar 		if (filtermap != FILTERMAP_MAPNEW)
250875ab91ffSBram Moolenaar 		{
2509f8ca03bfSBram Moolenaar 		    l->lv_first = NULL;
2510f8ca03bfSBram Moolenaar 		    l->lv_u.mat.lv_last = NULL;
2511f8ca03bfSBram Moolenaar 		    l->lv_len = 0;
2512f8ca03bfSBram Moolenaar 		    l->lv_u.mat.lv_idx_item = NULL;
251375ab91ffSBram Moolenaar 		}
2514f8ca03bfSBram Moolenaar 
2515f8ca03bfSBram Moolenaar 		for (idx = 0; idx < len; ++idx)
2516f8ca03bfSBram Moolenaar 		{
2517f8ca03bfSBram Moolenaar 		    typval_T tv;
2518f8ca03bfSBram Moolenaar 		    typval_T newtv;
2519f8ca03bfSBram Moolenaar 
2520f8ca03bfSBram Moolenaar 		    tv.v_type = VAR_NUMBER;
2521f8ca03bfSBram Moolenaar 		    tv.v_lock = 0;
2522f8ca03bfSBram Moolenaar 		    tv.vval.v_number = val;
2523f8ca03bfSBram Moolenaar 		    set_vim_var_nr(VV_KEY, idx);
2524f8ca03bfSBram Moolenaar 		    if (filter_map_one(&tv, expr, filtermap, &newtv, &rem)
2525f8ca03bfSBram Moolenaar 								       == FAIL)
2526f8ca03bfSBram Moolenaar 			break;
2527f8ca03bfSBram Moolenaar 		    if (did_emsg)
2528f8ca03bfSBram Moolenaar 		    {
2529f8ca03bfSBram Moolenaar 			clear_tv(&newtv);
2530f8ca03bfSBram Moolenaar 			break;
2531f8ca03bfSBram Moolenaar 		    }
2532f8ca03bfSBram Moolenaar 		    if (filtermap != FILTERMAP_FILTER)
2533f8ca03bfSBram Moolenaar 		    {
253470250fb4SBram Moolenaar 			if (filtermap == FILTERMAP_MAP && type != NULL
2535f785aa13SBram Moolenaar 				      && check_typval_arg_type(
25367a3fe3e1SBram Moolenaar 				     type->tt_member, &newtv,
25377a3fe3e1SBram Moolenaar 							 func_name, 0) == FAIL)
253870250fb4SBram Moolenaar 			{
253970250fb4SBram Moolenaar 			    clear_tv(&newtv);
254070250fb4SBram Moolenaar 			    break;
254170250fb4SBram Moolenaar 			}
2542f8ca03bfSBram Moolenaar 			// map(), mapnew(): always append the new value to the
2543f8ca03bfSBram Moolenaar 			// list
2544f8ca03bfSBram Moolenaar 			if (list_append_tv_move(filtermap == FILTERMAP_MAP
2545f8ca03bfSBram Moolenaar 						  ? l : l_ret, &newtv) == FAIL)
2546f8ca03bfSBram Moolenaar 			    break;
2547f8ca03bfSBram Moolenaar 		    }
2548f8ca03bfSBram Moolenaar 		    else if (!rem)
2549f8ca03bfSBram Moolenaar 		    {
2550f8ca03bfSBram Moolenaar 			// filter(): append the list item value when not rem
2551f8ca03bfSBram Moolenaar 			if (list_append_tv_move(l, &tv) == FAIL)
2552f8ca03bfSBram Moolenaar 			    break;
2553f8ca03bfSBram Moolenaar 		    }
2554f8ca03bfSBram Moolenaar 
2555f8ca03bfSBram Moolenaar 		    val += stride;
2556f8ca03bfSBram Moolenaar 		}
2557f8ca03bfSBram Moolenaar 	    }
2558f8ca03bfSBram Moolenaar 	    else
2559f8ca03bfSBram Moolenaar 	    {
256057cf4973SBram Moolenaar 		// Materialized list: loop over the items
25611e1d3004SBram Moolenaar 		for (li = l->lv_first; li != NULL; li = nli)
25621e1d3004SBram Moolenaar 		{
2563ea696852SBram Moolenaar 		    typval_T newtv;
2564ea696852SBram Moolenaar 
256557cf4973SBram Moolenaar 		    if (filtermap == FILTERMAP_MAP && value_check_lock(
2566f8ca03bfSBram Moolenaar 					   li->li_tv.v_lock, arg_errmsg, TRUE))
25671e1d3004SBram Moolenaar 			break;
25681e1d3004SBram Moolenaar 		    nli = li->li_next;
25691e1d3004SBram Moolenaar 		    set_vim_var_nr(VV_KEY, idx);
2570ea696852SBram Moolenaar 		    if (filter_map_one(&li->li_tv, expr, filtermap,
2571c56936e2SBram Moolenaar 				&newtv, &rem) == FAIL)
25721e1d3004SBram Moolenaar 			break;
2573c56936e2SBram Moolenaar 		    if (did_emsg)
2574c56936e2SBram Moolenaar 		    {
2575c56936e2SBram Moolenaar 			clear_tv(&newtv);
2576c56936e2SBram Moolenaar 			break;
2577c56936e2SBram Moolenaar 		    }
2578ea696852SBram Moolenaar 		    if (filtermap == FILTERMAP_MAP)
2579ea696852SBram Moolenaar 		    {
2580f785aa13SBram Moolenaar 			if (type != NULL && check_typval_arg_type(
25817a3fe3e1SBram Moolenaar 				type->tt_member, &newtv, func_name, 0) == FAIL)
258270250fb4SBram Moolenaar 			{
258370250fb4SBram Moolenaar 			    clear_tv(&newtv);
258470250fb4SBram Moolenaar 			    break;
258570250fb4SBram Moolenaar 			}
2586ea696852SBram Moolenaar 			// map(): replace the list item value
2587ea696852SBram Moolenaar 			clear_tv(&li->li_tv);
2588ea696852SBram Moolenaar 			newtv.v_lock = 0;
2589ea696852SBram Moolenaar 			li->li_tv = newtv;
2590ea696852SBram Moolenaar 		    }
2591ea696852SBram Moolenaar 		    else if (filtermap == FILTERMAP_MAPNEW)
2592ea696852SBram Moolenaar 		    {
2593ea696852SBram Moolenaar 			// mapnew(): append the list item value
2594ea696852SBram Moolenaar 			if (list_append_tv_move(l_ret, &newtv) == FAIL)
2595ea696852SBram Moolenaar 			    break;
2596ea696852SBram Moolenaar 		    }
2597ea696852SBram Moolenaar 		    else if (filtermap == FILTERMAP_FILTER && rem)
25981e1d3004SBram Moolenaar 			listitem_remove(l, li);
25991e1d3004SBram Moolenaar 		    ++idx;
26001e1d3004SBram Moolenaar 		}
2601f8ca03bfSBram Moolenaar 	    }
2602f8ca03bfSBram Moolenaar 
2603db661fb9SBram Moolenaar 	    l->lv_lock = prev_lock;
26041e1d3004SBram Moolenaar 	}
26051e1d3004SBram Moolenaar 
26061e1d3004SBram Moolenaar 	restore_vimvar(VV_KEY, &save_key);
26071e1d3004SBram Moolenaar 	restore_vimvar(VV_VAL, &save_val);
26081e1d3004SBram Moolenaar 
26091e1d3004SBram Moolenaar 	did_emsg |= save_did_emsg;
26101e1d3004SBram Moolenaar     }
261170250fb4SBram Moolenaar 
261270250fb4SBram Moolenaar theend:
261370250fb4SBram Moolenaar     if (type != NULL)
261470250fb4SBram Moolenaar 	clear_type_list(&type_list);
26151e1d3004SBram Moolenaar }
26161e1d3004SBram Moolenaar 
26171e1d3004SBram Moolenaar /*
26181e1d3004SBram Moolenaar  * "filter()" function
26191e1d3004SBram Moolenaar  */
26201e1d3004SBram Moolenaar     void
f_filter(typval_T * argvars,typval_T * rettv)26211e1d3004SBram Moolenaar f_filter(typval_T *argvars, typval_T *rettv)
26221e1d3004SBram Moolenaar {
2623ea696852SBram Moolenaar     filter_map(argvars, rettv, FILTERMAP_FILTER);
26241e1d3004SBram Moolenaar }
26251e1d3004SBram Moolenaar 
26261e1d3004SBram Moolenaar /*
26271e1d3004SBram Moolenaar  * "map()" function
26281e1d3004SBram Moolenaar  */
26291e1d3004SBram Moolenaar     void
f_map(typval_T * argvars,typval_T * rettv)26301e1d3004SBram Moolenaar f_map(typval_T *argvars, typval_T *rettv)
26311e1d3004SBram Moolenaar {
2632ea696852SBram Moolenaar     filter_map(argvars, rettv, FILTERMAP_MAP);
2633ea696852SBram Moolenaar }
2634ea696852SBram Moolenaar 
2635ea696852SBram Moolenaar /*
2636ea696852SBram Moolenaar  * "mapnew()" function
2637ea696852SBram Moolenaar  */
2638ea696852SBram Moolenaar     void
f_mapnew(typval_T * argvars,typval_T * rettv)2639ea696852SBram Moolenaar f_mapnew(typval_T *argvars, typval_T *rettv)
2640ea696852SBram Moolenaar {
2641ea696852SBram Moolenaar     filter_map(argvars, rettv, FILTERMAP_MAPNEW);
26421e1d3004SBram Moolenaar }
26431e1d3004SBram Moolenaar 
264408c308aeSBram Moolenaar /*
264508c308aeSBram Moolenaar  * "add(list, item)" function
264608c308aeSBram Moolenaar  */
264708c308aeSBram Moolenaar     void
f_add(typval_T * argvars,typval_T * rettv)264808c308aeSBram Moolenaar f_add(typval_T *argvars, typval_T *rettv)
264908c308aeSBram Moolenaar {
26504ba37b58SBram Moolenaar     rettv->vval.v_number = 1; // Default: Failed
26510ad871dcSYegappan Lakshmanan 
26520ad871dcSYegappan Lakshmanan     if (in_vim9script()
26530ad871dcSYegappan Lakshmanan 	    && (check_for_list_or_blob_arg(argvars, 0) == FAIL
26540ad871dcSYegappan Lakshmanan 		|| (argvars[0].v_type == VAR_BLOB
26550ad871dcSYegappan Lakshmanan 		    && check_for_number_arg(argvars, 1) == FAIL)))
26560ad871dcSYegappan Lakshmanan 	return;
26570ad871dcSYegappan Lakshmanan 
265808c308aeSBram Moolenaar     if (argvars[0].v_type == VAR_LIST)
265908c308aeSBram Moolenaar     {
2660b7c21afeSBram Moolenaar 	list_T	*l = argvars[0].vval.v_list;
2661b7c21afeSBram Moolenaar 
2662b7c21afeSBram Moolenaar 	if (l == NULL)
2663b7c21afeSBram Moolenaar 	{
2664b7c21afeSBram Moolenaar 	    if (in_vim9script())
2665b7c21afeSBram Moolenaar 		emsg(_(e_cannot_add_to_null_list));
2666b7c21afeSBram Moolenaar 	}
2667b7c21afeSBram Moolenaar 	else if (!value_check_lock(l->lv_lock,
266808c308aeSBram Moolenaar 					  (char_u *)N_("add() argument"), TRUE)
266908c308aeSBram Moolenaar 		&& list_append_tv(l, &argvars[1]) == OK)
2670b7c21afeSBram Moolenaar 	{
267108c308aeSBram Moolenaar 	    copy_tv(&argvars[0], rettv);
267208c308aeSBram Moolenaar 	}
2673b7c21afeSBram Moolenaar     }
267408c308aeSBram Moolenaar     else if (argvars[0].v_type == VAR_BLOB)
267508c308aeSBram Moolenaar     {
2676b7c21afeSBram Moolenaar 	blob_T	*b = argvars[0].vval.v_blob;
2677b7c21afeSBram Moolenaar 
2678b7c21afeSBram Moolenaar 	if (b == NULL)
2679b7c21afeSBram Moolenaar 	{
2680b7c21afeSBram Moolenaar 	    if (in_vim9script())
2681b7c21afeSBram Moolenaar 		emsg(_(e_cannot_add_to_null_blob));
2682b7c21afeSBram Moolenaar 	}
2683b7c21afeSBram Moolenaar 	else if (!value_check_lock(b->bv_lock,
268408c308aeSBram Moolenaar 					 (char_u *)N_("add() argument"), TRUE))
268508c308aeSBram Moolenaar 	{
268608c308aeSBram Moolenaar 	    int		error = FALSE;
268708c308aeSBram Moolenaar 	    varnumber_T n = tv_get_number_chk(&argvars[1], &error);
268808c308aeSBram Moolenaar 
268908c308aeSBram Moolenaar 	    if (!error)
269008c308aeSBram Moolenaar 	    {
269108c308aeSBram Moolenaar 		ga_append(&b->bv_ga, (int)n);
269208c308aeSBram Moolenaar 		copy_tv(&argvars[0], rettv);
269308c308aeSBram Moolenaar 	    }
269408c308aeSBram Moolenaar 	}
269508c308aeSBram Moolenaar     }
269608c308aeSBram Moolenaar     else
269708c308aeSBram Moolenaar 	emsg(_(e_listblobreq));
269808c308aeSBram Moolenaar }
269908c308aeSBram Moolenaar 
270008c308aeSBram Moolenaar /*
270108c308aeSBram Moolenaar  * "count()" function
270208c308aeSBram Moolenaar  */
270308c308aeSBram Moolenaar     void
f_count(typval_T * argvars,typval_T * rettv)270408c308aeSBram Moolenaar f_count(typval_T *argvars, typval_T *rettv)
270508c308aeSBram Moolenaar {
270608c308aeSBram Moolenaar     long	n = 0;
270708c308aeSBram Moolenaar     int		ic = FALSE;
270808c308aeSBram Moolenaar     int		error = FALSE;
270908c308aeSBram Moolenaar 
271083494b4aSYegappan Lakshmanan     if (in_vim9script()
27114490ec4eSYegappan Lakshmanan 	    && (check_for_string_or_list_or_dict_arg(argvars, 0) == FAIL
271283494b4aSYegappan Lakshmanan 		|| check_for_opt_bool_arg(argvars, 2) == FAIL
271383494b4aSYegappan Lakshmanan 		|| (argvars[2].v_type != VAR_UNKNOWN
271483494b4aSYegappan Lakshmanan 		    && check_for_opt_number_arg(argvars, 3) == FAIL)))
271583494b4aSYegappan Lakshmanan 	return;
271683494b4aSYegappan Lakshmanan 
271708c308aeSBram Moolenaar     if (argvars[2].v_type != VAR_UNKNOWN)
2718119f5572SBram Moolenaar 	ic = (int)tv_get_bool_chk(&argvars[2], &error);
271908c308aeSBram Moolenaar 
272008c308aeSBram Moolenaar     if (argvars[0].v_type == VAR_STRING)
272108c308aeSBram Moolenaar     {
272208c308aeSBram Moolenaar 	char_u *expr = tv_get_string_chk(&argvars[1]);
272308c308aeSBram Moolenaar 	char_u *p = argvars[0].vval.v_string;
272408c308aeSBram Moolenaar 	char_u *next;
272508c308aeSBram Moolenaar 
272608c308aeSBram Moolenaar 	if (!error && expr != NULL && *expr != NUL && p != NULL)
272708c308aeSBram Moolenaar 	{
272808c308aeSBram Moolenaar 	    if (ic)
272908c308aeSBram Moolenaar 	    {
273008c308aeSBram Moolenaar 		size_t len = STRLEN(expr);
273108c308aeSBram Moolenaar 
273208c308aeSBram Moolenaar 		while (*p != NUL)
273308c308aeSBram Moolenaar 		{
273408c308aeSBram Moolenaar 		    if (MB_STRNICMP(p, expr, len) == 0)
273508c308aeSBram Moolenaar 		    {
273608c308aeSBram Moolenaar 			++n;
273708c308aeSBram Moolenaar 			p += len;
273808c308aeSBram Moolenaar 		    }
273908c308aeSBram Moolenaar 		    else
274008c308aeSBram Moolenaar 			MB_PTR_ADV(p);
274108c308aeSBram Moolenaar 		}
274208c308aeSBram Moolenaar 	    }
274308c308aeSBram Moolenaar 	    else
274408c308aeSBram Moolenaar 		while ((next = (char_u *)strstr((char *)p, (char *)expr))
274508c308aeSBram Moolenaar 								       != NULL)
274608c308aeSBram Moolenaar 		{
274708c308aeSBram Moolenaar 		    ++n;
274808c308aeSBram Moolenaar 		    p = next + STRLEN(expr);
274908c308aeSBram Moolenaar 		}
275008c308aeSBram Moolenaar 	}
275108c308aeSBram Moolenaar 
275208c308aeSBram Moolenaar     }
275308c308aeSBram Moolenaar     else if (argvars[0].v_type == VAR_LIST)
275408c308aeSBram Moolenaar     {
275508c308aeSBram Moolenaar 	listitem_T	*li;
275608c308aeSBram Moolenaar 	list_T		*l;
275708c308aeSBram Moolenaar 	long		idx;
275808c308aeSBram Moolenaar 
275908c308aeSBram Moolenaar 	if ((l = argvars[0].vval.v_list) != NULL)
276008c308aeSBram Moolenaar 	{
27617e9f351bSBram Moolenaar 	    CHECK_LIST_MATERIALIZE(l);
276208c308aeSBram Moolenaar 	    li = l->lv_first;
276308c308aeSBram Moolenaar 	    if (argvars[2].v_type != VAR_UNKNOWN)
276408c308aeSBram Moolenaar 	    {
276508c308aeSBram Moolenaar 		if (argvars[3].v_type != VAR_UNKNOWN)
276608c308aeSBram Moolenaar 		{
276708c308aeSBram Moolenaar 		    idx = (long)tv_get_number_chk(&argvars[3], &error);
276808c308aeSBram Moolenaar 		    if (!error)
276908c308aeSBram Moolenaar 		    {
277008c308aeSBram Moolenaar 			li = list_find(l, idx);
277108c308aeSBram Moolenaar 			if (li == NULL)
277208c308aeSBram Moolenaar 			    semsg(_(e_listidx), idx);
277308c308aeSBram Moolenaar 		    }
277408c308aeSBram Moolenaar 		}
277508c308aeSBram Moolenaar 		if (error)
277608c308aeSBram Moolenaar 		    li = NULL;
277708c308aeSBram Moolenaar 	    }
277808c308aeSBram Moolenaar 
277908c308aeSBram Moolenaar 	    for ( ; li != NULL; li = li->li_next)
278008c308aeSBram Moolenaar 		if (tv_equal(&li->li_tv, &argvars[1], ic, FALSE))
278108c308aeSBram Moolenaar 		    ++n;
278208c308aeSBram Moolenaar 	}
278308c308aeSBram Moolenaar     }
278408c308aeSBram Moolenaar     else if (argvars[0].v_type == VAR_DICT)
278508c308aeSBram Moolenaar     {
278608c308aeSBram Moolenaar 	int		todo;
278708c308aeSBram Moolenaar 	dict_T		*d;
278808c308aeSBram Moolenaar 	hashitem_T	*hi;
278908c308aeSBram Moolenaar 
279008c308aeSBram Moolenaar 	if ((d = argvars[0].vval.v_dict) != NULL)
279108c308aeSBram Moolenaar 	{
279208c308aeSBram Moolenaar 	    if (argvars[2].v_type != VAR_UNKNOWN)
279308c308aeSBram Moolenaar 	    {
279408c308aeSBram Moolenaar 		if (argvars[3].v_type != VAR_UNKNOWN)
279508c308aeSBram Moolenaar 		    emsg(_(e_invarg));
279608c308aeSBram Moolenaar 	    }
279708c308aeSBram Moolenaar 
279808c308aeSBram Moolenaar 	    todo = error ? 0 : (int)d->dv_hashtab.ht_used;
279908c308aeSBram Moolenaar 	    for (hi = d->dv_hashtab.ht_array; todo > 0; ++hi)
280008c308aeSBram Moolenaar 	    {
280108c308aeSBram Moolenaar 		if (!HASHITEM_EMPTY(hi))
280208c308aeSBram Moolenaar 		{
280308c308aeSBram Moolenaar 		    --todo;
280408c308aeSBram Moolenaar 		    if (tv_equal(&HI2DI(hi)->di_tv, &argvars[1], ic, FALSE))
280508c308aeSBram Moolenaar 			++n;
280608c308aeSBram Moolenaar 		}
280708c308aeSBram Moolenaar 	    }
280808c308aeSBram Moolenaar 	}
280908c308aeSBram Moolenaar     }
281008c308aeSBram Moolenaar     else
281108c308aeSBram Moolenaar 	semsg(_(e_listdictarg), "count()");
281208c308aeSBram Moolenaar     rettv->vval.v_number = n;
281308c308aeSBram Moolenaar }
281408c308aeSBram Moolenaar 
281508c308aeSBram Moolenaar /*
2816b0e6b513SBram Moolenaar  * "extend()" or "extendnew()" function.  "is_new" is TRUE for extendnew().
281708c308aeSBram Moolenaar  */
2818b0e6b513SBram Moolenaar     static void
extend(typval_T * argvars,typval_T * rettv,char_u * arg_errmsg,int is_new)2819b0e6b513SBram Moolenaar extend(typval_T *argvars, typval_T *rettv, char_u *arg_errmsg, int is_new)
282008c308aeSBram Moolenaar {
2821c03f5c67SBram Moolenaar     type_T	*type = NULL;
2822c03f5c67SBram Moolenaar     garray_T	type_list;
28237a3fe3e1SBram Moolenaar     char	*func_name = is_new ? "extendnew()" : "extend()";
2824c03f5c67SBram Moolenaar 
2825c03f5c67SBram Moolenaar     if (!is_new && in_vim9script())
2826c03f5c67SBram Moolenaar     {
2827c03f5c67SBram Moolenaar 	// Check that map() does not change the type of the dict.
2828c03f5c67SBram Moolenaar 	ga_init2(&type_list, sizeof(type_T *), 10);
2829f2253963SBram Moolenaar 	type = typval2type(argvars, get_copyID(), &type_list, TRUE);
2830c03f5c67SBram Moolenaar     }
2831c03f5c67SBram Moolenaar 
283208c308aeSBram Moolenaar     if (argvars[0].v_type == VAR_LIST && argvars[1].v_type == VAR_LIST)
283308c308aeSBram Moolenaar     {
283408c308aeSBram Moolenaar 	list_T		*l1, *l2;
283508c308aeSBram Moolenaar 	listitem_T	*item;
283608c308aeSBram Moolenaar 	long		before;
283708c308aeSBram Moolenaar 	int		error = FALSE;
283808c308aeSBram Moolenaar 
283908c308aeSBram Moolenaar 	l1 = argvars[0].vval.v_list;
2840348be7edSBram Moolenaar 	if (l1 == NULL)
2841348be7edSBram Moolenaar 	{
2842348be7edSBram Moolenaar 	    emsg(_(e_cannot_extend_null_list));
2843c03f5c67SBram Moolenaar 	    goto theend;
2844348be7edSBram Moolenaar 	}
284508c308aeSBram Moolenaar 	l2 = argvars[1].vval.v_list;
2846b0e6b513SBram Moolenaar 	if ((is_new || !value_check_lock(l1->lv_lock, arg_errmsg, TRUE))
2847b0e6b513SBram Moolenaar 								 && l2 != NULL)
284808c308aeSBram Moolenaar 	{
2849b0e6b513SBram Moolenaar 	    if (is_new)
2850b0e6b513SBram Moolenaar 	    {
2851b0e6b513SBram Moolenaar 		l1 = list_copy(l1, FALSE, get_copyID());
2852b0e6b513SBram Moolenaar 		if (l1 == NULL)
2853c03f5c67SBram Moolenaar 		    goto theend;
2854b0e6b513SBram Moolenaar 	    }
2855b0e6b513SBram Moolenaar 
285608c308aeSBram Moolenaar 	    if (argvars[2].v_type != VAR_UNKNOWN)
285708c308aeSBram Moolenaar 	    {
285808c308aeSBram Moolenaar 		before = (long)tv_get_number_chk(&argvars[2], &error);
285908c308aeSBram Moolenaar 		if (error)
2860c03f5c67SBram Moolenaar 		    goto theend;	// type error; errmsg already given
286108c308aeSBram Moolenaar 
286208c308aeSBram Moolenaar 		if (before == l1->lv_len)
286308c308aeSBram Moolenaar 		    item = NULL;
286408c308aeSBram Moolenaar 		else
286508c308aeSBram Moolenaar 		{
286608c308aeSBram Moolenaar 		    item = list_find(l1, before);
286708c308aeSBram Moolenaar 		    if (item == NULL)
286808c308aeSBram Moolenaar 		    {
286908c308aeSBram Moolenaar 			semsg(_(e_listidx), before);
2870c03f5c67SBram Moolenaar 			goto theend;
287108c308aeSBram Moolenaar 		    }
287208c308aeSBram Moolenaar 		}
287308c308aeSBram Moolenaar 	    }
287408c308aeSBram Moolenaar 	    else
287508c308aeSBram Moolenaar 		item = NULL;
2876f785aa13SBram Moolenaar 	    if (type != NULL && check_typval_arg_type(
28777a3fe3e1SBram Moolenaar 				      type, &argvars[1], func_name, 2) == FAIL)
2878c03f5c67SBram Moolenaar 		goto theend;
287908c308aeSBram Moolenaar 	    list_extend(l1, l2, item);
288008c308aeSBram Moolenaar 
2881b0e6b513SBram Moolenaar 	    if (is_new)
2882b0e6b513SBram Moolenaar 	    {
2883b0e6b513SBram Moolenaar 		rettv->v_type = VAR_LIST;
2884b0e6b513SBram Moolenaar 		rettv->vval.v_list = l1;
2885b0e6b513SBram Moolenaar 		rettv->v_lock = FALSE;
2886b0e6b513SBram Moolenaar 	    }
2887b0e6b513SBram Moolenaar 	    else
288808c308aeSBram Moolenaar 		copy_tv(&argvars[0], rettv);
288908c308aeSBram Moolenaar 	}
289008c308aeSBram Moolenaar     }
289108c308aeSBram Moolenaar     else if (argvars[0].v_type == VAR_DICT && argvars[1].v_type == VAR_DICT)
289208c308aeSBram Moolenaar     {
289308c308aeSBram Moolenaar 	dict_T	*d1, *d2;
289408c308aeSBram Moolenaar 	char_u	*action;
289508c308aeSBram Moolenaar 	int	i;
289608c308aeSBram Moolenaar 
289708c308aeSBram Moolenaar 	d1 = argvars[0].vval.v_dict;
2898348be7edSBram Moolenaar 	if (d1 == NULL)
2899348be7edSBram Moolenaar 	{
2900348be7edSBram Moolenaar 	    emsg(_(e_cannot_extend_null_dict));
2901c03f5c67SBram Moolenaar 	    goto theend;
2902348be7edSBram Moolenaar 	}
290308c308aeSBram Moolenaar 	d2 = argvars[1].vval.v_dict;
2904b0e6b513SBram Moolenaar 	if ((is_new || !value_check_lock(d1->dv_lock, arg_errmsg, TRUE))
2905b0e6b513SBram Moolenaar 								 && d2 != NULL)
290608c308aeSBram Moolenaar 	{
2907b0e6b513SBram Moolenaar 	    if (is_new)
2908b0e6b513SBram Moolenaar 	    {
2909b0e6b513SBram Moolenaar 		d1 = dict_copy(d1, FALSE, get_copyID());
2910b0e6b513SBram Moolenaar 		if (d1 == NULL)
2911c03f5c67SBram Moolenaar 		    goto theend;
2912b0e6b513SBram Moolenaar 	    }
2913b0e6b513SBram Moolenaar 
29144ba37b58SBram Moolenaar 	    // Check the third argument.
291508c308aeSBram Moolenaar 	    if (argvars[2].v_type != VAR_UNKNOWN)
291608c308aeSBram Moolenaar 	    {
291708c308aeSBram Moolenaar 		static char *(av[]) = {"keep", "force", "error"};
291808c308aeSBram Moolenaar 
291908c308aeSBram Moolenaar 		action = tv_get_string_chk(&argvars[2]);
292008c308aeSBram Moolenaar 		if (action == NULL)
2921c03f5c67SBram Moolenaar 		    goto theend;	// type error; errmsg already given
292208c308aeSBram Moolenaar 		for (i = 0; i < 3; ++i)
292308c308aeSBram Moolenaar 		    if (STRCMP(action, av[i]) == 0)
292408c308aeSBram Moolenaar 			break;
292508c308aeSBram Moolenaar 		if (i == 3)
292608c308aeSBram Moolenaar 		{
292708c308aeSBram Moolenaar 		    semsg(_(e_invarg2), action);
2928c03f5c67SBram Moolenaar 		    goto theend;
292908c308aeSBram Moolenaar 		}
293008c308aeSBram Moolenaar 	    }
293108c308aeSBram Moolenaar 	    else
293208c308aeSBram Moolenaar 		action = (char_u *)"force";
293308c308aeSBram Moolenaar 
29347a3fe3e1SBram Moolenaar 	    if (type != NULL && check_typval_arg_type(type, &argvars[1],
29357a3fe3e1SBram Moolenaar 							 func_name, 2) == FAIL)
2936c03f5c67SBram Moolenaar 		goto theend;
29377a3fe3e1SBram Moolenaar 	    dict_extend(d1, d2, action, func_name);
293808c308aeSBram Moolenaar 
2939b0e6b513SBram Moolenaar 	    if (is_new)
2940b0e6b513SBram Moolenaar 	    {
2941b0e6b513SBram Moolenaar 		rettv->v_type = VAR_DICT;
2942b0e6b513SBram Moolenaar 		rettv->vval.v_dict = d1;
2943b0e6b513SBram Moolenaar 		rettv->v_lock = FALSE;
2944b0e6b513SBram Moolenaar 	    }
2945b0e6b513SBram Moolenaar 	    else
294608c308aeSBram Moolenaar 		copy_tv(&argvars[0], rettv);
294708c308aeSBram Moolenaar 	}
294808c308aeSBram Moolenaar     }
294908c308aeSBram Moolenaar     else
29507a3fe3e1SBram Moolenaar 	semsg(_(e_listdictarg), func_name);
2951c03f5c67SBram Moolenaar 
2952c03f5c67SBram Moolenaar theend:
2953c03f5c67SBram Moolenaar     if (type != NULL)
2954c03f5c67SBram Moolenaar 	clear_type_list(&type_list);
2955b0e6b513SBram Moolenaar }
2956b0e6b513SBram Moolenaar 
2957b0e6b513SBram Moolenaar /*
2958b0e6b513SBram Moolenaar  * "extend(list, list [, idx])" function
2959b0e6b513SBram Moolenaar  * "extend(dict, dict [, action])" function
2960b0e6b513SBram Moolenaar  */
2961b0e6b513SBram Moolenaar     void
f_extend(typval_T * argvars,typval_T * rettv)2962b0e6b513SBram Moolenaar f_extend(typval_T *argvars, typval_T *rettv)
2963b0e6b513SBram Moolenaar {
2964b0e6b513SBram Moolenaar     char_u      *errmsg = (char_u *)N_("extend() argument");
2965b0e6b513SBram Moolenaar 
2966b0e6b513SBram Moolenaar     extend(argvars, rettv, errmsg, FALSE);
2967b0e6b513SBram Moolenaar }
2968b0e6b513SBram Moolenaar 
2969b0e6b513SBram Moolenaar /*
2970b0e6b513SBram Moolenaar  * "extendnew(list, list [, idx])" function
2971b0e6b513SBram Moolenaar  * "extendnew(dict, dict [, action])" function
2972b0e6b513SBram Moolenaar  */
2973b0e6b513SBram Moolenaar     void
f_extendnew(typval_T * argvars,typval_T * rettv)2974b0e6b513SBram Moolenaar f_extendnew(typval_T *argvars, typval_T *rettv)
2975b0e6b513SBram Moolenaar {
2976b0e6b513SBram Moolenaar     char_u      *errmsg = (char_u *)N_("extendnew() argument");
2977b0e6b513SBram Moolenaar 
2978b0e6b513SBram Moolenaar     extend(argvars, rettv, errmsg, TRUE);
297908c308aeSBram Moolenaar }
298008c308aeSBram Moolenaar 
298108c308aeSBram Moolenaar /*
298208c308aeSBram Moolenaar  * "insert()" function
298308c308aeSBram Moolenaar  */
298408c308aeSBram Moolenaar     void
f_insert(typval_T * argvars,typval_T * rettv)298508c308aeSBram Moolenaar f_insert(typval_T *argvars, typval_T *rettv)
298608c308aeSBram Moolenaar {
298708c308aeSBram Moolenaar     long	before = 0;
298808c308aeSBram Moolenaar     listitem_T	*item;
298908c308aeSBram Moolenaar     int		error = FALSE;
299008c308aeSBram Moolenaar 
29910ad871dcSYegappan Lakshmanan     if (in_vim9script()
29920ad871dcSYegappan Lakshmanan 	    && (check_for_list_or_blob_arg(argvars, 0) == FAIL
29930ad871dcSYegappan Lakshmanan 		|| (argvars[0].v_type == VAR_BLOB
29940ad871dcSYegappan Lakshmanan 		    && check_for_number_arg(argvars, 1) == FAIL)
29950ad871dcSYegappan Lakshmanan 		|| check_for_opt_number_arg(argvars, 2) == FAIL))
29960ad871dcSYegappan Lakshmanan 	return;
29970ad871dcSYegappan Lakshmanan 
299808c308aeSBram Moolenaar     if (argvars[0].v_type == VAR_BLOB)
299908c308aeSBram Moolenaar     {
300080d7395dSSean Dewar 	blob_T	*b = argvars[0].vval.v_blob;
300108c308aeSBram Moolenaar 
300280d7395dSSean Dewar 	if (b == NULL)
300339211cbaSBram Moolenaar 	{
300439211cbaSBram Moolenaar 	    if (in_vim9script())
300539211cbaSBram Moolenaar 		emsg(_(e_cannot_add_to_null_blob));
300639211cbaSBram Moolenaar 	}
300780d7395dSSean Dewar 	else if (!value_check_lock(b->bv_lock,
300880d7395dSSean Dewar 				     (char_u *)N_("insert() argument"), TRUE))
300980d7395dSSean Dewar 	{
301080d7395dSSean Dewar 	    int		val, len;
301180d7395dSSean Dewar 	    char_u	*p;
301292b83ccfSBram Moolenaar 
301380d7395dSSean Dewar 	    len = blob_len(b);
301408c308aeSBram Moolenaar 	    if (argvars[2].v_type != VAR_UNKNOWN)
301508c308aeSBram Moolenaar 	    {
301608c308aeSBram Moolenaar 		before = (long)tv_get_number_chk(&argvars[2], &error);
301708c308aeSBram Moolenaar 		if (error)
301808c308aeSBram Moolenaar 		    return;		// type error; errmsg already given
301908c308aeSBram Moolenaar 		if (before < 0 || before > len)
302008c308aeSBram Moolenaar 		{
302108c308aeSBram Moolenaar 		    semsg(_(e_invarg2), tv_get_string(&argvars[2]));
302208c308aeSBram Moolenaar 		    return;
302308c308aeSBram Moolenaar 		}
302408c308aeSBram Moolenaar 	    }
302508c308aeSBram Moolenaar 	    val = tv_get_number_chk(&argvars[1], &error);
302608c308aeSBram Moolenaar 	    if (error)
302708c308aeSBram Moolenaar 		return;
302808c308aeSBram Moolenaar 	    if (val < 0 || val > 255)
302908c308aeSBram Moolenaar 	    {
303008c308aeSBram Moolenaar 		semsg(_(e_invarg2), tv_get_string(&argvars[1]));
303108c308aeSBram Moolenaar 		return;
303208c308aeSBram Moolenaar 	    }
303308c308aeSBram Moolenaar 
303480d7395dSSean Dewar 	    if (ga_grow(&b->bv_ga, 1) == FAIL)
303508c308aeSBram Moolenaar 		return;
303680d7395dSSean Dewar 	    p = (char_u *)b->bv_ga.ga_data;
303708c308aeSBram Moolenaar 	    mch_memmove(p + before + 1, p + before, (size_t)len - before);
303808c308aeSBram Moolenaar 	    *(p + before) = val;
303980d7395dSSean Dewar 	    ++b->bv_ga.ga_len;
304008c308aeSBram Moolenaar 
304108c308aeSBram Moolenaar 	    copy_tv(&argvars[0], rettv);
304208c308aeSBram Moolenaar 	}
304380d7395dSSean Dewar     }
304408c308aeSBram Moolenaar     else if (argvars[0].v_type != VAR_LIST)
304508c308aeSBram Moolenaar 	semsg(_(e_listblobarg), "insert()");
304639211cbaSBram Moolenaar     else
304739211cbaSBram Moolenaar     {
304839211cbaSBram Moolenaar 	list_T	*l = argvars[0].vval.v_list;
304939211cbaSBram Moolenaar 
305039211cbaSBram Moolenaar 	if (l == NULL)
305139211cbaSBram Moolenaar 	{
305239211cbaSBram Moolenaar 	    if (in_vim9script())
305339211cbaSBram Moolenaar 		emsg(_(e_cannot_add_to_null_list));
305439211cbaSBram Moolenaar 	}
305539211cbaSBram Moolenaar 	else if (!value_check_lock(l->lv_lock,
305608c308aeSBram Moolenaar 				     (char_u *)N_("insert() argument"), TRUE))
305708c308aeSBram Moolenaar 	{
305808c308aeSBram Moolenaar 	    if (argvars[2].v_type != VAR_UNKNOWN)
305908c308aeSBram Moolenaar 		before = (long)tv_get_number_chk(&argvars[2], &error);
306008c308aeSBram Moolenaar 	    if (error)
30614ba37b58SBram Moolenaar 		return;		// type error; errmsg already given
306208c308aeSBram Moolenaar 
306308c308aeSBram Moolenaar 	    if (before == l->lv_len)
306408c308aeSBram Moolenaar 		item = NULL;
306508c308aeSBram Moolenaar 	    else
306608c308aeSBram Moolenaar 	    {
306708c308aeSBram Moolenaar 		item = list_find(l, before);
306808c308aeSBram Moolenaar 		if (item == NULL)
306908c308aeSBram Moolenaar 		{
307008c308aeSBram Moolenaar 		    semsg(_(e_listidx), before);
307108c308aeSBram Moolenaar 		    l = NULL;
307208c308aeSBram Moolenaar 		}
307308c308aeSBram Moolenaar 	    }
307408c308aeSBram Moolenaar 	    if (l != NULL)
307508c308aeSBram Moolenaar 	    {
3076e6b5324eSBram Moolenaar 		(void)list_insert_tv(l, &argvars[1], item);
307708c308aeSBram Moolenaar 		copy_tv(&argvars[0], rettv);
307808c308aeSBram Moolenaar 	    }
307908c308aeSBram Moolenaar 	}
308008c308aeSBram Moolenaar     }
308139211cbaSBram Moolenaar }
308208c308aeSBram Moolenaar 
308308c308aeSBram Moolenaar /*
308408c308aeSBram Moolenaar  * "remove()" function
308508c308aeSBram Moolenaar  */
308608c308aeSBram Moolenaar     void
f_remove(typval_T * argvars,typval_T * rettv)308708c308aeSBram Moolenaar f_remove(typval_T *argvars, typval_T *rettv)
308808c308aeSBram Moolenaar {
308908c308aeSBram Moolenaar     char_u	*arg_errmsg = (char_u *)N_("remove() argument");
309008c308aeSBram Moolenaar 
30910ad871dcSYegappan Lakshmanan     if (in_vim9script()
30920ad871dcSYegappan Lakshmanan 	    && (check_for_list_or_dict_or_blob_arg(argvars, 0) == FAIL
30930ad871dcSYegappan Lakshmanan 		|| ((argvars[0].v_type == VAR_LIST
30940ad871dcSYegappan Lakshmanan 			|| argvars[0].v_type == VAR_BLOB)
30950ad871dcSYegappan Lakshmanan 		    && (check_for_number_arg(argvars, 1) == FAIL
30960ad871dcSYegappan Lakshmanan 			|| check_for_opt_number_arg(argvars, 2) == FAIL))
30970ad871dcSYegappan Lakshmanan 		|| (argvars[0].v_type == VAR_DICT
30980ad871dcSYegappan Lakshmanan 		    && check_for_string_or_number_arg(argvars, 1) == FAIL)))
30990ad871dcSYegappan Lakshmanan 	return;
31000ad871dcSYegappan Lakshmanan 
310108c308aeSBram Moolenaar     if (argvars[0].v_type == VAR_DICT)
310208c308aeSBram Moolenaar 	dict_remove(argvars, rettv, arg_errmsg);
310308c308aeSBram Moolenaar     else if (argvars[0].v_type == VAR_BLOB)
310480d7395dSSean Dewar 	blob_remove(argvars, rettv, arg_errmsg);
310508c308aeSBram Moolenaar     else if (argvars[0].v_type == VAR_LIST)
310608c308aeSBram Moolenaar 	list_remove(argvars, rettv, arg_errmsg);
310708c308aeSBram Moolenaar     else
310808c308aeSBram Moolenaar 	semsg(_(e_listdictblobarg), "remove()");
310908c308aeSBram Moolenaar }
311008c308aeSBram Moolenaar 
311108c308aeSBram Moolenaar /*
311208c308aeSBram Moolenaar  * "reverse({list})" function
311308c308aeSBram Moolenaar  */
311408c308aeSBram Moolenaar     void
f_reverse(typval_T * argvars,typval_T * rettv)311508c308aeSBram Moolenaar f_reverse(typval_T *argvars, typval_T *rettv)
311608c308aeSBram Moolenaar {
311708c308aeSBram Moolenaar     list_T	*l;
311808c308aeSBram Moolenaar     listitem_T	*li, *ni;
311908c308aeSBram Moolenaar 
31200ad871dcSYegappan Lakshmanan     if (in_vim9script() && check_for_list_or_blob_arg(argvars, 0) == FAIL)
31210ad871dcSYegappan Lakshmanan 	return;
31220ad871dcSYegappan Lakshmanan 
312308c308aeSBram Moolenaar     if (argvars[0].v_type == VAR_BLOB)
312408c308aeSBram Moolenaar     {
312508c308aeSBram Moolenaar 	blob_T	*b = argvars[0].vval.v_blob;
312608c308aeSBram Moolenaar 	int	i, len = blob_len(b);
312708c308aeSBram Moolenaar 
312808c308aeSBram Moolenaar 	for (i = 0; i < len / 2; i++)
312908c308aeSBram Moolenaar 	{
313008c308aeSBram Moolenaar 	    int tmp = blob_get(b, i);
313108c308aeSBram Moolenaar 
313208c308aeSBram Moolenaar 	    blob_set(b, i, blob_get(b, len - i - 1));
313308c308aeSBram Moolenaar 	    blob_set(b, len - i - 1, tmp);
313408c308aeSBram Moolenaar 	}
313508c308aeSBram Moolenaar 	rettv_blob_set(rettv, b);
313608c308aeSBram Moolenaar 	return;
313708c308aeSBram Moolenaar     }
313808c308aeSBram Moolenaar 
313908c308aeSBram Moolenaar     if (argvars[0].v_type != VAR_LIST)
314008c308aeSBram Moolenaar 	semsg(_(e_listblobarg), "reverse()");
3141ef982575SBram Moolenaar     else
3142ef982575SBram Moolenaar     {
3143ef982575SBram Moolenaar 	l = argvars[0].vval.v_list;
3144ef982575SBram Moolenaar 	rettv_list_set(rettv, l);
3145ef982575SBram Moolenaar 	if (l != NULL
3146a187c43cSBram Moolenaar 	    && !value_check_lock(l->lv_lock,
314708c308aeSBram Moolenaar 				    (char_u *)N_("reverse() argument"), TRUE))
314808c308aeSBram Moolenaar 	{
314989bfc821SBram Moolenaar 	    if (l->lv_first == &range_list_item)
315089bfc821SBram Moolenaar 	    {
31510ff6aad3SBram Moolenaar 		varnumber_T new_start = l->lv_u.nonmat.lv_start
31520ff6aad3SBram Moolenaar 				  + (l->lv_len - 1) * l->lv_u.nonmat.lv_stride;
31530ff6aad3SBram Moolenaar 		l->lv_u.nonmat.lv_end = new_start
31540ff6aad3SBram Moolenaar 			   - (l->lv_u.nonmat.lv_end - l->lv_u.nonmat.lv_start);
31550ff6aad3SBram Moolenaar 		l->lv_u.nonmat.lv_start = new_start;
31560ff6aad3SBram Moolenaar 		l->lv_u.nonmat.lv_stride = -l->lv_u.nonmat.lv_stride;
315789bfc821SBram Moolenaar 		return;
315889bfc821SBram Moolenaar 	    }
31590ff6aad3SBram Moolenaar 	    li = l->lv_u.mat.lv_last;
31600ff6aad3SBram Moolenaar 	    l->lv_first = l->lv_u.mat.lv_last = NULL;
316108c308aeSBram Moolenaar 	    l->lv_len = 0;
316208c308aeSBram Moolenaar 	    while (li != NULL)
316308c308aeSBram Moolenaar 	    {
316408c308aeSBram Moolenaar 		ni = li->li_prev;
316508c308aeSBram Moolenaar 		list_append(l, li);
316608c308aeSBram Moolenaar 		li = ni;
316708c308aeSBram Moolenaar 	    }
31680ff6aad3SBram Moolenaar 	    l->lv_u.mat.lv_idx = l->lv_len - l->lv_u.mat.lv_idx - 1;
316908c308aeSBram Moolenaar 	}
317008c308aeSBram Moolenaar     }
3171ef982575SBram Moolenaar }
317208c308aeSBram Moolenaar 
317385629985SBram Moolenaar /*
31748e7d6223SBram Moolenaar  * "reduce(list, { accumulator, element -> value } [, initial])" function
317585629985SBram Moolenaar  */
317685629985SBram Moolenaar     void
f_reduce(typval_T * argvars,typval_T * rettv)317785629985SBram Moolenaar f_reduce(typval_T *argvars, typval_T *rettv)
317885629985SBram Moolenaar {
317948b1c218SBram Moolenaar     typval_T	initial;
318085629985SBram Moolenaar     char_u	*func_name;
318185629985SBram Moolenaar     partial_T   *partial = NULL;
318285629985SBram Moolenaar     funcexe_T	funcexe;
318385629985SBram Moolenaar     typval_T	argv[3];
318485629985SBram Moolenaar 
318585629985SBram Moolenaar     if (argvars[0].v_type != VAR_LIST && argvars[0].v_type != VAR_BLOB)
318685629985SBram Moolenaar     {
318785629985SBram Moolenaar 	emsg(_(e_listblobreq));
318885629985SBram Moolenaar 	return;
318985629985SBram Moolenaar     }
319085629985SBram Moolenaar 
319185629985SBram Moolenaar     if (argvars[1].v_type == VAR_FUNC)
319285629985SBram Moolenaar 	func_name = argvars[1].vval.v_string;
319385629985SBram Moolenaar     else if (argvars[1].v_type == VAR_PARTIAL)
319485629985SBram Moolenaar     {
319585629985SBram Moolenaar 	partial = argvars[1].vval.v_partial;
319685629985SBram Moolenaar 	func_name = partial_name(partial);
319785629985SBram Moolenaar     }
319885629985SBram Moolenaar     else
319985629985SBram Moolenaar 	func_name = tv_get_string(&argvars[1]);
32000d90e728SBram Moolenaar     if (func_name == NULL || *func_name == NUL)
32010d90e728SBram Moolenaar     {
32020d90e728SBram Moolenaar 	emsg(_(e_missing_function_argument));
32030d90e728SBram Moolenaar 	return;
32040d90e728SBram Moolenaar     }
320585629985SBram Moolenaar 
320685629985SBram Moolenaar     vim_memset(&funcexe, 0, sizeof(funcexe));
320785629985SBram Moolenaar     funcexe.evaluate = TRUE;
320885629985SBram Moolenaar     funcexe.partial = partial;
320985629985SBram Moolenaar 
321085629985SBram Moolenaar     if (argvars[0].v_type == VAR_LIST)
321185629985SBram Moolenaar     {
321285629985SBram Moolenaar 	list_T	    *l = argvars[0].vval.v_list;
321385629985SBram Moolenaar 	listitem_T  *li = NULL;
321448b1c218SBram Moolenaar 	int	    r;
3215ca275a05SBram Moolenaar 	int	    called_emsg_start = called_emsg;
321685629985SBram Moolenaar 
3217fda20c4cSBram Moolenaar 	if (l != NULL)
321885629985SBram Moolenaar 	    CHECK_LIST_MATERIALIZE(l);
321985629985SBram Moolenaar 	if (argvars[2].v_type == VAR_UNKNOWN)
322085629985SBram Moolenaar 	{
322185629985SBram Moolenaar 	    if (l == NULL || l->lv_first == NULL)
322285629985SBram Moolenaar 	    {
322385629985SBram Moolenaar 		semsg(_(e_reduceempty), "List");
322485629985SBram Moolenaar 		return;
322585629985SBram Moolenaar 	    }
322648b1c218SBram Moolenaar 	    initial = l->lv_first->li_tv;
322785629985SBram Moolenaar 	    li = l->lv_first->li_next;
322885629985SBram Moolenaar 	}
322985629985SBram Moolenaar 	else
323085629985SBram Moolenaar 	{
323148b1c218SBram Moolenaar 	    initial = argvars[2];
323285629985SBram Moolenaar 	    if (l != NULL)
323385629985SBram Moolenaar 		li = l->lv_first;
323485629985SBram Moolenaar 	}
3235fda20c4cSBram Moolenaar 	copy_tv(&initial, rettv);
3236fda20c4cSBram Moolenaar 
3237fda20c4cSBram Moolenaar 	if (l != NULL)
3238fda20c4cSBram Moolenaar 	{
3239fda20c4cSBram Moolenaar 	    int	    prev_locked = l->lv_lock;
324085629985SBram Moolenaar 
3241ca275a05SBram Moolenaar 	    l->lv_lock = VAR_FIXED;  // disallow the list changing here
324285629985SBram Moolenaar 	    for ( ; li != NULL; li = li->li_next)
324385629985SBram Moolenaar 	    {
324448b1c218SBram Moolenaar 		argv[0] = *rettv;
324585629985SBram Moolenaar 		argv[1] = li->li_tv;
324648b1c218SBram Moolenaar 		rettv->v_type = VAR_UNKNOWN;
324748b1c218SBram Moolenaar 		r = call_func(func_name, -1, rettv, 2, argv, &funcexe);
324848b1c218SBram Moolenaar 		clear_tv(&argv[0]);
3249ca275a05SBram Moolenaar 		if (r == FAIL || called_emsg != called_emsg_start)
3250ca275a05SBram Moolenaar 		    break;
325185629985SBram Moolenaar 	    }
3252ca275a05SBram Moolenaar 	    l->lv_lock = prev_locked;
325385629985SBram Moolenaar 	}
3254fda20c4cSBram Moolenaar     }
325585629985SBram Moolenaar     else
325685629985SBram Moolenaar     {
325785629985SBram Moolenaar 	blob_T	*b = argvars[0].vval.v_blob;
325885629985SBram Moolenaar 	int	i;
325985629985SBram Moolenaar 
326085629985SBram Moolenaar 	if (argvars[2].v_type == VAR_UNKNOWN)
326185629985SBram Moolenaar 	{
326285629985SBram Moolenaar 	    if (b == NULL || b->bv_ga.ga_len == 0)
326385629985SBram Moolenaar 	    {
326485629985SBram Moolenaar 		semsg(_(e_reduceempty), "Blob");
326585629985SBram Moolenaar 		return;
326685629985SBram Moolenaar 	    }
326748b1c218SBram Moolenaar 	    initial.v_type = VAR_NUMBER;
326848b1c218SBram Moolenaar 	    initial.vval.v_number = blob_get(b, 0);
326985629985SBram Moolenaar 	    i = 1;
327085629985SBram Moolenaar 	}
327148b1c218SBram Moolenaar 	else if (argvars[2].v_type != VAR_NUMBER)
327248b1c218SBram Moolenaar 	{
3273e29a27f6SBram Moolenaar 	    emsg(_(e_number_expected));
327448b1c218SBram Moolenaar 	    return;
327548b1c218SBram Moolenaar 	}
327685629985SBram Moolenaar 	else
327785629985SBram Moolenaar 	{
327848b1c218SBram Moolenaar 	    initial = argvars[2];
327985629985SBram Moolenaar 	    i = 0;
328085629985SBram Moolenaar 	}
328185629985SBram Moolenaar 
328248b1c218SBram Moolenaar 	copy_tv(&initial, rettv);
328385629985SBram Moolenaar 	if (b != NULL)
328485629985SBram Moolenaar 	{
328585629985SBram Moolenaar 	    for ( ; i < b->bv_ga.ga_len; i++)
328685629985SBram Moolenaar 	    {
328748b1c218SBram Moolenaar 		argv[0] = *rettv;
328885629985SBram Moolenaar 		argv[1].v_type = VAR_NUMBER;
328985629985SBram Moolenaar 		argv[1].vval.v_number = blob_get(b, i);
329085629985SBram Moolenaar 		if (call_func(func_name, -1, rettv, 2, argv, &funcexe) == FAIL)
329185629985SBram Moolenaar 		    return;
329285629985SBram Moolenaar 	    }
329385629985SBram Moolenaar 	}
329485629985SBram Moolenaar     }
329585629985SBram Moolenaar }
329685629985SBram Moolenaar 
32971e1d3004SBram Moolenaar #endif // defined(FEAT_EVAL)
3298