blob: 4071a7d7fe3a15161da45c7d4caf42df36b367a8 [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;
129 l->lv_last = li + count - 1;
130 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 Moolenaarda861d62016-07-17 15:46:27 +0200409 if (l->lv_idx_item != NULL)
410 {
411 if (n < l->lv_idx / 2)
412 {
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 }
417 else if (n > (l->lv_idx + l->lv_len) / 2)
418 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100419 // closest to the end of the list
Bram Moolenaarda861d62016-07-17 15:46:27 +0200420 item = l->lv_last;
421 idx = l->lv_len - 1;
422 }
423 else
424 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100425 // closest to the cached index
Bram Moolenaarda861d62016-07-17 15:46:27 +0200426 item = l->lv_idx_item;
427 idx = l->lv_idx;
428 }
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 Moolenaarda861d62016-07-17 15:46:27 +0200441 item = l->lv_last;
442 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 Moolenaarda861d62016-07-17 15:46:27 +0200460 l->lv_idx = idx;
461 l->lv_idx_item = item;
462
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
494 return l->lv_start + n * l->lv_stride;
495 }
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 Moolenaarda861d62016-07-17 15:46:27 +0200552 if (l->lv_last == NULL)
553 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100554 // empty list
Bram Moolenaarda861d62016-07-17 15:46:27 +0200555 l->lv_first = item;
556 l->lv_last = item;
557 item->li_prev = NULL;
558 }
559 else
560 {
561 l->lv_last->li_next = item;
562 item->li_prev = l->lv_last;
563 l->lv_last = item;
564 }
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;
713 ++l->lv_idx;
714 }
715 else
716 {
717 item->li_prev->li_next = ni;
718 l->lv_idx_item = NULL;
719 }
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)
849 l->lv_last = item->li_prev;
850 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;
856 l->lv_idx_item = NULL;
857}
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)
1086 semsg(_("E697: Missing end of List ']': %s"), *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];
1152 l->lv_last = &sl->sl_items[9];
1153 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;
1283 l->lv_last = item2;
1284 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 Moolenaar9f9fe372019-07-27 23:12:12 +02001608 l->lv_first = l->lv_last = l->lv_idx_item = NULL;
1609 l->lv_len = 0;
1610 for (i = 0; i < len; ++i)
1611 list_append(l, ptrs[i].item);
1612 }
1613 }
1614 }
1615 else
1616 {
1617 int (*item_compare_func_ptr)(const void *, const void *);
1618
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001619 // f_uniq(): ptrs will be a stack of items to remove
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001620 info.item_compare_func_err = FALSE;
1621 info.item_compare_keep_zero = TRUE;
1622 item_compare_func_ptr = info.item_compare_func != NULL
1623 || info.item_compare_partial != NULL
1624 ? item_compare2 : item_compare;
1625
1626 for (li = l->lv_first; li != NULL && li->li_next != NULL;
1627 li = li->li_next)
1628 {
1629 if (item_compare_func_ptr((void *)&li, (void *)&li->li_next)
1630 == 0)
1631 ptrs[i++].item = li;
1632 if (info.item_compare_func_err)
1633 {
1634 emsg(_("E882: Uniq compare function failed"));
1635 break;
1636 }
1637 }
1638
1639 if (!info.item_compare_func_err)
1640 {
1641 while (--i >= 0)
1642 {
1643 li = ptrs[i].item->li_next;
1644 ptrs[i].item->li_next = li->li_next;
1645 if (li->li_next != NULL)
1646 li->li_next->li_prev = ptrs[i].item;
1647 else
1648 l->lv_last = ptrs[i].item;
1649 list_fix_watch(l, li);
Bram Moolenaar8a7d6542020-01-26 15:56:19 +01001650 listitem_free(l, li);
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001651 l->lv_len--;
1652 }
1653 }
1654 }
1655
1656 vim_free(ptrs);
1657 }
1658theend:
1659 sortinfo = old_sortinfo;
1660}
1661
1662/*
1663 * "sort({list})" function
1664 */
1665 void
1666f_sort(typval_T *argvars, typval_T *rettv)
1667{
1668 do_sort_uniq(argvars, rettv, TRUE);
1669}
1670
1671/*
1672 * "uniq({list})" function
1673 */
1674 void
1675f_uniq(typval_T *argvars, typval_T *rettv)
1676{
1677 do_sort_uniq(argvars, rettv, FALSE);
1678}
1679
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02001680/*
1681 * Handle one item for map() and filter().
1682 */
1683 static int
1684filter_map_one(typval_T *tv, typval_T *expr, int map, int *remp)
1685{
1686 typval_T rettv;
1687 typval_T argv[3];
1688 int retval = FAIL;
1689
1690 copy_tv(tv, get_vim_var_tv(VV_VAL));
1691 argv[0] = *get_vim_var_tv(VV_KEY);
1692 argv[1] = *get_vim_var_tv(VV_VAL);
1693 if (eval_expr_typval(expr, argv, 2, &rettv) == FAIL)
1694 goto theend;
1695 if (map)
1696 {
1697 // map(): replace the list item value
1698 clear_tv(tv);
1699 rettv.v_lock = 0;
1700 *tv = rettv;
1701 }
1702 else
1703 {
1704 int error = FALSE;
1705
1706 // filter(): when expr is zero remove the item
1707 *remp = (tv_get_number_chk(&rettv, &error) == 0);
1708 clear_tv(&rettv);
1709 // On type error, nothing has been removed; return FAIL to stop the
1710 // loop. The error message was given by tv_get_number_chk().
1711 if (error)
1712 goto theend;
1713 }
1714 retval = OK;
1715theend:
1716 clear_tv(get_vim_var_tv(VV_VAL));
1717 return retval;
1718}
1719
1720/*
1721 * Implementation of map() and filter().
1722 */
1723 static void
1724filter_map(typval_T *argvars, typval_T *rettv, int map)
1725{
1726 typval_T *expr;
1727 listitem_T *li, *nli;
1728 list_T *l = NULL;
1729 dictitem_T *di;
1730 hashtab_T *ht;
1731 hashitem_T *hi;
1732 dict_T *d = NULL;
1733 blob_T *b = NULL;
1734 int rem;
1735 int todo;
1736 char_u *ermsg = (char_u *)(map ? "map()" : "filter()");
1737 char_u *arg_errmsg = (char_u *)(map ? N_("map() argument")
1738 : N_("filter() argument"));
1739 int save_did_emsg;
1740 int idx = 0;
1741
1742 if (argvars[0].v_type == VAR_BLOB)
1743 {
1744 if ((b = argvars[0].vval.v_blob) == NULL)
1745 return;
1746 }
1747 else if (argvars[0].v_type == VAR_LIST)
1748 {
1749 if ((l = argvars[0].vval.v_list) == NULL
1750 || (!map && var_check_lock(l->lv_lock, arg_errmsg, TRUE)))
1751 return;
1752 }
1753 else if (argvars[0].v_type == VAR_DICT)
1754 {
1755 if ((d = argvars[0].vval.v_dict) == NULL
1756 || (!map && var_check_lock(d->dv_lock, arg_errmsg, TRUE)))
1757 return;
1758 }
1759 else
1760 {
1761 semsg(_(e_listdictarg), ermsg);
1762 return;
1763 }
1764
1765 expr = &argvars[1];
1766 // On type errors, the preceding call has already displayed an error
1767 // message. Avoid a misleading error message for an empty string that
1768 // was not passed as argument.
1769 if (expr->v_type != VAR_UNKNOWN)
1770 {
1771 typval_T save_val;
1772 typval_T save_key;
1773
1774 prepare_vimvar(VV_VAL, &save_val);
1775 prepare_vimvar(VV_KEY, &save_key);
1776
1777 // We reset "did_emsg" to be able to detect whether an error
1778 // occurred during evaluation of the expression.
1779 save_did_emsg = did_emsg;
1780 did_emsg = FALSE;
1781
1782 if (argvars[0].v_type == VAR_DICT)
1783 {
1784 ht = &d->dv_hashtab;
1785 hash_lock(ht);
1786 todo = (int)ht->ht_used;
1787 for (hi = ht->ht_array; todo > 0; ++hi)
1788 {
1789 if (!HASHITEM_EMPTY(hi))
1790 {
1791 int r;
1792
1793 --todo;
1794 di = HI2DI(hi);
1795 if (map && (var_check_lock(di->di_tv.v_lock,
1796 arg_errmsg, TRUE)
1797 || var_check_ro(di->di_flags,
1798 arg_errmsg, TRUE)))
1799 break;
1800 set_vim_var_string(VV_KEY, di->di_key, -1);
1801 r = filter_map_one(&di->di_tv, expr, map, &rem);
1802 clear_tv(get_vim_var_tv(VV_KEY));
1803 if (r == FAIL || did_emsg)
1804 break;
1805 if (!map && rem)
1806 {
1807 if (var_check_fixed(di->di_flags, arg_errmsg, TRUE)
1808 || var_check_ro(di->di_flags, arg_errmsg, TRUE))
1809 break;
1810 dictitem_remove(d, di);
1811 }
1812 }
1813 }
1814 hash_unlock(ht);
1815 }
1816 else if (argvars[0].v_type == VAR_BLOB)
1817 {
1818 int i;
1819 typval_T tv;
Bram Moolenaar49c57ce2020-01-15 20:51:34 +01001820 varnumber_T val;
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02001821
1822 // set_vim_var_nr() doesn't set the type
1823 set_vim_var_type(VV_KEY, VAR_NUMBER);
1824
1825 for (i = 0; i < b->bv_ga.ga_len; i++)
1826 {
1827 tv.v_type = VAR_NUMBER;
Bram Moolenaar49c57ce2020-01-15 20:51:34 +01001828 val = blob_get(b, i);
1829 tv.vval.v_number = val;
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02001830 set_vim_var_nr(VV_KEY, idx);
1831 if (filter_map_one(&tv, expr, map, &rem) == FAIL || did_emsg)
1832 break;
1833 if (tv.v_type != VAR_NUMBER)
1834 {
1835 emsg(_(e_invalblob));
1836 break;
1837 }
Bram Moolenaar49c57ce2020-01-15 20:51:34 +01001838 if (map)
1839 {
1840 if (tv.vval.v_number != val)
1841 blob_set(b, i, tv.vval.v_number);
1842 }
1843 else if (rem)
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02001844 {
1845 char_u *p = (char_u *)argvars[0].vval.v_blob->bv_ga.ga_data;
1846
Bram Moolenaar49c57ce2020-01-15 20:51:34 +01001847 mch_memmove(p + i, p + i + 1,
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02001848 (size_t)b->bv_ga.ga_len - i - 1);
1849 --b->bv_ga.ga_len;
1850 --i;
1851 }
Bram Moolenaar49c57ce2020-01-15 20:51:34 +01001852 ++idx;
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02001853 }
1854 }
1855 else // argvars[0].v_type == VAR_LIST
1856 {
1857 // set_vim_var_nr() doesn't set the type
1858 set_vim_var_type(VV_KEY, VAR_NUMBER);
1859
Bram Moolenaar8a7d6542020-01-26 15:56:19 +01001860 range_list_materialize(l);
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02001861 for (li = l->lv_first; li != NULL; li = nli)
1862 {
1863 if (map && var_check_lock(li->li_tv.v_lock, arg_errmsg, TRUE))
1864 break;
1865 nli = li->li_next;
1866 set_vim_var_nr(VV_KEY, idx);
1867 if (filter_map_one(&li->li_tv, expr, map, &rem) == FAIL
1868 || did_emsg)
1869 break;
1870 if (!map && rem)
1871 listitem_remove(l, li);
1872 ++idx;
1873 }
1874 }
1875
1876 restore_vimvar(VV_KEY, &save_key);
1877 restore_vimvar(VV_VAL, &save_val);
1878
1879 did_emsg |= save_did_emsg;
1880 }
1881
1882 copy_tv(&argvars[0], rettv);
1883}
1884
1885/*
1886 * "filter()" function
1887 */
1888 void
1889f_filter(typval_T *argvars, typval_T *rettv)
1890{
1891 filter_map(argvars, rettv, FALSE);
1892}
1893
1894/*
1895 * "map()" function
1896 */
1897 void
1898f_map(typval_T *argvars, typval_T *rettv)
1899{
1900 filter_map(argvars, rettv, TRUE);
1901}
1902
Bram Moolenaar08c308a2019-09-04 17:48:15 +02001903/*
1904 * "add(list, item)" function
1905 */
1906 void
1907f_add(typval_T *argvars, typval_T *rettv)
1908{
1909 list_T *l;
1910 blob_T *b;
1911
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001912 rettv->vval.v_number = 1; // Default: Failed
Bram Moolenaar08c308a2019-09-04 17:48:15 +02001913 if (argvars[0].v_type == VAR_LIST)
1914 {
1915 if ((l = argvars[0].vval.v_list) != NULL
1916 && !var_check_lock(l->lv_lock,
1917 (char_u *)N_("add() argument"), TRUE)
1918 && list_append_tv(l, &argvars[1]) == OK)
1919 copy_tv(&argvars[0], rettv);
1920 }
1921 else if (argvars[0].v_type == VAR_BLOB)
1922 {
1923 if ((b = argvars[0].vval.v_blob) != NULL
1924 && !var_check_lock(b->bv_lock,
1925 (char_u *)N_("add() argument"), TRUE))
1926 {
1927 int error = FALSE;
1928 varnumber_T n = tv_get_number_chk(&argvars[1], &error);
1929
1930 if (!error)
1931 {
1932 ga_append(&b->bv_ga, (int)n);
1933 copy_tv(&argvars[0], rettv);
1934 }
1935 }
1936 }
1937 else
1938 emsg(_(e_listblobreq));
1939}
1940
1941/*
1942 * "count()" function
1943 */
1944 void
1945f_count(typval_T *argvars, typval_T *rettv)
1946{
1947 long n = 0;
1948 int ic = FALSE;
1949 int error = FALSE;
1950
1951 if (argvars[2].v_type != VAR_UNKNOWN)
1952 ic = (int)tv_get_number_chk(&argvars[2], &error);
1953
1954 if (argvars[0].v_type == VAR_STRING)
1955 {
1956 char_u *expr = tv_get_string_chk(&argvars[1]);
1957 char_u *p = argvars[0].vval.v_string;
1958 char_u *next;
1959
1960 if (!error && expr != NULL && *expr != NUL && p != NULL)
1961 {
1962 if (ic)
1963 {
1964 size_t len = STRLEN(expr);
1965
1966 while (*p != NUL)
1967 {
1968 if (MB_STRNICMP(p, expr, len) == 0)
1969 {
1970 ++n;
1971 p += len;
1972 }
1973 else
1974 MB_PTR_ADV(p);
1975 }
1976 }
1977 else
1978 while ((next = (char_u *)strstr((char *)p, (char *)expr))
1979 != NULL)
1980 {
1981 ++n;
1982 p = next + STRLEN(expr);
1983 }
1984 }
1985
1986 }
1987 else if (argvars[0].v_type == VAR_LIST)
1988 {
1989 listitem_T *li;
1990 list_T *l;
1991 long idx;
1992
1993 if ((l = argvars[0].vval.v_list) != NULL)
1994 {
1995 li = l->lv_first;
1996 if (argvars[2].v_type != VAR_UNKNOWN)
1997 {
1998 if (argvars[3].v_type != VAR_UNKNOWN)
1999 {
2000 idx = (long)tv_get_number_chk(&argvars[3], &error);
2001 if (!error)
2002 {
2003 li = list_find(l, idx);
2004 if (li == NULL)
2005 semsg(_(e_listidx), idx);
2006 }
2007 }
2008 if (error)
2009 li = NULL;
2010 }
2011
2012 for ( ; li != NULL; li = li->li_next)
2013 if (tv_equal(&li->li_tv, &argvars[1], ic, FALSE))
2014 ++n;
2015 }
2016 }
2017 else if (argvars[0].v_type == VAR_DICT)
2018 {
2019 int todo;
2020 dict_T *d;
2021 hashitem_T *hi;
2022
2023 if ((d = argvars[0].vval.v_dict) != NULL)
2024 {
2025 if (argvars[2].v_type != VAR_UNKNOWN)
2026 {
2027 if (argvars[3].v_type != VAR_UNKNOWN)
2028 emsg(_(e_invarg));
2029 }
2030
2031 todo = error ? 0 : (int)d->dv_hashtab.ht_used;
2032 for (hi = d->dv_hashtab.ht_array; todo > 0; ++hi)
2033 {
2034 if (!HASHITEM_EMPTY(hi))
2035 {
2036 --todo;
2037 if (tv_equal(&HI2DI(hi)->di_tv, &argvars[1], ic, FALSE))
2038 ++n;
2039 }
2040 }
2041 }
2042 }
2043 else
2044 semsg(_(e_listdictarg), "count()");
2045 rettv->vval.v_number = n;
2046}
2047
2048/*
2049 * "extend(list, list [, idx])" function
2050 * "extend(dict, dict [, action])" function
2051 */
2052 void
2053f_extend(typval_T *argvars, typval_T *rettv)
2054{
2055 char_u *arg_errmsg = (char_u *)N_("extend() argument");
2056
2057 if (argvars[0].v_type == VAR_LIST && argvars[1].v_type == VAR_LIST)
2058 {
2059 list_T *l1, *l2;
2060 listitem_T *item;
2061 long before;
2062 int error = FALSE;
2063
2064 l1 = argvars[0].vval.v_list;
2065 l2 = argvars[1].vval.v_list;
2066 if (l1 != NULL && !var_check_lock(l1->lv_lock, arg_errmsg, TRUE)
2067 && l2 != NULL)
2068 {
2069 if (argvars[2].v_type != VAR_UNKNOWN)
2070 {
2071 before = (long)tv_get_number_chk(&argvars[2], &error);
2072 if (error)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002073 return; // type error; errmsg already given
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002074
2075 if (before == l1->lv_len)
2076 item = NULL;
2077 else
2078 {
2079 item = list_find(l1, before);
2080 if (item == NULL)
2081 {
2082 semsg(_(e_listidx), before);
2083 return;
2084 }
2085 }
2086 }
2087 else
2088 item = NULL;
2089 list_extend(l1, l2, item);
2090
2091 copy_tv(&argvars[0], rettv);
2092 }
2093 }
2094 else if (argvars[0].v_type == VAR_DICT && argvars[1].v_type == VAR_DICT)
2095 {
2096 dict_T *d1, *d2;
2097 char_u *action;
2098 int i;
2099
2100 d1 = argvars[0].vval.v_dict;
2101 d2 = argvars[1].vval.v_dict;
2102 if (d1 != NULL && !var_check_lock(d1->dv_lock, arg_errmsg, TRUE)
2103 && d2 != NULL)
2104 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002105 // Check the third argument.
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002106 if (argvars[2].v_type != VAR_UNKNOWN)
2107 {
2108 static char *(av[]) = {"keep", "force", "error"};
2109
2110 action = tv_get_string_chk(&argvars[2]);
2111 if (action == NULL)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002112 return; // type error; errmsg already given
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002113 for (i = 0; i < 3; ++i)
2114 if (STRCMP(action, av[i]) == 0)
2115 break;
2116 if (i == 3)
2117 {
2118 semsg(_(e_invarg2), action);
2119 return;
2120 }
2121 }
2122 else
2123 action = (char_u *)"force";
2124
2125 dict_extend(d1, d2, action);
2126
2127 copy_tv(&argvars[0], rettv);
2128 }
2129 }
2130 else
2131 semsg(_(e_listdictarg), "extend()");
2132}
2133
2134/*
2135 * "insert()" function
2136 */
2137 void
2138f_insert(typval_T *argvars, typval_T *rettv)
2139{
2140 long before = 0;
2141 listitem_T *item;
2142 list_T *l;
2143 int error = FALSE;
2144
2145 if (argvars[0].v_type == VAR_BLOB)
2146 {
2147 int val, len;
2148 char_u *p;
2149
2150 len = blob_len(argvars[0].vval.v_blob);
2151 if (argvars[2].v_type != VAR_UNKNOWN)
2152 {
2153 before = (long)tv_get_number_chk(&argvars[2], &error);
2154 if (error)
2155 return; // type error; errmsg already given
2156 if (before < 0 || before > len)
2157 {
2158 semsg(_(e_invarg2), tv_get_string(&argvars[2]));
2159 return;
2160 }
2161 }
2162 val = tv_get_number_chk(&argvars[1], &error);
2163 if (error)
2164 return;
2165 if (val < 0 || val > 255)
2166 {
2167 semsg(_(e_invarg2), tv_get_string(&argvars[1]));
2168 return;
2169 }
2170
2171 if (ga_grow(&argvars[0].vval.v_blob->bv_ga, 1) == FAIL)
2172 return;
2173 p = (char_u *)argvars[0].vval.v_blob->bv_ga.ga_data;
2174 mch_memmove(p + before + 1, p + before, (size_t)len - before);
2175 *(p + before) = val;
2176 ++argvars[0].vval.v_blob->bv_ga.ga_len;
2177
2178 copy_tv(&argvars[0], rettv);
2179 }
2180 else if (argvars[0].v_type != VAR_LIST)
2181 semsg(_(e_listblobarg), "insert()");
2182 else if ((l = argvars[0].vval.v_list) != NULL
2183 && !var_check_lock(l->lv_lock,
2184 (char_u *)N_("insert() argument"), TRUE))
2185 {
2186 if (argvars[2].v_type != VAR_UNKNOWN)
2187 before = (long)tv_get_number_chk(&argvars[2], &error);
2188 if (error)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002189 return; // type error; errmsg already given
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002190
2191 if (before == l->lv_len)
2192 item = NULL;
2193 else
2194 {
2195 item = list_find(l, before);
2196 if (item == NULL)
2197 {
2198 semsg(_(e_listidx), before);
2199 l = NULL;
2200 }
2201 }
2202 if (l != NULL)
2203 {
2204 list_insert_tv(l, &argvars[1], item);
2205 copy_tv(&argvars[0], rettv);
2206 }
2207 }
2208}
2209
2210/*
2211 * "remove()" function
2212 */
2213 void
2214f_remove(typval_T *argvars, typval_T *rettv)
2215{
2216 char_u *arg_errmsg = (char_u *)N_("remove() argument");
2217
2218 if (argvars[0].v_type == VAR_DICT)
2219 dict_remove(argvars, rettv, arg_errmsg);
2220 else if (argvars[0].v_type == VAR_BLOB)
2221 blob_remove(argvars, rettv);
2222 else if (argvars[0].v_type == VAR_LIST)
2223 list_remove(argvars, rettv, arg_errmsg);
2224 else
2225 semsg(_(e_listdictblobarg), "remove()");
2226}
2227
2228/*
2229 * "reverse({list})" function
2230 */
2231 void
2232f_reverse(typval_T *argvars, typval_T *rettv)
2233{
2234 list_T *l;
2235 listitem_T *li, *ni;
2236
2237 if (argvars[0].v_type == VAR_BLOB)
2238 {
2239 blob_T *b = argvars[0].vval.v_blob;
2240 int i, len = blob_len(b);
2241
2242 for (i = 0; i < len / 2; i++)
2243 {
2244 int tmp = blob_get(b, i);
2245
2246 blob_set(b, i, blob_get(b, len - i - 1));
2247 blob_set(b, len - i - 1, tmp);
2248 }
2249 rettv_blob_set(rettv, b);
2250 return;
2251 }
2252
2253 if (argvars[0].v_type != VAR_LIST)
2254 semsg(_(e_listblobarg), "reverse()");
2255 else if ((l = argvars[0].vval.v_list) != NULL
2256 && !var_check_lock(l->lv_lock,
2257 (char_u *)N_("reverse() argument"), TRUE))
2258 {
2259 li = l->lv_last;
2260 l->lv_first = l->lv_last = NULL;
2261 l->lv_len = 0;
2262 while (li != NULL)
2263 {
2264 ni = li->li_prev;
2265 list_append(l, li);
2266 li = ni;
2267 }
2268 rettv_list_set(rettv, l);
2269 l->lv_idx = l->lv_len - l->lv_idx - 1;
2270 }
2271}
2272
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02002273#endif // defined(FEAT_EVAL)