blob: b532e28ebca83a61792889fff2402f8ff4d26b89 [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
Bram Moolenaarbdff0122020-04-05 18:56:05 +020023static void list_free_item(list_T *l, listitem_T *item);
24
Bram Moolenaarda861d62016-07-17 15:46:27 +020025/*
26 * Add a watcher to a list.
27 */
28 void
29list_add_watch(list_T *l, listwatch_T *lw)
30{
31 lw->lw_next = l->lv_watch;
32 l->lv_watch = lw;
33}
34
35/*
36 * Remove a watcher from a list.
37 * No warning when it isn't found...
38 */
39 void
40list_rem_watch(list_T *l, listwatch_T *lwrem)
41{
42 listwatch_T *lw, **lwp;
43
44 lwp = &l->lv_watch;
45 for (lw = l->lv_watch; lw != NULL; lw = lw->lw_next)
46 {
47 if (lw == lwrem)
48 {
49 *lwp = lw->lw_next;
50 break;
51 }
52 lwp = &lw->lw_next;
53 }
54}
55
56/*
57 * Just before removing an item from a list: advance watchers to the next
58 * item.
59 */
Bram Moolenaar5843f5f2019-08-20 20:13:45 +020060 static void
Bram Moolenaarda861d62016-07-17 15:46:27 +020061list_fix_watch(list_T *l, listitem_T *item)
62{
63 listwatch_T *lw;
64
65 for (lw = l->lv_watch; lw != NULL; lw = lw->lw_next)
66 if (lw->lw_item == item)
67 lw->lw_item = item->li_next;
68}
69
Bram Moolenaar8a7d6542020-01-26 15:56:19 +010070 static void
71list_init(list_T *l)
72{
73 // Prepend the list to the list of lists for garbage collection.
74 if (first_list != NULL)
75 first_list->lv_used_prev = l;
76 l->lv_used_prev = NULL;
77 l->lv_used_next = first_list;
78 first_list = l;
79}
80
Bram Moolenaarda861d62016-07-17 15:46:27 +020081/*
82 * Allocate an empty header for a list.
83 * Caller should take care of the reference count.
84 */
85 list_T *
86list_alloc(void)
87{
88 list_T *l;
89
Bram Moolenaarc799fe22019-05-28 23:08:19 +020090 l = ALLOC_CLEAR_ONE(list_T);
Bram Moolenaarda861d62016-07-17 15:46:27 +020091 if (l != NULL)
Bram Moolenaar8a7d6542020-01-26 15:56:19 +010092 list_init(l);
Bram Moolenaarda861d62016-07-17 15:46:27 +020093 return l;
94}
95
96/*
Bram Moolenaarf49cc602018-11-11 15:21:05 +010097 * list_alloc() with an ID for alloc_fail().
98 */
99 list_T *
100list_alloc_id(alloc_id_T id UNUSED)
101{
102#ifdef FEAT_EVAL
Bram Moolenaar51e14382019-05-25 20:21:28 +0200103 if (alloc_fail_id == id && alloc_does_fail(sizeof(list_T)))
Bram Moolenaarf49cc602018-11-11 15:21:05 +0100104 return NULL;
105#endif
106 return (list_alloc());
107}
108
109/*
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100110 * Allocate space for a list, plus "count" items.
111 * Next list_set_item() must be called for each item.
112 */
113 list_T *
114list_alloc_with_items(int count)
115{
116 list_T *l;
117
118 l = (list_T *)alloc_clear(sizeof(list_T) + count * sizeof(listitem_T));
119 if (l != NULL)
120 {
121 list_init(l);
122
123 if (count > 0)
124 {
125 listitem_T *li = (listitem_T *)(l + 1);
126 int i;
127
128 l->lv_len = count;
129 l->lv_with_items = count;
130 l->lv_first = li;
Bram Moolenaar0ff6aad2020-01-29 21:27:21 +0100131 l->lv_u.mat.lv_last = li + count - 1;
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100132 for (i = 0; i < count; ++i)
133 {
134 if (i == 0)
135 li->li_prev = NULL;
136 else
137 li->li_prev = li - 1;
138 if (i == count - 1)
139 li->li_next = NULL;
140 else
141 li->li_next = li + 1;
142 ++li;
143 }
144 }
145 }
146 return l;
147}
148
149/*
150 * Set item "idx" for a list previously allocated with list_alloc_with_items().
151 * The contents of "tv" is moved into the list item.
152 * Each item must be set exactly once.
153 */
154 void
155list_set_item(list_T *l, int idx, typval_T *tv)
156{
157 listitem_T *li = (listitem_T *)(l + 1) + idx;
158
159 li->li_tv = *tv;
160}
161
162/*
Bram Moolenaarda861d62016-07-17 15:46:27 +0200163 * Allocate an empty list for a return value, with reference count set.
164 * Returns OK or FAIL.
165 */
166 int
167rettv_list_alloc(typval_T *rettv)
168{
169 list_T *l = list_alloc();
170
171 if (l == NULL)
172 return FAIL;
173
Bram Moolenaarda861d62016-07-17 15:46:27 +0200174 rettv->v_lock = 0;
Bram Moolenaar45cf6e92017-04-30 20:25:19 +0200175 rettv_list_set(rettv, l);
Bram Moolenaarda861d62016-07-17 15:46:27 +0200176 return OK;
177}
178
179/*
Bram Moolenaar162b7142018-12-21 15:17:36 +0100180 * Same as rettv_list_alloc() but uses an allocation id for testing.
181 */
182 int
183rettv_list_alloc_id(typval_T *rettv, alloc_id_T id UNUSED)
184{
185#ifdef FEAT_EVAL
Bram Moolenaar51e14382019-05-25 20:21:28 +0200186 if (alloc_fail_id == id && alloc_does_fail(sizeof(list_T)))
Bram Moolenaar162b7142018-12-21 15:17:36 +0100187 return FAIL;
188#endif
189 return rettv_list_alloc(rettv);
190}
191
192
193/*
Bram Moolenaare809a4e2019-07-04 17:35:05 +0200194 * Set a list as the return value. Increments the reference count.
Bram Moolenaar45cf6e92017-04-30 20:25:19 +0200195 */
196 void
197rettv_list_set(typval_T *rettv, list_T *l)
198{
199 rettv->v_type = VAR_LIST;
200 rettv->vval.v_list = l;
201 if (l != NULL)
202 ++l->lv_refcount;
203}
204
205/*
Bram Moolenaarda861d62016-07-17 15:46:27 +0200206 * Unreference a list: decrement the reference count and free it when it
207 * becomes zero.
208 */
209 void
210list_unref(list_T *l)
211{
212 if (l != NULL && --l->lv_refcount <= 0)
213 list_free(l);
214}
215
216/*
217 * Free a list, including all non-container items it points to.
218 * Ignores the reference count.
219 */
220 static void
221list_free_contents(list_T *l)
222{
223 listitem_T *item;
224
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100225 if (l->lv_first != &range_list_item)
226 for (item = l->lv_first; item != NULL; item = l->lv_first)
227 {
228 // Remove the item before deleting it.
229 l->lv_first = item->li_next;
230 clear_tv(&item->li_tv);
231 list_free_item(l, item);
232 }
Bram Moolenaarda861d62016-07-17 15:46:27 +0200233}
234
235/*
236 * Go through the list of lists and free items without the copyID.
237 * But don't free a list that has a watcher (used in a for loop), these
238 * are not referenced anywhere.
239 */
240 int
241list_free_nonref(int copyID)
242{
243 list_T *ll;
244 int did_free = FALSE;
245
246 for (ll = first_list; ll != NULL; ll = ll->lv_used_next)
247 if ((ll->lv_copyID & COPYID_MASK) != (copyID & COPYID_MASK)
248 && ll->lv_watch == NULL)
249 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100250 // Free the List and ordinary items it contains, but don't recurse
251 // into Lists and Dictionaries, they will be in the list of dicts
252 // or list of lists.
Bram Moolenaarda861d62016-07-17 15:46:27 +0200253 list_free_contents(ll);
254 did_free = TRUE;
255 }
256 return did_free;
257}
258
259 static void
260list_free_list(list_T *l)
261{
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100262 // Remove the list from the list of lists for garbage collection.
Bram Moolenaarda861d62016-07-17 15:46:27 +0200263 if (l->lv_used_prev == NULL)
264 first_list = l->lv_used_next;
265 else
266 l->lv_used_prev->lv_used_next = l->lv_used_next;
267 if (l->lv_used_next != NULL)
268 l->lv_used_next->lv_used_prev = l->lv_used_prev;
269
270 vim_free(l);
271}
272
273 void
274list_free_items(int copyID)
275{
276 list_T *ll, *ll_next;
277
278 for (ll = first_list; ll != NULL; ll = ll_next)
279 {
280 ll_next = ll->lv_used_next;
281 if ((ll->lv_copyID & COPYID_MASK) != (copyID & COPYID_MASK)
282 && ll->lv_watch == NULL)
283 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100284 // Free the List and ordinary items it contains, but don't recurse
285 // into Lists and Dictionaries, they will be in the list of dicts
286 // or list of lists.
Bram Moolenaarda861d62016-07-17 15:46:27 +0200287 list_free_list(ll);
288 }
289 }
290}
291
292 void
293list_free(list_T *l)
294{
295 if (!in_free_unref_items)
296 {
297 list_free_contents(l);
298 list_free_list(l);
299 }
300}
301
302/*
303 * Allocate a list item.
304 * It is not initialized, don't forget to set v_lock.
305 */
306 listitem_T *
307listitem_alloc(void)
308{
Bram Moolenaarc799fe22019-05-28 23:08:19 +0200309 return ALLOC_ONE(listitem_T);
Bram Moolenaarda861d62016-07-17 15:46:27 +0200310}
311
312/*
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100313 * Free a list item, unless it was allocated together with the list itself.
314 * Does not clear the value. Does not notify watchers.
Bram Moolenaarda861d62016-07-17 15:46:27 +0200315 */
Bram Moolenaarbdff0122020-04-05 18:56:05 +0200316 static void
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100317list_free_item(list_T *l, listitem_T *item)
318{
319 if (l->lv_with_items == 0 || item < (listitem_T *)l
320 || item >= (listitem_T *)(l + 1) + l->lv_with_items)
321 vim_free(item);
322}
323
324/*
325 * Free a list item, unless it was allocated together with the list itself.
326 * Also clears the value. Does not notify watchers.
327 */
328 void
329listitem_free(list_T *l, listitem_T *item)
Bram Moolenaarda861d62016-07-17 15:46:27 +0200330{
331 clear_tv(&item->li_tv);
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100332 list_free_item(l, item);
Bram Moolenaarda861d62016-07-17 15:46:27 +0200333}
334
335/*
336 * Remove a list item from a List and free it. Also clears the value.
337 */
338 void
339listitem_remove(list_T *l, listitem_T *item)
340{
341 vimlist_remove(l, item, item);
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100342 listitem_free(l, item);
Bram Moolenaarda861d62016-07-17 15:46:27 +0200343}
344
345/*
346 * Get the number of items in a list.
347 */
348 long
349list_len(list_T *l)
350{
351 if (l == NULL)
352 return 0L;
353 return l->lv_len;
354}
355
356/*
357 * Return TRUE when two lists have exactly the same values.
358 */
359 int
360list_equal(
361 list_T *l1,
362 list_T *l2,
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100363 int ic, // ignore case for strings
364 int recursive) // TRUE when used recursively
Bram Moolenaarda861d62016-07-17 15:46:27 +0200365{
366 listitem_T *item1, *item2;
367
368 if (l1 == NULL || l2 == NULL)
369 return FALSE;
370 if (l1 == l2)
371 return TRUE;
372 if (list_len(l1) != list_len(l2))
373 return FALSE;
374
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100375 range_list_materialize(l1);
376 range_list_materialize(l2);
377
Bram Moolenaarda861d62016-07-17 15:46:27 +0200378 for (item1 = l1->lv_first, item2 = l2->lv_first;
379 item1 != NULL && item2 != NULL;
380 item1 = item1->li_next, item2 = item2->li_next)
381 if (!tv_equal(&item1->li_tv, &item2->li_tv, ic, recursive))
382 return FALSE;
383 return item1 == NULL && item2 == NULL;
384}
385
386/*
387 * Locate item with index "n" in list "l" and return it.
388 * A negative index is counted from the end; -1 is the last item.
389 * Returns NULL when "n" is out of range.
390 */
391 listitem_T *
392list_find(list_T *l, long n)
393{
394 listitem_T *item;
395 long idx;
396
397 if (l == NULL)
398 return NULL;
399
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100400 // Negative index is relative to the end.
Bram Moolenaarda861d62016-07-17 15:46:27 +0200401 if (n < 0)
402 n = l->lv_len + n;
403
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100404 // Check for index out of range.
Bram Moolenaarda861d62016-07-17 15:46:27 +0200405 if (n < 0 || n >= l->lv_len)
406 return NULL;
407
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100408 range_list_materialize(l);
409
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100410 // When there is a cached index may start search from there.
Bram Moolenaar0ff6aad2020-01-29 21:27:21 +0100411 if (l->lv_u.mat.lv_idx_item != NULL)
Bram Moolenaarda861d62016-07-17 15:46:27 +0200412 {
Bram Moolenaar0ff6aad2020-01-29 21:27:21 +0100413 if (n < l->lv_u.mat.lv_idx / 2)
Bram Moolenaarda861d62016-07-17 15:46:27 +0200414 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100415 // closest to the start of the list
Bram Moolenaarda861d62016-07-17 15:46:27 +0200416 item = l->lv_first;
417 idx = 0;
418 }
Bram Moolenaar0ff6aad2020-01-29 21:27:21 +0100419 else if (n > (l->lv_u.mat.lv_idx + l->lv_len) / 2)
Bram Moolenaarda861d62016-07-17 15:46:27 +0200420 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100421 // closest to the end of the list
Bram Moolenaar0ff6aad2020-01-29 21:27:21 +0100422 item = l->lv_u.mat.lv_last;
Bram Moolenaarda861d62016-07-17 15:46:27 +0200423 idx = l->lv_len - 1;
424 }
425 else
426 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100427 // closest to the cached index
Bram Moolenaar0ff6aad2020-01-29 21:27:21 +0100428 item = l->lv_u.mat.lv_idx_item;
429 idx = l->lv_u.mat.lv_idx;
Bram Moolenaarda861d62016-07-17 15:46:27 +0200430 }
431 }
432 else
433 {
434 if (n < l->lv_len / 2)
435 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100436 // closest to the start of the list
Bram Moolenaarda861d62016-07-17 15:46:27 +0200437 item = l->lv_first;
438 idx = 0;
439 }
440 else
441 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100442 // closest to the end of the list
Bram Moolenaar0ff6aad2020-01-29 21:27:21 +0100443 item = l->lv_u.mat.lv_last;
Bram Moolenaarda861d62016-07-17 15:46:27 +0200444 idx = l->lv_len - 1;
445 }
446 }
447
448 while (n > idx)
449 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100450 // search forward
Bram Moolenaarda861d62016-07-17 15:46:27 +0200451 item = item->li_next;
452 ++idx;
453 }
454 while (n < idx)
455 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100456 // search backward
Bram Moolenaarda861d62016-07-17 15:46:27 +0200457 item = item->li_prev;
458 --idx;
459 }
460
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100461 // cache the used index
Bram Moolenaar0ff6aad2020-01-29 21:27:21 +0100462 l->lv_u.mat.lv_idx = idx;
463 l->lv_u.mat.lv_idx_item = item;
Bram Moolenaarda861d62016-07-17 15:46:27 +0200464
465 return item;
466}
467
468/*
469 * Get list item "l[idx]" as a number.
470 */
471 long
472list_find_nr(
473 list_T *l,
474 long idx,
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100475 int *errorp) // set to TRUE when something wrong
Bram Moolenaarda861d62016-07-17 15:46:27 +0200476{
477 listitem_T *li;
478
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100479 if (l != NULL && l->lv_first == &range_list_item)
480 {
481 long n = idx;
482
483 // not materialized range() list: compute the value.
484 // Negative index is relative to the end.
485 if (n < 0)
486 n = l->lv_len + n;
487
488 // Check for index out of range.
489 if (n < 0 || n >= l->lv_len)
490 {
491 if (errorp != NULL)
492 *errorp = TRUE;
493 return -1L;
494 }
495
Bram Moolenaar0ff6aad2020-01-29 21:27:21 +0100496 return l->lv_u.nonmat.lv_start + n * l->lv_u.nonmat.lv_stride;
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100497 }
498
Bram Moolenaarda861d62016-07-17 15:46:27 +0200499 li = list_find(l, idx);
500 if (li == NULL)
501 {
502 if (errorp != NULL)
503 *errorp = TRUE;
504 return -1L;
505 }
Bram Moolenaard155d7a2018-12-21 16:04:21 +0100506 return (long)tv_get_number_chk(&li->li_tv, errorp);
Bram Moolenaarda861d62016-07-17 15:46:27 +0200507}
508
509/*
510 * Get list item "l[idx - 1]" as a string. Returns NULL for failure.
511 */
512 char_u *
513list_find_str(list_T *l, long idx)
514{
515 listitem_T *li;
516
517 li = list_find(l, idx - 1);
518 if (li == NULL)
519 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100520 semsg(_(e_listidx), idx);
Bram Moolenaarda861d62016-07-17 15:46:27 +0200521 return NULL;
522 }
Bram Moolenaard155d7a2018-12-21 16:04:21 +0100523 return tv_get_string(&li->li_tv);
Bram Moolenaarda861d62016-07-17 15:46:27 +0200524}
525
526/*
527 * Locate "item" list "l" and return its index.
528 * Returns -1 when "item" is not in the list.
529 */
530 long
531list_idx_of_item(list_T *l, listitem_T *item)
532{
533 long idx = 0;
534 listitem_T *li;
535
536 if (l == NULL)
537 return -1;
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100538 range_list_materialize(l);
Bram Moolenaarda861d62016-07-17 15:46:27 +0200539 idx = 0;
540 for (li = l->lv_first; li != NULL && li != item; li = li->li_next)
541 ++idx;
542 if (li == NULL)
543 return -1;
544 return idx;
545}
546
547/*
548 * Append item "item" to the end of list "l".
549 */
550 void
551list_append(list_T *l, listitem_T *item)
552{
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100553 range_list_materialize(l);
Bram Moolenaar0ff6aad2020-01-29 21:27:21 +0100554 if (l->lv_u.mat.lv_last == NULL)
Bram Moolenaarda861d62016-07-17 15:46:27 +0200555 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100556 // empty list
Bram Moolenaarda861d62016-07-17 15:46:27 +0200557 l->lv_first = item;
Bram Moolenaar0ff6aad2020-01-29 21:27:21 +0100558 l->lv_u.mat.lv_last = item;
Bram Moolenaarda861d62016-07-17 15:46:27 +0200559 item->li_prev = NULL;
560 }
561 else
562 {
Bram Moolenaar0ff6aad2020-01-29 21:27:21 +0100563 l->lv_u.mat.lv_last->li_next = item;
564 item->li_prev = l->lv_u.mat.lv_last;
565 l->lv_u.mat.lv_last = item;
Bram Moolenaarda861d62016-07-17 15:46:27 +0200566 }
567 ++l->lv_len;
568 item->li_next = NULL;
569}
570
571/*
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100572 * Append typval_T "tv" to the end of list "l". "tv" is copied.
Bram Moolenaarda861d62016-07-17 15:46:27 +0200573 * Return FAIL when out of memory.
574 */
575 int
576list_append_tv(list_T *l, typval_T *tv)
577{
578 listitem_T *li = listitem_alloc();
579
580 if (li == NULL)
581 return FAIL;
582 copy_tv(tv, &li->li_tv);
583 list_append(l, li);
584 return OK;
585}
586
587/*
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100588 * As list_append_tv() but move the value instead of copying it.
589 * Return FAIL when out of memory.
590 */
591 int
592list_append_tv_move(list_T *l, typval_T *tv)
593{
594 listitem_T *li = listitem_alloc();
595
596 if (li == NULL)
597 return FAIL;
598 li->li_tv = *tv;
599 list_append(l, li);
600 return OK;
601}
602
603/*
Bram Moolenaarda861d62016-07-17 15:46:27 +0200604 * Add a dictionary to a list. Used by getqflist().
605 * Return FAIL when out of memory.
606 */
607 int
608list_append_dict(list_T *list, dict_T *dict)
609{
610 listitem_T *li = listitem_alloc();
611
612 if (li == NULL)
613 return FAIL;
614 li->li_tv.v_type = VAR_DICT;
615 li->li_tv.v_lock = 0;
616 li->li_tv.vval.v_dict = dict;
617 list_append(list, li);
618 ++dict->dv_refcount;
619 return OK;
620}
621
622/*
Bram Moolenaar4f505882018-02-10 21:06:32 +0100623 * Append list2 to list1.
624 * Return FAIL when out of memory.
625 */
626 int
Bram Moolenaar6f8d2ac2018-07-25 19:49:45 +0200627list_append_list(list_T *list1, list_T *list2)
Bram Moolenaar4f505882018-02-10 21:06:32 +0100628{
629 listitem_T *li = listitem_alloc();
630
631 if (li == NULL)
632 return FAIL;
633 li->li_tv.v_type = VAR_LIST;
634 li->li_tv.v_lock = 0;
635 li->li_tv.vval.v_list = list2;
636 list_append(list1, li);
637 ++list2->lv_refcount;
638 return OK;
639}
640
641/*
Bram Moolenaarda861d62016-07-17 15:46:27 +0200642 * Make a copy of "str" and append it as an item to list "l".
643 * When "len" >= 0 use "str[len]".
644 * Returns FAIL when out of memory.
645 */
646 int
647list_append_string(list_T *l, char_u *str, int len)
648{
649 listitem_T *li = listitem_alloc();
650
651 if (li == NULL)
652 return FAIL;
653 list_append(l, li);
654 li->li_tv.v_type = VAR_STRING;
655 li->li_tv.v_lock = 0;
656 if (str == NULL)
657 li->li_tv.vval.v_string = NULL;
658 else if ((li->li_tv.vval.v_string = (len >= 0 ? vim_strnsave(str, len)
659 : vim_strsave(str))) == NULL)
660 return FAIL;
661 return OK;
662}
663
664/*
665 * Append "n" to list "l".
666 * Returns FAIL when out of memory.
667 */
668 int
669list_append_number(list_T *l, varnumber_T n)
670{
671 listitem_T *li;
672
673 li = listitem_alloc();
674 if (li == NULL)
675 return FAIL;
676 li->li_tv.v_type = VAR_NUMBER;
677 li->li_tv.v_lock = 0;
678 li->li_tv.vval.v_number = n;
679 list_append(l, li);
680 return OK;
681}
682
683/*
684 * Insert typval_T "tv" in list "l" before "item".
685 * If "item" is NULL append at the end.
686 * Return FAIL when out of memory.
687 */
688 int
689list_insert_tv(list_T *l, typval_T *tv, listitem_T *item)
690{
691 listitem_T *ni = listitem_alloc();
692
693 if (ni == NULL)
694 return FAIL;
695 copy_tv(tv, &ni->li_tv);
696 list_insert(l, ni, item);
697 return OK;
698}
699
700 void
701list_insert(list_T *l, listitem_T *ni, listitem_T *item)
702{
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100703 range_list_materialize(l);
Bram Moolenaarda861d62016-07-17 15:46:27 +0200704 if (item == NULL)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100705 // Append new item at end of list.
Bram Moolenaarda861d62016-07-17 15:46:27 +0200706 list_append(l, ni);
707 else
708 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100709 // Insert new item before existing item.
Bram Moolenaarda861d62016-07-17 15:46:27 +0200710 ni->li_prev = item->li_prev;
711 ni->li_next = item;
712 if (item->li_prev == NULL)
713 {
714 l->lv_first = ni;
Bram Moolenaar0ff6aad2020-01-29 21:27:21 +0100715 ++l->lv_u.mat.lv_idx;
Bram Moolenaarda861d62016-07-17 15:46:27 +0200716 }
717 else
718 {
719 item->li_prev->li_next = ni;
Bram Moolenaar0ff6aad2020-01-29 21:27:21 +0100720 l->lv_u.mat.lv_idx_item = NULL;
Bram Moolenaarda861d62016-07-17 15:46:27 +0200721 }
722 item->li_prev = ni;
723 ++l->lv_len;
724 }
725}
726
727/*
728 * Extend "l1" with "l2".
729 * If "bef" is NULL append at the end, otherwise insert before this item.
730 * Returns FAIL when out of memory.
731 */
732 int
733list_extend(list_T *l1, list_T *l2, listitem_T *bef)
734{
735 listitem_T *item;
736 int todo = l2->lv_len;
737
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100738 range_list_materialize(l1);
739 range_list_materialize(l2);
740
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100741 // We also quit the loop when we have inserted the original item count of
742 // the list, avoid a hang when we extend a list with itself.
Bram Moolenaarda861d62016-07-17 15:46:27 +0200743 for (item = l2->lv_first; item != NULL && --todo >= 0; item = item->li_next)
744 if (list_insert_tv(l1, &item->li_tv, bef) == FAIL)
745 return FAIL;
746 return OK;
747}
748
749/*
750 * Concatenate lists "l1" and "l2" into a new list, stored in "tv".
751 * Return FAIL when out of memory.
752 */
753 int
754list_concat(list_T *l1, list_T *l2, typval_T *tv)
755{
756 list_T *l;
757
758 if (l1 == NULL || l2 == NULL)
759 return FAIL;
760
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100761 // make a copy of the first list.
Bram Moolenaarda861d62016-07-17 15:46:27 +0200762 l = list_copy(l1, FALSE, 0);
763 if (l == NULL)
764 return FAIL;
765 tv->v_type = VAR_LIST;
766 tv->vval.v_list = l;
767
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100768 // append all items from the second list
Bram Moolenaarda861d62016-07-17 15:46:27 +0200769 return list_extend(l, l2, NULL);
770}
771
772/*
773 * Make a copy of list "orig". Shallow if "deep" is FALSE.
774 * The refcount of the new list is set to 1.
775 * See item_copy() for "copyID".
776 * Returns NULL when out of memory.
777 */
778 list_T *
779list_copy(list_T *orig, int deep, int copyID)
780{
781 list_T *copy;
782 listitem_T *item;
783 listitem_T *ni;
784
785 if (orig == NULL)
786 return NULL;
787
788 copy = list_alloc();
789 if (copy != NULL)
790 {
791 if (copyID != 0)
792 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100793 // Do this before adding the items, because one of the items may
794 // refer back to this list.
Bram Moolenaarda861d62016-07-17 15:46:27 +0200795 orig->lv_copyID = copyID;
796 orig->lv_copylist = copy;
797 }
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100798 range_list_materialize(orig);
Bram Moolenaarda861d62016-07-17 15:46:27 +0200799 for (item = orig->lv_first; item != NULL && !got_int;
800 item = item->li_next)
801 {
802 ni = listitem_alloc();
803 if (ni == NULL)
804 break;
805 if (deep)
806 {
807 if (item_copy(&item->li_tv, &ni->li_tv, deep, copyID) == FAIL)
808 {
809 vim_free(ni);
810 break;
811 }
812 }
813 else
814 copy_tv(&item->li_tv, &ni->li_tv);
815 list_append(copy, ni);
816 }
817 ++copy->lv_refcount;
818 if (item != NULL)
819 {
820 list_unref(copy);
821 copy = NULL;
822 }
823 }
824
825 return copy;
826}
827
828/*
829 * Remove items "item" to "item2" from list "l".
830 * Does not free the listitem or the value!
831 * This used to be called list_remove, but that conflicts with a Sun header
832 * file.
833 */
834 void
835vimlist_remove(list_T *l, listitem_T *item, listitem_T *item2)
836{
837 listitem_T *ip;
838
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100839 range_list_materialize(l);
840
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100841 // notify watchers
Bram Moolenaarda861d62016-07-17 15:46:27 +0200842 for (ip = item; ip != NULL; ip = ip->li_next)
843 {
844 --l->lv_len;
845 list_fix_watch(l, ip);
846 if (ip == item2)
847 break;
848 }
849
850 if (item2->li_next == NULL)
Bram Moolenaar0ff6aad2020-01-29 21:27:21 +0100851 l->lv_u.mat.lv_last = item->li_prev;
Bram Moolenaarda861d62016-07-17 15:46:27 +0200852 else
853 item2->li_next->li_prev = item->li_prev;
854 if (item->li_prev == NULL)
855 l->lv_first = item2->li_next;
856 else
857 item->li_prev->li_next = item2->li_next;
Bram Moolenaar0ff6aad2020-01-29 21:27:21 +0100858 l->lv_u.mat.lv_idx_item = NULL;
Bram Moolenaarda861d62016-07-17 15:46:27 +0200859}
860
861/*
862 * Return an allocated string with the string representation of a list.
863 * May return NULL.
864 */
865 char_u *
866list2string(typval_T *tv, int copyID, int restore_copyID)
867{
868 garray_T ga;
869
870 if (tv->vval.v_list == NULL)
871 return NULL;
872 ga_init2(&ga, (int)sizeof(char), 80);
873 ga_append(&ga, '[');
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100874 range_list_materialize(tv->vval.v_list);
Bram Moolenaarda861d62016-07-17 15:46:27 +0200875 if (list_join(&ga, tv->vval.v_list, (char_u *)", ",
876 FALSE, restore_copyID, copyID) == FAIL)
877 {
878 vim_free(ga.ga_data);
879 return NULL;
880 }
881 ga_append(&ga, ']');
882 ga_append(&ga, NUL);
883 return (char_u *)ga.ga_data;
884}
885
886typedef struct join_S {
887 char_u *s;
888 char_u *tofree;
889} join_T;
890
891 static int
892list_join_inner(
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100893 garray_T *gap, // to store the result in
Bram Moolenaarda861d62016-07-17 15:46:27 +0200894 list_T *l,
895 char_u *sep,
896 int echo_style,
897 int restore_copyID,
898 int copyID,
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100899 garray_T *join_gap) // to keep each list item string
Bram Moolenaarda861d62016-07-17 15:46:27 +0200900{
901 int i;
902 join_T *p;
903 int len;
904 int sumlen = 0;
905 int first = TRUE;
906 char_u *tofree;
907 char_u numbuf[NUMBUFLEN];
908 listitem_T *item;
909 char_u *s;
910
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100911 // Stringify each item in the list.
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100912 range_list_materialize(l);
Bram Moolenaarda861d62016-07-17 15:46:27 +0200913 for (item = l->lv_first; item != NULL && !got_int; item = item->li_next)
914 {
915 s = echo_string_core(&item->li_tv, &tofree, numbuf, copyID,
Bram Moolenaar35422f42017-08-05 16:33:56 +0200916 echo_style, restore_copyID, !echo_style);
Bram Moolenaarda861d62016-07-17 15:46:27 +0200917 if (s == NULL)
918 return FAIL;
919
920 len = (int)STRLEN(s);
921 sumlen += len;
922
923 (void)ga_grow(join_gap, 1);
924 p = ((join_T *)join_gap->ga_data) + (join_gap->ga_len++);
925 if (tofree != NULL || s != numbuf)
926 {
927 p->s = s;
928 p->tofree = tofree;
929 }
930 else
931 {
932 p->s = vim_strnsave(s, len);
933 p->tofree = p->s;
934 }
935
936 line_breakcheck();
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100937 if (did_echo_string_emsg) // recursion error, bail out
Bram Moolenaarda861d62016-07-17 15:46:27 +0200938 break;
939 }
940
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100941 // Allocate result buffer with its total size, avoid re-allocation and
942 // multiple copy operations. Add 2 for a tailing ']' and NUL.
Bram Moolenaarda861d62016-07-17 15:46:27 +0200943 if (join_gap->ga_len >= 2)
944 sumlen += (int)STRLEN(sep) * (join_gap->ga_len - 1);
945 if (ga_grow(gap, sumlen + 2) == FAIL)
946 return FAIL;
947
948 for (i = 0; i < join_gap->ga_len && !got_int; ++i)
949 {
950 if (first)
951 first = FALSE;
952 else
953 ga_concat(gap, sep);
954 p = ((join_T *)join_gap->ga_data) + i;
955
956 if (p->s != NULL)
957 ga_concat(gap, p->s);
958 line_breakcheck();
959 }
960
961 return OK;
962}
963
964/*
965 * Join list "l" into a string in "*gap", using separator "sep".
966 * When "echo_style" is TRUE use String as echoed, otherwise as inside a List.
967 * Return FAIL or OK.
968 */
969 int
970list_join(
971 garray_T *gap,
972 list_T *l,
973 char_u *sep,
974 int echo_style,
975 int restore_copyID,
976 int copyID)
977{
978 garray_T join_ga;
979 int retval;
980 join_T *p;
981 int i;
982
983 if (l->lv_len < 1)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100984 return OK; // nothing to do
Bram Moolenaarda861d62016-07-17 15:46:27 +0200985 ga_init2(&join_ga, (int)sizeof(join_T), l->lv_len);
986 retval = list_join_inner(gap, l, sep, echo_style, restore_copyID,
987 copyID, &join_ga);
988
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100989 // Dispose each item in join_ga.
Bram Moolenaarda861d62016-07-17 15:46:27 +0200990 if (join_ga.ga_data != NULL)
991 {
992 p = (join_T *)join_ga.ga_data;
993 for (i = 0; i < join_ga.ga_len; ++i)
994 {
995 vim_free(p->tofree);
996 ++p;
997 }
998 ga_clear(&join_ga);
999 }
1000
1001 return retval;
1002}
1003
1004/*
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001005 * "join()" function
1006 */
1007 void
1008f_join(typval_T *argvars, typval_T *rettv)
1009{
1010 garray_T ga;
1011 char_u *sep;
1012
1013 if (argvars[0].v_type != VAR_LIST)
1014 {
1015 emsg(_(e_listreq));
1016 return;
1017 }
1018 if (argvars[0].vval.v_list == NULL)
1019 return;
1020 if (argvars[1].v_type == VAR_UNKNOWN)
1021 sep = (char_u *)" ";
1022 else
1023 sep = tv_get_string_chk(&argvars[1]);
1024
1025 rettv->v_type = VAR_STRING;
1026
1027 if (sep != NULL)
1028 {
1029 ga_init2(&ga, (int)sizeof(char), 80);
1030 list_join(&ga, argvars[0].vval.v_list, sep, TRUE, FALSE, 0);
1031 ga_append(&ga, NUL);
1032 rettv->vval.v_string = (char_u *)ga.ga_data;
1033 }
1034 else
1035 rettv->vval.v_string = NULL;
1036}
1037
1038/*
Bram Moolenaarda861d62016-07-17 15:46:27 +02001039 * Allocate a variable for a List and fill it from "*arg".
1040 * Return OK or FAIL.
1041 */
1042 int
Bram Moolenaar8a7d6542020-01-26 15:56:19 +01001043get_list_tv(char_u **arg, typval_T *rettv, int evaluate, int do_error)
Bram Moolenaarda861d62016-07-17 15:46:27 +02001044{
1045 list_T *l = NULL;
1046 typval_T tv;
1047 listitem_T *item;
1048
1049 if (evaluate)
1050 {
1051 l = list_alloc();
1052 if (l == NULL)
1053 return FAIL;
1054 }
1055
1056 *arg = skipwhite(*arg + 1);
1057 while (**arg != ']' && **arg != NUL)
1058 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001059 if (eval1(arg, &tv, evaluate) == FAIL) // recursive!
Bram Moolenaarda861d62016-07-17 15:46:27 +02001060 goto failret;
1061 if (evaluate)
1062 {
1063 item = listitem_alloc();
1064 if (item != NULL)
1065 {
1066 item->li_tv = tv;
1067 item->li_tv.v_lock = 0;
1068 list_append(l, item);
1069 }
1070 else
1071 clear_tv(&tv);
1072 }
1073
1074 if (**arg == ']')
1075 break;
1076 if (**arg != ',')
1077 {
Bram Moolenaar8a7d6542020-01-26 15:56:19 +01001078 if (do_error)
1079 semsg(_("E696: Missing comma in List: %s"), *arg);
Bram Moolenaarda861d62016-07-17 15:46:27 +02001080 goto failret;
1081 }
1082 *arg = skipwhite(*arg + 1);
1083 }
1084
1085 if (**arg != ']')
1086 {
Bram Moolenaar8a7d6542020-01-26 15:56:19 +01001087 if (do_error)
Bram Moolenaaree619e52020-03-28 21:38:06 +01001088 semsg(_(e_list_end), *arg);
Bram Moolenaarda861d62016-07-17 15:46:27 +02001089failret:
1090 if (evaluate)
1091 list_free(l);
1092 return FAIL;
1093 }
1094
1095 *arg = skipwhite(*arg + 1);
1096 if (evaluate)
Bram Moolenaar45cf6e92017-04-30 20:25:19 +02001097 rettv_list_set(rettv, l);
Bram Moolenaarda861d62016-07-17 15:46:27 +02001098
1099 return OK;
1100}
1101
Bram Moolenaar73dad1e2016-07-17 22:13:49 +02001102/*
Bram Moolenaarcaa55b62017-01-10 13:51:09 +01001103 * Write "list" of strings to file "fd".
Bram Moolenaar73dad1e2016-07-17 22:13:49 +02001104 */
1105 int
1106write_list(FILE *fd, list_T *list, int binary)
1107{
1108 listitem_T *li;
1109 int c;
1110 int ret = OK;
1111 char_u *s;
1112
Bram Moolenaar8a7d6542020-01-26 15:56:19 +01001113 range_list_materialize(list);
Bram Moolenaaraeea7212020-04-02 18:50:46 +02001114 FOR_ALL_LIST_ITEMS(list, li)
Bram Moolenaar73dad1e2016-07-17 22:13:49 +02001115 {
Bram Moolenaard155d7a2018-12-21 16:04:21 +01001116 for (s = tv_get_string(&li->li_tv); *s != NUL; ++s)
Bram Moolenaar73dad1e2016-07-17 22:13:49 +02001117 {
1118 if (*s == '\n')
1119 c = putc(NUL, fd);
1120 else
1121 c = putc(*s, fd);
1122 if (c == EOF)
1123 {
1124 ret = FAIL;
1125 break;
1126 }
1127 }
1128 if (!binary || li->li_next != NULL)
1129 if (putc('\n', fd) == EOF)
1130 {
1131 ret = FAIL;
1132 break;
1133 }
1134 if (ret == FAIL)
1135 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001136 emsg(_(e_write));
Bram Moolenaar73dad1e2016-07-17 22:13:49 +02001137 break;
1138 }
1139 }
1140 return ret;
1141}
1142
Bram Moolenaardf48fb42016-07-22 21:50:18 +02001143/*
1144 * Initialize a static list with 10 items.
1145 */
1146 void
1147init_static_list(staticList10_T *sl)
1148{
1149 list_T *l = &sl->sl_list;
1150 int i;
1151
1152 memset(sl, 0, sizeof(staticList10_T));
1153 l->lv_first = &sl->sl_items[0];
Bram Moolenaar0ff6aad2020-01-29 21:27:21 +01001154 l->lv_u.mat.lv_last = &sl->sl_items[9];
Bram Moolenaardf48fb42016-07-22 21:50:18 +02001155 l->lv_refcount = DO_NOT_FREE_CNT;
1156 l->lv_lock = VAR_FIXED;
1157 sl->sl_list.lv_len = 10;
1158
1159 for (i = 0; i < 10; ++i)
1160 {
1161 listitem_T *li = &sl->sl_items[i];
1162
1163 if (i == 0)
1164 li->li_prev = NULL;
1165 else
1166 li->li_prev = li - 1;
1167 if (i == 9)
1168 li->li_next = NULL;
1169 else
1170 li->li_next = li + 1;
1171 }
1172}
1173
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001174/*
1175 * "list2str()" function
1176 */
1177 void
1178f_list2str(typval_T *argvars, typval_T *rettv)
1179{
1180 list_T *l;
1181 listitem_T *li;
1182 garray_T ga;
1183 int utf8 = FALSE;
1184
1185 rettv->v_type = VAR_STRING;
1186 rettv->vval.v_string = NULL;
1187 if (argvars[0].v_type != VAR_LIST)
1188 {
1189 emsg(_(e_invarg));
1190 return;
1191 }
1192
1193 l = argvars[0].vval.v_list;
1194 if (l == NULL)
1195 return; // empty list results in empty string
1196
1197 if (argvars[1].v_type != VAR_UNKNOWN)
1198 utf8 = (int)tv_get_number_chk(&argvars[1], NULL);
1199
Bram Moolenaar8a7d6542020-01-26 15:56:19 +01001200 range_list_materialize(l);
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001201 ga_init2(&ga, 1, 80);
1202 if (has_mbyte || utf8)
1203 {
1204 char_u buf[MB_MAXBYTES + 1];
1205 int (*char2bytes)(int, char_u *);
1206
1207 if (utf8 || enc_utf8)
1208 char2bytes = utf_char2bytes;
1209 else
1210 char2bytes = mb_char2bytes;
1211
Bram Moolenaaraeea7212020-04-02 18:50:46 +02001212 FOR_ALL_LIST_ITEMS(l, li)
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001213 {
1214 buf[(*char2bytes)(tv_get_number(&li->li_tv), buf)] = NUL;
1215 ga_concat(&ga, buf);
1216 }
1217 ga_append(&ga, NUL);
1218 }
1219 else if (ga_grow(&ga, list_len(l) + 1) == OK)
1220 {
Bram Moolenaaraeea7212020-04-02 18:50:46 +02001221 FOR_ALL_LIST_ITEMS(l, li)
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001222 ga_append(&ga, tv_get_number(&li->li_tv));
1223 ga_append(&ga, NUL);
1224 }
1225
1226 rettv->v_type = VAR_STRING;
1227 rettv->vval.v_string = ga.ga_data;
1228}
1229
Bram Moolenaarbdff0122020-04-05 18:56:05 +02001230 static void
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001231list_remove(typval_T *argvars, typval_T *rettv, char_u *arg_errmsg)
1232{
1233 list_T *l;
1234 listitem_T *item, *item2;
1235 listitem_T *li;
1236 int error = FALSE;
1237 int idx;
1238
1239 if ((l = argvars[0].vval.v_list) == NULL
1240 || var_check_lock(l->lv_lock, arg_errmsg, TRUE))
1241 return;
1242
1243 idx = (long)tv_get_number_chk(&argvars[1], &error);
1244 if (error)
1245 ; // type error: do nothing, errmsg already given
1246 else if ((item = list_find(l, idx)) == NULL)
1247 semsg(_(e_listidx), idx);
1248 else
1249 {
1250 if (argvars[2].v_type == VAR_UNKNOWN)
1251 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001252 // Remove one item, return its value.
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001253 vimlist_remove(l, item, item);
1254 *rettv = item->li_tv;
Bram Moolenaar8a7d6542020-01-26 15:56:19 +01001255 list_free_item(l, item);
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001256 }
1257 else
1258 {
1259 // Remove range of items, return list with values.
1260 int end = (long)tv_get_number_chk(&argvars[2], &error);
1261
1262 if (error)
1263 ; // type error: do nothing
1264 else if ((item2 = list_find(l, end)) == NULL)
1265 semsg(_(e_listidx), end);
1266 else
1267 {
1268 int cnt = 0;
1269
1270 for (li = item; li != NULL; li = li->li_next)
1271 {
1272 ++cnt;
1273 if (li == item2)
1274 break;
1275 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001276 if (li == NULL) // didn't find "item2" after "item"
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001277 emsg(_(e_invrange));
1278 else
1279 {
1280 vimlist_remove(l, item, item2);
1281 if (rettv_list_alloc(rettv) == OK)
1282 {
1283 l = rettv->vval.v_list;
1284 l->lv_first = item;
Bram Moolenaar0ff6aad2020-01-29 21:27:21 +01001285 l->lv_u.mat.lv_last = item2;
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001286 item->li_prev = NULL;
1287 item2->li_next = NULL;
1288 l->lv_len = cnt;
1289 }
1290 }
1291 }
1292 }
1293 }
1294}
1295
1296static int item_compare(const void *s1, const void *s2);
1297static int item_compare2(const void *s1, const void *s2);
1298
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001299// struct used in the array that's given to qsort()
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001300typedef struct
1301{
1302 listitem_T *item;
1303 int idx;
1304} sortItem_T;
1305
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001306// struct storing information about current sort
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001307typedef struct
1308{
1309 int item_compare_ic;
1310 int item_compare_numeric;
1311 int item_compare_numbers;
1312#ifdef FEAT_FLOAT
1313 int item_compare_float;
1314#endif
1315 char_u *item_compare_func;
1316 partial_T *item_compare_partial;
1317 dict_T *item_compare_selfdict;
1318 int item_compare_func_err;
1319 int item_compare_keep_zero;
1320} sortinfo_T;
1321static sortinfo_T *sortinfo = NULL;
1322#define ITEM_COMPARE_FAIL 999
1323
1324/*
1325 * Compare functions for f_sort() and f_uniq() below.
1326 */
1327 static int
1328item_compare(const void *s1, const void *s2)
1329{
1330 sortItem_T *si1, *si2;
1331 typval_T *tv1, *tv2;
1332 char_u *p1, *p2;
1333 char_u *tofree1 = NULL, *tofree2 = NULL;
1334 int res;
1335 char_u numbuf1[NUMBUFLEN];
1336 char_u numbuf2[NUMBUFLEN];
1337
1338 si1 = (sortItem_T *)s1;
1339 si2 = (sortItem_T *)s2;
1340 tv1 = &si1->item->li_tv;
1341 tv2 = &si2->item->li_tv;
1342
1343 if (sortinfo->item_compare_numbers)
1344 {
1345 varnumber_T v1 = tv_get_number(tv1);
1346 varnumber_T v2 = tv_get_number(tv2);
1347
1348 return v1 == v2 ? 0 : v1 > v2 ? 1 : -1;
1349 }
1350
1351#ifdef FEAT_FLOAT
1352 if (sortinfo->item_compare_float)
1353 {
1354 float_T v1 = tv_get_float(tv1);
1355 float_T v2 = tv_get_float(tv2);
1356
1357 return v1 == v2 ? 0 : v1 > v2 ? 1 : -1;
1358 }
1359#endif
1360
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001361 // tv2string() puts quotes around a string and allocates memory. Don't do
1362 // that for string variables. Use a single quote when comparing with a
1363 // non-string to do what the docs promise.
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001364 if (tv1->v_type == VAR_STRING)
1365 {
1366 if (tv2->v_type != VAR_STRING || sortinfo->item_compare_numeric)
1367 p1 = (char_u *)"'";
1368 else
1369 p1 = tv1->vval.v_string;
1370 }
1371 else
1372 p1 = tv2string(tv1, &tofree1, numbuf1, 0);
1373 if (tv2->v_type == VAR_STRING)
1374 {
1375 if (tv1->v_type != VAR_STRING || sortinfo->item_compare_numeric)
1376 p2 = (char_u *)"'";
1377 else
1378 p2 = tv2->vval.v_string;
1379 }
1380 else
1381 p2 = tv2string(tv2, &tofree2, numbuf2, 0);
1382 if (p1 == NULL)
1383 p1 = (char_u *)"";
1384 if (p2 == NULL)
1385 p2 = (char_u *)"";
1386 if (!sortinfo->item_compare_numeric)
1387 {
1388 if (sortinfo->item_compare_ic)
1389 res = STRICMP(p1, p2);
1390 else
1391 res = STRCMP(p1, p2);
1392 }
1393 else
1394 {
1395 double n1, n2;
1396 n1 = strtod((char *)p1, (char **)&p1);
1397 n2 = strtod((char *)p2, (char **)&p2);
1398 res = n1 == n2 ? 0 : n1 > n2 ? 1 : -1;
1399 }
1400
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001401 // When the result would be zero, compare the item indexes. Makes the
1402 // sort stable.
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001403 if (res == 0 && !sortinfo->item_compare_keep_zero)
1404 res = si1->idx > si2->idx ? 1 : -1;
1405
1406 vim_free(tofree1);
1407 vim_free(tofree2);
1408 return res;
1409}
1410
1411 static int
1412item_compare2(const void *s1, const void *s2)
1413{
1414 sortItem_T *si1, *si2;
1415 int res;
1416 typval_T rettv;
1417 typval_T argv[3];
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001418 char_u *func_name;
1419 partial_T *partial = sortinfo->item_compare_partial;
Bram Moolenaarc6538bc2019-08-03 18:17:11 +02001420 funcexe_T funcexe;
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001421
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001422 // shortcut after failure in previous call; compare all items equal
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001423 if (sortinfo->item_compare_func_err)
1424 return 0;
1425
1426 si1 = (sortItem_T *)s1;
1427 si2 = (sortItem_T *)s2;
1428
1429 if (partial == NULL)
1430 func_name = sortinfo->item_compare_func;
1431 else
1432 func_name = partial_name(partial);
1433
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001434 // Copy the values. This is needed to be able to set v_lock to VAR_FIXED
1435 // in the copy without changing the original list items.
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001436 copy_tv(&si1->item->li_tv, &argv[0]);
1437 copy_tv(&si2->item->li_tv, &argv[1]);
1438
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001439 rettv.v_type = VAR_UNKNOWN; // clear_tv() uses this
Bram Moolenaarc6538bc2019-08-03 18:17:11 +02001440 vim_memset(&funcexe, 0, sizeof(funcexe));
1441 funcexe.evaluate = TRUE;
1442 funcexe.partial = partial;
1443 funcexe.selfdict = sortinfo->item_compare_selfdict;
1444 res = call_func(func_name, -1, &rettv, 2, argv, &funcexe);
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001445 clear_tv(&argv[0]);
1446 clear_tv(&argv[1]);
1447
1448 if (res == FAIL)
1449 res = ITEM_COMPARE_FAIL;
1450 else
1451 res = (int)tv_get_number_chk(&rettv, &sortinfo->item_compare_func_err);
1452 if (sortinfo->item_compare_func_err)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001453 res = ITEM_COMPARE_FAIL; // return value has wrong type
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001454 clear_tv(&rettv);
1455
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001456 // When the result would be zero, compare the pointers themselves. Makes
1457 // the sort stable.
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001458 if (res == 0 && !sortinfo->item_compare_keep_zero)
1459 res = si1->idx > si2->idx ? 1 : -1;
1460
1461 return res;
1462}
1463
1464/*
1465 * "sort()" or "uniq()" function
1466 */
1467 static void
1468do_sort_uniq(typval_T *argvars, typval_T *rettv, int sort)
1469{
1470 list_T *l;
1471 listitem_T *li;
1472 sortItem_T *ptrs;
1473 sortinfo_T *old_sortinfo;
1474 sortinfo_T info;
1475 long len;
1476 long i;
1477
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001478 // Pointer to current info struct used in compare function. Save and
1479 // restore the current one for nested calls.
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001480 old_sortinfo = sortinfo;
1481 sortinfo = &info;
1482
1483 if (argvars[0].v_type != VAR_LIST)
1484 semsg(_(e_listarg), sort ? "sort()" : "uniq()");
1485 else
1486 {
1487 l = argvars[0].vval.v_list;
1488 if (l == NULL || var_check_lock(l->lv_lock,
1489 (char_u *)(sort ? N_("sort() argument") : N_("uniq() argument")),
1490 TRUE))
1491 goto theend;
1492 rettv_list_set(rettv, l);
Bram Moolenaar8a7d6542020-01-26 15:56:19 +01001493 range_list_materialize(l);
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001494
1495 len = list_len(l);
1496 if (len <= 1)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001497 goto theend; // short list sorts pretty quickly
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001498
1499 info.item_compare_ic = FALSE;
1500 info.item_compare_numeric = FALSE;
1501 info.item_compare_numbers = FALSE;
1502#ifdef FEAT_FLOAT
1503 info.item_compare_float = FALSE;
1504#endif
1505 info.item_compare_func = NULL;
1506 info.item_compare_partial = NULL;
1507 info.item_compare_selfdict = NULL;
1508 if (argvars[1].v_type != VAR_UNKNOWN)
1509 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001510 // optional second argument: {func}
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001511 if (argvars[1].v_type == VAR_FUNC)
1512 info.item_compare_func = argvars[1].vval.v_string;
1513 else if (argvars[1].v_type == VAR_PARTIAL)
1514 info.item_compare_partial = argvars[1].vval.v_partial;
1515 else
1516 {
1517 int error = FALSE;
1518
1519 i = (long)tv_get_number_chk(&argvars[1], &error);
1520 if (error)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001521 goto theend; // type error; errmsg already given
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001522 if (i == 1)
1523 info.item_compare_ic = TRUE;
1524 else if (argvars[1].v_type != VAR_NUMBER)
1525 info.item_compare_func = tv_get_string(&argvars[1]);
1526 else if (i != 0)
1527 {
1528 emsg(_(e_invarg));
1529 goto theend;
1530 }
1531 if (info.item_compare_func != NULL)
1532 {
1533 if (*info.item_compare_func == NUL)
1534 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001535 // empty string means default sort
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001536 info.item_compare_func = NULL;
1537 }
1538 else if (STRCMP(info.item_compare_func, "n") == 0)
1539 {
1540 info.item_compare_func = NULL;
1541 info.item_compare_numeric = TRUE;
1542 }
1543 else if (STRCMP(info.item_compare_func, "N") == 0)
1544 {
1545 info.item_compare_func = NULL;
1546 info.item_compare_numbers = TRUE;
1547 }
1548#ifdef FEAT_FLOAT
1549 else if (STRCMP(info.item_compare_func, "f") == 0)
1550 {
1551 info.item_compare_func = NULL;
1552 info.item_compare_float = TRUE;
1553 }
1554#endif
1555 else if (STRCMP(info.item_compare_func, "i") == 0)
1556 {
1557 info.item_compare_func = NULL;
1558 info.item_compare_ic = TRUE;
1559 }
1560 }
1561 }
1562
1563 if (argvars[2].v_type != VAR_UNKNOWN)
1564 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001565 // optional third argument: {dict}
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001566 if (argvars[2].v_type != VAR_DICT)
1567 {
1568 emsg(_(e_dictreq));
1569 goto theend;
1570 }
1571 info.item_compare_selfdict = argvars[2].vval.v_dict;
1572 }
1573 }
1574
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001575 // Make an array with each entry pointing to an item in the List.
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001576 ptrs = ALLOC_MULT(sortItem_T, len);
1577 if (ptrs == NULL)
1578 goto theend;
1579
1580 i = 0;
1581 if (sort)
1582 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001583 // sort(): ptrs will be the list to sort
Bram Moolenaaraeea7212020-04-02 18:50:46 +02001584 FOR_ALL_LIST_ITEMS(l, li)
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001585 {
1586 ptrs[i].item = li;
1587 ptrs[i].idx = i;
1588 ++i;
1589 }
1590
1591 info.item_compare_func_err = FALSE;
1592 info.item_compare_keep_zero = FALSE;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001593 // test the compare function
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001594 if ((info.item_compare_func != NULL
1595 || info.item_compare_partial != NULL)
1596 && item_compare2((void *)&ptrs[0], (void *)&ptrs[1])
1597 == ITEM_COMPARE_FAIL)
1598 emsg(_("E702: Sort compare function failed"));
1599 else
1600 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001601 // Sort the array with item pointers.
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001602 qsort((void *)ptrs, (size_t)len, sizeof(sortItem_T),
1603 info.item_compare_func == NULL
1604 && info.item_compare_partial == NULL
1605 ? item_compare : item_compare2);
1606
1607 if (!info.item_compare_func_err)
1608 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001609 // Clear the List and append the items in sorted order.
Bram Moolenaar0ff6aad2020-01-29 21:27:21 +01001610 l->lv_first = l->lv_u.mat.lv_last
1611 = l->lv_u.mat.lv_idx_item = NULL;
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001612 l->lv_len = 0;
1613 for (i = 0; i < len; ++i)
1614 list_append(l, ptrs[i].item);
1615 }
1616 }
1617 }
1618 else
1619 {
1620 int (*item_compare_func_ptr)(const void *, const void *);
1621
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001622 // f_uniq(): ptrs will be a stack of items to remove
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001623 info.item_compare_func_err = FALSE;
1624 info.item_compare_keep_zero = TRUE;
1625 item_compare_func_ptr = info.item_compare_func != NULL
1626 || info.item_compare_partial != NULL
1627 ? item_compare2 : item_compare;
1628
1629 for (li = l->lv_first; li != NULL && li->li_next != NULL;
1630 li = li->li_next)
1631 {
1632 if (item_compare_func_ptr((void *)&li, (void *)&li->li_next)
1633 == 0)
1634 ptrs[i++].item = li;
1635 if (info.item_compare_func_err)
1636 {
1637 emsg(_("E882: Uniq compare function failed"));
1638 break;
1639 }
1640 }
1641
1642 if (!info.item_compare_func_err)
1643 {
1644 while (--i >= 0)
1645 {
1646 li = ptrs[i].item->li_next;
1647 ptrs[i].item->li_next = li->li_next;
1648 if (li->li_next != NULL)
1649 li->li_next->li_prev = ptrs[i].item;
1650 else
Bram Moolenaar0ff6aad2020-01-29 21:27:21 +01001651 l->lv_u.mat.lv_last = ptrs[i].item;
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001652 list_fix_watch(l, li);
Bram Moolenaar8a7d6542020-01-26 15:56:19 +01001653 listitem_free(l, li);
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001654 l->lv_len--;
1655 }
1656 }
1657 }
1658
1659 vim_free(ptrs);
1660 }
1661theend:
1662 sortinfo = old_sortinfo;
1663}
1664
1665/*
1666 * "sort({list})" function
1667 */
1668 void
1669f_sort(typval_T *argvars, typval_T *rettv)
1670{
1671 do_sort_uniq(argvars, rettv, TRUE);
1672}
1673
1674/*
1675 * "uniq({list})" function
1676 */
1677 void
1678f_uniq(typval_T *argvars, typval_T *rettv)
1679{
1680 do_sort_uniq(argvars, rettv, FALSE);
1681}
1682
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02001683/*
1684 * Handle one item for map() and filter().
1685 */
1686 static int
1687filter_map_one(typval_T *tv, typval_T *expr, int map, int *remp)
1688{
1689 typval_T rettv;
1690 typval_T argv[3];
1691 int retval = FAIL;
1692
1693 copy_tv(tv, get_vim_var_tv(VV_VAL));
1694 argv[0] = *get_vim_var_tv(VV_KEY);
1695 argv[1] = *get_vim_var_tv(VV_VAL);
1696 if (eval_expr_typval(expr, argv, 2, &rettv) == FAIL)
1697 goto theend;
1698 if (map)
1699 {
1700 // map(): replace the list item value
1701 clear_tv(tv);
1702 rettv.v_lock = 0;
1703 *tv = rettv;
1704 }
1705 else
1706 {
1707 int error = FALSE;
1708
1709 // filter(): when expr is zero remove the item
1710 *remp = (tv_get_number_chk(&rettv, &error) == 0);
1711 clear_tv(&rettv);
1712 // On type error, nothing has been removed; return FAIL to stop the
1713 // loop. The error message was given by tv_get_number_chk().
1714 if (error)
1715 goto theend;
1716 }
1717 retval = OK;
1718theend:
1719 clear_tv(get_vim_var_tv(VV_VAL));
1720 return retval;
1721}
1722
1723/*
1724 * Implementation of map() and filter().
1725 */
1726 static void
1727filter_map(typval_T *argvars, typval_T *rettv, int map)
1728{
1729 typval_T *expr;
1730 listitem_T *li, *nli;
1731 list_T *l = NULL;
1732 dictitem_T *di;
1733 hashtab_T *ht;
1734 hashitem_T *hi;
1735 dict_T *d = NULL;
1736 blob_T *b = NULL;
1737 int rem;
1738 int todo;
1739 char_u *ermsg = (char_u *)(map ? "map()" : "filter()");
1740 char_u *arg_errmsg = (char_u *)(map ? N_("map() argument")
1741 : N_("filter() argument"));
1742 int save_did_emsg;
1743 int idx = 0;
1744
1745 if (argvars[0].v_type == VAR_BLOB)
1746 {
1747 if ((b = argvars[0].vval.v_blob) == NULL)
1748 return;
1749 }
1750 else if (argvars[0].v_type == VAR_LIST)
1751 {
1752 if ((l = argvars[0].vval.v_list) == NULL
1753 || (!map && var_check_lock(l->lv_lock, arg_errmsg, TRUE)))
1754 return;
1755 }
1756 else if (argvars[0].v_type == VAR_DICT)
1757 {
1758 if ((d = argvars[0].vval.v_dict) == NULL
1759 || (!map && var_check_lock(d->dv_lock, arg_errmsg, TRUE)))
1760 return;
1761 }
1762 else
1763 {
1764 semsg(_(e_listdictarg), ermsg);
1765 return;
1766 }
1767
1768 expr = &argvars[1];
1769 // On type errors, the preceding call has already displayed an error
1770 // message. Avoid a misleading error message for an empty string that
1771 // was not passed as argument.
1772 if (expr->v_type != VAR_UNKNOWN)
1773 {
1774 typval_T save_val;
1775 typval_T save_key;
1776
1777 prepare_vimvar(VV_VAL, &save_val);
1778 prepare_vimvar(VV_KEY, &save_key);
1779
1780 // We reset "did_emsg" to be able to detect whether an error
1781 // occurred during evaluation of the expression.
1782 save_did_emsg = did_emsg;
1783 did_emsg = FALSE;
1784
1785 if (argvars[0].v_type == VAR_DICT)
1786 {
Bram Moolenaardb661fb2020-01-29 22:17:16 +01001787 int prev_lock = d->dv_lock;
1788
1789 if (map && d->dv_lock == 0)
1790 d->dv_lock = VAR_LOCKED;
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02001791 ht = &d->dv_hashtab;
1792 hash_lock(ht);
1793 todo = (int)ht->ht_used;
1794 for (hi = ht->ht_array; todo > 0; ++hi)
1795 {
1796 if (!HASHITEM_EMPTY(hi))
1797 {
1798 int r;
1799
1800 --todo;
1801 di = HI2DI(hi);
1802 if (map && (var_check_lock(di->di_tv.v_lock,
1803 arg_errmsg, TRUE)
1804 || var_check_ro(di->di_flags,
1805 arg_errmsg, TRUE)))
1806 break;
1807 set_vim_var_string(VV_KEY, di->di_key, -1);
1808 r = filter_map_one(&di->di_tv, expr, map, &rem);
1809 clear_tv(get_vim_var_tv(VV_KEY));
1810 if (r == FAIL || did_emsg)
1811 break;
1812 if (!map && rem)
1813 {
1814 if (var_check_fixed(di->di_flags, arg_errmsg, TRUE)
1815 || var_check_ro(di->di_flags, arg_errmsg, TRUE))
1816 break;
1817 dictitem_remove(d, di);
1818 }
1819 }
1820 }
1821 hash_unlock(ht);
Bram Moolenaardb661fb2020-01-29 22:17:16 +01001822 d->dv_lock = prev_lock;
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02001823 }
1824 else if (argvars[0].v_type == VAR_BLOB)
1825 {
1826 int i;
1827 typval_T tv;
Bram Moolenaar49c57ce2020-01-15 20:51:34 +01001828 varnumber_T val;
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02001829
1830 // set_vim_var_nr() doesn't set the type
1831 set_vim_var_type(VV_KEY, VAR_NUMBER);
1832
1833 for (i = 0; i < b->bv_ga.ga_len; i++)
1834 {
1835 tv.v_type = VAR_NUMBER;
Bram Moolenaar49c57ce2020-01-15 20:51:34 +01001836 val = blob_get(b, i);
1837 tv.vval.v_number = val;
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02001838 set_vim_var_nr(VV_KEY, idx);
1839 if (filter_map_one(&tv, expr, map, &rem) == FAIL || did_emsg)
1840 break;
1841 if (tv.v_type != VAR_NUMBER)
1842 {
1843 emsg(_(e_invalblob));
1844 break;
1845 }
Bram Moolenaar49c57ce2020-01-15 20:51:34 +01001846 if (map)
1847 {
1848 if (tv.vval.v_number != val)
1849 blob_set(b, i, tv.vval.v_number);
1850 }
1851 else if (rem)
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02001852 {
1853 char_u *p = (char_u *)argvars[0].vval.v_blob->bv_ga.ga_data;
1854
Bram Moolenaar49c57ce2020-01-15 20:51:34 +01001855 mch_memmove(p + i, p + i + 1,
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02001856 (size_t)b->bv_ga.ga_len - i - 1);
1857 --b->bv_ga.ga_len;
1858 --i;
1859 }
Bram Moolenaar49c57ce2020-01-15 20:51:34 +01001860 ++idx;
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02001861 }
1862 }
1863 else // argvars[0].v_type == VAR_LIST
1864 {
Bram Moolenaardb661fb2020-01-29 22:17:16 +01001865 int prev_lock = l->lv_lock;
1866
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02001867 // set_vim_var_nr() doesn't set the type
1868 set_vim_var_type(VV_KEY, VAR_NUMBER);
1869
Bram Moolenaar8a7d6542020-01-26 15:56:19 +01001870 range_list_materialize(l);
Bram Moolenaardb661fb2020-01-29 22:17:16 +01001871 if (map && l->lv_lock == 0)
1872 l->lv_lock = VAR_LOCKED;
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02001873 for (li = l->lv_first; li != NULL; li = nli)
1874 {
1875 if (map && var_check_lock(li->li_tv.v_lock, arg_errmsg, TRUE))
1876 break;
1877 nli = li->li_next;
1878 set_vim_var_nr(VV_KEY, idx);
1879 if (filter_map_one(&li->li_tv, expr, map, &rem) == FAIL
1880 || did_emsg)
1881 break;
1882 if (!map && rem)
1883 listitem_remove(l, li);
1884 ++idx;
1885 }
Bram Moolenaardb661fb2020-01-29 22:17:16 +01001886 l->lv_lock = prev_lock;
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02001887 }
1888
1889 restore_vimvar(VV_KEY, &save_key);
1890 restore_vimvar(VV_VAL, &save_val);
1891
1892 did_emsg |= save_did_emsg;
1893 }
1894
1895 copy_tv(&argvars[0], rettv);
1896}
1897
1898/*
1899 * "filter()" function
1900 */
1901 void
1902f_filter(typval_T *argvars, typval_T *rettv)
1903{
1904 filter_map(argvars, rettv, FALSE);
1905}
1906
1907/*
1908 * "map()" function
1909 */
1910 void
1911f_map(typval_T *argvars, typval_T *rettv)
1912{
1913 filter_map(argvars, rettv, TRUE);
1914}
1915
Bram Moolenaar08c308a2019-09-04 17:48:15 +02001916/*
1917 * "add(list, item)" function
1918 */
1919 void
1920f_add(typval_T *argvars, typval_T *rettv)
1921{
1922 list_T *l;
1923 blob_T *b;
1924
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001925 rettv->vval.v_number = 1; // Default: Failed
Bram Moolenaar08c308a2019-09-04 17:48:15 +02001926 if (argvars[0].v_type == VAR_LIST)
1927 {
1928 if ((l = argvars[0].vval.v_list) != NULL
1929 && !var_check_lock(l->lv_lock,
1930 (char_u *)N_("add() argument"), TRUE)
1931 && list_append_tv(l, &argvars[1]) == OK)
1932 copy_tv(&argvars[0], rettv);
1933 }
1934 else if (argvars[0].v_type == VAR_BLOB)
1935 {
1936 if ((b = argvars[0].vval.v_blob) != NULL
1937 && !var_check_lock(b->bv_lock,
1938 (char_u *)N_("add() argument"), TRUE))
1939 {
1940 int error = FALSE;
1941 varnumber_T n = tv_get_number_chk(&argvars[1], &error);
1942
1943 if (!error)
1944 {
1945 ga_append(&b->bv_ga, (int)n);
1946 copy_tv(&argvars[0], rettv);
1947 }
1948 }
1949 }
1950 else
1951 emsg(_(e_listblobreq));
1952}
1953
1954/*
1955 * "count()" function
1956 */
1957 void
1958f_count(typval_T *argvars, typval_T *rettv)
1959{
1960 long n = 0;
1961 int ic = FALSE;
1962 int error = FALSE;
1963
1964 if (argvars[2].v_type != VAR_UNKNOWN)
1965 ic = (int)tv_get_number_chk(&argvars[2], &error);
1966
1967 if (argvars[0].v_type == VAR_STRING)
1968 {
1969 char_u *expr = tv_get_string_chk(&argvars[1]);
1970 char_u *p = argvars[0].vval.v_string;
1971 char_u *next;
1972
1973 if (!error && expr != NULL && *expr != NUL && p != NULL)
1974 {
1975 if (ic)
1976 {
1977 size_t len = STRLEN(expr);
1978
1979 while (*p != NUL)
1980 {
1981 if (MB_STRNICMP(p, expr, len) == 0)
1982 {
1983 ++n;
1984 p += len;
1985 }
1986 else
1987 MB_PTR_ADV(p);
1988 }
1989 }
1990 else
1991 while ((next = (char_u *)strstr((char *)p, (char *)expr))
1992 != NULL)
1993 {
1994 ++n;
1995 p = next + STRLEN(expr);
1996 }
1997 }
1998
1999 }
2000 else if (argvars[0].v_type == VAR_LIST)
2001 {
2002 listitem_T *li;
2003 list_T *l;
2004 long idx;
2005
2006 if ((l = argvars[0].vval.v_list) != NULL)
2007 {
Bram Moolenaar89bfc822020-01-27 22:37:23 +01002008 range_list_materialize(l);
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002009 li = l->lv_first;
2010 if (argvars[2].v_type != VAR_UNKNOWN)
2011 {
2012 if (argvars[3].v_type != VAR_UNKNOWN)
2013 {
2014 idx = (long)tv_get_number_chk(&argvars[3], &error);
2015 if (!error)
2016 {
2017 li = list_find(l, idx);
2018 if (li == NULL)
2019 semsg(_(e_listidx), idx);
2020 }
2021 }
2022 if (error)
2023 li = NULL;
2024 }
2025
2026 for ( ; li != NULL; li = li->li_next)
2027 if (tv_equal(&li->li_tv, &argvars[1], ic, FALSE))
2028 ++n;
2029 }
2030 }
2031 else if (argvars[0].v_type == VAR_DICT)
2032 {
2033 int todo;
2034 dict_T *d;
2035 hashitem_T *hi;
2036
2037 if ((d = argvars[0].vval.v_dict) != NULL)
2038 {
2039 if (argvars[2].v_type != VAR_UNKNOWN)
2040 {
2041 if (argvars[3].v_type != VAR_UNKNOWN)
2042 emsg(_(e_invarg));
2043 }
2044
2045 todo = error ? 0 : (int)d->dv_hashtab.ht_used;
2046 for (hi = d->dv_hashtab.ht_array; todo > 0; ++hi)
2047 {
2048 if (!HASHITEM_EMPTY(hi))
2049 {
2050 --todo;
2051 if (tv_equal(&HI2DI(hi)->di_tv, &argvars[1], ic, FALSE))
2052 ++n;
2053 }
2054 }
2055 }
2056 }
2057 else
2058 semsg(_(e_listdictarg), "count()");
2059 rettv->vval.v_number = n;
2060}
2061
2062/*
2063 * "extend(list, list [, idx])" function
2064 * "extend(dict, dict [, action])" function
2065 */
2066 void
2067f_extend(typval_T *argvars, typval_T *rettv)
2068{
2069 char_u *arg_errmsg = (char_u *)N_("extend() argument");
2070
2071 if (argvars[0].v_type == VAR_LIST && argvars[1].v_type == VAR_LIST)
2072 {
2073 list_T *l1, *l2;
2074 listitem_T *item;
2075 long before;
2076 int error = FALSE;
2077
2078 l1 = argvars[0].vval.v_list;
2079 l2 = argvars[1].vval.v_list;
2080 if (l1 != NULL && !var_check_lock(l1->lv_lock, arg_errmsg, TRUE)
2081 && l2 != NULL)
2082 {
2083 if (argvars[2].v_type != VAR_UNKNOWN)
2084 {
2085 before = (long)tv_get_number_chk(&argvars[2], &error);
2086 if (error)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002087 return; // type error; errmsg already given
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002088
2089 if (before == l1->lv_len)
2090 item = NULL;
2091 else
2092 {
2093 item = list_find(l1, before);
2094 if (item == NULL)
2095 {
2096 semsg(_(e_listidx), before);
2097 return;
2098 }
2099 }
2100 }
2101 else
2102 item = NULL;
2103 list_extend(l1, l2, item);
2104
2105 copy_tv(&argvars[0], rettv);
2106 }
2107 }
2108 else if (argvars[0].v_type == VAR_DICT && argvars[1].v_type == VAR_DICT)
2109 {
2110 dict_T *d1, *d2;
2111 char_u *action;
2112 int i;
2113
2114 d1 = argvars[0].vval.v_dict;
2115 d2 = argvars[1].vval.v_dict;
2116 if (d1 != NULL && !var_check_lock(d1->dv_lock, arg_errmsg, TRUE)
2117 && d2 != NULL)
2118 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002119 // Check the third argument.
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002120 if (argvars[2].v_type != VAR_UNKNOWN)
2121 {
2122 static char *(av[]) = {"keep", "force", "error"};
2123
2124 action = tv_get_string_chk(&argvars[2]);
2125 if (action == NULL)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002126 return; // type error; errmsg already given
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002127 for (i = 0; i < 3; ++i)
2128 if (STRCMP(action, av[i]) == 0)
2129 break;
2130 if (i == 3)
2131 {
2132 semsg(_(e_invarg2), action);
2133 return;
2134 }
2135 }
2136 else
2137 action = (char_u *)"force";
2138
2139 dict_extend(d1, d2, action);
2140
2141 copy_tv(&argvars[0], rettv);
2142 }
2143 }
2144 else
2145 semsg(_(e_listdictarg), "extend()");
2146}
2147
2148/*
2149 * "insert()" function
2150 */
2151 void
2152f_insert(typval_T *argvars, typval_T *rettv)
2153{
2154 long before = 0;
2155 listitem_T *item;
2156 list_T *l;
2157 int error = FALSE;
2158
2159 if (argvars[0].v_type == VAR_BLOB)
2160 {
2161 int val, len;
2162 char_u *p;
2163
2164 len = blob_len(argvars[0].vval.v_blob);
2165 if (argvars[2].v_type != VAR_UNKNOWN)
2166 {
2167 before = (long)tv_get_number_chk(&argvars[2], &error);
2168 if (error)
2169 return; // type error; errmsg already given
2170 if (before < 0 || before > len)
2171 {
2172 semsg(_(e_invarg2), tv_get_string(&argvars[2]));
2173 return;
2174 }
2175 }
2176 val = tv_get_number_chk(&argvars[1], &error);
2177 if (error)
2178 return;
2179 if (val < 0 || val > 255)
2180 {
2181 semsg(_(e_invarg2), tv_get_string(&argvars[1]));
2182 return;
2183 }
2184
2185 if (ga_grow(&argvars[0].vval.v_blob->bv_ga, 1) == FAIL)
2186 return;
2187 p = (char_u *)argvars[0].vval.v_blob->bv_ga.ga_data;
2188 mch_memmove(p + before + 1, p + before, (size_t)len - before);
2189 *(p + before) = val;
2190 ++argvars[0].vval.v_blob->bv_ga.ga_len;
2191
2192 copy_tv(&argvars[0], rettv);
2193 }
2194 else if (argvars[0].v_type != VAR_LIST)
2195 semsg(_(e_listblobarg), "insert()");
2196 else if ((l = argvars[0].vval.v_list) != NULL
2197 && !var_check_lock(l->lv_lock,
2198 (char_u *)N_("insert() argument"), TRUE))
2199 {
2200 if (argvars[2].v_type != VAR_UNKNOWN)
2201 before = (long)tv_get_number_chk(&argvars[2], &error);
2202 if (error)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002203 return; // type error; errmsg already given
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002204
2205 if (before == l->lv_len)
2206 item = NULL;
2207 else
2208 {
2209 item = list_find(l, before);
2210 if (item == NULL)
2211 {
2212 semsg(_(e_listidx), before);
2213 l = NULL;
2214 }
2215 }
2216 if (l != NULL)
2217 {
2218 list_insert_tv(l, &argvars[1], item);
2219 copy_tv(&argvars[0], rettv);
2220 }
2221 }
2222}
2223
2224/*
2225 * "remove()" function
2226 */
2227 void
2228f_remove(typval_T *argvars, typval_T *rettv)
2229{
2230 char_u *arg_errmsg = (char_u *)N_("remove() argument");
2231
2232 if (argvars[0].v_type == VAR_DICT)
2233 dict_remove(argvars, rettv, arg_errmsg);
2234 else if (argvars[0].v_type == VAR_BLOB)
2235 blob_remove(argvars, rettv);
2236 else if (argvars[0].v_type == VAR_LIST)
2237 list_remove(argvars, rettv, arg_errmsg);
2238 else
2239 semsg(_(e_listdictblobarg), "remove()");
2240}
2241
2242/*
2243 * "reverse({list})" function
2244 */
2245 void
2246f_reverse(typval_T *argvars, typval_T *rettv)
2247{
2248 list_T *l;
2249 listitem_T *li, *ni;
2250
2251 if (argvars[0].v_type == VAR_BLOB)
2252 {
2253 blob_T *b = argvars[0].vval.v_blob;
2254 int i, len = blob_len(b);
2255
2256 for (i = 0; i < len / 2; i++)
2257 {
2258 int tmp = blob_get(b, i);
2259
2260 blob_set(b, i, blob_get(b, len - i - 1));
2261 blob_set(b, len - i - 1, tmp);
2262 }
2263 rettv_blob_set(rettv, b);
2264 return;
2265 }
2266
2267 if (argvars[0].v_type != VAR_LIST)
2268 semsg(_(e_listblobarg), "reverse()");
2269 else if ((l = argvars[0].vval.v_list) != NULL
2270 && !var_check_lock(l->lv_lock,
2271 (char_u *)N_("reverse() argument"), TRUE))
2272 {
Bram Moolenaar89bfc822020-01-27 22:37:23 +01002273 if (l->lv_first == &range_list_item)
2274 {
Bram Moolenaar0ff6aad2020-01-29 21:27:21 +01002275 varnumber_T new_start = l->lv_u.nonmat.lv_start
2276 + (l->lv_len - 1) * l->lv_u.nonmat.lv_stride;
2277 l->lv_u.nonmat.lv_end = new_start
2278 - (l->lv_u.nonmat.lv_end - l->lv_u.nonmat.lv_start);
2279 l->lv_u.nonmat.lv_start = new_start;
2280 l->lv_u.nonmat.lv_stride = -l->lv_u.nonmat.lv_stride;
Bram Moolenaar89bfc822020-01-27 22:37:23 +01002281 rettv_list_set(rettv, l);
2282 return;
2283 }
Bram Moolenaar0ff6aad2020-01-29 21:27:21 +01002284 li = l->lv_u.mat.lv_last;
2285 l->lv_first = l->lv_u.mat.lv_last = NULL;
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002286 l->lv_len = 0;
2287 while (li != NULL)
2288 {
2289 ni = li->li_prev;
2290 list_append(l, li);
2291 li = ni;
2292 }
2293 rettv_list_set(rettv, l);
Bram Moolenaar0ff6aad2020-01-29 21:27:21 +01002294 l->lv_u.mat.lv_idx = l->lv_len - l->lv_u.mat.lv_idx - 1;
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002295 }
2296}
2297
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02002298#endif // defined(FEAT_EVAL)