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