xref: /vim-8.2.3635/src/list.c (revision e16b00a1)
1 /* vi:set ts=8 sts=4 sw=4 noet:
2  *
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. */
19 static list_T		*first_list = NULL;	/* list of all lists */
20 
21 /*
22  * Add a watcher to a list.
23  */
24     void
25 list_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
36 list_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
57 list_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 *
71 list_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
93 rettv_list_alloc(typval_T *rettv)
94 {
95     list_T	*l = list_alloc();
96 
97     if (l == NULL)
98 	return FAIL;
99 
100     rettv->v_lock = 0;
101     rettv_list_set(rettv, l);
102     return OK;
103 }
104 
105 /*
106  * Set a list as the return value
107  */
108     void
109 rettv_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 /*
118  * Unreference a list: decrement the reference count and free it when it
119  * becomes zero.
120  */
121     void
122 list_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
133 list_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
152 list_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
171 list_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
185 list_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
204 list_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 *
218 listitem_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
227 listitem_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
237 listitem_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
247 list_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
258 list_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 *
287 list_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
365 list_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 *
386 list_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
404 list_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
423 list_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
447 list_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
463 list_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 /*
478  * Make a copy of "str" and append it as an item to list "l".
479  * When "len" >= 0 use "str[len]".
480  * Returns FAIL when out of memory.
481  */
482     int
483 list_append_string(list_T *l, char_u *str, int len)
484 {
485     listitem_T *li = listitem_alloc();
486 
487     if (li == NULL)
488 	return FAIL;
489     list_append(l, li);
490     li->li_tv.v_type = VAR_STRING;
491     li->li_tv.v_lock = 0;
492     if (str == NULL)
493 	li->li_tv.vval.v_string = NULL;
494     else if ((li->li_tv.vval.v_string = (len >= 0 ? vim_strnsave(str, len)
495 						 : vim_strsave(str))) == NULL)
496 	return FAIL;
497     return OK;
498 }
499 
500 /*
501  * Append "n" to list "l".
502  * Returns FAIL when out of memory.
503  */
504     int
505 list_append_number(list_T *l, varnumber_T n)
506 {
507     listitem_T	*li;
508 
509     li = listitem_alloc();
510     if (li == NULL)
511 	return FAIL;
512     li->li_tv.v_type = VAR_NUMBER;
513     li->li_tv.v_lock = 0;
514     li->li_tv.vval.v_number = n;
515     list_append(l, li);
516     return OK;
517 }
518 
519 /*
520  * Insert typval_T "tv" in list "l" before "item".
521  * If "item" is NULL append at the end.
522  * Return FAIL when out of memory.
523  */
524     int
525 list_insert_tv(list_T *l, typval_T *tv, listitem_T *item)
526 {
527     listitem_T	*ni = listitem_alloc();
528 
529     if (ni == NULL)
530 	return FAIL;
531     copy_tv(tv, &ni->li_tv);
532     list_insert(l, ni, item);
533     return OK;
534 }
535 
536     void
537 list_insert(list_T *l, listitem_T *ni, listitem_T *item)
538 {
539     if (item == NULL)
540 	/* Append new item at end of list. */
541 	list_append(l, ni);
542     else
543     {
544 	/* Insert new item before existing item. */
545 	ni->li_prev = item->li_prev;
546 	ni->li_next = item;
547 	if (item->li_prev == NULL)
548 	{
549 	    l->lv_first = ni;
550 	    ++l->lv_idx;
551 	}
552 	else
553 	{
554 	    item->li_prev->li_next = ni;
555 	    l->lv_idx_item = NULL;
556 	}
557 	item->li_prev = ni;
558 	++l->lv_len;
559     }
560 }
561 
562 /*
563  * Extend "l1" with "l2".
564  * If "bef" is NULL append at the end, otherwise insert before this item.
565  * Returns FAIL when out of memory.
566  */
567     int
568 list_extend(list_T *l1, list_T *l2, listitem_T *bef)
569 {
570     listitem_T	*item;
571     int		todo = l2->lv_len;
572 
573     /* We also quit the loop when we have inserted the original item count of
574      * the list, avoid a hang when we extend a list with itself. */
575     for (item = l2->lv_first; item != NULL && --todo >= 0; item = item->li_next)
576 	if (list_insert_tv(l1, &item->li_tv, bef) == FAIL)
577 	    return FAIL;
578     return OK;
579 }
580 
581 /*
582  * Concatenate lists "l1" and "l2" into a new list, stored in "tv".
583  * Return FAIL when out of memory.
584  */
585     int
586 list_concat(list_T *l1, list_T *l2, typval_T *tv)
587 {
588     list_T	*l;
589 
590     if (l1 == NULL || l2 == NULL)
591 	return FAIL;
592 
593     /* make a copy of the first list. */
594     l = list_copy(l1, FALSE, 0);
595     if (l == NULL)
596 	return FAIL;
597     tv->v_type = VAR_LIST;
598     tv->vval.v_list = l;
599 
600     /* append all items from the second list */
601     return list_extend(l, l2, NULL);
602 }
603 
604 /*
605  * Make a copy of list "orig".  Shallow if "deep" is FALSE.
606  * The refcount of the new list is set to 1.
607  * See item_copy() for "copyID".
608  * Returns NULL when out of memory.
609  */
610     list_T *
611 list_copy(list_T *orig, int deep, int copyID)
612 {
613     list_T	*copy;
614     listitem_T	*item;
615     listitem_T	*ni;
616 
617     if (orig == NULL)
618 	return NULL;
619 
620     copy = list_alloc();
621     if (copy != NULL)
622     {
623 	if (copyID != 0)
624 	{
625 	    /* Do this before adding the items, because one of the items may
626 	     * refer back to this list. */
627 	    orig->lv_copyID = copyID;
628 	    orig->lv_copylist = copy;
629 	}
630 	for (item = orig->lv_first; item != NULL && !got_int;
631 							 item = item->li_next)
632 	{
633 	    ni = listitem_alloc();
634 	    if (ni == NULL)
635 		break;
636 	    if (deep)
637 	    {
638 		if (item_copy(&item->li_tv, &ni->li_tv, deep, copyID) == FAIL)
639 		{
640 		    vim_free(ni);
641 		    break;
642 		}
643 	    }
644 	    else
645 		copy_tv(&item->li_tv, &ni->li_tv);
646 	    list_append(copy, ni);
647 	}
648 	++copy->lv_refcount;
649 	if (item != NULL)
650 	{
651 	    list_unref(copy);
652 	    copy = NULL;
653 	}
654     }
655 
656     return copy;
657 }
658 
659 /*
660  * Remove items "item" to "item2" from list "l".
661  * Does not free the listitem or the value!
662  * This used to be called list_remove, but that conflicts with a Sun header
663  * file.
664  */
665     void
666 vimlist_remove(list_T *l, listitem_T *item, listitem_T *item2)
667 {
668     listitem_T	*ip;
669 
670     /* notify watchers */
671     for (ip = item; ip != NULL; ip = ip->li_next)
672     {
673 	--l->lv_len;
674 	list_fix_watch(l, ip);
675 	if (ip == item2)
676 	    break;
677     }
678 
679     if (item2->li_next == NULL)
680 	l->lv_last = item->li_prev;
681     else
682 	item2->li_next->li_prev = item->li_prev;
683     if (item->li_prev == NULL)
684 	l->lv_first = item2->li_next;
685     else
686 	item->li_prev->li_next = item2->li_next;
687     l->lv_idx_item = NULL;
688 }
689 
690 /*
691  * Return an allocated string with the string representation of a list.
692  * May return NULL.
693  */
694     char_u *
695 list2string(typval_T *tv, int copyID, int restore_copyID)
696 {
697     garray_T	ga;
698 
699     if (tv->vval.v_list == NULL)
700 	return NULL;
701     ga_init2(&ga, (int)sizeof(char), 80);
702     ga_append(&ga, '[');
703     if (list_join(&ga, tv->vval.v_list, (char_u *)", ",
704 				       FALSE, restore_copyID, copyID) == FAIL)
705     {
706 	vim_free(ga.ga_data);
707 	return NULL;
708     }
709     ga_append(&ga, ']');
710     ga_append(&ga, NUL);
711     return (char_u *)ga.ga_data;
712 }
713 
714 typedef struct join_S {
715     char_u	*s;
716     char_u	*tofree;
717 } join_T;
718 
719     static int
720 list_join_inner(
721     garray_T	*gap,		/* to store the result in */
722     list_T	*l,
723     char_u	*sep,
724     int		echo_style,
725     int		restore_copyID,
726     int		copyID,
727     garray_T	*join_gap)	/* to keep each list item string */
728 {
729     int		i;
730     join_T	*p;
731     int		len;
732     int		sumlen = 0;
733     int		first = TRUE;
734     char_u	*tofree;
735     char_u	numbuf[NUMBUFLEN];
736     listitem_T	*item;
737     char_u	*s;
738 
739     /* Stringify each item in the list. */
740     for (item = l->lv_first; item != NULL && !got_int; item = item->li_next)
741     {
742 	s = echo_string_core(&item->li_tv, &tofree, numbuf, copyID,
743 				      echo_style, restore_copyID, !echo_style);
744 	if (s == NULL)
745 	    return FAIL;
746 
747 	len = (int)STRLEN(s);
748 	sumlen += len;
749 
750 	(void)ga_grow(join_gap, 1);
751 	p = ((join_T *)join_gap->ga_data) + (join_gap->ga_len++);
752 	if (tofree != NULL || s != numbuf)
753 	{
754 	    p->s = s;
755 	    p->tofree = tofree;
756 	}
757 	else
758 	{
759 	    p->s = vim_strnsave(s, len);
760 	    p->tofree = p->s;
761 	}
762 
763 	line_breakcheck();
764 	if (did_echo_string_emsg)  /* recursion error, bail out */
765 	    break;
766     }
767 
768     /* Allocate result buffer with its total size, avoid re-allocation and
769      * multiple copy operations.  Add 2 for a tailing ']' and NUL. */
770     if (join_gap->ga_len >= 2)
771 	sumlen += (int)STRLEN(sep) * (join_gap->ga_len - 1);
772     if (ga_grow(gap, sumlen + 2) == FAIL)
773 	return FAIL;
774 
775     for (i = 0; i < join_gap->ga_len && !got_int; ++i)
776     {
777 	if (first)
778 	    first = FALSE;
779 	else
780 	    ga_concat(gap, sep);
781 	p = ((join_T *)join_gap->ga_data) + i;
782 
783 	if (p->s != NULL)
784 	    ga_concat(gap, p->s);
785 	line_breakcheck();
786     }
787 
788     return OK;
789 }
790 
791 /*
792  * Join list "l" into a string in "*gap", using separator "sep".
793  * When "echo_style" is TRUE use String as echoed, otherwise as inside a List.
794  * Return FAIL or OK.
795  */
796     int
797 list_join(
798     garray_T	*gap,
799     list_T	*l,
800     char_u	*sep,
801     int		echo_style,
802     int		restore_copyID,
803     int		copyID)
804 {
805     garray_T	join_ga;
806     int		retval;
807     join_T	*p;
808     int		i;
809 
810     if (l->lv_len < 1)
811 	return OK; /* nothing to do */
812     ga_init2(&join_ga, (int)sizeof(join_T), l->lv_len);
813     retval = list_join_inner(gap, l, sep, echo_style, restore_copyID,
814 							    copyID, &join_ga);
815 
816     /* Dispose each item in join_ga. */
817     if (join_ga.ga_data != NULL)
818     {
819 	p = (join_T *)join_ga.ga_data;
820 	for (i = 0; i < join_ga.ga_len; ++i)
821 	{
822 	    vim_free(p->tofree);
823 	    ++p;
824 	}
825 	ga_clear(&join_ga);
826     }
827 
828     return retval;
829 }
830 
831 /*
832  * Allocate a variable for a List and fill it from "*arg".
833  * Return OK or FAIL.
834  */
835     int
836 get_list_tv(char_u **arg, typval_T *rettv, int evaluate)
837 {
838     list_T	*l = NULL;
839     typval_T	tv;
840     listitem_T	*item;
841 
842     if (evaluate)
843     {
844 	l = list_alloc();
845 	if (l == NULL)
846 	    return FAIL;
847     }
848 
849     *arg = skipwhite(*arg + 1);
850     while (**arg != ']' && **arg != NUL)
851     {
852 	if (eval1(arg, &tv, evaluate) == FAIL)	/* recursive! */
853 	    goto failret;
854 	if (evaluate)
855 	{
856 	    item = listitem_alloc();
857 	    if (item != NULL)
858 	    {
859 		item->li_tv = tv;
860 		item->li_tv.v_lock = 0;
861 		list_append(l, item);
862 	    }
863 	    else
864 		clear_tv(&tv);
865 	}
866 
867 	if (**arg == ']')
868 	    break;
869 	if (**arg != ',')
870 	{
871 	    EMSG2(_("E696: Missing comma in List: %s"), *arg);
872 	    goto failret;
873 	}
874 	*arg = skipwhite(*arg + 1);
875     }
876 
877     if (**arg != ']')
878     {
879 	EMSG2(_("E697: Missing end of List ']': %s"), *arg);
880 failret:
881 	if (evaluate)
882 	    list_free(l);
883 	return FAIL;
884     }
885 
886     *arg = skipwhite(*arg + 1);
887     if (evaluate)
888 	rettv_list_set(rettv, l);
889 
890     return OK;
891 }
892 
893 /*
894  * Write "list" of strings to file "fd".
895  */
896     int
897 write_list(FILE *fd, list_T *list, int binary)
898 {
899     listitem_T	*li;
900     int		c;
901     int		ret = OK;
902     char_u	*s;
903 
904     for (li = list->lv_first; li != NULL; li = li->li_next)
905     {
906 	for (s = get_tv_string(&li->li_tv); *s != NUL; ++s)
907 	{
908 	    if (*s == '\n')
909 		c = putc(NUL, fd);
910 	    else
911 		c = putc(*s, fd);
912 	    if (c == EOF)
913 	    {
914 		ret = FAIL;
915 		break;
916 	    }
917 	}
918 	if (!binary || li->li_next != NULL)
919 	    if (putc('\n', fd) == EOF)
920 	    {
921 		ret = FAIL;
922 		break;
923 	    }
924 	if (ret == FAIL)
925 	{
926 	    EMSG(_(e_write));
927 	    break;
928 	}
929     }
930     return ret;
931 }
932 
933 /*
934  * Initialize a static list with 10 items.
935  */
936     void
937 init_static_list(staticList10_T *sl)
938 {
939     list_T  *l = &sl->sl_list;
940     int	    i;
941 
942     memset(sl, 0, sizeof(staticList10_T));
943     l->lv_first = &sl->sl_items[0];
944     l->lv_last = &sl->sl_items[9];
945     l->lv_refcount = DO_NOT_FREE_CNT;
946     l->lv_lock = VAR_FIXED;
947     sl->sl_list.lv_len = 10;
948 
949     for (i = 0; i < 10; ++i)
950     {
951 	listitem_T *li = &sl->sl_items[i];
952 
953 	if (i == 0)
954 	    li->li_prev = NULL;
955 	else
956 	    li->li_prev = li - 1;
957 	if (i == 9)
958 	    li->li_next = NULL;
959 	else
960 	    li->li_next = li + 1;
961     }
962 }
963 
964 #endif /* defined(FEAT_EVAL) */
965