/*
 * linklist.c - linked lists
 *
 * This file is part of zsh, the Z shell.
 *
 * Copyright (c) 1992-1997 Paul Falstad
 * All rights reserved.
 *
 * Permission is hereby granted, without written agreement and without
 * license or royalty fees, to use, copy, modify, and distribute this
 * software and to distribute modified versions of this software for any
 * purpose, provided that the above copyright notice and the following
 * two paragraphs appear in all copies of this software.
 *
 * In no event shall Paul Falstad or the Zsh Development Group be liable
 * to any party for direct, indirect, special, incidental, or consequential
 * damages arising out of the use of this software and its documentation,
 * even if Paul Falstad and the Zsh Development Group have been advised of
 * the possibility of such damage.
 *
 * Paul Falstad and the Zsh Development Group specifically disclaim any
 * warranties, including, but not limited to, the implied warranties of
 * merchantability and fitness for a particular purpose.  The software
 * provided hereunder is on an "as is" basis, and Paul Falstad and the
 * Zsh Development Group have no obligation to provide maintenance,
 * support, updates, enhancements, or modifications.
 *
 */

#include "zsh.mdh"
#include "linklist.pro"

/*
 * Anatomy of a LinkList
 *
 * LinkList with 4 nodes:
 *
 * LinkList is a        first   last   flags   (LinkList)
 * union; see zsh.h     next    prev   dat     (LinkNode)
 *                    +-------+------+------+
 *                    |       |      |      | See comment in subst.c
 *     +------------> |   |   |   |  |      | about LF_ARRAY.
 *     |              +---|---+---|--+------+
 *     |                  |       |
 *     |     +------------+       +--------------+
 *     |     |                                   |
 *     |    \|/                                 \|/
 *     |   +----+      +----+      +----+      +----+
 *     |   |    |      |    |      |    |      | \/ |  X here is NULL.
 *     |   |  -------> |  -------> |  -------> | /\ |
 *     |   |next|      |next|      |next|      |next|
 *     |   +----+      +----+      +----+      +----+
 *     |   |    |      |    |      |    |      |    |
 *     +------  | <-------  | <-------  | <-------  |
 *         |prev|      |prev|      |prev|      |prev|
 *         +----+      +----+      +----+      +----+
 *         |    |      |    |      |    |      |    | Pointers to data,
 *         |dat |      |dat |      |dat |      |dat | usually char **.
 *         +----+      +----+      +----+      +----+
 *        LinkNode    LinkNode    LinkNode    LinkNode
 *
 *
 * Empty LinkList:
 *                    first   last   flags
 *                   +------+------+-------+
 *             +---> | NULL |      |   0   |
 *             |     |      |   |  |       |
 *             |     +------+---|--+-------+
 *             |                |
 *             +----------------+
 *
 * Traversing a LinkList:
 * Traversing forward through a list uses an iterator-style paradigm.
 * for (LinkNode node = firstnode(list); node; incnode(node)) {
 *     // Access/manipulate the node using macros (see zsh.h)
 * }
 *
 * Traversing backwards is the same, with a small caveat.
 * for (LinkNode node = lastnode(list); node != &list->node; decnode(node)) {
 *     // The loop condition should be obvious given the above diagrams.
 * }
 *
 * If you're going to be moving back and forth, best to AND both
 * conditions.
 *
 * while (node && node != &list->node) {
 *     // If both incnode(list) and decnode(list) are used, and it's
 *     // unknown at which end of the list traversal will stop.
 * }
 *
 * Macros and functions prefixed with 'z' (ie znewlinklist,
 * zinsertlinknode) use permanent allocation, which you have to free
 * manually (with freelinklist(), maybe?). Non-z-prefixed
 * macros/functions allocate from heap, which will be automatically
 * freed.
 *
 */

/* Get an empty linked list header */

/**/
mod_export LinkList
newlinklist(void)
{
    LinkList list;

    list = (LinkList) zhalloc(sizeof *list);
    list->list.first = NULL;
    list->list.last = &list->node;
    list->list.flags = 0;
    return list;
}

/**/
mod_export LinkList
znewlinklist(void)
{
    LinkList list;

    list = (LinkList) zalloc(sizeof *list);
    if (!list)
	return NULL;
    list->list.first = NULL;
    list->list.last = &list->node;
    list->list.flags = 0;
    return list;
}

/* Insert a node in a linked list after a given node */

/**/
mod_export LinkNode
insertlinknode(LinkList list, LinkNode node, void *dat)
{
    LinkNode tmp, new;

    tmp = node->next;
    node->next = new = (LinkNode) zhalloc(sizeof *tmp);
    new->prev = node;
    new->dat = dat;
    new->next = tmp;
    if (tmp)
	tmp->prev = new;
    else
	list->list.last = new;
    return new;
}

/**/
mod_export LinkNode
zinsertlinknode(LinkList list, LinkNode node, void *dat)
{
    LinkNode tmp, new;

    tmp = node->next;
    node->next = new = (LinkNode) zalloc(sizeof *tmp);
    if (!new)
	return NULL;
    new->prev = node;
    new->dat = dat;
    new->next = tmp;
    if (tmp)
	tmp->prev = new;
    else
	list->list.last = new;
    return new;
}

/* Insert an already-existing node into a linked list after a given node */

/**/
mod_export LinkNode
uinsertlinknode(LinkList list, LinkNode node, LinkNode new)
{
    LinkNode tmp = node->next;
    node->next = new;
    new->prev = node;
    new->next = tmp;
    if (tmp)
	tmp->prev = new;
    else
	list->list.last = new;
    return new;
}

/* Insert a list in another list */

/**/
mod_export void
insertlinklist(LinkList l, LinkNode where, LinkList x)
{
    LinkNode nx;

    nx = where->next;
    if (!firstnode(l))
	return;
    where->next = firstnode(l);
    l->list.last->next = nx;
    l->list.first->prev = where;
    if (nx)
	nx->prev = lastnode(l);
    else
	x->list.last = lastnode(l);
}

/* Pop the top node off a linked list and free it. */

/**/
mod_export void *
getlinknode(LinkList list)
{
    void *dat;
    LinkNode node;

    if (!(node = firstnode(list)))
	return NULL;
    dat = node->dat;
    list->list.first = node->next;
    if (node->next)
	node->next->prev = &list->node;
    else
	list->list.last = &list->node;
    zfree(node, sizeof *node);
    return dat;
}

/* Pop the top node off a linked list without freeing it. */

/**/
mod_export void *
ugetnode(LinkList list)
{
    void *dat;
    LinkNode node;

    if (!(node = firstnode(list)))
	return NULL;
    dat = node->dat;
    list->list.first = node->next;
    if (node->next)
	node->next->prev = &list->node;
    else
	list->list.last = &list->node;
    return dat;
}

/* Remove a node from a linked list */

/**/
mod_export void *
remnode(LinkList list, LinkNode nd)
{
    void *dat;

    nd->prev->next = nd->next;
    if (nd->next)
	nd->next->prev = nd->prev;
    else
	list->list.last = nd->prev;
    dat = nd->dat;
    zfree(nd, sizeof *nd);

    return dat;
}

/* Remove a node from a linked list without freeing */

/**/
mod_export void *
uremnode(LinkList list, LinkNode nd)
{
    void *dat;

    nd->prev->next = nd->next;
    if (nd->next)
	nd->next->prev = nd->prev;
    else
	list->list.last = nd->prev;
    dat = nd->dat;
    return dat;
}

/* Free a linked list */

/**/
mod_export void
freelinklist(LinkList list, FreeFunc freefunc)
{
    LinkNode node, next;

    for (node = firstnode(list); node; node = next) {
	next = node->next;
	if (freefunc)
	    freefunc(node->dat);
	zfree(node, sizeof *node);
    }
    zfree(list, sizeof *list);
}

/* Count the number of nodes in a linked list */

/**/
mod_export int
countlinknodes(LinkList list)
{
    LinkNode nd;
    int ct = 0;

    for (nd = firstnode(list); nd; incnode(nd), ct++);
    return ct;
}

/* Make specified node first, moving preceding nodes to end */

/**/
mod_export void
rolllist(LinkList l, LinkNode nd)
{
    l->list.last->next = firstnode(l);
    l->list.first->prev = lastnode(l);
    l->list.first = nd;
    l->list.last = nd->prev;
    nd->prev = &l->node;
    l->list.last->next = 0;
}

/* Create linklist of specified size. node->dats are not initialized. */

/**/
mod_export LinkList
newsizedlist(int size)
{
    LinkList list;
    LinkNode node;

    list = (LinkList) zhalloc(sizeof *list + (size * sizeof *node));

    list->list.first = &list[1].node;
    for (node = firstnode(list); size; size--, node++) {
	node->prev = node - 1;
	node->next = node + 1;
    }
    list->list.last = node - 1;
    list->list.first->prev = &list->node;
    node[-1].next = NULL;

    return list;
}

/*
 * Return the node whose data is the pointer "dat", else NULL.
 * Can be used as a boolean test.
 */

/**/
mod_export LinkNode
linknodebydatum(LinkList list, void *dat)
{
    LinkNode node;

    for (node = firstnode(list); node; incnode(node))
	if (getdata(node) == dat)
	    return node;

    return NULL;
}

/*
 * Return the node whose data matches the string "dat", else NULL.
 */

/**/
mod_export LinkNode
linknodebystring(LinkList list, char *dat)
{
    LinkNode node;

    for (node = firstnode(list); node; incnode(node))
	if (!strcmp((char *)getdata(node), dat))
	    return node;

    return NULL;
}

/*
 * Convert a linked list whose data elements are strings to
 * an array.  Memory is off the heap and the elements of the
 * array are the same elements as the linked list data if copy is
 * 0, else copied onto the heap.
 */

/**/
mod_export char **
hlinklist2array(LinkList list, int copy)
{
    int l = countlinknodes(list);
    char **ret = (char **) zhalloc((l + 1) * sizeof(char *)), **p;
    LinkNode n;

    for (n = firstnode(list), p = ret; n; incnode(n), p++) {
	*p = (char *) getdata(n);
	if (copy)
	    *p = dupstring(*p);
    }
    *p = NULL;

    return ret;
}

/*
 * Convert a linked list whose data elements are strings to
 * an array.  The result is a permanently allocated, freearrayable
 * array.
 */

/**/
mod_export char **
zlinklist2array(LinkList list)
{
    int l = countlinknodes(list);
    char **ret = (char **) zalloc((l + 1) * sizeof(char *)), **p;
    LinkNode n;

    for (n = firstnode(list), p = ret; n; incnode(n), p++) {
	*p = ztrdup((char *) getdata(n));
    }
    *p = NULL;

    return ret;
}
