mirror of
https://github.com/zsh-users/zsh
synced 2024-11-02 09:47:59 +00:00
459 lines
11 KiB
C
459 lines
11 KiB
C
/*
|
|
* 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;
|
|
}
|
|
|
|
/*
|
|
* Join two linked lists. Neither may be null, though either
|
|
* may be empty.
|
|
*
|
|
* It is assumed the pieces come from the heap, but if not it is
|
|
* safe to free LinkList second.
|
|
*/
|
|
|
|
/**/
|
|
mod_export LinkList
|
|
joinlists(LinkList first, LinkList second)
|
|
{
|
|
LinkNode moveme = firstnode(second);
|
|
if (moveme) {
|
|
if (firstnode(first)) {
|
|
LinkNode anchor = lastnode(first);
|
|
anchor->next = moveme;
|
|
moveme->prev = anchor;
|
|
} else {
|
|
first->list.first = moveme;
|
|
moveme->prev = &first->node;
|
|
}
|
|
first->list.last = second->list.last;
|
|
|
|
second->list.first = second->list.last = NULL;
|
|
}
|
|
return first;
|
|
}
|
|
|
|
/*
|
|
* 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;
|
|
}
|