blob: 9a348a8a62b6b5f54e85b58a9ef28d73e1e71449 [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/*
11 * list.c: List support
12 */
13
14#include "vim.h"
15
16#if defined(FEAT_EVAL) || defined(PROTO)
17
18/* List heads for garbage collection. */
19static list_T *first_list = NULL; /* list of all lists */
20
21/*
22 * Add a watcher to a list.
23 */
24 void
25list_add_watch(list_T *l, listwatch_T *lw)
26{
27 lw->lw_next = l->lv_watch;
28 l->lv_watch = lw;
29}
30
31/*
32 * Remove a watcher from a list.
33 * No warning when it isn't found...
34 */
35 void
36list_rem_watch(list_T *l, listwatch_T *lwrem)
37{
38 listwatch_T *lw, **lwp;
39
40 lwp = &l->lv_watch;
41 for (lw = l->lv_watch; lw != NULL; lw = lw->lw_next)
42 {
43 if (lw == lwrem)
44 {
45 *lwp = lw->lw_next;
46 break;
47 }
48 lwp = &lw->lw_next;
49 }
50}
51
52/*
53 * Just before removing an item from a list: advance watchers to the next
54 * item.
55 */
56 void
57list_fix_watch(list_T *l, listitem_T *item)
58{
59 listwatch_T *lw;
60
61 for (lw = l->lv_watch; lw != NULL; lw = lw->lw_next)
62 if (lw->lw_item == item)
63 lw->lw_item = item->li_next;
64}
65
66/*
67 * Allocate an empty header for a list.
68 * Caller should take care of the reference count.
69 */
70 list_T *
71list_alloc(void)
72{
73 list_T *l;
74
75 l = (list_T *)alloc_clear(sizeof(list_T));
76 if (l != NULL)
77 {
78 /* Prepend the list to the list of lists for garbage collection. */
79 if (first_list != NULL)
80 first_list->lv_used_prev = l;
81 l->lv_used_prev = NULL;
82 l->lv_used_next = first_list;
83 first_list = l;
84 }
85 return l;
86}
87
88/*
89 * Allocate an empty list for a return value, with reference count set.
90 * Returns OK or FAIL.
91 */
92 int
93rettv_list_alloc(typval_T *rettv)
94{
95 list_T *l = list_alloc();
96
97 if (l == NULL)
98 return FAIL;
99
Bram Moolenaarda861d62016-07-17 15:46:27 +0200100 rettv->v_lock = 0;
Bram Moolenaar45cf6e92017-04-30 20:25:19 +0200101 rettv_list_set(rettv, l);
Bram Moolenaarda861d62016-07-17 15:46:27 +0200102 return OK;
103}
104
105/*
Bram Moolenaar45cf6e92017-04-30 20:25:19 +0200106 * Set a list as the return value
107 */
108 void
109rettv_list_set(typval_T *rettv, list_T *l)
110{
111 rettv->v_type = VAR_LIST;
112 rettv->vval.v_list = l;
113 if (l != NULL)
114 ++l->lv_refcount;
115}
116
117/*
Bram Moolenaarda861d62016-07-17 15:46:27 +0200118 * Unreference a list: decrement the reference count and free it when it
119 * becomes zero.
120 */
121 void
122list_unref(list_T *l)
123{
124 if (l != NULL && --l->lv_refcount <= 0)
125 list_free(l);
126}
127
128/*
129 * Free a list, including all non-container items it points to.
130 * Ignores the reference count.
131 */
132 static void
133list_free_contents(list_T *l)
134{
135 listitem_T *item;
136
137 for (item = l->lv_first; item != NULL; item = l->lv_first)
138 {
139 /* Remove the item before deleting it. */
140 l->lv_first = item->li_next;
141 clear_tv(&item->li_tv);
142 vim_free(item);
143 }
144}
145
146/*
147 * Go through the list of lists and free items without the copyID.
148 * But don't free a list that has a watcher (used in a for loop), these
149 * are not referenced anywhere.
150 */
151 int
152list_free_nonref(int copyID)
153{
154 list_T *ll;
155 int did_free = FALSE;
156
157 for (ll = first_list; ll != NULL; ll = ll->lv_used_next)
158 if ((ll->lv_copyID & COPYID_MASK) != (copyID & COPYID_MASK)
159 && ll->lv_watch == NULL)
160 {
161 /* Free the List and ordinary items it contains, but don't recurse
162 * into Lists and Dictionaries, they will be in the list of dicts
163 * or list of lists. */
164 list_free_contents(ll);
165 did_free = TRUE;
166 }
167 return did_free;
168}
169
170 static void
171list_free_list(list_T *l)
172{
173 /* Remove the list from the list of lists for garbage collection. */
174 if (l->lv_used_prev == NULL)
175 first_list = l->lv_used_next;
176 else
177 l->lv_used_prev->lv_used_next = l->lv_used_next;
178 if (l->lv_used_next != NULL)
179 l->lv_used_next->lv_used_prev = l->lv_used_prev;
180
181 vim_free(l);
182}
183
184 void
185list_free_items(int copyID)
186{
187 list_T *ll, *ll_next;
188
189 for (ll = first_list; ll != NULL; ll = ll_next)
190 {
191 ll_next = ll->lv_used_next;
192 if ((ll->lv_copyID & COPYID_MASK) != (copyID & COPYID_MASK)
193 && ll->lv_watch == NULL)
194 {
195 /* Free the List and ordinary items it contains, but don't recurse
196 * into Lists and Dictionaries, they will be in the list of dicts
197 * or list of lists. */
198 list_free_list(ll);
199 }
200 }
201}
202
203 void
204list_free(list_T *l)
205{
206 if (!in_free_unref_items)
207 {
208 list_free_contents(l);
209 list_free_list(l);
210 }
211}
212
213/*
214 * Allocate a list item.
215 * It is not initialized, don't forget to set v_lock.
216 */
217 listitem_T *
218listitem_alloc(void)
219{
220 return (listitem_T *)alloc(sizeof(listitem_T));
221}
222
223/*
224 * Free a list item. Also clears the value. Does not notify watchers.
225 */
226 void
227listitem_free(listitem_T *item)
228{
229 clear_tv(&item->li_tv);
230 vim_free(item);
231}
232
233/*
234 * Remove a list item from a List and free it. Also clears the value.
235 */
236 void
237listitem_remove(list_T *l, listitem_T *item)
238{
239 vimlist_remove(l, item, item);
240 listitem_free(item);
241}
242
243/*
244 * Get the number of items in a list.
245 */
246 long
247list_len(list_T *l)
248{
249 if (l == NULL)
250 return 0L;
251 return l->lv_len;
252}
253
254/*
255 * Return TRUE when two lists have exactly the same values.
256 */
257 int
258list_equal(
259 list_T *l1,
260 list_T *l2,
261 int ic, /* ignore case for strings */
262 int recursive) /* TRUE when used recursively */
263{
264 listitem_T *item1, *item2;
265
266 if (l1 == NULL || l2 == NULL)
267 return FALSE;
268 if (l1 == l2)
269 return TRUE;
270 if (list_len(l1) != list_len(l2))
271 return FALSE;
272
273 for (item1 = l1->lv_first, item2 = l2->lv_first;
274 item1 != NULL && item2 != NULL;
275 item1 = item1->li_next, item2 = item2->li_next)
276 if (!tv_equal(&item1->li_tv, &item2->li_tv, ic, recursive))
277 return FALSE;
278 return item1 == NULL && item2 == NULL;
279}
280
281/*
282 * Locate item with index "n" in list "l" and return it.
283 * A negative index is counted from the end; -1 is the last item.
284 * Returns NULL when "n" is out of range.
285 */
286 listitem_T *
287list_find(list_T *l, long n)
288{
289 listitem_T *item;
290 long idx;
291
292 if (l == NULL)
293 return NULL;
294
295 /* Negative index is relative to the end. */
296 if (n < 0)
297 n = l->lv_len + n;
298
299 /* Check for index out of range. */
300 if (n < 0 || n >= l->lv_len)
301 return NULL;
302
303 /* When there is a cached index may start search from there. */
304 if (l->lv_idx_item != NULL)
305 {
306 if (n < l->lv_idx / 2)
307 {
308 /* closest to the start of the list */
309 item = l->lv_first;
310 idx = 0;
311 }
312 else if (n > (l->lv_idx + l->lv_len) / 2)
313 {
314 /* closest to the end of the list */
315 item = l->lv_last;
316 idx = l->lv_len - 1;
317 }
318 else
319 {
320 /* closest to the cached index */
321 item = l->lv_idx_item;
322 idx = l->lv_idx;
323 }
324 }
325 else
326 {
327 if (n < l->lv_len / 2)
328 {
329 /* closest to the start of the list */
330 item = l->lv_first;
331 idx = 0;
332 }
333 else
334 {
335 /* closest to the end of the list */
336 item = l->lv_last;
337 idx = l->lv_len - 1;
338 }
339 }
340
341 while (n > idx)
342 {
343 /* search forward */
344 item = item->li_next;
345 ++idx;
346 }
347 while (n < idx)
348 {
349 /* search backward */
350 item = item->li_prev;
351 --idx;
352 }
353
354 /* cache the used index */
355 l->lv_idx = idx;
356 l->lv_idx_item = item;
357
358 return item;
359}
360
361/*
362 * Get list item "l[idx]" as a number.
363 */
364 long
365list_find_nr(
366 list_T *l,
367 long idx,
368 int *errorp) /* set to TRUE when something wrong */
369{
370 listitem_T *li;
371
372 li = list_find(l, idx);
373 if (li == NULL)
374 {
375 if (errorp != NULL)
376 *errorp = TRUE;
377 return -1L;
378 }
379 return (long)get_tv_number_chk(&li->li_tv, errorp);
380}
381
382/*
383 * Get list item "l[idx - 1]" as a string. Returns NULL for failure.
384 */
385 char_u *
386list_find_str(list_T *l, long idx)
387{
388 listitem_T *li;
389
390 li = list_find(l, idx - 1);
391 if (li == NULL)
392 {
393 EMSGN(_(e_listidx), idx);
394 return NULL;
395 }
396 return get_tv_string(&li->li_tv);
397}
398
399/*
400 * Locate "item" list "l" and return its index.
401 * Returns -1 when "item" is not in the list.
402 */
403 long
404list_idx_of_item(list_T *l, listitem_T *item)
405{
406 long idx = 0;
407 listitem_T *li;
408
409 if (l == NULL)
410 return -1;
411 idx = 0;
412 for (li = l->lv_first; li != NULL && li != item; li = li->li_next)
413 ++idx;
414 if (li == NULL)
415 return -1;
416 return idx;
417}
418
419/*
420 * Append item "item" to the end of list "l".
421 */
422 void
423list_append(list_T *l, listitem_T *item)
424{
425 if (l->lv_last == NULL)
426 {
427 /* empty list */
428 l->lv_first = item;
429 l->lv_last = item;
430 item->li_prev = NULL;
431 }
432 else
433 {
434 l->lv_last->li_next = item;
435 item->li_prev = l->lv_last;
436 l->lv_last = item;
437 }
438 ++l->lv_len;
439 item->li_next = NULL;
440}
441
442/*
443 * Append typval_T "tv" to the end of list "l".
444 * Return FAIL when out of memory.
445 */
446 int
447list_append_tv(list_T *l, typval_T *tv)
448{
449 listitem_T *li = listitem_alloc();
450
451 if (li == NULL)
452 return FAIL;
453 copy_tv(tv, &li->li_tv);
454 list_append(l, li);
455 return OK;
456}
457
458/*
459 * Add a dictionary to a list. Used by getqflist().
460 * Return FAIL when out of memory.
461 */
462 int
463list_append_dict(list_T *list, dict_T *dict)
464{
465 listitem_T *li = listitem_alloc();
466
467 if (li == NULL)
468 return FAIL;
469 li->li_tv.v_type = VAR_DICT;
470 li->li_tv.v_lock = 0;
471 li->li_tv.vval.v_dict = dict;
472 list_append(list, li);
473 ++dict->dv_refcount;
474 return OK;
475}
476
477/*
Bram Moolenaar4f505882018-02-10 21:06:32 +0100478 * Append list2 to list1.
479 * Return FAIL when out of memory.
480 */
481 int
Bram Moolenaar6f8d2ac2018-07-25 19:49:45 +0200482list_append_list(list_T *list1, list_T *list2)
Bram Moolenaar4f505882018-02-10 21:06:32 +0100483{
484 listitem_T *li = listitem_alloc();
485
486 if (li == NULL)
487 return FAIL;
488 li->li_tv.v_type = VAR_LIST;
489 li->li_tv.v_lock = 0;
490 li->li_tv.vval.v_list = list2;
491 list_append(list1, li);
492 ++list2->lv_refcount;
493 return OK;
494}
495
496/*
Bram Moolenaarda861d62016-07-17 15:46:27 +0200497 * Make a copy of "str" and append it as an item to list "l".
498 * When "len" >= 0 use "str[len]".
499 * Returns FAIL when out of memory.
500 */
501 int
502list_append_string(list_T *l, char_u *str, int len)
503{
504 listitem_T *li = listitem_alloc();
505
506 if (li == NULL)
507 return FAIL;
508 list_append(l, li);
509 li->li_tv.v_type = VAR_STRING;
510 li->li_tv.v_lock = 0;
511 if (str == NULL)
512 li->li_tv.vval.v_string = NULL;
513 else if ((li->li_tv.vval.v_string = (len >= 0 ? vim_strnsave(str, len)
514 : vim_strsave(str))) == NULL)
515 return FAIL;
516 return OK;
517}
518
519/*
520 * Append "n" to list "l".
521 * Returns FAIL when out of memory.
522 */
523 int
524list_append_number(list_T *l, varnumber_T n)
525{
526 listitem_T *li;
527
528 li = listitem_alloc();
529 if (li == NULL)
530 return FAIL;
531 li->li_tv.v_type = VAR_NUMBER;
532 li->li_tv.v_lock = 0;
533 li->li_tv.vval.v_number = n;
534 list_append(l, li);
535 return OK;
536}
537
538/*
539 * Insert typval_T "tv" in list "l" before "item".
540 * If "item" is NULL append at the end.
541 * Return FAIL when out of memory.
542 */
543 int
544list_insert_tv(list_T *l, typval_T *tv, listitem_T *item)
545{
546 listitem_T *ni = listitem_alloc();
547
548 if (ni == NULL)
549 return FAIL;
550 copy_tv(tv, &ni->li_tv);
551 list_insert(l, ni, item);
552 return OK;
553}
554
555 void
556list_insert(list_T *l, listitem_T *ni, listitem_T *item)
557{
558 if (item == NULL)
559 /* Append new item at end of list. */
560 list_append(l, ni);
561 else
562 {
563 /* Insert new item before existing item. */
564 ni->li_prev = item->li_prev;
565 ni->li_next = item;
566 if (item->li_prev == NULL)
567 {
568 l->lv_first = ni;
569 ++l->lv_idx;
570 }
571 else
572 {
573 item->li_prev->li_next = ni;
574 l->lv_idx_item = NULL;
575 }
576 item->li_prev = ni;
577 ++l->lv_len;
578 }
579}
580
581/*
582 * Extend "l1" with "l2".
583 * If "bef" is NULL append at the end, otherwise insert before this item.
584 * Returns FAIL when out of memory.
585 */
586 int
587list_extend(list_T *l1, list_T *l2, listitem_T *bef)
588{
589 listitem_T *item;
590 int todo = l2->lv_len;
591
592 /* We also quit the loop when we have inserted the original item count of
593 * the list, avoid a hang when we extend a list with itself. */
594 for (item = l2->lv_first; item != NULL && --todo >= 0; item = item->li_next)
595 if (list_insert_tv(l1, &item->li_tv, bef) == FAIL)
596 return FAIL;
597 return OK;
598}
599
600/*
601 * Concatenate lists "l1" and "l2" into a new list, stored in "tv".
602 * Return FAIL when out of memory.
603 */
604 int
605list_concat(list_T *l1, list_T *l2, typval_T *tv)
606{
607 list_T *l;
608
609 if (l1 == NULL || l2 == NULL)
610 return FAIL;
611
612 /* make a copy of the first list. */
613 l = list_copy(l1, FALSE, 0);
614 if (l == NULL)
615 return FAIL;
616 tv->v_type = VAR_LIST;
617 tv->vval.v_list = l;
618
619 /* append all items from the second list */
620 return list_extend(l, l2, NULL);
621}
622
623/*
624 * Make a copy of list "orig". Shallow if "deep" is FALSE.
625 * The refcount of the new list is set to 1.
626 * See item_copy() for "copyID".
627 * Returns NULL when out of memory.
628 */
629 list_T *
630list_copy(list_T *orig, int deep, int copyID)
631{
632 list_T *copy;
633 listitem_T *item;
634 listitem_T *ni;
635
636 if (orig == NULL)
637 return NULL;
638
639 copy = list_alloc();
640 if (copy != NULL)
641 {
642 if (copyID != 0)
643 {
644 /* Do this before adding the items, because one of the items may
645 * refer back to this list. */
646 orig->lv_copyID = copyID;
647 orig->lv_copylist = copy;
648 }
649 for (item = orig->lv_first; item != NULL && !got_int;
650 item = item->li_next)
651 {
652 ni = listitem_alloc();
653 if (ni == NULL)
654 break;
655 if (deep)
656 {
657 if (item_copy(&item->li_tv, &ni->li_tv, deep, copyID) == FAIL)
658 {
659 vim_free(ni);
660 break;
661 }
662 }
663 else
664 copy_tv(&item->li_tv, &ni->li_tv);
665 list_append(copy, ni);
666 }
667 ++copy->lv_refcount;
668 if (item != NULL)
669 {
670 list_unref(copy);
671 copy = NULL;
672 }
673 }
674
675 return copy;
676}
677
678/*
679 * Remove items "item" to "item2" from list "l".
680 * Does not free the listitem or the value!
681 * This used to be called list_remove, but that conflicts with a Sun header
682 * file.
683 */
684 void
685vimlist_remove(list_T *l, listitem_T *item, listitem_T *item2)
686{
687 listitem_T *ip;
688
689 /* notify watchers */
690 for (ip = item; ip != NULL; ip = ip->li_next)
691 {
692 --l->lv_len;
693 list_fix_watch(l, ip);
694 if (ip == item2)
695 break;
696 }
697
698 if (item2->li_next == NULL)
699 l->lv_last = item->li_prev;
700 else
701 item2->li_next->li_prev = item->li_prev;
702 if (item->li_prev == NULL)
703 l->lv_first = item2->li_next;
704 else
705 item->li_prev->li_next = item2->li_next;
706 l->lv_idx_item = NULL;
707}
708
709/*
710 * Return an allocated string with the string representation of a list.
711 * May return NULL.
712 */
713 char_u *
714list2string(typval_T *tv, int copyID, int restore_copyID)
715{
716 garray_T ga;
717
718 if (tv->vval.v_list == NULL)
719 return NULL;
720 ga_init2(&ga, (int)sizeof(char), 80);
721 ga_append(&ga, '[');
722 if (list_join(&ga, tv->vval.v_list, (char_u *)", ",
723 FALSE, restore_copyID, copyID) == FAIL)
724 {
725 vim_free(ga.ga_data);
726 return NULL;
727 }
728 ga_append(&ga, ']');
729 ga_append(&ga, NUL);
730 return (char_u *)ga.ga_data;
731}
732
733typedef struct join_S {
734 char_u *s;
735 char_u *tofree;
736} join_T;
737
738 static int
739list_join_inner(
740 garray_T *gap, /* to store the result in */
741 list_T *l,
742 char_u *sep,
743 int echo_style,
744 int restore_copyID,
745 int copyID,
746 garray_T *join_gap) /* to keep each list item string */
747{
748 int i;
749 join_T *p;
750 int len;
751 int sumlen = 0;
752 int first = TRUE;
753 char_u *tofree;
754 char_u numbuf[NUMBUFLEN];
755 listitem_T *item;
756 char_u *s;
757
758 /* Stringify each item in the list. */
759 for (item = l->lv_first; item != NULL && !got_int; item = item->li_next)
760 {
761 s = echo_string_core(&item->li_tv, &tofree, numbuf, copyID,
Bram Moolenaar35422f42017-08-05 16:33:56 +0200762 echo_style, restore_copyID, !echo_style);
Bram Moolenaarda861d62016-07-17 15:46:27 +0200763 if (s == NULL)
764 return FAIL;
765
766 len = (int)STRLEN(s);
767 sumlen += len;
768
769 (void)ga_grow(join_gap, 1);
770 p = ((join_T *)join_gap->ga_data) + (join_gap->ga_len++);
771 if (tofree != NULL || s != numbuf)
772 {
773 p->s = s;
774 p->tofree = tofree;
775 }
776 else
777 {
778 p->s = vim_strnsave(s, len);
779 p->tofree = p->s;
780 }
781
782 line_breakcheck();
783 if (did_echo_string_emsg) /* recursion error, bail out */
784 break;
785 }
786
787 /* Allocate result buffer with its total size, avoid re-allocation and
788 * multiple copy operations. Add 2 for a tailing ']' and NUL. */
789 if (join_gap->ga_len >= 2)
790 sumlen += (int)STRLEN(sep) * (join_gap->ga_len - 1);
791 if (ga_grow(gap, sumlen + 2) == FAIL)
792 return FAIL;
793
794 for (i = 0; i < join_gap->ga_len && !got_int; ++i)
795 {
796 if (first)
797 first = FALSE;
798 else
799 ga_concat(gap, sep);
800 p = ((join_T *)join_gap->ga_data) + i;
801
802 if (p->s != NULL)
803 ga_concat(gap, p->s);
804 line_breakcheck();
805 }
806
807 return OK;
808}
809
810/*
811 * Join list "l" into a string in "*gap", using separator "sep".
812 * When "echo_style" is TRUE use String as echoed, otherwise as inside a List.
813 * Return FAIL or OK.
814 */
815 int
816list_join(
817 garray_T *gap,
818 list_T *l,
819 char_u *sep,
820 int echo_style,
821 int restore_copyID,
822 int copyID)
823{
824 garray_T join_ga;
825 int retval;
826 join_T *p;
827 int i;
828
829 if (l->lv_len < 1)
830 return OK; /* nothing to do */
831 ga_init2(&join_ga, (int)sizeof(join_T), l->lv_len);
832 retval = list_join_inner(gap, l, sep, echo_style, restore_copyID,
833 copyID, &join_ga);
834
835 /* Dispose each item in join_ga. */
836 if (join_ga.ga_data != NULL)
837 {
838 p = (join_T *)join_ga.ga_data;
839 for (i = 0; i < join_ga.ga_len; ++i)
840 {
841 vim_free(p->tofree);
842 ++p;
843 }
844 ga_clear(&join_ga);
845 }
846
847 return retval;
848}
849
850/*
851 * Allocate a variable for a List and fill it from "*arg".
852 * Return OK or FAIL.
853 */
854 int
855get_list_tv(char_u **arg, typval_T *rettv, int evaluate)
856{
857 list_T *l = NULL;
858 typval_T tv;
859 listitem_T *item;
860
861 if (evaluate)
862 {
863 l = list_alloc();
864 if (l == NULL)
865 return FAIL;
866 }
867
868 *arg = skipwhite(*arg + 1);
869 while (**arg != ']' && **arg != NUL)
870 {
871 if (eval1(arg, &tv, evaluate) == FAIL) /* recursive! */
872 goto failret;
873 if (evaluate)
874 {
875 item = listitem_alloc();
876 if (item != NULL)
877 {
878 item->li_tv = tv;
879 item->li_tv.v_lock = 0;
880 list_append(l, item);
881 }
882 else
883 clear_tv(&tv);
884 }
885
886 if (**arg == ']')
887 break;
888 if (**arg != ',')
889 {
890 EMSG2(_("E696: Missing comma in List: %s"), *arg);
891 goto failret;
892 }
893 *arg = skipwhite(*arg + 1);
894 }
895
896 if (**arg != ']')
897 {
898 EMSG2(_("E697: Missing end of List ']': %s"), *arg);
899failret:
900 if (evaluate)
901 list_free(l);
902 return FAIL;
903 }
904
905 *arg = skipwhite(*arg + 1);
906 if (evaluate)
Bram Moolenaar45cf6e92017-04-30 20:25:19 +0200907 rettv_list_set(rettv, l);
Bram Moolenaarda861d62016-07-17 15:46:27 +0200908
909 return OK;
910}
911
Bram Moolenaar73dad1e2016-07-17 22:13:49 +0200912/*
Bram Moolenaarcaa55b62017-01-10 13:51:09 +0100913 * Write "list" of strings to file "fd".
Bram Moolenaar73dad1e2016-07-17 22:13:49 +0200914 */
915 int
916write_list(FILE *fd, list_T *list, int binary)
917{
918 listitem_T *li;
919 int c;
920 int ret = OK;
921 char_u *s;
922
923 for (li = list->lv_first; li != NULL; li = li->li_next)
924 {
925 for (s = get_tv_string(&li->li_tv); *s != NUL; ++s)
926 {
927 if (*s == '\n')
928 c = putc(NUL, fd);
929 else
930 c = putc(*s, fd);
931 if (c == EOF)
932 {
933 ret = FAIL;
934 break;
935 }
936 }
937 if (!binary || li->li_next != NULL)
938 if (putc('\n', fd) == EOF)
939 {
940 ret = FAIL;
941 break;
942 }
943 if (ret == FAIL)
944 {
945 EMSG(_(e_write));
946 break;
947 }
948 }
949 return ret;
950}
951
Bram Moolenaardf48fb42016-07-22 21:50:18 +0200952/*
953 * Initialize a static list with 10 items.
954 */
955 void
956init_static_list(staticList10_T *sl)
957{
958 list_T *l = &sl->sl_list;
959 int i;
960
961 memset(sl, 0, sizeof(staticList10_T));
962 l->lv_first = &sl->sl_items[0];
963 l->lv_last = &sl->sl_items[9];
964 l->lv_refcount = DO_NOT_FREE_CNT;
965 l->lv_lock = VAR_FIXED;
966 sl->sl_list.lv_len = 10;
967
968 for (i = 0; i < 10; ++i)
969 {
970 listitem_T *li = &sl->sl_items[i];
971
972 if (i == 0)
973 li->li_prev = NULL;
974 else
975 li->li_prev = li - 1;
976 if (i == 9)
977 li->li_next = NULL;
978 else
979 li->li_next = li + 1;
980 }
981}
982
Bram Moolenaar73dad1e2016-07-17 22:13:49 +0200983#endif /* defined(FEAT_EVAL) */