mirror of
https://github.com/ultimatepp/ultimatepp.git
synced 2026-05-17 22:03:07 -06:00
796 lines
25 KiB
C
796 lines
25 KiB
C
/*====================================================================*
|
|
- Copyright (C) 2001 Leptonica. All rights reserved.
|
|
- This software is distributed in the hope that it will be
|
|
- useful, but with NO WARRANTY OF ANY KIND.
|
|
- No author or distributor accepts responsibility to anyone for the
|
|
- consequences of using this software, or for whether it serves any
|
|
- particular purpose or works at all, unless he or she says so in
|
|
- writing. Everyone is granted permission to copy, modify and
|
|
- redistribute this source code, for commercial or non-commercial
|
|
- purposes, with the following restrictions: (1) the origin of this
|
|
- source code must not be misrepresented; (2) modified versions must
|
|
- be plainly marked as such; and (3) this notice may not be removed
|
|
- or altered from any source or modified source distribution.
|
|
*====================================================================*/
|
|
|
|
/*
|
|
* list.c
|
|
*
|
|
* Inserting and removing elements
|
|
*
|
|
* void listDestroy()
|
|
* DLLIST *listAddToHead()
|
|
* l_int32 listAddToTail()
|
|
* l_int32 listInsertBefore()
|
|
* l_int32 listInsertAfter()
|
|
* void *listRemoveElement()
|
|
* void *listRemoveFromHead()
|
|
* void *listRemoveFromTail()
|
|
*
|
|
* Other list operations
|
|
*
|
|
* DLLIST *listFindElement()
|
|
* DLLIST *listFindTail()
|
|
* l_int32 listGetCount()
|
|
* l_int32 listReverse()
|
|
* DLLIST *listJoin()
|
|
*
|
|
* Lists are much harder to handle than arrays. There is
|
|
* more overhead for the programmer, both cognitive and
|
|
* codewise, and more likelihood that an error can be made.
|
|
* For that reason, lists should only be used when it is
|
|
* inefficient to use arrays, such as when elements are
|
|
* routinely inserted or deleted from inside arrays whose
|
|
* average size is greater than about 10.
|
|
*
|
|
* A list of data structures can be implemented in a number
|
|
* of ways. The two most popular are:
|
|
*
|
|
* (1) The list can be composed of a linked list of
|
|
* pointer cells ("cons cells"), where the data structures
|
|
* are hung off the cells. This is more difficult
|
|
* to use because you have to keep track of both
|
|
* your hanging data and the cell structures.
|
|
* It requires 3 pointers for every data structure
|
|
* that is put in a list. There is no problem
|
|
* cloning (using reference counts) for structures that
|
|
* are put in such a list. We implement lists by this
|
|
* method here.
|
|
*
|
|
* (2) The list pointers can be inserted directly into
|
|
* the data structures. This is easy to implement
|
|
* and easier to use, but it adds 2 ptrs of overhead
|
|
* to every data structure in which the ptrs are embedded.
|
|
* It also requires special care not to put the ptrs
|
|
* in any data that is cloned with a reference count;
|
|
* else your lists will break.
|
|
*
|
|
* Writing C code that uses list pointers explicitly to make
|
|
* and alter lists is difficult and prone to error.
|
|
* Consequently, a generic list utility that handles lists
|
|
* of arbitrary objects and doesn't force the programmer to
|
|
* touch the "next" and "prev" pointers, is quite useful.
|
|
* Such functions are provided here. However, the usual
|
|
* situation requires traversing a list and applying some
|
|
* function to one or more of the list elements. Macros
|
|
* for traversing the list are, in general, necessary, to
|
|
* achieve the goal of invisibly handling all "next" and "prev"
|
|
* pointers in generic lists. We provide macros for
|
|
* traversing a list in both forward and reverse directions.
|
|
*
|
|
* Because of the typing in C, implementation of a general
|
|
* list utility requires casting. If macros are used, the
|
|
* casting can be done implicitly; otherwise, using functions,
|
|
* some of the casts must be explicit. Fortunately, this
|
|
* can be implemented with void* so the programmer using
|
|
* the library will not have to make any casts! (Unless you
|
|
* compile with g++, in which case the rules on implicit
|
|
* conversion are more strict.)
|
|
*
|
|
* For example, to add an arbitrary data structure foo to the
|
|
* tail of a list, use
|
|
* listAddToTail(&head, &tail, pfoo);
|
|
* where head and tail are list cell ptrs and pfoo is
|
|
* a pointer to the foo object.
|
|
* And to remove an arbitrary data structure foo from a
|
|
* list, when you know the list cell element it is hanging from,
|
|
* use
|
|
* pfoo = listRemoveElement(&head, elem)
|
|
* where head and elem are list cell ptrs and pfoo is a pointer
|
|
* to the foo object. No casts are required for foo in
|
|
* either direction in ANSI C. (However, casts are
|
|
* required for ANSI C++).
|
|
*
|
|
* We use lists that are composed of doubly-linked
|
|
* cells with data structures hanging off the cells.
|
|
* We use doubly-linked cells to simplify insertion
|
|
* and deletion, and to allow operations to proceed in either
|
|
* direction along the list. With doubly-linked lists,
|
|
* it is tempting to make them circular, by setting head->prev
|
|
* to the tail of the list and tail->next to the head.
|
|
* The circular list costs nothing extra in storage, and
|
|
* allows operations to proceed from either end of the list
|
|
* with equal speed. However, the circular link adds
|
|
* cognitive overhead for the application programmer in
|
|
* general, and it greatly complicates list traversal when
|
|
* arbitrary list elements can be added or removed as you
|
|
* move through. It can be done, but in the spirit of
|
|
* simplicity, we avoid the temptation. The price to be paid
|
|
* is the extra cost to find the tail of a list -- a full
|
|
* traversal -- before the tail can be used. This is a
|
|
* cheap price to pay to avoid major headaches and buggy code.
|
|
*
|
|
* When you are only applying some function to each element
|
|
* in a list, you can go either forwards or backwards.
|
|
* To run through a list forwards, use:
|
|
*
|
|
* for (elem = head; elem; elem = nextelem) {
|
|
* nextelem = elem->next; (in case we destroy elem)
|
|
* <do something with elem->data>
|
|
* }
|
|
*
|
|
* To run through a list backwards, find the tail and use:
|
|
*
|
|
* for (elem = tail; elem; elem = prevelem) {
|
|
# prevelem = elem->prev; (in case we destroy elem)
|
|
* <do something with elem->data>
|
|
* }
|
|
*
|
|
* Even though these patterns are very simple, they are so common
|
|
* that we've provided macros for them in list.h. Using the
|
|
* macros, this becomes:
|
|
*
|
|
* L_BEGIN_LIST_FORWARD(head, elem)
|
|
* <do something with elem->data>
|
|
* L_END_LIST
|
|
*
|
|
* L_BEGIN_LIST_REVERSE(tail, elem)
|
|
* <do something with elem->data>
|
|
* L_END_LIST
|
|
*
|
|
* Note again that with macros, the application programmer does
|
|
* not need to refer explicitly to next and prev fields. Also,
|
|
* in the reverse case, note that we do not explicitly
|
|
* show the head of the list. However, the head of the list
|
|
* is always in scope, and functions can be called within the
|
|
* iterator that change the head.
|
|
*
|
|
* Some special cases are simpler. For example, when
|
|
* removing all items from the head of the list, you can use
|
|
*
|
|
* while (head) {
|
|
* obj = listRemoveFromHead(&head);
|
|
* <do something with obj>
|
|
* }
|
|
*
|
|
* Removing successive elements from the tail is equally simple:
|
|
*
|
|
* while (tail) {
|
|
* obj = listRemoveFromTail(&head, &tail);
|
|
* <do something with obj>
|
|
* }
|
|
*
|
|
* When removing an arbitrary element from a list, use
|
|
*
|
|
* obj = listRemoveElement(&head, elem);
|
|
*
|
|
* All the listRemove*() functions hand you the object,
|
|
* destroy the list cell to which it was attached, and
|
|
* reset the list pointers if necessary.
|
|
*
|
|
* Several other list operations, that do not involve
|
|
* inserting or removing objects, are also provided.
|
|
* The function listFindElement() locates a list pointer
|
|
* by matching the object hanging on it to a given
|
|
* object. The function listFindTail() gets a handle
|
|
* to the tail list ptr, allowing backwards traversals of
|
|
* the list. listGetCount() gives the number of elements
|
|
* in a list. Functions that reverse a list and concatenate
|
|
* two lists are also provided.
|
|
*
|
|
* These functions can be modified for efficiency in the
|
|
* situation where there is a large amount of creation and
|
|
* destruction of list cells. If millions of cells are
|
|
* made and destroyed, but a relatively small number are
|
|
* around at any time, the list cells can be stored for
|
|
* later re-use in a stack (see the generic stack functions
|
|
* in stack.c).
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include "allheaders.h"
|
|
|
|
|
|
/*---------------------------------------------------------------------*
|
|
* Inserting and removing elements *
|
|
*---------------------------------------------------------------------*/
|
|
/*!
|
|
* listDestroy()
|
|
*
|
|
* Input: &head (<to be nulled> head of list)
|
|
* Return: void
|
|
*
|
|
* Notes:
|
|
* (1) This only destroys the cons cells. Before destroying
|
|
* the list, it is necessary to remove all data and set the
|
|
* data pointers in each cons cell to NULL.
|
|
* (2) listDestroy() will give a warning message for each data
|
|
* ptr that is not NULL.
|
|
*/
|
|
void
|
|
listDestroy(DLLIST **phead)
|
|
{
|
|
DLLIST *elem, *next, *head;
|
|
|
|
PROCNAME("listDestroy");
|
|
|
|
if (phead == NULL) {
|
|
L_WARNING("ptr address is null!", procName);
|
|
return;
|
|
}
|
|
|
|
if ((head = *phead) == NULL)
|
|
return;
|
|
|
|
for (elem = head; elem; elem = next) {
|
|
if (elem->data)
|
|
L_WARNING("list data ptr is not null", procName);
|
|
next = elem->next;
|
|
FREE(elem);
|
|
}
|
|
*phead = NULL;
|
|
return;
|
|
}
|
|
|
|
|
|
/*!
|
|
* listAddToHead()
|
|
*
|
|
* Input: &head (<optional> input head)
|
|
* data (void* ptr, to be added)
|
|
* Return: 0 if OK; 1 on error
|
|
*
|
|
* Notes:
|
|
* (1) This makes a new cell, attaches the data, and adds the
|
|
* cell to the head of the list.
|
|
* (2) When consing from NULL, be sure to initialize head to NULL
|
|
* before calling this function.
|
|
*/
|
|
l_int32
|
|
listAddToHead(DLLIST **phead,
|
|
void *data)
|
|
{
|
|
DLLIST *cell, *head;
|
|
|
|
PROCNAME("listAddToHead");
|
|
|
|
if (!phead)
|
|
return ERROR_INT("&head not defined", procName, 1);
|
|
head = *phead;
|
|
if (!data)
|
|
return ERROR_INT("data not defined", procName, 1);
|
|
|
|
if ((cell = (DLLIST *)CALLOC(1, sizeof(DLLIST))) == NULL)
|
|
return ERROR_INT("cell not made", procName, 1);
|
|
cell->data = data;
|
|
|
|
if (!head) { /* start the list; initialize the ptrs */
|
|
cell->prev = NULL;
|
|
cell->next = NULL;
|
|
}
|
|
else {
|
|
cell->prev = NULL;
|
|
cell->next = head;
|
|
head->prev = cell;
|
|
}
|
|
*phead = cell;
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*!
|
|
* listAddToTail()
|
|
*
|
|
* Input: &head (<may be updated>, head can be null)
|
|
* &tail (<updated>, tail can be null)
|
|
* data (void* ptr, to be hung on tail cons cell)
|
|
* Return: 0 if OK; 1 on error
|
|
*
|
|
* Notes:
|
|
* (1) This makes a new cell, attaches the data, and adds the
|
|
* cell to the tail of the list.
|
|
* (2) &head is input to allow the list to be "cons'd" up from NULL.
|
|
* (3) &tail is input to allow the tail to be updated
|
|
* for efficient sequential operation with this function.
|
|
* (4) We assume that if *phead and/or *ptail are not NULL,
|
|
* then they are valid addresses. Therefore:
|
|
* (a) when consing from NULL, be sure to initialize both
|
|
* head and tail to NULL.
|
|
* (b) when tail == NULL for an existing list, the tail
|
|
* will be found and updated.
|
|
*/
|
|
l_int32
|
|
listAddToTail(DLLIST **phead,
|
|
DLLIST **ptail,
|
|
void *data)
|
|
{
|
|
DLLIST *cell, *head, *tail;
|
|
|
|
PROCNAME("listAddToTail");
|
|
|
|
if (!phead)
|
|
return ERROR_INT("&head not defined", procName, 1);
|
|
head = *phead;
|
|
if (!ptail)
|
|
return ERROR_INT("&tail not defined", procName, 1);
|
|
if (!data)
|
|
return ERROR_INT("data not defined", procName, 1);
|
|
|
|
if ((cell = (DLLIST *)CALLOC(1, sizeof(DLLIST))) == NULL)
|
|
return ERROR_INT("cell not made", procName, 1);
|
|
cell->data = data;
|
|
|
|
if (!head) { /* Start the list and initialize the ptrs. *ptail
|
|
* should also have been initialized to NULL */
|
|
cell->prev = NULL;
|
|
cell->next = NULL;
|
|
*phead = cell;
|
|
*ptail = cell;
|
|
}
|
|
else {
|
|
if ((tail = *ptail) == NULL)
|
|
tail = listFindTail(head);
|
|
cell->prev = tail;
|
|
cell->next = NULL;
|
|
tail->next = cell;
|
|
*ptail = cell;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*!
|
|
* listInsertBefore()
|
|
*
|
|
* Input: &head (<optional> input head)
|
|
* elem (list element to be inserted in front of;
|
|
* must be null if head is null)
|
|
* data (void* address, to be added)
|
|
* Return: 0 if OK; 1 on error
|
|
*
|
|
* Notes:
|
|
* (1) This can be called on a null list, in which case both
|
|
* head and elem must be null.
|
|
* (2) If you are searching through a list, looking for a condition
|
|
* to add an element, you can do something like this:
|
|
* L_BEGIN_LIST_FORWARD(head, elem)
|
|
* <identify an elem to insert before>
|
|
* listInsertBefore(&head, elem, data);
|
|
* L_END_LIST
|
|
*
|
|
*/
|
|
l_int32
|
|
listInsertBefore(DLLIST **phead,
|
|
DLLIST *elem,
|
|
void *data)
|
|
{
|
|
DLLIST *cell, *head;
|
|
|
|
PROCNAME("listInsertBefore");
|
|
|
|
if (!phead)
|
|
return ERROR_INT("&head not defined", procName, 1);
|
|
head = *phead;
|
|
if (!data)
|
|
return ERROR_INT("data not defined", procName, 1);
|
|
if ((!head && elem) || (head && !elem))
|
|
return ERROR_INT("head and elem not consistent", procName, 1);
|
|
|
|
/* New cell to insert */
|
|
if ((cell = (DLLIST *)CALLOC(1, sizeof(DLLIST))) == NULL)
|
|
return ERROR_INT("cell not made", procName, 1);
|
|
cell->data = data;
|
|
|
|
if (!head) { /* start the list; initialize the ptrs */
|
|
cell->prev = NULL;
|
|
cell->next = NULL;
|
|
*phead = cell;
|
|
}
|
|
else if (head == elem) { /* insert before head of list */
|
|
cell->prev = NULL;
|
|
cell->next = head;
|
|
head->prev = cell;
|
|
*phead = cell;
|
|
}
|
|
else { /* insert before elem and after head of list */
|
|
cell->prev = elem->prev;
|
|
cell->next = elem;
|
|
elem->prev->next = cell;
|
|
elem->prev = cell;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*!
|
|
* listInsertAfter()
|
|
*
|
|
* Input: &head (<optional> input head)
|
|
* elem (list element to be inserted after;
|
|
* must be null if head is null)
|
|
* data (void* ptr, to be added)
|
|
* Return: 0 if OK; 1 on error
|
|
*
|
|
* Notes:
|
|
* (1) This can be called on a null list, in which case both
|
|
* head and elem must be null. The head is included
|
|
* in the call to allow "consing" up from NULL.
|
|
* (2) If you are searching through a list, looking for a condition
|
|
* to add an element, you can do something like this:
|
|
* L_BEGIN_LIST_FORWARD(head, elem)
|
|
* <identify an elem to insert after>
|
|
* listInsertAfter(&head, elem, data);
|
|
* L_END_LIST
|
|
*/
|
|
l_int32
|
|
listInsertAfter(DLLIST **phead,
|
|
DLLIST *elem,
|
|
void *data)
|
|
{
|
|
DLLIST *cell, *head;
|
|
|
|
PROCNAME("listInsertAfter");
|
|
|
|
if (!phead)
|
|
return ERROR_INT("&head not defined", procName, 1);
|
|
head = *phead;
|
|
if (!data)
|
|
return ERROR_INT("data not defined", procName, 1);
|
|
if ((!head && elem) || (head && !elem))
|
|
return ERROR_INT("head and elem not consistent", procName, 1);
|
|
|
|
/* New cell to insert */
|
|
if ((cell = (DLLIST *)CALLOC(1, sizeof(DLLIST))) == NULL)
|
|
return ERROR_INT("cell not made", procName, 1);
|
|
cell->data = data;
|
|
|
|
if (!head) { /* start the list; initialize the ptrs */
|
|
cell->prev = NULL;
|
|
cell->next = NULL;
|
|
*phead = cell;
|
|
}
|
|
else if (elem->next == NULL) { /* insert after last */
|
|
cell->prev = elem;
|
|
cell->next = NULL;
|
|
elem->next = cell;
|
|
}
|
|
else { /* insert after elem and before the end */
|
|
cell->prev = elem;
|
|
cell->next = elem->next;
|
|
elem->next->prev = cell;
|
|
elem->next = cell;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*!
|
|
* listRemoveElement()
|
|
*
|
|
* Input: &head (<can be changed> input head)
|
|
* elem (list element to be removed)
|
|
* Return: data (void* struct on cell)
|
|
*
|
|
* Notes:
|
|
* (1) in ANSI C, it is not necessary to cast return to actual type; e.g.,
|
|
* pix = listRemoveElement(&head, elem);
|
|
* but in ANSI C++, it is necessary to do the cast:
|
|
* pix = (Pix *)listRemoveElement(&head, elem);
|
|
*/
|
|
void *
|
|
listRemoveElement(DLLIST **phead,
|
|
DLLIST *elem)
|
|
{
|
|
void *data;
|
|
DLLIST *head;
|
|
|
|
PROCNAME("listRemoveElement");
|
|
|
|
if (!phead)
|
|
return (void *)ERROR_PTR("&head not defined", procName, NULL);
|
|
head = *phead;
|
|
if (!head)
|
|
return (void *)ERROR_PTR("head not defined", procName, NULL);
|
|
if (!elem)
|
|
return (void *)ERROR_PTR("elem not defined", procName, NULL);
|
|
|
|
data = elem->data;
|
|
|
|
if (head->next == NULL) { /* only one */
|
|
if (elem != head)
|
|
return (void *)ERROR_PTR("elem must be head", procName, NULL);
|
|
*phead = NULL;
|
|
}
|
|
else if (head == elem) { /* first one */
|
|
elem->next->prev = NULL;
|
|
*phead = elem->next;
|
|
}
|
|
else if (elem->next == NULL) { /* last one */
|
|
elem->prev->next = NULL;
|
|
}
|
|
else { /* neither the first nor the last one */
|
|
elem->next->prev = elem->prev;
|
|
elem->prev->next = elem->next;
|
|
}
|
|
|
|
FREE(elem);
|
|
return data;
|
|
}
|
|
|
|
|
|
/*!
|
|
* listRemoveFromHead()
|
|
*
|
|
* Input: &head (<to be updated> head of list)
|
|
* Return: data (void* struct on cell), or null on error
|
|
*
|
|
* Notes:
|
|
* (1) in ANSI C, it is not necessary to cast return to actual type; e.g.,
|
|
* pix = listRemoveFromHead(&head);
|
|
* but in ANSI C++, it is necessary to do the cast; e.g.,
|
|
* pix = (Pix *)listRemoveFromHead(&head);
|
|
*/
|
|
void *
|
|
listRemoveFromHead(DLLIST **phead)
|
|
{
|
|
DLLIST *head;
|
|
void *data;
|
|
|
|
PROCNAME("listRemoveFromHead");
|
|
|
|
if (!phead)
|
|
return (void *)ERROR_PTR("&head not defined", procName, NULL);
|
|
if ((head = *phead) == NULL)
|
|
return (void *)ERROR_PTR("head not defined", procName, NULL);
|
|
|
|
if (head->next == NULL) /* only one */
|
|
*phead = NULL;
|
|
else {
|
|
head->next->prev = NULL;
|
|
*phead = head->next;
|
|
}
|
|
|
|
data = head->data;
|
|
FREE(head);
|
|
return data;
|
|
}
|
|
|
|
|
|
/*!
|
|
* listRemoveFromTail()
|
|
*
|
|
* Input: &head (<may be changed>, head must NOT be null)
|
|
* &tail (<always updated>, tail may be null)
|
|
* Return: data (void* struct on cell) or null on error
|
|
*
|
|
* Notes:
|
|
* (1) We include &head so that it can be set to NULL if
|
|
* if the only element in the list is removed.
|
|
* (2) The function is relying on the fact that if tail is
|
|
* not NULL, then is is a valid address. You can use
|
|
* this function with tail == NULL for an existing list, in
|
|
* which case the tail is found and updated, and the
|
|
* removed element is returned.
|
|
* (3) In ANSI C, it is not necessary to cast return to actual type; e.g.,
|
|
* pix = listRemoveFromTail(&head, &tail);
|
|
* but in ANSI C++, it is necessary to do the cast; e.g.,
|
|
* pix = (Pix *)listRemoveFromTail(&head, &tail);
|
|
*/
|
|
void *
|
|
listRemoveFromTail(DLLIST **phead,
|
|
DLLIST **ptail)
|
|
{
|
|
DLLIST *head, *tail;
|
|
void *data;
|
|
|
|
PROCNAME("listRemoveFromTail");
|
|
|
|
if (!phead)
|
|
return (void *)ERROR_PTR("&head not defined", procName, NULL);
|
|
if ((head = *phead) == NULL)
|
|
return (void *)ERROR_PTR("head not defined", procName, NULL);
|
|
if (!ptail)
|
|
return (void *)ERROR_PTR("&tail not defined", procName, NULL);
|
|
if ((tail = *ptail) == NULL)
|
|
tail = listFindTail(head);
|
|
|
|
if (head->next == NULL) { /* only one */
|
|
*phead = NULL;
|
|
*ptail = NULL;
|
|
}
|
|
else {
|
|
tail->prev->next = NULL;
|
|
*ptail = tail->prev;
|
|
}
|
|
|
|
data = tail->data;
|
|
FREE(tail);
|
|
return data;
|
|
}
|
|
|
|
|
|
|
|
/*---------------------------------------------------------------------*
|
|
* Other list operations *
|
|
*---------------------------------------------------------------------*/
|
|
/*!
|
|
* listFindElement()
|
|
*
|
|
* Input: head (list head)
|
|
* data (void* address, to be searched for)
|
|
* Return: cell (the containing cell, or null if not found or on error)
|
|
*
|
|
* Notes:
|
|
* (1) This returns a ptr to the cell, which is still embedded in
|
|
* the list.
|
|
* (2) This handle and the attached data have not been copied or
|
|
* reference counted, so they must not be destroyed. This
|
|
* violates our basic rule that every handle returned from a
|
|
* function is owned by that function and must be destroyed,
|
|
* but if rules aren't there to be broken, why have them?
|
|
*/
|
|
DLLIST *
|
|
listFindElement(DLLIST *head,
|
|
void *data)
|
|
{
|
|
DLLIST *cell;
|
|
|
|
PROCNAME("listFindElement");
|
|
|
|
if (!head)
|
|
return (DLLIST *)ERROR_PTR("head not defined", procName, NULL);
|
|
if (!data)
|
|
return (DLLIST *)ERROR_PTR("data not defined", procName, NULL);
|
|
|
|
for (cell = head; cell; cell = cell->next) {
|
|
if (cell->data == data)
|
|
return cell;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/*!
|
|
* listFindTail()
|
|
*
|
|
* Input: head
|
|
* Return: tail, or null on error
|
|
*/
|
|
DLLIST *
|
|
listFindTail(DLLIST *head)
|
|
{
|
|
DLLIST *cell;
|
|
|
|
PROCNAME("listFindTail");
|
|
|
|
if (!head)
|
|
return (DLLIST *)ERROR_PTR("head not defined", procName, NULL);
|
|
|
|
for (cell = head; cell; cell = cell->next) {
|
|
if (cell->next == NULL)
|
|
return cell;
|
|
}
|
|
|
|
return (DLLIST *)ERROR_PTR("tail not found !!", procName, NULL);
|
|
}
|
|
|
|
|
|
/*!
|
|
* listGetCount()
|
|
*
|
|
* Input: head (of list)
|
|
* Return: number of elements; 0 if no list or on error
|
|
*/
|
|
l_int32
|
|
listGetCount(DLLIST *head)
|
|
{
|
|
l_int32 count;
|
|
DLLIST *elem;
|
|
|
|
PROCNAME("listGetCount");
|
|
|
|
if (!head)
|
|
return ERROR_INT("head not defined", procName, 0);
|
|
|
|
count = 0;
|
|
for (elem = head; elem; elem = elem->next)
|
|
count++;
|
|
|
|
return count;
|
|
}
|
|
|
|
|
|
/*!
|
|
* listReverse()
|
|
*
|
|
* Input: &head (<may be changed> list head)
|
|
* Return: 0 if OK, 1 on error
|
|
*
|
|
* Notes:
|
|
* (1) This reverses the list in-place.
|
|
*/
|
|
l_int32
|
|
listReverse(DLLIST **phead)
|
|
{
|
|
void *obj; /* whatever */
|
|
DLLIST *head, *rhead;
|
|
|
|
PROCNAME("listReverse");
|
|
|
|
if (!phead)
|
|
return ERROR_INT("&head not defined", procName, 1);
|
|
if ((head = *phead) == NULL)
|
|
return ERROR_INT("head not defined", procName, 1);
|
|
|
|
rhead = NULL;
|
|
while (head) {
|
|
obj = listRemoveFromHead(&head);
|
|
listAddToHead(&rhead, obj);
|
|
}
|
|
|
|
*phead = rhead;
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*!
|
|
* listJoin()
|
|
*
|
|
* Input: &head1 (<may be changed> head of first list)
|
|
* &head2 (<to be nulled> head of second list)
|
|
* Return: 0 if OK, 1 on error
|
|
*
|
|
* Notes:
|
|
* (1) The concatenated list is returned with head1 as the new head.
|
|
* (2) Both input ptrs must exist, though either can have the value NULL.
|
|
*/
|
|
l_int32
|
|
listJoin(DLLIST **phead1,
|
|
DLLIST **phead2)
|
|
{
|
|
void *obj;
|
|
DLLIST *head1, *head2, *tail1;
|
|
|
|
PROCNAME("listJoin");
|
|
|
|
if (!phead1)
|
|
return ERROR_INT("&head1 not defined", procName, 1);
|
|
if (!phead2)
|
|
return ERROR_INT("&head2 not defined", procName, 1);
|
|
|
|
/* If no list2, just return list1 unchanged */
|
|
if ((head2 = *phead2) == NULL)
|
|
return 0;
|
|
|
|
/* If no list1, just return list2 */
|
|
if ((head1 = *phead1) == NULL) {
|
|
*phead1 = head2;
|
|
*phead2 = NULL;
|
|
return 0;
|
|
}
|
|
|
|
/* General case for concatenation into list 1 */
|
|
tail1 = listFindTail(head1);
|
|
while (head2) {
|
|
obj = listRemoveFromHead(&head2);
|
|
listAddToTail(&head1, &tail1, obj);
|
|
}
|
|
*phead2 = NULL;
|
|
return 0;
|
|
}
|
|
|
|
|