/* vi:set ts=8 sts=4 sw=4 noet:
*
* VIM - Vi IMproved by Bram Moolenaar
*
* Do ":help uganda" in Vim to read copying and usage conditions.
* Do ":help credits" in Vim to see a list of people who contributed.
* See README.txt for an overview of the Vim source code.
*/
/*
* list.c: List support
*/
#include "vim.h"
#if defined(FEAT_EVAL) || defined(PROTO)
/* List heads for garbage collection. */
static list_T *first_list = NULL; /* list of all lists */
/*
* Add a watcher to a list.
*/
void
list_add_watch(list_T *l, listwatch_T *lw)
{
lw->lw_next = l->lv_watch;
l->lv_watch = lw;
}
/*
* Remove a watcher from a list.
* No warning when it isn't found...
*/
void
list_rem_watch(list_T *l, listwatch_T *lwrem)
{
listwatch_T *lw, **lwp;
lwp = &l->lv_watch;
for (lw = l->lv_watch; lw != NULL; lw = lw->lw_next)
{
if (lw == lwrem)
{
*lwp = lw->lw_next;
break;
}
lwp = &lw->lw_next;
}
}
/*
* Just before removing an item from a list: advance watchers to the next
* item.
*/
void
list_fix_watch(list_T *l, listitem_T *item)
{
listwatch_T *lw;
for (lw = l->lv_watch; lw != NULL; lw = lw->lw_next)
if (lw->lw_item == item)
lw->lw_item = item->li_next;
}
/*
* Allocate an empty header for a list.
* Caller should take care of the reference count.
*/
list_T *
list_alloc(void)
{
list_T *l;
l = (list_T *)alloc_clear(sizeof(list_T));
if (l != NULL)
{
/* Prepend the list to the list of lists for garbage collection. */
if (first_list != NULL)
first_list->lv_used_prev = l;
l->lv_used_prev = NULL;
l->lv_used_next = first_list;
first_list = l;
}
return l;
}
/*
* list_alloc() with an ID for alloc_fail().
*/
list_T *
list_alloc_id(alloc_id_T id UNUSED)
{
#ifdef FEAT_EVAL
if (alloc_fail_id == id && alloc_does_fail((long_u)sizeof(list_T)))
return NULL;
#endif
return (list_alloc());
}
/*
* Allocate an empty list for a return value, with reference count set.
* Returns OK or FAIL.
*/
int
rettv_list_alloc(typval_T *rettv)
{
list_T *l = list_alloc();
if (l == NULL)
return FAIL;
rettv->v_lock = 0;
rettv_list_set(rettv, l);
return OK;
}
/*
* Same as rettv_list_alloc() but uses an allocation id for testing.
*/
int
rettv_list_alloc_id(typval_T *rettv, alloc_id_T id UNUSED)
{
#ifdef FEAT_EVAL
if (alloc_fail_id == id && alloc_does_fail((long_u)sizeof(list_T)))
return FAIL;
#endif
return rettv_list_alloc(rettv);
}
/*
* Set a list as the return value
*/
void
rettv_list_set(typval_T *rettv, list_T *l)
{
rettv->v_type = VAR_LIST;
rettv->vval.v_list = l;
if (l != NULL)
++l->lv_refcount;
}
/*
* Unreference a list: decrement the reference count and free it when it
* becomes zero.
*/
void
list_unref(list_T *l)
{
if (l != NULL && --l->lv_refcount <= 0)
list_free(l);
}
/*
* Free a list, including all non-container items it points to.
* Ignores the reference count.
*/
static void
list_free_contents(list_T *l)
{
listitem_T *item;
for (item = l->lv_first; item != NULL; item = l->lv_first)
{
/* Remove the item before deleting it. */
l->lv_first = item->li_next;
clear_tv(&item->li_tv);
vim_free(item);
}
}
/*
* Go through the list of lists and free items without the copyID.
* But don't free a list that has a watcher (used in a for loop), these
* are not referenced anywhere.
*/
int
list_free_nonref(int copyID)
{
list_T *ll;
int did_free = FALSE;
for (ll = first_list; ll != NULL; ll = ll->lv_used_next)
if ((ll->lv_copyID & COPYID_MASK) != (copyID & COPYID_MASK)
&& ll->lv_watch == NULL)
{
/* Free the List and ordinary items it contains, but don't recurse
* into Lists and Dictionaries, they will be in the list of dicts
* or list of lists. */
list_free_contents(ll);
did_free = TRUE;