blob: 918626e1690f548e4334a37d78bb41fbc39e42a1 [file] [log] [blame]
Bram Moolenaaredf3f972016-08-29 22:49:24 +02001/* vi:set ts=8 sts=4 sw=4 noet:
Bram Moolenaarda861d62016-07-17 15:46:27 +02002 *
3 * VIM - Vi IMproved by Bram Moolenaar
4 *
5 * Do ":help uganda" in Vim to read copying and usage conditions.
6 * Do ":help credits" in Vim to see a list of people who contributed.
7 * See README.txt for an overview of the Vim source code.
8 */
9
10/*
Bram Moolenaar08c308a2019-09-04 17:48:15 +020011 * list.c: List support and container (List, Dict, Blob) functions.
Bram Moolenaarda861d62016-07-17 15:46:27 +020012 */
13
14#include "vim.h"
15
16#if defined(FEAT_EVAL) || defined(PROTO)
17
Bram Moolenaar08c308a2019-09-04 17:48:15 +020018static char *e_listblobarg = N_("E899: Argument of %s must be a List or Blob");
19
Bram Moolenaar4ba37b52019-12-04 21:57:43 +010020// List heads for garbage collection.
21static list_T *first_list = NULL; // list of all lists
Bram Moolenaarda861d62016-07-17 15:46:27 +020022
23/*
24 * Add a watcher to a list.
25 */
26 void
27list_add_watch(list_T *l, listwatch_T *lw)
28{
29 lw->lw_next = l->lv_watch;
30 l->lv_watch = lw;
31}
32
33/*
34 * Remove a watcher from a list.
35 * No warning when it isn't found...
36 */
37 void
38list_rem_watch(list_T *l, listwatch_T *lwrem)
39{
40 listwatch_T *lw, **lwp;
41
42 lwp = &l->lv_watch;
43 for (lw = l->lv_watch; lw != NULL; lw = lw->lw_next)
44 {
45 if (lw == lwrem)
46 {
47 *lwp = lw->lw_next;
48 break;
49 }
50 lwp = &lw->lw_next;
51 }
52}
53
54/*
55 * Just before removing an item from a list: advance watchers to the next
56 * item.
57 */
Bram Moolenaar5843f5f2019-08-20 20:13:45 +020058 static void
Bram Moolenaarda861d62016-07-17 15:46:27 +020059list_fix_watch(list_T *l, listitem_T *item)
60{
61 listwatch_T *lw;
62
63 for (lw = l->lv_watch; lw != NULL; lw = lw->lw_next)
64 if (lw->lw_item == item)
65 lw->lw_item = item->li_next;
66}
67
Bram Moolenaar8a7d6542020-01-26 15:56:19 +010068 static void
69list_init(list_T *l)
70{
71 // Prepend the list to the list of lists for garbage collection.
72 if (first_list != NULL)
73 first_list->lv_used_prev = l;
74 l->lv_used_prev = NULL;
75 l->lv_used_next = first_list;
76 first_list = l;
77}
78
Bram Moolenaarda861d62016-07-17 15:46:27 +020079/*
80 * Allocate an empty header for a list.
81 * Caller should take care of the reference count.
82 */
83 list_T *
84list_alloc(void)
85{
86 list_T *l;
87
Bram Moolenaarc799fe22019-05-28 23:08:19 +020088 l = ALLOC_CLEAR_ONE(list_T);
Bram Moolenaarda861d62016-07-17 15:46:27 +020089 if (l != NULL)
Bram Moolenaar8a7d6542020-01-26 15:56:19 +010090 list_init(l);
Bram Moolenaarda861d62016-07-17 15:46:27 +020091 return l;
92}
93
94/*
Bram Moolenaarf49cc602018-11-11 15:21:05 +010095 * list_alloc() with an ID for alloc_fail().
96 */
97 list_T *
98list_alloc_id(alloc_id_T id UNUSED)
99{
100#ifdef FEAT_EVAL
Bram Moolenaar51e14382019-05-25 20:21:28 +0200101 if (alloc_fail_id == id && alloc_does_fail(sizeof(list_T)))
Bram Moolenaarf49cc602018-11-11 15:21:05 +0100102 return NULL;
103#endif
104 return (list_alloc());
105}
106
107/*
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100108 * Allocate space for a list, plus "count" items.
109 * Next list_set_item() must be called for each item.
110 */
111 list_T *
112list_alloc_with_items(int count)
113{
114 list_T *l;
115
116 l = (list_T *)alloc_clear(sizeof(list_T) + count * sizeof(listitem_T));
117 if (l != NULL)
118 {
119 list_init(l);
120
121 if (count > 0)
122 {
123 listitem_T *li = (listitem_T *)(l + 1);
124 int i;
125
126 l->lv_len = count;
127 l->lv_with_items = count;
128 l->lv_first = li;
Bram Moolenaar0ff6aad2020-01-29 21:27:21 +0100129 l->lv_u.mat.lv_last = li + count - 1;
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100130 for (i = 0; i < count; ++i)
131 {
132 if (i == 0)
133 li->li_prev = NULL;
134 else
135 li->li_prev = li - 1;
136 if (i == count - 1)
137 li->li_next = NULL;
138 else
139 li->li_next = li + 1;
140 ++li;
141 }
142 }
143 }
144 return l;
145}
146
147/*
148 * Set item "idx" for a list previously allocated with list_alloc_with_items().
149 * The contents of "tv" is moved into the list item.
150 * Each item must be set exactly once.
151 */
152 void
153list_set_item(list_T *l, int idx, typval_T *tv)
154{
155 listitem_T *li = (listitem_T *)(l + 1) + idx;
156
157 li->li_tv = *tv;
158}
159
160/*
Bram Moolenaarda861d62016-07-17 15:46:27 +0200161 * Allocate an empty list for a return value, with reference count set.
162 * Returns OK or FAIL.
163 */
164 int
165rettv_list_alloc(typval_T *rettv)
166{
167 list_T *l = list_alloc();
168
169 if (l == NULL)
170 return FAIL;
171
Bram Moolenaarda861d62016-07-17 15:46:27 +0200172 rettv->v_lock = 0;
Bram Moolenaar45cf6e92017-04-30 20:25:19 +0200173 rettv_list_set(rettv, l);
Bram Moolenaarda861d62016-07-17 15:46:27 +0200174 return OK;
175}
176
177/*
Bram Moolenaar162b7142018-12-21 15:17:36 +0100178 * Same as rettv_list_alloc() but uses an allocation id for testing.
179 */
180 int
181rettv_list_alloc_id(typval_T *rettv, alloc_id_T id UNUSED)
182{
183#ifdef FEAT_EVAL
Bram Moolenaar51e14382019-05-25 20:21:28 +0200184 if (alloc_fail_id == id && alloc_does_fail(sizeof(list_T)))
Bram Moolenaar162b7142018-12-21 15:17:36 +0100185 return FAIL;
186#endif
187 return rettv_list_alloc(rettv);
188}
189
190
191/*
Bram Moolenaare809a4e2019-07-04 17:35:05 +0200192 * Set a list as the return value. Increments the reference count.
Bram Moolenaar45cf6e92017-04-30 20:25:19 +0200193 */
194 void
195rettv_list_set(typval_T *rettv, list_T *l)
196{
197 rettv->v_type = VAR_LIST;
198 rettv->vval.v_list = l;
199 if (l != NULL)
200 ++l->lv_refcount;
201}
202
203/*
Bram Moolenaarda861d62016-07-17 15:46:27 +0200204 * Unreference a list: decrement the reference count and free it when it
205 * becomes zero.
206 */
207 void
208list_unref(list_T *l)
209{
210 if (l != NULL && --l->lv_refcount <= 0)
211 list_free(l);
212}
213
214/*
215 * Free a list, including all non-container items it points to.
216 * Ignores the reference count.
217 */
218 static void
219list_free_contents(list_T *l)
220{
221 listitem_T *item;
222
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100223 if (l->lv_first != &range_list_item)
224 for (item = l->lv_first; item != NULL; item = l->lv_first)
225 {
226 // Remove the item before deleting it.
227 l->lv_first = item->li_next;
228 clear_tv(&item->li_tv);
229 list_free_item(l, item);
230 }
Bram Moolenaarda861d62016-07-17 15:46:27 +0200231}
232
233/*
234 * Go through the list of lists and free items without the copyID.
235 * But don't free a list that has a watcher (used in a for loop), these
236 * are not referenced anywhere.
237 */
238 int
239list_free_nonref(int copyID)
240{
241 list_T *ll;
242 int did_free = FALSE;
243
244 for (ll = first_list; ll != NULL; ll = ll->lv_used_next)
245 if ((ll->lv_copyID & COPYID_MASK) != (copyID & COPYID_MASK)
246 && ll->lv_watch == NULL)
247 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100248 // Free the List and ordinary items it contains, but don't recurse
249 // into Lists and Dictionaries, they will be in the list of dicts
250 // or list of lists.
Bram Moolenaarda861d62016-07-17 15:46:27 +0200251 list_free_contents(ll);
252 did_free = TRUE;
253 }
254 return did_free;
255}
256
257 static void
258list_free_list(list_T *l)
259{
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100260 // Remove the list from the list of lists for garbage collection.
Bram Moolenaarda861d62016-07-17 15:46:27 +0200261 if (l->lv_used_prev == NULL)
262 first_list = l->lv_used_next;
263 else
264 l->lv_used_prev->lv_used_next = l->lv_used_next;
265 if (l->lv_used_next != NULL)
266 l->lv_used_next->lv_used_prev = l->lv_used_prev;
267
268 vim_free(l);
269}
270
271 void
272list_free_items(int copyID)
273{
274 list_T *ll, *ll_next;
275
276 for (ll = first_list; ll != NULL; ll = ll_next)
277 {
278 ll_next = ll->lv_used_next;
279 if ((ll->lv_copyID & COPYID_MASK) != (copyID & COPYID_MASK)
280 && ll->lv_watch == NULL)
281 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100282 // Free the List and ordinary items it contains, but don't recurse
283 // into Lists and Dictionaries, they will be in the list of dicts
284 // or list of lists.
Bram Moolenaarda861d62016-07-17 15:46:27 +0200285 list_free_list(ll);
286 }
287 }
288}
289
290 void
291list_free(list_T *l)
292{
293 if (!in_free_unref_items)
294 {
295 list_free_contents(l);
296 list_free_list(l);
297 }
298}
299
300/*
301 * Allocate a list item.
302 * It is not initialized, don't forget to set v_lock.
303 */
304 listitem_T *
305listitem_alloc(void)
306{
Bram Moolenaarc799fe22019-05-28 23:08:19 +0200307 return ALLOC_ONE(listitem_T);
Bram Moolenaarda861d62016-07-17 15:46:27 +0200308}
309
310/*
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100311 * Free a list item, unless it was allocated together with the list itself.
312 * Does not clear the value. Does not notify watchers.
Bram Moolenaarda861d62016-07-17 15:46:27 +0200313 */
314 void
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100315list_free_item(list_T *l, listitem_T *item)
316{
317 if (l->lv_with_items == 0 || item < (listitem_T *)l
318 || item >= (listitem_T *)(l + 1) + l->lv_with_items)
319 vim_free(item);
320}
321
322/*
323 * Free a list item, unless it was allocated together with the list itself.
324 * Also clears the value. Does not notify watchers.
325 */
326 void
327listitem_free(list_T *l, listitem_T *item)
Bram Moolenaarda861d62016-07-17 15:46:27 +0200328{
329 clear_tv(&item->li_tv);
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100330 list_free_item(l, item);
Bram Moolenaarda861d62016-07-17 15:46:27 +0200331}
332
333/*
334 * Remove a list item from a List and free it. Also clears the value.
335 */
336 void
337listitem_remove(list_T *l, listitem_T *item)
338{
339 vimlist_remove(l, item, item);
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100340 listitem_free(l, item);
Bram Moolenaarda861d62016-07-17 15:46:27 +0200341}
342
343/*
344 * Get the number of items in a list.
345 */
346 long
347list_len(list_T *l)
348{
349 if (l == NULL)
350 return 0L;
351 return l->lv_len;
352}
353
354/*
355 * Return TRUE when two lists have exactly the same values.
356 */
357 int
358list_equal(
359 list_T *l1,
360 list_T *l2,
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100361 int ic, // ignore case for strings
362 int recursive) // TRUE when used recursively
Bram Moolenaarda861d62016-07-17 15:46:27 +0200363{
364 listitem_T *item1, *item2;
365
366 if (l1 == NULL || l2 == NULL)
367 return FALSE;
368 if (l1 == l2)
369 return TRUE;
370 if (list_len(l1) != list_len(l2))
371 return FALSE;
372
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100373 range_list_materialize(l1);
374 range_list_materialize(l2);
375
Bram Moolenaarda861d62016-07-17 15:46:27 +0200376 for (item1 = l1->lv_first, item2 = l2->lv_first;
377 item1 != NULL && item2 != NULL;
378 item1 = item1->li_next, item2 = item2->li_next)
379 if (!tv_equal(&item1->li_tv, &item2->li_tv, ic, recursive))
380 return FALSE;
381 return item1 == NULL && item2 == NULL;
382}
383
384/*
385 * Locate item with index "n" in list "l" and return it.
386 * A negative index is counted from the end; -1 is the last item.
387 * Returns NULL when "n" is out of range.
388 */
389 listitem_T *
390list_find(list_T *l, long n)
391{
392 listitem_T *item;
393 long idx;
394
395 if (l == NULL)
396 return NULL;
397
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100398 // Negative index is relative to the end.
Bram Moolenaarda861d62016-07-17 15:46:27 +0200399 if (n < 0)
400 n = l->lv_len + n;
401
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100402 // Check for index out of range.
Bram Moolenaarda861d62016-07-17 15:46:27 +0200403 if (n < 0 || n >= l->lv_len)
404 return NULL;
405
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100406 range_list_materialize(l);
407
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100408 // When there is a cached index may start search from there.
Bram Moolenaar0ff6aad2020-01-29 21:27:21 +0100409 if (l->lv_u.mat.lv_idx_item != NULL)
Bram Moolenaarda861d62016-07-17 15:46:27 +0200410 {
Bram Moolenaar0ff6aad2020-01-29 21:27:21 +0100411 if (n < l->lv_u.mat.lv_idx / 2)
Bram Moolenaarda861d62016-07-17 15:46:27 +0200412 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100413 // closest to the start of the list
Bram Moolenaarda861d62016-07-17 15:46:27 +0200414 item = l->lv_first;
415 idx = 0;
416 }
Bram Moolenaar0ff6aad2020-01-29 21:27:21 +0100417 else if (n > (l->lv_u.mat.lv_idx + l->lv_len) / 2)
Bram Moolenaarda861d62016-07-17 15:46:27 +0200418 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100419 // closest to the end of the list
Bram Moolenaar0ff6aad2020-01-29 21:27:21 +0100420 item = l->lv_u.mat.lv_last;
Bram Moolenaarda861d62016-07-17 15:46:27 +0200421 idx = l->lv_len - 1;
422 }
423 else
424 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100425 // closest to the cached index
Bram Moolenaar0ff6aad2020-01-29 21:27:21 +0100426 item = l->lv_u.mat.lv_idx_item;
427 idx = l->lv_u.mat.lv_idx;
Bram Moolenaarda861d62016-07-17 15:46:27 +0200428 }
429 }
430 else
431 {
432 if (n < l->lv_len / 2)
433 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100434 // closest to the start of the list
Bram Moolenaarda861d62016-07-17 15:46:27 +0200435 item = l->lv_first;
436 idx = 0;
437 }
438 else
439 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100440 // closest to the end of the list
Bram Moolenaar0ff6aad2020-01-29 21:27:21 +0100441 item = l->lv_u.mat.lv_last;
Bram Moolenaarda861d62016-07-17 15:46:27 +0200442 idx = l->lv_len - 1;
443 }
444 }
445
446 while (n > idx)
447 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100448 // search forward
Bram Moolenaarda861d62016-07-17 15:46:27 +0200449 item = item->li_next;
450 ++idx;
451 }
452 while (n < idx)
453 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100454 // search backward
Bram Moolenaarda861d62016-07-17 15:46:27 +0200455 item = item->li_prev;
456 --idx;
457 }
458
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100459 // cache the used index
Bram Moolenaar0ff6aad2020-01-29 21:27:21 +0100460 l->lv_u.mat.lv_idx = idx;
461 l->lv_u.mat.lv_idx_item = item;
Bram Moolenaarda861d62016-07-17 15:46:27 +0200462
463 return item;
464}
465
466/*
467 * Get list item "l[idx]" as a number.
468 */
469 long
470list_find_nr(
471 list_T *l,
472 long idx,
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100473 int *errorp) // set to TRUE when something wrong
Bram Moolenaarda861d62016-07-17 15:46:27 +0200474{
475 listitem_T *li;
476
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100477 if (l != NULL && l->lv_first == &range_list_item)
478 {
479 long n = idx;
480
481 // not materialized range() list: compute the value.
482 // Negative index is relative to the end.
483 if (n < 0)
484 n = l->lv_len + n;
485
486 // Check for index out of range.
487 if (n < 0 || n >= l->lv_len)
488 {
489 if (errorp != NULL)
490 *errorp = TRUE;
491 return -1L;
492 }
493
Bram Moolenaar0ff6aad2020-01-29 21:27:21 +0100494 return l->lv_u.nonmat.lv_start + n * l->lv_u.nonmat.lv_stride;
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100495 }
496
Bram Moolenaarda861d62016-07-17 15:46:27 +0200497 li = list_find(l, idx);
498 if (li == NULL)
499 {
500 if (errorp != NULL)
501 *errorp = TRUE;
502 return -1L;
503 }
Bram Moolenaard155d7a2018-12-21 16:04:21 +0100504 return (long)tv_get_number_chk(&li->li_tv, errorp);
Bram Moolenaarda861d62016-07-17 15:46:27 +0200505}
506
507/*
508 * Get list item "l[idx - 1]" as a string. Returns NULL for failure.
509 */
510 char_u *
511list_find_str(list_T *l, long idx)
512{
513 listitem_T *li;
514
515 li = list_find(l, idx - 1);
516 if (li == NULL)
517 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100518 semsg(_(e_listidx), idx);
Bram Moolenaarda861d62016-07-17 15:46:27 +0200519 return NULL;
520 }
Bram Moolenaard155d7a2018-12-21 16:04:21 +0100521 return tv_get_string(&li->li_tv);
Bram Moolenaarda861d62016-07-17 15:46:27 +0200522}
523
524/*
525 * Locate "item" list "l" and return its index.
526 * Returns -1 when "item" is not in the list.
527 */
528 long
529list_idx_of_item(list_T *l, listitem_T *item)
530{
531 long idx = 0;
532 listitem_T *li;
533
534 if (l == NULL)
535 return -1;
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100536 range_list_materialize(l);
Bram Moolenaarda861d62016-07-17 15:46:27 +0200537 idx = 0;
538 for (li = l->lv_first; li != NULL && li != item; li = li->li_next)
539 ++idx;
540 if (li == NULL)
541 return -1;
542 return idx;
543}
544
545/*
546 * Append item "item" to the end of list "l".
547 */
548 void
549list_append(list_T *l, listitem_T *item)
550{
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100551 range_list_materialize(l);
Bram Moolenaar0ff6aad2020-01-29 21:27:21 +0100552 if (l->lv_u.mat.lv_last == NULL)
Bram Moolenaarda861d62016-07-17 15:46:27 +0200553 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100554 // empty list
Bram Moolenaarda861d62016-07-17 15:46:27 +0200555 l->lv_first = item;
Bram Moolenaar0ff6aad2020-01-29 21:27:21 +0100556 l->lv_u.mat.lv_last = item;
Bram Moolenaarda861d62016-07-17 15:46:27 +0200557 item->li_prev = NULL;
558 }
559 else
560 {
Bram Moolenaar0ff6aad2020-01-29 21:27:21 +0100561 l->lv_u.mat.lv_last->li_next = item;
562 item->li_prev = l->lv_u.mat.lv_last;
563 l->lv_u.mat.lv_last = item;
Bram Moolenaarda861d62016-07-17 15:46:27 +0200564 }
565 ++l->lv_len;
566 item->li_next = NULL;
567}
568
569/*
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100570 * Append typval_T "tv" to the end of list "l". "tv" is copied.
Bram Moolenaarda861d62016-07-17 15:46:27 +0200571 * Return FAIL when out of memory.
572 */
573 int
574list_append_tv(list_T *l, typval_T *tv)
575{
576 listitem_T *li = listitem_alloc();
577
578 if (li == NULL)
579 return FAIL;
580 copy_tv(tv, &li->li_tv);
581 list_append(l, li);
582 return OK;
583}
584
585/*
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100586 * As list_append_tv() but move the value instead of copying it.
587 * Return FAIL when out of memory.
588 */
589 int
590list_append_tv_move(list_T *l, typval_T *tv)
591{
592 listitem_T *li = listitem_alloc();
593
594 if (li == NULL)
595 return FAIL;
596 li->li_tv = *tv;
597 list_append(l, li);
598 return OK;
599}
600
601/*
Bram Moolenaarda861d62016-07-17 15:46:27 +0200602 * Add a dictionary to a list. Used by getqflist().
603 * Return FAIL when out of memory.
604 */
605 int
606list_append_dict(list_T *list, dict_T *dict)
607{
608 listitem_T *li = listitem_alloc();
609
610 if (li == NULL)
611 return FAIL;
612 li->li_tv.v_type = VAR_DICT;
613 li->li_tv.v_lock = 0;
614 li->li_tv.vval.v_dict = dict;
615 list_append(list, li);
616 ++dict->dv_refcount;
617 return OK;
618}
619
620/*
Bram Moolenaar4f505882018-02-10 21:06:32 +0100621 * Append list2 to list1.
622 * Return FAIL when out of memory.
623 */
624 int
Bram Moolenaar6f8d2ac2018-07-25 19:49:45 +0200625list_append_list(list_T *list1, list_T *list2)
Bram Moolenaar4f505882018-02-10 21:06:32 +0100626{
627 listitem_T *li = listitem_alloc();
628
629 if (li == NULL)
630 return FAIL;
631 li->li_tv.v_type = VAR_LIST;
632 li->li_tv.v_lock = 0;
633 li->li_tv.vval.v_list = list2;
634 list_append(list1, li);
635 ++list2->lv_refcount;
636 return OK;
637}
638
639/*
Bram Moolenaarda861d62016-07-17 15:46:27 +0200640 * Make a copy of "str" and append it as an item to list "l".
641 * When "len" >= 0 use "str[len]".
642 * Returns FAIL when out of memory.
643 */
644 int
645list_append_string(list_T *l, char_u *str, int len)
646{
647 listitem_T *li = listitem_alloc();
648
649 if (li == NULL)
650 return FAIL;
651 list_append(l, li);
652 li->li_tv.v_type = VAR_STRING;
653 li->li_tv.v_lock = 0;
654 if (str == NULL)
655 li->li_tv.vval.v_string = NULL;
656 else if ((li->li_tv.vval.v_string = (len >= 0 ? vim_strnsave(str, len)
657 : vim_strsave(str))) == NULL)
658 return FAIL;
659 return OK;
660}
661
662/*
663 * Append "n" to list "l".
664 * Returns FAIL when out of memory.
665 */
666 int
667list_append_number(list_T *l, varnumber_T n)
668{
669 listitem_T *li;
670
671 li = listitem_alloc();
672 if (li == NULL)
673 return FAIL;
674 li->li_tv.v_type = VAR_NUMBER;
675 li->li_tv.v_lock = 0;
676 li->li_tv.vval.v_number = n;
677 list_append(l, li);
678 return OK;
679}
680
681/*
682 * Insert typval_T "tv" in list "l" before "item".
683 * If "item" is NULL append at the end.
684 * Return FAIL when out of memory.
685 */
686 int
687list_insert_tv(list_T *l, typval_T *tv, listitem_T *item)
688{
689 listitem_T *ni = listitem_alloc();
690
691 if (ni == NULL)
692 return FAIL;
693 copy_tv(tv, &ni->li_tv);
694 list_insert(l, ni, item);
695 return OK;
696}
697
698 void
699list_insert(list_T *l, listitem_T *ni, listitem_T *item)
700{
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100701 range_list_materialize(l);
Bram Moolenaarda861d62016-07-17 15:46:27 +0200702 if (item == NULL)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100703 // Append new item at end of list.
Bram Moolenaarda861d62016-07-17 15:46:27 +0200704 list_append(l, ni);
705 else
706 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100707 // Insert new item before existing item.
Bram Moolenaarda861d62016-07-17 15:46:27 +0200708 ni->li_prev = item->li_prev;
709 ni->li_next = item;
710 if (item->li_prev == NULL)
711 {
712 l->lv_first = ni;
Bram Moolenaar0ff6aad2020-01-29 21:27:21 +0100713 ++l->lv_u.mat.lv_idx;
Bram Moolenaarda861d62016-07-17 15:46:27 +0200714 }
715 else
716 {
717 item->li_prev->li_next = ni;
Bram Moolenaar0ff6aad2020-01-29 21:27:21 +0100718 l->lv_u.mat.lv_idx_item = NULL;
Bram Moolenaarda861d62016-07-17 15:46:27 +0200719 }
720 item->li_prev = ni;
721 ++l->lv_len;
722 }
723}
724
725/*
726 * Extend "l1" with "l2".
727 * If "bef" is NULL append at the end, otherwise insert before this item.
728 * Returns FAIL when out of memory.
729 */
730 int
731list_extend(list_T *l1, list_T *l2, listitem_T *bef)
732{
733 listitem_T *item;
734 int todo = l2->lv_len;
735
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100736 range_list_materialize(l1);
737 range_list_materialize(l2);
738
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100739 // We also quit the loop when we have inserted the original item count of
740 // the list, avoid a hang when we extend a list with itself.
Bram Moolenaarda861d62016-07-17 15:46:27 +0200741 for (item = l2->lv_first; item != NULL && --todo >= 0; item = item->li_next)
742 if (list_insert_tv(l1, &item->li_tv, bef) == FAIL)
743 return FAIL;
744 return OK;
745}
746
747/*
748 * Concatenate lists "l1" and "l2" into a new list, stored in "tv".
749 * Return FAIL when out of memory.
750 */
751 int
752list_concat(list_T *l1, list_T *l2, typval_T *tv)
753{
754 list_T *l;
755
756 if (l1 == NULL || l2 == NULL)
757 return FAIL;
758
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100759 // make a copy of the first list.
Bram Moolenaarda861d62016-07-17 15:46:27 +0200760 l = list_copy(l1, FALSE, 0);
761 if (l == NULL)
762 return FAIL;
763 tv->v_type = VAR_LIST;
764 tv->vval.v_list = l;
765
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100766 // append all items from the second list
Bram Moolenaarda861d62016-07-17 15:46:27 +0200767 return list_extend(l, l2, NULL);
768}
769
770/*
771 * Make a copy of list "orig". Shallow if "deep" is FALSE.
772 * The refcount of the new list is set to 1.
773 * See item_copy() for "copyID".
774 * Returns NULL when out of memory.
775 */
776 list_T *
777list_copy(list_T *orig, int deep, int copyID)
778{
779 list_T *copy;
780 listitem_T *item;
781 listitem_T *ni;
782
783 if (orig == NULL)
784 return NULL;
785
786 copy = list_alloc();
787 if (copy != NULL)
788 {
789 if (copyID != 0)
790 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100791 // Do this before adding the items, because one of the items may
792 // refer back to this list.
Bram Moolenaarda861d62016-07-17 15:46:27 +0200793 orig->lv_copyID = copyID;
794 orig->lv_copylist = copy;
795 }
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100796 range_list_materialize(orig);
Bram Moolenaarda861d62016-07-17 15:46:27 +0200797 for (item = orig->lv_first; item != NULL && !got_int;
798 item = item->li_next)
799 {
800 ni = listitem_alloc();
801 if (ni == NULL)
802 break;
803 if (deep)
804 {
805 if (item_copy(&item->li_tv, &ni->li_tv, deep, copyID) == FAIL)
806 {
807 vim_free(ni);
808 break;
809 }
810 }
811 else
812 copy_tv(&item->li_tv, &ni->li_tv);
813 list_append(copy, ni);
814 }
815 ++copy->lv_refcount;
816 if (item != NULL)
817 {
818 list_unref(copy);
819 copy = NULL;
820 }
821 }
822
823 return copy;
824}
825
826/*
827 * Remove items "item" to "item2" from list "l".
828 * Does not free the listitem or the value!
829 * This used to be called list_remove, but that conflicts with a Sun header
830 * file.
831 */
832 void
833vimlist_remove(list_T *l, listitem_T *item, listitem_T *item2)
834{
835 listitem_T *ip;
836
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100837 range_list_materialize(l);
838
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100839 // notify watchers
Bram Moolenaarda861d62016-07-17 15:46:27 +0200840 for (ip = item; ip != NULL; ip = ip->li_next)
841 {
842 --l->lv_len;
843 list_fix_watch(l, ip);
844 if (ip == item2)
845 break;
846 }
847
848 if (item2->li_next == NULL)
Bram Moolenaar0ff6aad2020-01-29 21:27:21 +0100849 l->lv_u.mat.lv_last = item->li_prev;
Bram Moolenaarda861d62016-07-17 15:46:27 +0200850 else
851 item2->li_next->li_prev = item->li_prev;
852 if (item->li_prev == NULL)
853 l->lv_first = item2->li_next;
854 else
855 item->li_prev->li_next = item2->li_next;
Bram Moolenaar0ff6aad2020-01-29 21:27:21 +0100856 l->lv_u.mat.lv_idx_item = NULL;
Bram Moolenaarda861d62016-07-17 15:46:27 +0200857}
858
859/*
860 * Return an allocated string with the string representation of a list.
861 * May return NULL.
862 */
863 char_u *
864list2string(typval_T *tv, int copyID, int restore_copyID)
865{
866 garray_T ga;
867
868 if (tv->vval.v_list == NULL)
869 return NULL;
870 ga_init2(&ga, (int)sizeof(char), 80);
871 ga_append(&ga, '[');
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100872 range_list_materialize(tv->vval.v_list);
Bram Moolenaarda861d62016-07-17 15:46:27 +0200873 if (list_join(&ga, tv->vval.v_list, (char_u *)", ",
874 FALSE, restore_copyID, copyID) == FAIL)
875 {
876 vim_free(ga.ga_data);
877 return NULL;
878 }
879 ga_append(&ga, ']');
880 ga_append(&ga, NUL);
881 return (char_u *)ga.ga_data;
882}
883
884typedef struct join_S {
885 char_u *s;
886 char_u *tofree;
887} join_T;
888
889 static int
890list_join_inner(
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100891 garray_T *gap, // to store the result in
Bram Moolenaarda861d62016-07-17 15:46:27 +0200892 list_T *l,
893 char_u *sep,
894 int echo_style,
895 int restore_copyID,
896 int copyID,
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100897 garray_T *join_gap) // to keep each list item string
Bram Moolenaarda861d62016-07-17 15:46:27 +0200898{
899 int i;
900 join_T *p;
901 int len;
902 int sumlen = 0;
903 int first = TRUE;
904 char_u *tofree;
905 char_u numbuf[NUMBUFLEN];
906 listitem_T *item;
907 char_u *s;
908
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100909 // Stringify each item in the list.
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100910 range_list_materialize(l);
Bram Moolenaarda861d62016-07-17 15:46:27 +0200911 for (item = l->lv_first; item != NULL && !got_int; item = item->li_next)
912 {
913 s = echo_string_core(&item->li_tv, &tofree, numbuf, copyID,
Bram Moolenaar35422f42017-08-05 16:33:56 +0200914 echo_style, restore_copyID, !echo_style);
Bram Moolenaarda861d62016-07-17 15:46:27 +0200915 if (s == NULL)
916 return FAIL;
917
918 len = (int)STRLEN(s);
919 sumlen += len;
920
921 (void)ga_grow(join_gap, 1);
922 p = ((join_T *)join_gap->ga_data) + (join_gap->ga_len++);
923 if (tofree != NULL || s != numbuf)
924 {
925 p->s = s;
926 p->tofree = tofree;
927 }
928 else
929 {
930 p->s = vim_strnsave(s, len);
931 p->tofree = p->s;
932 }
933
934 line_breakcheck();
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100935 if (did_echo_string_emsg) // recursion error, bail out
Bram Moolenaarda861d62016-07-17 15:46:27 +0200936 break;
937 }
938
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100939 // Allocate result buffer with its total size, avoid re-allocation and
940 // multiple copy operations. Add 2 for a tailing ']' and NUL.
Bram Moolenaarda861d62016-07-17 15:46:27 +0200941 if (join_gap->ga_len >= 2)
942 sumlen += (int)STRLEN(sep) * (join_gap->ga_len - 1);
943 if (ga_grow(gap, sumlen + 2) == FAIL)
944 return FAIL;
945
946 for (i = 0; i < join_gap->ga_len && !got_int; ++i)
947 {
948 if (first)
949 first = FALSE;
950 else
951 ga_concat(gap, sep);
952 p = ((join_T *)join_gap->ga_data) + i;
953
954 if (p->s != NULL)
955 ga_concat(gap, p->s);
956 line_breakcheck();
957 }
958
959 return OK;
960}
961
962/*
963 * Join list "l" into a string in "*gap", using separator "sep".
964 * When "echo_style" is TRUE use String as echoed, otherwise as inside a List.
965 * Return FAIL or OK.
966 */
967 int
968list_join(
969 garray_T *gap,
970 list_T *l,
971 char_u *sep,
972 int echo_style,
973 int restore_copyID,
974 int copyID)
975{
976 garray_T join_ga;
977 int retval;
978 join_T *p;
979 int i;
980
981 if (l->lv_len < 1)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100982 return OK; // nothing to do
Bram Moolenaarda861d62016-07-17 15:46:27 +0200983 ga_init2(&join_ga, (int)sizeof(join_T), l->lv_len);
984 retval = list_join_inner(gap, l, sep, echo_style, restore_copyID,
985 copyID, &join_ga);
986
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100987 // Dispose each item in join_ga.
Bram Moolenaarda861d62016-07-17 15:46:27 +0200988 if (join_ga.ga_data != NULL)
989 {
990 p = (join_T *)join_ga.ga_data;
991 for (i = 0; i < join_ga.ga_len; ++i)
992 {
993 vim_free(p->tofree);
994 ++p;
995 }
996 ga_clear(&join_ga);
997 }
998
999 return retval;
1000}
1001
1002/*
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001003 * "join()" function
1004 */
1005 void
1006f_join(typval_T *argvars, typval_T *rettv)
1007{
1008 garray_T ga;
1009 char_u *sep;
1010
1011 if (argvars[0].v_type != VAR_LIST)
1012 {
1013 emsg(_(e_listreq));
1014 return;
1015 }
1016 if (argvars[0].vval.v_list == NULL)
1017 return;
1018 if (argvars[1].v_type == VAR_UNKNOWN)
1019 sep = (char_u *)" ";
1020 else
1021 sep = tv_get_string_chk(&argvars[1]);
1022
1023 rettv->v_type = VAR_STRING;
1024
1025 if (sep != NULL)
1026 {
1027 ga_init2(&ga, (int)sizeof(char), 80);
1028 list_join(&ga, argvars[0].vval.v_list, sep, TRUE, FALSE, 0);
1029 ga_append(&ga, NUL);
1030 rettv->vval.v_string = (char_u *)ga.ga_data;
1031 }
1032 else
1033 rettv->vval.v_string = NULL;
1034}
1035
1036/*
Bram Moolenaarda861d62016-07-17 15:46:27 +02001037 * Allocate a variable for a List and fill it from "*arg".
1038 * Return OK or FAIL.
1039 */
1040 int
Bram Moolenaar8a7d6542020-01-26 15:56:19 +01001041get_list_tv(char_u **arg, typval_T *rettv, int evaluate, int do_error)
Bram Moolenaarda861d62016-07-17 15:46:27 +02001042{
1043 list_T *l = NULL;
1044 typval_T tv;
1045 listitem_T *item;
1046
1047 if (evaluate)
1048 {
1049 l = list_alloc();
1050 if (l == NULL)
1051 return FAIL;
1052 }
1053
1054 *arg = skipwhite(*arg + 1);
1055 while (**arg != ']' && **arg != NUL)
1056 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001057 if (eval1(arg, &tv, evaluate) == FAIL) // recursive!
Bram Moolenaarda861d62016-07-17 15:46:27 +02001058 goto failret;
1059 if (evaluate)
1060 {
1061 item = listitem_alloc();
1062 if (item != NULL)
1063 {
1064 item->li_tv = tv;
1065 item->li_tv.v_lock = 0;
1066 list_append(l, item);
1067 }
1068 else
1069 clear_tv(&tv);
1070 }
1071
1072 if (**arg == ']')
1073 break;
1074 if (**arg != ',')
1075 {
Bram Moolenaar8a7d6542020-01-26 15:56:19 +01001076 if (do_error)
1077 semsg(_("E696: Missing comma in List: %s"), *arg);
Bram Moolenaarda861d62016-07-17 15:46:27 +02001078 goto failret;
1079 }
1080 *arg = skipwhite(*arg + 1);
1081 }
1082
1083 if (**arg != ']')
1084 {
Bram Moolenaar8a7d6542020-01-26 15:56:19 +01001085 if (do_error)
Bram Moolenaaree619e52020-03-28 21:38:06 +01001086 semsg(_(e_list_end), *arg);
Bram Moolenaarda861d62016-07-17 15:46:27 +02001087failret:
1088 if (evaluate)
1089 list_free(l);
1090 return FAIL;
1091 }
1092
1093 *arg = skipwhite(*arg + 1);
1094 if (evaluate)
Bram Moolenaar45cf6e92017-04-30 20:25:19 +02001095 rettv_list_set(rettv, l);
Bram Moolenaarda861d62016-07-17 15:46:27 +02001096
1097 return OK;
1098}
1099
Bram Moolenaar73dad1e2016-07-17 22:13:49 +02001100/*
Bram Moolenaarcaa55b62017-01-10 13:51:09 +01001101 * Write "list" of strings to file "fd".
Bram Moolenaar73dad1e2016-07-17 22:13:49 +02001102 */
1103 int
1104write_list(FILE *fd, list_T *list, int binary)
1105{
1106 listitem_T *li;
1107 int c;
1108 int ret = OK;
1109 char_u *s;
1110
Bram Moolenaar8a7d6542020-01-26 15:56:19 +01001111 range_list_materialize(list);
Bram Moolenaar73dad1e2016-07-17 22:13:49 +02001112 for (li = list->lv_first; li != NULL; li = li->li_next)
1113 {
Bram Moolenaard155d7a2018-12-21 16:04:21 +01001114 for (s = tv_get_string(&li->li_tv); *s != NUL; ++s)
Bram Moolenaar73dad1e2016-07-17 22:13:49 +02001115 {
1116 if (*s == '\n')
1117 c = putc(NUL, fd);
1118 else
1119 c = putc(*s, fd);
1120 if (c == EOF)
1121 {
1122 ret = FAIL;
1123 break;
1124 }
1125 }
1126 if (!binary || li->li_next != NULL)
1127 if (putc('\n', fd) == EOF)
1128 {
1129 ret = FAIL;
1130 break;
1131 }
1132 if (ret == FAIL)
1133 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001134 emsg(_(e_write));
Bram Moolenaar73dad1e2016-07-17 22:13:49 +02001135 break;
1136 }
1137 }
1138 return ret;
1139}
1140
Bram Moolenaardf48fb42016-07-22 21:50:18 +02001141/*
1142 * Initialize a static list with 10 items.
1143 */
1144 void
1145init_static_list(staticList10_T *sl)
1146{
1147 list_T *l = &sl->sl_list;
1148 int i;
1149
1150 memset(sl, 0, sizeof(staticList10_T));
1151 l->lv_first = &sl->sl_items[0];
Bram Moolenaar0ff6aad2020-01-29 21:27:21 +01001152 l->lv_u.mat.lv_last = &sl->sl_items[9];
Bram Moolenaardf48fb42016-07-22 21:50:18 +02001153 l->lv_refcount = DO_NOT_FREE_CNT;
1154 l->lv_lock = VAR_FIXED;
1155 sl->sl_list.lv_len = 10;
1156
1157 for (i = 0; i < 10; ++i)
1158 {
1159 listitem_T *li = &sl->sl_items[i];
1160
1161 if (i == 0)
1162 li->li_prev = NULL;
1163 else
1164 li->li_prev = li - 1;
1165 if (i == 9)
1166 li->li_next = NULL;
1167 else
1168 li->li_next = li + 1;
1169 }
1170}
1171
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001172/*
1173 * "list2str()" function
1174 */
1175 void
1176f_list2str(typval_T *argvars, typval_T *rettv)
1177{
1178 list_T *l;
1179 listitem_T *li;
1180 garray_T ga;
1181 int utf8 = FALSE;
1182
1183 rettv->v_type = VAR_STRING;
1184 rettv->vval.v_string = NULL;
1185 if (argvars[0].v_type != VAR_LIST)
1186 {
1187 emsg(_(e_invarg));
1188 return;
1189 }
1190
1191 l = argvars[0].vval.v_list;
1192 if (l == NULL)
1193 return; // empty list results in empty string
1194
1195 if (argvars[1].v_type != VAR_UNKNOWN)
1196 utf8 = (int)tv_get_number_chk(&argvars[1], NULL);
1197
Bram Moolenaar8a7d6542020-01-26 15:56:19 +01001198 range_list_materialize(l);
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001199 ga_init2(&ga, 1, 80);
1200 if (has_mbyte || utf8)
1201 {
1202 char_u buf[MB_MAXBYTES + 1];
1203 int (*char2bytes)(int, char_u *);
1204
1205 if (utf8 || enc_utf8)
1206 char2bytes = utf_char2bytes;
1207 else
1208 char2bytes = mb_char2bytes;
1209
1210 for (li = l->lv_first; li != NULL; li = li->li_next)
1211 {
1212 buf[(*char2bytes)(tv_get_number(&li->li_tv), buf)] = NUL;
1213 ga_concat(&ga, buf);
1214 }
1215 ga_append(&ga, NUL);
1216 }
1217 else if (ga_grow(&ga, list_len(l) + 1) == OK)
1218 {
1219 for (li = l->lv_first; li != NULL; li = li->li_next)
1220 ga_append(&ga, tv_get_number(&li->li_tv));
1221 ga_append(&ga, NUL);
1222 }
1223
1224 rettv->v_type = VAR_STRING;
1225 rettv->vval.v_string = ga.ga_data;
1226}
1227
1228 void
1229list_remove(typval_T *argvars, typval_T *rettv, char_u *arg_errmsg)
1230{
1231 list_T *l;
1232 listitem_T *item, *item2;
1233 listitem_T *li;
1234 int error = FALSE;
1235 int idx;
1236
1237 if ((l = argvars[0].vval.v_list) == NULL
1238 || var_check_lock(l->lv_lock, arg_errmsg, TRUE))
1239 return;
1240
1241 idx = (long)tv_get_number_chk(&argvars[1], &error);
1242 if (error)
1243 ; // type error: do nothing, errmsg already given
1244 else if ((item = list_find(l, idx)) == NULL)
1245 semsg(_(e_listidx), idx);
1246 else
1247 {
1248 if (argvars[2].v_type == VAR_UNKNOWN)
1249 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001250 // Remove one item, return its value.
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001251 vimlist_remove(l, item, item);
1252 *rettv = item->li_tv;
Bram Moolenaar8a7d6542020-01-26 15:56:19 +01001253 list_free_item(l, item);
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001254 }
1255 else
1256 {
1257 // Remove range of items, return list with values.
1258 int end = (long)tv_get_number_chk(&argvars[2], &error);
1259
1260 if (error)
1261 ; // type error: do nothing
1262 else if ((item2 = list_find(l, end)) == NULL)
1263 semsg(_(e_listidx), end);
1264 else
1265 {
1266 int cnt = 0;
1267
1268 for (li = item; li != NULL; li = li->li_next)
1269 {
1270 ++cnt;
1271 if (li == item2)
1272 break;
1273 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001274 if (li == NULL) // didn't find "item2" after "item"
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001275 emsg(_(e_invrange));
1276 else
1277 {
1278 vimlist_remove(l, item, item2);
1279 if (rettv_list_alloc(rettv) == OK)
1280 {
1281 l = rettv->vval.v_list;
1282 l->lv_first = item;
Bram Moolenaar0ff6aad2020-01-29 21:27:21 +01001283 l->lv_u.mat.lv_last = item2;
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001284 item->li_prev = NULL;
1285 item2->li_next = NULL;
1286 l->lv_len = cnt;
1287 }
1288 }
1289 }
1290 }
1291 }
1292}
1293
1294static int item_compare(const void *s1, const void *s2);
1295static int item_compare2(const void *s1, const void *s2);
1296
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001297// struct used in the array that's given to qsort()
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001298typedef struct
1299{
1300 listitem_T *item;
1301 int idx;
1302} sortItem_T;
1303
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001304// struct storing information about current sort
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001305typedef struct
1306{
1307 int item_compare_ic;
1308 int item_compare_numeric;
1309 int item_compare_numbers;
1310#ifdef FEAT_FLOAT
1311 int item_compare_float;
1312#endif
1313 char_u *item_compare_func;
1314 partial_T *item_compare_partial;
1315 dict_T *item_compare_selfdict;
1316 int item_compare_func_err;
1317 int item_compare_keep_zero;
1318} sortinfo_T;
1319static sortinfo_T *sortinfo = NULL;
1320#define ITEM_COMPARE_FAIL 999
1321
1322/*
1323 * Compare functions for f_sort() and f_uniq() below.
1324 */
1325 static int
1326item_compare(const void *s1, const void *s2)
1327{
1328 sortItem_T *si1, *si2;
1329 typval_T *tv1, *tv2;
1330 char_u *p1, *p2;
1331 char_u *tofree1 = NULL, *tofree2 = NULL;
1332 int res;
1333 char_u numbuf1[NUMBUFLEN];
1334 char_u numbuf2[NUMBUFLEN];
1335
1336 si1 = (sortItem_T *)s1;
1337 si2 = (sortItem_T *)s2;
1338 tv1 = &si1->item->li_tv;
1339 tv2 = &si2->item->li_tv;
1340
1341 if (sortinfo->item_compare_numbers)
1342 {
1343 varnumber_T v1 = tv_get_number(tv1);
1344 varnumber_T v2 = tv_get_number(tv2);
1345
1346 return v1 == v2 ? 0 : v1 > v2 ? 1 : -1;
1347 }
1348
1349#ifdef FEAT_FLOAT
1350 if (sortinfo->item_compare_float)
1351 {
1352 float_T v1 = tv_get_float(tv1);
1353 float_T v2 = tv_get_float(tv2);
1354
1355 return v1 == v2 ? 0 : v1 > v2 ? 1 : -1;
1356 }
1357#endif
1358
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001359 // tv2string() puts quotes around a string and allocates memory. Don't do
1360 // that for string variables. Use a single quote when comparing with a
1361 // non-string to do what the docs promise.
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001362 if (tv1->v_type == VAR_STRING)
1363 {
1364 if (tv2->v_type != VAR_STRING || sortinfo->item_compare_numeric)
1365 p1 = (char_u *)"'";
1366 else
1367 p1 = tv1->vval.v_string;
1368 }
1369 else
1370 p1 = tv2string(tv1, &tofree1, numbuf1, 0);
1371 if (tv2->v_type == VAR_STRING)
1372 {
1373 if (tv1->v_type != VAR_STRING || sortinfo->item_compare_numeric)
1374 p2 = (char_u *)"'";
1375 else
1376 p2 = tv2->vval.v_string;
1377 }
1378 else
1379 p2 = tv2string(tv2, &tofree2, numbuf2, 0);
1380 if (p1 == NULL)
1381 p1 = (char_u *)"";
1382 if (p2 == NULL)
1383 p2 = (char_u *)"";
1384 if (!sortinfo->item_compare_numeric)
1385 {
1386 if (sortinfo->item_compare_ic)
1387 res = STRICMP(p1, p2);
1388 else
1389 res = STRCMP(p1, p2);
1390 }
1391 else
1392 {
1393 double n1, n2;
1394 n1 = strtod((char *)p1, (char **)&p1);
1395 n2 = strtod((char *)p2, (char **)&p2);
1396 res = n1 == n2 ? 0 : n1 > n2 ? 1 : -1;
1397 }
1398
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001399 // When the result would be zero, compare the item indexes. Makes the
1400 // sort stable.
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001401 if (res == 0 && !sortinfo->item_compare_keep_zero)
1402 res = si1->idx > si2->idx ? 1 : -1;
1403
1404 vim_free(tofree1);
1405 vim_free(tofree2);
1406 return res;
1407}
1408
1409 static int
1410item_compare2(const void *s1, const void *s2)
1411{
1412 sortItem_T *si1, *si2;
1413 int res;
1414 typval_T rettv;
1415 typval_T argv[3];
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001416 char_u *func_name;
1417 partial_T *partial = sortinfo->item_compare_partial;
Bram Moolenaarc6538bc2019-08-03 18:17:11 +02001418 funcexe_T funcexe;
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001419
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001420 // shortcut after failure in previous call; compare all items equal
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001421 if (sortinfo->item_compare_func_err)
1422 return 0;
1423
1424 si1 = (sortItem_T *)s1;
1425 si2 = (sortItem_T *)s2;
1426
1427 if (partial == NULL)
1428 func_name = sortinfo->item_compare_func;
1429 else
1430 func_name = partial_name(partial);
1431
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001432 // Copy the values. This is needed to be able to set v_lock to VAR_FIXED
1433 // in the copy without changing the original list items.
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001434 copy_tv(&si1->item->li_tv, &argv[0]);
1435 copy_tv(&si2->item->li_tv, &argv[1]);
1436
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001437 rettv.v_type = VAR_UNKNOWN; // clear_tv() uses this
Bram Moolenaarc6538bc2019-08-03 18:17:11 +02001438 vim_memset(&funcexe, 0, sizeof(funcexe));
1439 funcexe.evaluate = TRUE;
1440 funcexe.partial = partial;
1441 funcexe.selfdict = sortinfo->item_compare_selfdict;
1442 res = call_func(func_name, -1, &rettv, 2, argv, &funcexe);
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001443 clear_tv(&argv[0]);
1444 clear_tv(&argv[1]);
1445
1446 if (res == FAIL)
1447 res = ITEM_COMPARE_FAIL;
1448 else
1449 res = (int)tv_get_number_chk(&rettv, &sortinfo->item_compare_func_err);
1450 if (sortinfo->item_compare_func_err)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001451 res = ITEM_COMPARE_FAIL; // return value has wrong type
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001452 clear_tv(&rettv);
1453
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001454 // When the result would be zero, compare the pointers themselves. Makes
1455 // the sort stable.
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001456 if (res == 0 && !sortinfo->item_compare_keep_zero)
1457 res = si1->idx > si2->idx ? 1 : -1;
1458
1459 return res;
1460}
1461
1462/*
1463 * "sort()" or "uniq()" function
1464 */
1465 static void
1466do_sort_uniq(typval_T *argvars, typval_T *rettv, int sort)
1467{
1468 list_T *l;
1469 listitem_T *li;
1470 sortItem_T *ptrs;
1471 sortinfo_T *old_sortinfo;
1472 sortinfo_T info;
1473 long len;
1474 long i;
1475
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001476 // Pointer to current info struct used in compare function. Save and
1477 // restore the current one for nested calls.
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001478 old_sortinfo = sortinfo;
1479 sortinfo = &info;
1480
1481 if (argvars[0].v_type != VAR_LIST)
1482 semsg(_(e_listarg), sort ? "sort()" : "uniq()");
1483 else
1484 {
1485 l = argvars[0].vval.v_list;
1486 if (l == NULL || var_check_lock(l->lv_lock,
1487 (char_u *)(sort ? N_("sort() argument") : N_("uniq() argument")),
1488 TRUE))
1489 goto theend;
1490 rettv_list_set(rettv, l);
Bram Moolenaar8a7d6542020-01-26 15:56:19 +01001491 range_list_materialize(l);
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001492
1493 len = list_len(l);
1494 if (len <= 1)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001495 goto theend; // short list sorts pretty quickly
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001496
1497 info.item_compare_ic = FALSE;
1498 info.item_compare_numeric = FALSE;
1499 info.item_compare_numbers = FALSE;
1500#ifdef FEAT_FLOAT
1501 info.item_compare_float = FALSE;
1502#endif
1503 info.item_compare_func = NULL;
1504 info.item_compare_partial = NULL;
1505 info.item_compare_selfdict = NULL;
1506 if (argvars[1].v_type != VAR_UNKNOWN)
1507 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001508 // optional second argument: {func}
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001509 if (argvars[1].v_type == VAR_FUNC)
1510 info.item_compare_func = argvars[1].vval.v_string;
1511 else if (argvars[1].v_type == VAR_PARTIAL)
1512 info.item_compare_partial = argvars[1].vval.v_partial;
1513 else
1514 {
1515 int error = FALSE;
1516
1517 i = (long)tv_get_number_chk(&argvars[1], &error);
1518 if (error)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001519 goto theend; // type error; errmsg already given
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001520 if (i == 1)
1521 info.item_compare_ic = TRUE;
1522 else if (argvars[1].v_type != VAR_NUMBER)
1523 info.item_compare_func = tv_get_string(&argvars[1]);
1524 else if (i != 0)
1525 {
1526 emsg(_(e_invarg));
1527 goto theend;
1528 }
1529 if (info.item_compare_func != NULL)
1530 {
1531 if (*info.item_compare_func == NUL)
1532 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001533 // empty string means default sort
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001534 info.item_compare_func = NULL;
1535 }
1536 else if (STRCMP(info.item_compare_func, "n") == 0)
1537 {
1538 info.item_compare_func = NULL;
1539 info.item_compare_numeric = TRUE;
1540 }
1541 else if (STRCMP(info.item_compare_func, "N") == 0)
1542 {
1543 info.item_compare_func = NULL;
1544 info.item_compare_numbers = TRUE;
1545 }
1546#ifdef FEAT_FLOAT
1547 else if (STRCMP(info.item_compare_func, "f") == 0)
1548 {
1549 info.item_compare_func = NULL;
1550 info.item_compare_float = TRUE;
1551 }
1552#endif
1553 else if (STRCMP(info.item_compare_func, "i") == 0)
1554 {
1555 info.item_compare_func = NULL;
1556 info.item_compare_ic = TRUE;
1557 }
1558 }
1559 }
1560
1561 if (argvars[2].v_type != VAR_UNKNOWN)
1562 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001563 // optional third argument: {dict}
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001564 if (argvars[2].v_type != VAR_DICT)
1565 {
1566 emsg(_(e_dictreq));
1567 goto theend;
1568 }
1569 info.item_compare_selfdict = argvars[2].vval.v_dict;
1570 }
1571 }
1572
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001573 // Make an array with each entry pointing to an item in the List.
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001574 ptrs = ALLOC_MULT(sortItem_T, len);
1575 if (ptrs == NULL)
1576 goto theend;
1577
1578 i = 0;
1579 if (sort)
1580 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001581 // sort(): ptrs will be the list to sort
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001582 for (li = l->lv_first; li != NULL; li = li->li_next)
1583 {
1584 ptrs[i].item = li;
1585 ptrs[i].idx = i;
1586 ++i;
1587 }
1588
1589 info.item_compare_func_err = FALSE;
1590 info.item_compare_keep_zero = FALSE;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001591 // test the compare function
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001592 if ((info.item_compare_func != NULL
1593 || info.item_compare_partial != NULL)
1594 && item_compare2((void *)&ptrs[0], (void *)&ptrs[1])
1595 == ITEM_COMPARE_FAIL)
1596 emsg(_("E702: Sort compare function failed"));
1597 else
1598 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001599 // Sort the array with item pointers.
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001600 qsort((void *)ptrs, (size_t)len, sizeof(sortItem_T),
1601 info.item_compare_func == NULL
1602 && info.item_compare_partial == NULL
1603 ? item_compare : item_compare2);
1604
1605 if (!info.item_compare_func_err)
1606 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001607 // Clear the List and append the items in sorted order.
Bram Moolenaar0ff6aad2020-01-29 21:27:21 +01001608 l->lv_first = l->lv_u.mat.lv_last
1609 = l->lv_u.mat.lv_idx_item = NULL;
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001610 l->lv_len = 0;
1611 for (i = 0; i < len; ++i)
1612 list_append(l, ptrs[i].item);
1613 }
1614 }
1615 }
1616 else
1617 {
1618 int (*item_compare_func_ptr)(const void *, const void *);
1619
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001620 // f_uniq(): ptrs will be a stack of items to remove
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001621 info.item_compare_func_err = FALSE;
1622 info.item_compare_keep_zero = TRUE;
1623 item_compare_func_ptr = info.item_compare_func != NULL
1624 || info.item_compare_partial != NULL
1625 ? item_compare2 : item_compare;
1626
1627 for (li = l->lv_first; li != NULL && li->li_next != NULL;
1628 li = li->li_next)
1629 {
1630 if (item_compare_func_ptr((void *)&li, (void *)&li->li_next)
1631 == 0)
1632 ptrs[i++].item = li;
1633 if (info.item_compare_func_err)
1634 {
1635 emsg(_("E882: Uniq compare function failed"));
1636 break;
1637 }
1638 }
1639
1640 if (!info.item_compare_func_err)
1641 {
1642 while (--i >= 0)
1643 {
1644 li = ptrs[i].item->li_next;
1645 ptrs[i].item->li_next = li->li_next;
1646 if (li->li_next != NULL)
1647 li->li_next->li_prev = ptrs[i].item;
1648 else
Bram Moolenaar0ff6aad2020-01-29 21:27:21 +01001649 l->lv_u.mat.lv_last = ptrs[i].item;
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001650 list_fix_watch(l, li);
Bram Moolenaar8a7d6542020-01-26 15:56:19 +01001651 listitem_free(l, li);
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001652 l->lv_len--;
1653 }
1654 }
1655 }
1656
1657 vim_free(ptrs);
1658 }
1659theend:
1660 sortinfo = old_sortinfo;
1661}
1662
1663/*
1664 * "sort({list})" function
1665 */
1666 void
1667f_sort(typval_T *argvars, typval_T *rettv)
1668{
1669 do_sort_uniq(argvars, rettv, TRUE);
1670}
1671
1672/*
1673 * "uniq({list})" function
1674 */
1675 void
1676f_uniq(typval_T *argvars, typval_T *rettv)
1677{
1678 do_sort_uniq(argvars, rettv, FALSE);
1679}
1680
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02001681/*
1682 * Handle one item for map() and filter().
1683 */
1684 static int
1685filter_map_one(typval_T *tv, typval_T *expr, int map, int *remp)
1686{
1687 typval_T rettv;
1688 typval_T argv[3];
1689 int retval = FAIL;
1690
1691 copy_tv(tv, get_vim_var_tv(VV_VAL));
1692 argv[0] = *get_vim_var_tv(VV_KEY);
1693 argv[1] = *get_vim_var_tv(VV_VAL);
1694 if (eval_expr_typval(expr, argv, 2, &rettv) == FAIL)
1695 goto theend;
1696 if (map)
1697 {
1698 // map(): replace the list item value
1699 clear_tv(tv);
1700 rettv.v_lock = 0;
1701 *tv = rettv;
1702 }
1703 else
1704 {
1705 int error = FALSE;
1706
1707 // filter(): when expr is zero remove the item
1708 *remp = (tv_get_number_chk(&rettv, &error) == 0);
1709 clear_tv(&rettv);
1710 // On type error, nothing has been removed; return FAIL to stop the
1711 // loop. The error message was given by tv_get_number_chk().
1712 if (error)
1713 goto theend;
1714 }
1715 retval = OK;
1716theend:
1717 clear_tv(get_vim_var_tv(VV_VAL));
1718 return retval;
1719}
1720
1721/*
1722 * Implementation of map() and filter().
1723 */
1724 static void
1725filter_map(typval_T *argvars, typval_T *rettv, int map)
1726{
1727 typval_T *expr;
1728 listitem_T *li, *nli;
1729 list_T *l = NULL;
1730 dictitem_T *di;
1731 hashtab_T *ht;
1732 hashitem_T *hi;
1733 dict_T *d = NULL;
1734 blob_T *b = NULL;
1735 int rem;
1736 int todo;
1737 char_u *ermsg = (char_u *)(map ? "map()" : "filter()");
1738 char_u *arg_errmsg = (char_u *)(map ? N_("map() argument")
1739 : N_("filter() argument"));
1740 int save_did_emsg;
1741 int idx = 0;
1742
1743 if (argvars[0].v_type == VAR_BLOB)
1744 {
1745 if ((b = argvars[0].vval.v_blob) == NULL)
1746 return;
1747 }
1748 else if (argvars[0].v_type == VAR_LIST)
1749 {
1750 if ((l = argvars[0].vval.v_list) == NULL
1751 || (!map && var_check_lock(l->lv_lock, arg_errmsg, TRUE)))
1752 return;
1753 }
1754 else if (argvars[0].v_type == VAR_DICT)
1755 {
1756 if ((d = argvars[0].vval.v_dict) == NULL
1757 || (!map && var_check_lock(d->dv_lock, arg_errmsg, TRUE)))
1758 return;
1759 }
1760 else
1761 {
1762 semsg(_(e_listdictarg), ermsg);
1763 return;
1764 }
1765
1766 expr = &argvars[1];
1767 // On type errors, the preceding call has already displayed an error
1768 // message. Avoid a misleading error message for an empty string that
1769 // was not passed as argument.
1770 if (expr->v_type != VAR_UNKNOWN)
1771 {
1772 typval_T save_val;
1773 typval_T save_key;
1774
1775 prepare_vimvar(VV_VAL, &save_val);
1776 prepare_vimvar(VV_KEY, &save_key);
1777
1778 // We reset "did_emsg" to be able to detect whether an error
1779 // occurred during evaluation of the expression.
1780 save_did_emsg = did_emsg;
1781 did_emsg = FALSE;
1782
1783 if (argvars[0].v_type == VAR_DICT)
1784 {
Bram Moolenaardb661fb2020-01-29 22:17:16 +01001785 int prev_lock = d->dv_lock;
1786
1787 if (map && d->dv_lock == 0)
1788 d->dv_lock = VAR_LOCKED;
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02001789 ht = &d->dv_hashtab;
1790 hash_lock(ht);
1791 todo = (int)ht->ht_used;
1792 for (hi = ht->ht_array; todo > 0; ++hi)
1793 {
1794 if (!HASHITEM_EMPTY(hi))
1795 {
1796 int r;
1797
1798 --todo;
1799 di = HI2DI(hi);
1800 if (map && (var_check_lock(di->di_tv.v_lock,
1801 arg_errmsg, TRUE)
1802 || var_check_ro(di->di_flags,
1803 arg_errmsg, TRUE)))
1804 break;
1805 set_vim_var_string(VV_KEY, di->di_key, -1);
1806 r = filter_map_one(&di->di_tv, expr, map, &rem);
1807 clear_tv(get_vim_var_tv(VV_KEY));
1808 if (r == FAIL || did_emsg)
1809 break;
1810 if (!map && rem)
1811 {
1812 if (var_check_fixed(di->di_flags, arg_errmsg, TRUE)
1813 || var_check_ro(di->di_flags, arg_errmsg, TRUE))
1814 break;
1815 dictitem_remove(d, di);
1816 }
1817 }
1818 }
1819 hash_unlock(ht);
Bram Moolenaardb661fb2020-01-29 22:17:16 +01001820 d->dv_lock = prev_lock;
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02001821 }
1822 else if (argvars[0].v_type == VAR_BLOB)
1823 {
1824 int i;
1825 typval_T tv;
Bram Moolenaar49c57ce2020-01-15 20:51:34 +01001826 varnumber_T val;
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02001827
1828 // set_vim_var_nr() doesn't set the type
1829 set_vim_var_type(VV_KEY, VAR_NUMBER);
1830
1831 for (i = 0; i < b->bv_ga.ga_len; i++)
1832 {
1833 tv.v_type = VAR_NUMBER;
Bram Moolenaar49c57ce2020-01-15 20:51:34 +01001834 val = blob_get(b, i);
1835 tv.vval.v_number = val;
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02001836 set_vim_var_nr(VV_KEY, idx);
1837 if (filter_map_one(&tv, expr, map, &rem) == FAIL || did_emsg)
1838 break;
1839 if (tv.v_type != VAR_NUMBER)
1840 {
1841 emsg(_(e_invalblob));
1842 break;
1843 }
Bram Moolenaar49c57ce2020-01-15 20:51:34 +01001844 if (map)
1845 {
1846 if (tv.vval.v_number != val)
1847 blob_set(b, i, tv.vval.v_number);
1848 }
1849 else if (rem)
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02001850 {
1851 char_u *p = (char_u *)argvars[0].vval.v_blob->bv_ga.ga_data;
1852
Bram Moolenaar49c57ce2020-01-15 20:51:34 +01001853 mch_memmove(p + i, p + i + 1,
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02001854 (size_t)b->bv_ga.ga_len - i - 1);
1855 --b->bv_ga.ga_len;
1856 --i;
1857 }
Bram Moolenaar49c57ce2020-01-15 20:51:34 +01001858 ++idx;
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02001859 }
1860 }
1861 else // argvars[0].v_type == VAR_LIST
1862 {
Bram Moolenaardb661fb2020-01-29 22:17:16 +01001863 int prev_lock = l->lv_lock;
1864
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02001865 // set_vim_var_nr() doesn't set the type
1866 set_vim_var_type(VV_KEY, VAR_NUMBER);
1867
Bram Moolenaar8a7d6542020-01-26 15:56:19 +01001868 range_list_materialize(l);
Bram Moolenaardb661fb2020-01-29 22:17:16 +01001869 if (map && l->lv_lock == 0)
1870 l->lv_lock = VAR_LOCKED;
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02001871 for (li = l->lv_first; li != NULL; li = nli)
1872 {
1873 if (map && var_check_lock(li->li_tv.v_lock, arg_errmsg, TRUE))
1874 break;
1875 nli = li->li_next;
1876 set_vim_var_nr(VV_KEY, idx);
1877 if (filter_map_one(&li->li_tv, expr, map, &rem) == FAIL
1878 || did_emsg)
1879 break;
1880 if (!map && rem)
1881 listitem_remove(l, li);
1882 ++idx;
1883 }
Bram Moolenaardb661fb2020-01-29 22:17:16 +01001884 l->lv_lock = prev_lock;
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02001885 }
1886
1887 restore_vimvar(VV_KEY, &save_key);
1888 restore_vimvar(VV_VAL, &save_val);
1889
1890 did_emsg |= save_did_emsg;
1891 }
1892
1893 copy_tv(&argvars[0], rettv);
1894}
1895
1896/*
1897 * "filter()" function
1898 */
1899 void
1900f_filter(typval_T *argvars, typval_T *rettv)
1901{
1902 filter_map(argvars, rettv, FALSE);
1903}
1904
1905/*
1906 * "map()" function
1907 */
1908 void
1909f_map(typval_T *argvars, typval_T *rettv)
1910{
1911 filter_map(argvars, rettv, TRUE);
1912}
1913
Bram Moolenaar08c308a2019-09-04 17:48:15 +02001914/*
1915 * "add(list, item)" function
1916 */
1917 void
1918f_add(typval_T *argvars, typval_T *rettv)
1919{
1920 list_T *l;
1921 blob_T *b;
1922
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001923 rettv->vval.v_number = 1; // Default: Failed
Bram Moolenaar08c308a2019-09-04 17:48:15 +02001924 if (argvars[0].v_type == VAR_LIST)
1925 {
1926 if ((l = argvars[0].vval.v_list) != NULL
1927 && !var_check_lock(l->lv_lock,
1928 (char_u *)N_("add() argument"), TRUE)
1929 && list_append_tv(l, &argvars[1]) == OK)
1930 copy_tv(&argvars[0], rettv);
1931 }
1932 else if (argvars[0].v_type == VAR_BLOB)
1933 {
1934 if ((b = argvars[0].vval.v_blob) != NULL
1935 && !var_check_lock(b->bv_lock,
1936 (char_u *)N_("add() argument"), TRUE))
1937 {
1938 int error = FALSE;
1939 varnumber_T n = tv_get_number_chk(&argvars[1], &error);
1940
1941 if (!error)
1942 {
1943 ga_append(&b->bv_ga, (int)n);
1944 copy_tv(&argvars[0], rettv);
1945 }
1946 }
1947 }
1948 else
1949 emsg(_(e_listblobreq));
1950}
1951
1952/*
1953 * "count()" function
1954 */
1955 void
1956f_count(typval_T *argvars, typval_T *rettv)
1957{
1958 long n = 0;
1959 int ic = FALSE;
1960 int error = FALSE;
1961
1962 if (argvars[2].v_type != VAR_UNKNOWN)
1963 ic = (int)tv_get_number_chk(&argvars[2], &error);
1964
1965 if (argvars[0].v_type == VAR_STRING)
1966 {
1967 char_u *expr = tv_get_string_chk(&argvars[1]);
1968 char_u *p = argvars[0].vval.v_string;
1969 char_u *next;
1970
1971 if (!error && expr != NULL && *expr != NUL && p != NULL)
1972 {
1973 if (ic)
1974 {
1975 size_t len = STRLEN(expr);
1976
1977 while (*p != NUL)
1978 {
1979 if (MB_STRNICMP(p, expr, len) == 0)
1980 {
1981 ++n;
1982 p += len;
1983 }
1984 else
1985 MB_PTR_ADV(p);
1986 }
1987 }
1988 else
1989 while ((next = (char_u *)strstr((char *)p, (char *)expr))
1990 != NULL)
1991 {
1992 ++n;
1993 p = next + STRLEN(expr);
1994 }
1995 }
1996
1997 }
1998 else if (argvars[0].v_type == VAR_LIST)
1999 {
2000 listitem_T *li;
2001 list_T *l;
2002 long idx;
2003
2004 if ((l = argvars[0].vval.v_list) != NULL)
2005 {
Bram Moolenaar89bfc822020-01-27 22:37:23 +01002006 range_list_materialize(l);
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002007 li = l->lv_first;
2008 if (argvars[2].v_type != VAR_UNKNOWN)
2009 {
2010 if (argvars[3].v_type != VAR_UNKNOWN)
2011 {
2012 idx = (long)tv_get_number_chk(&argvars[3], &error);
2013 if (!error)
2014 {
2015 li = list_find(l, idx);
2016 if (li == NULL)
2017 semsg(_(e_listidx), idx);
2018 }
2019 }
2020 if (error)
2021 li = NULL;
2022 }
2023
2024 for ( ; li != NULL; li = li->li_next)
2025 if (tv_equal(&li->li_tv, &argvars[1], ic, FALSE))
2026 ++n;
2027 }
2028 }
2029 else if (argvars[0].v_type == VAR_DICT)
2030 {
2031 int todo;
2032 dict_T *d;
2033 hashitem_T *hi;
2034
2035 if ((d = argvars[0].vval.v_dict) != NULL)
2036 {
2037 if (argvars[2].v_type != VAR_UNKNOWN)
2038 {
2039 if (argvars[3].v_type != VAR_UNKNOWN)
2040 emsg(_(e_invarg));
2041 }
2042
2043 todo = error ? 0 : (int)d->dv_hashtab.ht_used;
2044 for (hi = d->dv_hashtab.ht_array; todo > 0; ++hi)
2045 {
2046 if (!HASHITEM_EMPTY(hi))
2047 {
2048 --todo;
2049 if (tv_equal(&HI2DI(hi)->di_tv, &argvars[1], ic, FALSE))
2050 ++n;
2051 }
2052 }
2053 }
2054 }
2055 else
2056 semsg(_(e_listdictarg), "count()");
2057 rettv->vval.v_number = n;
2058}
2059
2060/*
2061 * "extend(list, list [, idx])" function
2062 * "extend(dict, dict [, action])" function
2063 */
2064 void
2065f_extend(typval_T *argvars, typval_T *rettv)
2066{
2067 char_u *arg_errmsg = (char_u *)N_("extend() argument");
2068
2069 if (argvars[0].v_type == VAR_LIST && argvars[1].v_type == VAR_LIST)
2070 {
2071 list_T *l1, *l2;
2072 listitem_T *item;
2073 long before;
2074 int error = FALSE;
2075
2076 l1 = argvars[0].vval.v_list;
2077 l2 = argvars[1].vval.v_list;
2078 if (l1 != NULL && !var_check_lock(l1->lv_lock, arg_errmsg, TRUE)
2079 && l2 != NULL)
2080 {
2081 if (argvars[2].v_type != VAR_UNKNOWN)
2082 {
2083 before = (long)tv_get_number_chk(&argvars[2], &error);
2084 if (error)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002085 return; // type error; errmsg already given
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002086
2087 if (before == l1->lv_len)
2088 item = NULL;
2089 else
2090 {
2091 item = list_find(l1, before);
2092 if (item == NULL)
2093 {
2094 semsg(_(e_listidx), before);
2095 return;
2096 }
2097 }
2098 }
2099 else
2100 item = NULL;
2101 list_extend(l1, l2, item);
2102
2103 copy_tv(&argvars[0], rettv);
2104 }
2105 }
2106 else if (argvars[0].v_type == VAR_DICT && argvars[1].v_type == VAR_DICT)
2107 {
2108 dict_T *d1, *d2;
2109 char_u *action;
2110 int i;
2111
2112 d1 = argvars[0].vval.v_dict;
2113 d2 = argvars[1].vval.v_dict;
2114 if (d1 != NULL && !var_check_lock(d1->dv_lock, arg_errmsg, TRUE)
2115 && d2 != NULL)
2116 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002117 // Check the third argument.
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002118 if (argvars[2].v_type != VAR_UNKNOWN)
2119 {
2120 static char *(av[]) = {"keep", "force", "error"};
2121
2122 action = tv_get_string_chk(&argvars[2]);
2123 if (action == NULL)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002124 return; // type error; errmsg already given
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002125 for (i = 0; i < 3; ++i)
2126 if (STRCMP(action, av[i]) == 0)
2127 break;
2128 if (i == 3)
2129 {
2130 semsg(_(e_invarg2), action);
2131 return;
2132 }
2133 }
2134 else
2135 action = (char_u *)"force";
2136
2137 dict_extend(d1, d2, action);
2138
2139 copy_tv(&argvars[0], rettv);
2140 }
2141 }
2142 else
2143 semsg(_(e_listdictarg), "extend()");
2144}
2145
2146/*
2147 * "insert()" function
2148 */
2149 void
2150f_insert(typval_T *argvars, typval_T *rettv)
2151{
2152 long before = 0;
2153 listitem_T *item;
2154 list_T *l;
2155 int error = FALSE;
2156
2157 if (argvars[0].v_type == VAR_BLOB)
2158 {
2159 int val, len;
2160 char_u *p;
2161
2162 len = blob_len(argvars[0].vval.v_blob);
2163 if (argvars[2].v_type != VAR_UNKNOWN)
2164 {
2165 before = (long)tv_get_number_chk(&argvars[2], &error);
2166 if (error)
2167 return; // type error; errmsg already given
2168 if (before < 0 || before > len)
2169 {
2170 semsg(_(e_invarg2), tv_get_string(&argvars[2]));
2171 return;
2172 }
2173 }
2174 val = tv_get_number_chk(&argvars[1], &error);
2175 if (error)
2176 return;
2177 if (val < 0 || val > 255)
2178 {
2179 semsg(_(e_invarg2), tv_get_string(&argvars[1]));
2180 return;
2181 }
2182
2183 if (ga_grow(&argvars[0].vval.v_blob->bv_ga, 1) == FAIL)
2184 return;
2185 p = (char_u *)argvars[0].vval.v_blob->bv_ga.ga_data;
2186 mch_memmove(p + before + 1, p + before, (size_t)len - before);
2187 *(p + before) = val;
2188 ++argvars[0].vval.v_blob->bv_ga.ga_len;
2189
2190 copy_tv(&argvars[0], rettv);
2191 }
2192 else if (argvars[0].v_type != VAR_LIST)
2193 semsg(_(e_listblobarg), "insert()");
2194 else if ((l = argvars[0].vval.v_list) != NULL
2195 && !var_check_lock(l->lv_lock,
2196 (char_u *)N_("insert() argument"), TRUE))
2197 {
2198 if (argvars[2].v_type != VAR_UNKNOWN)
2199 before = (long)tv_get_number_chk(&argvars[2], &error);
2200 if (error)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002201 return; // type error; errmsg already given
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002202
2203 if (before == l->lv_len)
2204 item = NULL;
2205 else
2206 {
2207 item = list_find(l, before);
2208 if (item == NULL)
2209 {
2210 semsg(_(e_listidx), before);
2211 l = NULL;
2212 }
2213 }
2214 if (l != NULL)
2215 {
2216 list_insert_tv(l, &argvars[1], item);
2217 copy_tv(&argvars[0], rettv);
2218 }
2219 }
2220}
2221
2222/*
2223 * "remove()" function
2224 */
2225 void
2226f_remove(typval_T *argvars, typval_T *rettv)
2227{
2228 char_u *arg_errmsg = (char_u *)N_("remove() argument");
2229
2230 if (argvars[0].v_type == VAR_DICT)
2231 dict_remove(argvars, rettv, arg_errmsg);
2232 else if (argvars[0].v_type == VAR_BLOB)
2233 blob_remove(argvars, rettv);
2234 else if (argvars[0].v_type == VAR_LIST)
2235 list_remove(argvars, rettv, arg_errmsg);
2236 else
2237 semsg(_(e_listdictblobarg), "remove()");
2238}
2239
2240/*
2241 * "reverse({list})" function
2242 */
2243 void
2244f_reverse(typval_T *argvars, typval_T *rettv)
2245{
2246 list_T *l;
2247 listitem_T *li, *ni;
2248
2249 if (argvars[0].v_type == VAR_BLOB)
2250 {
2251 blob_T *b = argvars[0].vval.v_blob;
2252 int i, len = blob_len(b);
2253
2254 for (i = 0; i < len / 2; i++)
2255 {
2256 int tmp = blob_get(b, i);
2257
2258 blob_set(b, i, blob_get(b, len - i - 1));
2259 blob_set(b, len - i - 1, tmp);
2260 }
2261 rettv_blob_set(rettv, b);
2262 return;
2263 }
2264
2265 if (argvars[0].v_type != VAR_LIST)
2266 semsg(_(e_listblobarg), "reverse()");
2267 else if ((l = argvars[0].vval.v_list) != NULL
2268 && !var_check_lock(l->lv_lock,
2269 (char_u *)N_("reverse() argument"), TRUE))
2270 {
Bram Moolenaar89bfc822020-01-27 22:37:23 +01002271 if (l->lv_first == &range_list_item)
2272 {
Bram Moolenaar0ff6aad2020-01-29 21:27:21 +01002273 varnumber_T new_start = l->lv_u.nonmat.lv_start
2274 + (l->lv_len - 1) * l->lv_u.nonmat.lv_stride;
2275 l->lv_u.nonmat.lv_end = new_start
2276 - (l->lv_u.nonmat.lv_end - l->lv_u.nonmat.lv_start);
2277 l->lv_u.nonmat.lv_start = new_start;
2278 l->lv_u.nonmat.lv_stride = -l->lv_u.nonmat.lv_stride;
Bram Moolenaar89bfc822020-01-27 22:37:23 +01002279 rettv_list_set(rettv, l);
2280 return;
2281 }
Bram Moolenaar0ff6aad2020-01-29 21:27:21 +01002282 li = l->lv_u.mat.lv_last;
2283 l->lv_first = l->lv_u.mat.lv_last = NULL;
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002284 l->lv_len = 0;
2285 while (li != NULL)
2286 {
2287 ni = li->li_prev;
2288 list_append(l, li);
2289 li = ni;
2290 }
2291 rettv_list_set(rettv, l);
Bram Moolenaar0ff6aad2020-01-29 21:27:21 +01002292 l->lv_u.mat.lv_idx = l->lv_len - l->lv_u.mat.lv_idx - 1;
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002293 }
2294}
2295
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02002296#endif // defined(FEAT_EVAL)