/***************************************************************************
** dlist.c:  contains routines for operating on dlist (doubly-linked list)
**           structures.
**
**      Unpublished Confidential Information of Hewlett-Packard
**      Company. Do not disclose.  Copyright (c) Hewlett-Packard
**      Company 1995. All rights reserved
** 
** The contents of this software are proprietary and confidential to
** the Hewlett- Packard Company, and are limited in distribution to
** those with a direct need to know.  Individuals having access to
** this software are responsible for maintaining the confidentiality
** of the content and for keeping the software secure when not in use.
** Transfer to any party is strictly forbidden other than as expressly
** permitted in writing by Hewlett-Packard Company.  Unauthorized
** transfer to or possession by any unauthorized party may be a
** criminal offense.
**
**                  RESTRICTED RIGHTS LEGEND
**
**      Use,  duplication,  or disclosure by the U. S. Government is
**      subject to restrictions as set forth in subparagraph (c) (1)
**      (ii) in the Rights in Technical Data and Computer Software
**      clause in DFARS 252.227-7013 or any other successor clause.
**
**                      HEWLETT-PACKARD COMPANY
**                      3000 Hanover St.
**                      Palo Alto, CA  94304 U.S.A.
**
**      Rights for non-DOD U.S. Government Departments and Agencies
**      are as set forth in FAR 52.227-19 (c) (1,2) or any other
**      successor clause.
**
** Programmer:  Jon Hirota
** Language:    ANSI C but can be compiled under Kerningham & Ritchie C.
** Compiler:    initially compiled under Borland C++ version 2.00.
**              compiled under Borland C++ version 3.1     11/93
**              complied under HP-UX 9.0x cc compiler version 09.33  10/94
**
***************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include "dlist.h"

#ifndef lint
static char rcsid[] = "$Header: dlist.c,v 1.4 95/10/25 17:26:54 hmgr Exp $";
#endif

/*
** Static functions:  private to this file
*/

#ifdef __STDC__                 /* ANSI C */

static dlist_node *dlistNewnode(void *);  /* Allocates memory for new node */

#else                           /* Kerningham and Ritchie C */

static dlist_node *dlistNewnode();        /* Allocates memory for new node */

#endif                          /* __STDC__ */

/*
** These macros are for making it easier to port between DOS and Unix (to
** support DOS memory models).
*/

#if defined __BORLANDC__ && __LARGE__

#define DLMALLOC(size)          farmalloc(size)
#define DLFREE(ptr)             farfree(ptr)

#else

#define DLMALLOC(size)          malloc(size)
#define DLFREE(ptr)             free(ptr)

#endif

/***************************************************************************
** dlistCreate():  creates a dlist structure and initializes header values.
**
** Preconditions:   argument comparison function is non-NULL.
** Postconditions:  memory will be allocated for a dlist structure and its
**                  fields will be initialized appropriately.
** Return values:   returns a pointer to the new list upon success, NULL
**                  on failure.
**
***************************************************************************/

#ifdef __STDC__
dlist *
dlistCreate
(
    int (*cmp_func)(void *,void *)      /* Pointer to comparison function */
)
#else
dlist *
dlistCreate(cmp_func)
int     (*cmp_func)();
#endif          /* __STDC */
{
    dlist       *newlist;

    /*
    ** Check argument for invalid values
    */

#ifdef __STDC__
    if(cmp_func == (int (*)(void *,void *)) NULL)
#else
    if(cmp_func == (int (*)()) NULL)
#endif          /* __STDC__ */
    {
        return (dlist *) NULL;
    }

    newlist = (dlist *) DLMALLOC(sizeof(*newlist));
    if(newlist == (dlist *) NULL)               /* Malloc failed? */
    {
        return (dlist *) NULL;
    }

    /*
    ** Fill in header fields
    */

    newlist->head = (dlist_node *) NULL;
    newlist->tail = (dlist_node *) NULL;
    newlist->cmp_func = cmp_func;
    newlist->count = 0;
    newlist->dlist_err = DLERR_NOERR;

    return newlist;
}

/***************************************************************************
** dlistDestroy():  destroys a dlist and frees all memory associated with
**                  the dlist header and nodes.  NOTE:  this function does
**                  NOT free memory occupied by user data.  It is the user's
**                  responsibility to free data contained in the dlist.
**
** Preconditions:   the argument dlist has been initialized by a previous
**                  call to dlistCreate().
** Postconditions:  see above.
** Return values:   returns one of the following values:
**
** DLERR_NOERR          The function completed successfully.
** DLERR_NULL           A NULL list was passed to the function.
***************************************************************************/

#ifdef __STDC__
int
dlistDestroy
(
    dlist       *list           /* The list to be destroyed */
)
#else
int
dlistDestroy(list)
dlist           *list;
#endif          /* __STDC__ */
{
    dlist_node  *cur,*next;             /* For traversing the list */

    /*
    ** Check for NULL argument
    */

    if(list == (dlist *) NULL)
    {
        return DLERR_NULL;
    }

    /*
    ** Traverse list and free each node's memory
    */

    cur = list->head;

    while(cur != (dlist_node *) NULL)
    {
        next = cur->next;
        DLFREE((void *) cur);
        cur = next;
    }

    /*
    ** Initialize (zero out) header fields, then free header
    */

    list->head = (dlist_node *) NULL;
    list->tail = (dlist_node *) NULL;
    list->count = 0;
#ifdef __STDC__
    list->cmp_func = (int (*)(void *,void *)) NULL;
#else
    list->cmp_func = (int (*)()) NULL;
#endif          /* __STDC__ */
    DLFREE((void *) list);

    return DLERR_NOERR;
}

/***************************************************************************
** dlistPrepend():  Adds a data item to the beginning of a dlist.
**
** Preconditions:   argument list has been initialized by a previous call to
**                  dlistCreate(); data pointer is non-NULL.
** Postconditions:  a new node will be allocated and added to the dlist.
** Return values:   returns one of the following values:
**
** DLERR_NOERR          The function completed successfully.
** DLERR_NULL           Argument dlist or data pointer is NULL.
** DLERR_NOMEM          A DLMALLOC() failure occurred; insufficient memory to
**                      create the new node.
***************************************************************************/

#ifdef __STDC__
int
dlistPrepend
(
    dlist       *list,          /* List to which to add data item */
    void        *datap          /* Data item to add */
)
#else
int
dlistPrepend(list,datap)
dlist           *list;
void            *datap;
#endif          /* __STDC__ */
{
    dlist_node  *newnode;

    /*
    ** Check for invalid arguments
    */

    if((list == (dlist *) NULL))
    {
        return DLERR_NULL;
    }
    if(datap == (void *) NULL)
    {
        list->dlist_err = DLERR_NULL;
        return DLERR_NULL;
    }

    /*
    ** Allocate memory for the new node
    */

    newnode = dlistNewnode(datap);
    if(newnode == (dlist_node *) NULL)
    {
        list->dlist_err = DLERR_NOMEM;
        return DLERR_NOMEM;
    }

    /*
    ** Fill in the new node's fields and attach to the dlist
    */

    newnode->prev = (dlist_node *) NULL;      /* newnode is now dlist head */
    newnode->next = list->head;

    if(list->head == (dlist_node *) NULL)     /* First node in list! */
    {
        list->head = newnode;
        list->tail = newnode;
    }
    else
    {
        list->head->prev = newnode;
        list->head = newnode;
    }
    (list->count)++;

    list->dlist_err = DLERR_NOERR;
    return DLERR_NOERR;
}

/***************************************************************************
** dlistInsert():  Adds a data item to a list in sorted order according to
**                 the dlist's comparison function.
**
** Preconditions:   The argument list has been initialized by a previous call
**                  to dlistCreate(); the argument data pointer is non-NULL.
** Postconditions:  memory will be allocated for the new node, and the node
**                  will be added to the dlist in sorted order.
** Return values:   returns one of the following values:
**
** DLERR_NOERR          The function completed successfully.
** DLERR_NULL           Argument dlist or data pointer is NULL.
** DLERR_NOMEM          A DLMALLOC() failure occurred; insufficient memory to
**                      create the new node.
**
** Notes:           The compare function passed as a parameter is defined
**                  as follows:  int cmp_func(void *d1, void *d2).  The
**                  cmp_func must return the following values:
**                  *d1 < *d2           |            < 0
**                  *d1 = *d2           |              0
**                  *d1 > *d2           |            > 0
***************************************************************************/

#ifdef __STDC__
int
dlistInsert
(
    dlist       *list,          /* list to which to add new item */
    void        *datap          /* data item to add */
)
#else
int
dlistInsert(list,datap)
dlist           *list;
void            *datap;
#endif          /* __STDC__ */
{
    dlist_node  *newnode;
    dlist_node  *cur;           /* used for traversing the list */

    /*
    ** Check for invalid arguments
    */

    if(list == (dlist *) NULL)
    {
        return DLERR_NULL;
    }
    if(datap == (void *) NULL)
    {
        list->dlist_err = DLERR_NULL;
        return DLERR_NULL;
    }

    /*
    ** Allocate memory for the node and fill in the data
    */

    newnode = dlistNewnode(datap);
    if(newnode == (dlist_node *) NULL)
    {
        list->dlist_err = DLERR_NOMEM;
        return DLERR_NOMEM;
    }

    /*
    ** Find the place to put the new node!  After the while loop, cur points
    ** to the node AFTER which to add newnode.
    */

    if(list->head == (dlist_node *) NULL)       /* First node in list! */
    {
        newnode->prev = (dlist_node *) NULL;
        newnode->next = (dlist_node *) NULL;
        list->head = newnode;
        list->tail = newnode;
    }
    else
    {
        cur = list->head;
        while(cur != (dlist_node *) NULL)
        {
            if(list->cmp_func(datap,cur->data) <= 0)    /* Found the place */
            {
                cur = cur->prev;
                break;
            }
            if(cur->next == (dlist_node *) NULL)
            {
                break;
            }
            else
            {
                cur = cur-> next;
            }
        }

        /*
        ** If cur is NULL, then newnode becomes the new head of the list.
        */

        if(cur == (dlist_node *) NULL)
        {
            newnode->prev = (dlist_node *) NULL;
            newnode->next = list->head;
            list->head->prev = newnode;
            list->head = newnode;
        }
        else
        {
            newnode->prev = cur;
            newnode->next = cur->next;
            if(cur->next != (dlist_node *) NULL)
            {
               (cur->next)->prev = newnode;
            }
            cur->next = newnode;

            /*
            ** Check to see if cur is list->tail!
            */

            if(cur == list->tail)
            {
                list->tail = newnode;
            }
        }
    }
    (list->count)++;

    list->dlist_err = DLERR_NOERR;
    return DLERR_NOERR;
}

/***************************************************************************
** dlistAppend():   adds a data item to the end of a dlist.
**
** Preconditions:   The argument list has been initialized by a previous
**                  call to dlistCreate().
** Postconditions:  A new node will be created and added to the list.
** Return values:   returns one of the following values:
**
** DLERR_NOERR          The function completed successfully.
** DLERR_NULL           Argument dlist or data pointer is NULL.
** DLERR_NOMEM          A DLMALLOC() failure occurred; insufficient memory to
**                      create the new node.
***************************************************************************/

#ifdef __STDC__
int
dlistAppend
(
    dlist *list,                        /* List to which to add data item */
    void  *datap                        /* Data item to be added */
)
#else
int
dlistAppend(list,datap)
dlist           *list;
void            *datap;
#endif          /* __STDC__ */
{
    dlist_node  *newnode;

    /*
    ** Check for invalid arguments
    */
     
    if(datap == (void *) NULL)
    {
        list->dlist_err = DLERR_NULL;
        return DLERR_NULL;
    }

    /*
    ** Allocate memory for the node and fill in the data
    */

    newnode = dlistNewnode(datap);
    if(newnode == (dlist_node *) NULL)
    {
        list->dlist_err = DLERR_NOMEM;
        return DLERR_NOMEM;
    }

    if(list->tail == (dlist_node *) NULL)       /* First node in list! */
    {
        newnode->prev = (dlist_node *) NULL;
        newnode->next = (dlist_node *) NULL;
        list->head = newnode;
        list->tail = newnode;
    }
    else
    {
        newnode->prev = list->tail;
        list->tail->next = newnode;
        newnode->next = (dlist_node *) NULL;
        list->tail = newnode;
    }
    (list->count)++;

    list->dlist_err = DLERR_NOERR;
    return DLERR_NOERR;
}

/***************************************************************************
** dlistFind():     Attempts to find a particular data item in a list.
**
** Preconditions:   All arguments are non-NULL
** Postconditions:  see return values
** Return values:   Returns a pointer to the node containing the desired
**                  data item if found, returns NULL otherwise.
***************************************************************************/

#ifdef __STDC__
dlist_node *
dlistFind
(
    dlist *list,              /* List to be searched */
    void  *datap              /* Data item to be found */
)
#else
dlist_node *
dlistFind(list,datap)
dlist           *list;
void            *datap;
#endif          /* __STDC__ */
{
    dlist_node   *cur;

    /*
    ** Check for invalid arguments
    */

    if(list == (dlist *) NULL) 
    {
        return (dlist_node *) NULL;
    }
    if(datap == (void *) NULL)
    {
        list->dlist_err = DLERR_NULL;
        return (dlist_node *) NULL;
    }
 
    /*
    ** Traverse the list and search for the data item
    */

    cur = list->head;
    while(cur != (dlist_node *) NULL)
    {
        if(list->cmp_func(datap,cur->data) == 0)
        {
            list->dlist_err = DLERR_NOERR;
            return cur;
        }
        cur = cur->next;
    }

    /*
    ** If we got here, we didn't find it
    */

    list->dlist_err = DLERR_NOTFND;
    return (dlist_node *) NULL;
}

/***************************************************************************
** dlistDelete():   Deletes the specified node.
**
** Preconditions:   The arguments must be non-NULL; additionally, a check
**                  is made to ensure that the node to be deleted it
**                  actually in the list passed as argument.
** Postconditions:  The node will be removed and its memory freed; the
**                  list will be reattached as necessary.
** Return values:   Returns a pointer to the deleted node's data if
**                  successful, returns NULL otherwise.
** Notes:           The node-is-in-the-list check forces this function to
**                  exhibit O(N) performance rather than O(1), but allows
**                  for greater safety from the application programmer's
**                  standpoint.  User data is NOT freed by this function.
***************************************************************************/

#ifdef __STDC__
void *
dlistDelete
(
    dlist       *list,                  /* The list from which to delete */
    dlist_node  *dnode                  /* Node to be deleted */
)
#else
void *
dlistDelete(list,dnode)
dlist           *list;
dlist_node      *dnode;
#endif          /* __STDC__ */
{
    dlist_node  *cur;
    void        *datap;
 
    /*
    ** Check for invalid arguments
    */

    if(list == (dlist *) NULL)
    {
        return (void *) NULL;
    }
    if(dnode == (dlist_node *) NULL)
    {
        list->dlist_err = DLERR_NULL;
        return (void *) NULL;
    }

    /*
    ** Empty list case
    */

    if(list->count == 0)
    {
        list->dlist_err = DLERR_NOTFND;
        return (void *) NULL;
    }

    /*
    ** Only 1 item in the list
    */

    if(list->count == 1)
    {
        if(list->head == dnode)         /* It's there */
        {
            datap = dnode->data;                /* Save data to return */
            list->head = (dlist_node *) NULL;
            list->tail = (dlist_node *) NULL;
            DLFREE((void *) dnode);
            list->dlist_err = DLERR_NOERR;
            list->count = 0;
            return datap;
        }
        else                            /* It's not there */
        {
            list->dlist_err = DLERR_NOTFND;
            return (void *) NULL;
        }
    }

    /*
    ** The list is traversed to ensure that the node the user wants to
    ** delete is actually in the list.  If it's not, an error is returned
    ** and the delete aborted.
    */
 
    cur = list->head;
    while(cur != (dlist_node *) NULL)
    {
        if(cur == dnode)                        /* Found it! */
        {
            if(cur == list->head)
            {
                list->head = cur->next;
                list->head->prev = (dlist_node *) NULL;
                datap = cur->data;
                DLFREE((void *) cur);
                (list->count)--;
                list->dlist_err = DLERR_NOERR;
                return datap;
            }
            else
            {
                if(cur == list->tail)
                {
                    list->tail = cur->prev;
                    list->tail->next = (dlist_node *) NULL;
                    datap = cur->data;
                    DLFREE((void *) cur);
                    (list->count)--;
                    list->dlist_err = DLERR_NOERR;
                    return datap;
                }
                else            /* Generic case */
                {
                    cur->prev->next = cur->next;
                    cur->next->prev = cur->prev;
                    datap = cur->data;
                    DLFREE((void *) cur);
                    (list->count)--;
                    list->dlist_err = DLERR_NOERR;
                    return datap;
                }
            }
        }
        else
        {
            cur = cur->next;
        }
    }                   /* End of while loop */

    /*
    ** If we got out of the while loop, we didn't find it
    */

    list->dlist_err = DLERR_NOTFND;
    return (void *) NULL;
}

/***************************************************************************
** dlistDatadelete():  finds a data item and deletes its node from the list.
**
** Preconditions:      The arguments are non-NULL.
** Postconditions:     The node containing the data, if it is found, will
**                     be deleted and its memory freed.
** Return values:      Returns the data pointer of the deleted node on
**                     success, NULL on failure.
***************************************************************************/

#ifdef __STDC__
void *
dlistDatadelete
(
    dlist *list,                        /* List from which to delete */
    void  *datap                        /* Data item to delete */
)
#else
void *
dlistDatadelete(list,datap)
dlist           *list;
void            *datap;
#endif          /* __STDC__ */
{
    dlist_node  *node_ptr;
    void                *old_data;

    /*
    ** Check for invalid arguments
    */

    if(list == (dlist *) NULL)
    {
        return (void *) NULL;
    }
    if(datap == (void *) NULL)
    {
        list->dlist_err = DLERR_NULL;
        return (void *) NULL;
    }
 
    /*
    ** Try to find the data item
    */

    node_ptr = dlistFind(list,datap);
    if(node_ptr == (dlist_node *) NULL) /* Didn't find it! */
    {
        list->dlist_err = DLERR_NOTFND;
        return (void *) NULL;
    }

    old_data = dlistDelete(list,node_ptr);
    if(old_data == (void *) NULL)       /* This should NEVER happen! */
    {
        list->dlist_err = DLERR_NOTFND;
        return (void *) NULL;
    }

    list->dlist_err = DLERR_NOERR;
    return old_data;
}

/***************************************************************************
** dlistNewnode():  allocates a new dlist_node and initializes its field.
**                  This function is private to this module.
**
** Preconditions:   None.
** Postconditions:  See above.
** Return values:   A pointer to the new node on success, NULL on failure.
** Notes:           This function does NOT set dlist_err!
***************************************************************************/

#ifdef __STDC__
static dlist_node *
dlistNewnode
(
    void *datap
)
#else
static dlist_node *
dlistNewnode(datap)
void            *datap;
#endif          /* __STDC__ */
{
    dlist_node  *newnode;

    newnode = (dlist_node *) DLMALLOC(sizeof(*newnode));
    if(newnode == (dlist_node *) NULL)
    {
        return (dlist_node *) NULL;     /* Malloc failed */
    }

    newnode->next = (dlist_node *) NULL;
    newnode->prev = (dlist_node *) NULL;
    newnode->data = datap;
 
    return newnode;
}

/***************************************************************************
** dlistApply():     Applies a function to all data items in a list.
**
** Preconditions:    all arguments are non-NULL.
** Postconditions:   The function will be applied to all data items in the
**                   list.  The apply function must return an int, in which
**                   a non-zero value indicates failure.  If failsafe = 1,
**                   then application of the function halts after the first
**                   failure.  
** Return values:    The final return value of the apply function is returned.
***************************************************************************/

#ifdef __STDC__
int
dlistApply
(
    dlist *list,
    int   (*apply_func)(void *),
    int  failsafe
)
#else
int
dlistApply(list,apply_func,failsafe)
dlist           *list;
int             (*apply_func)();
int             failsafe;
#endif          /* __STDC__ */
{
    dlist_node *cur;
    int        rc;

    /*
    ** Check for invalid arguments
    */

    if(list == (dlist *) NULL)
    {
        return -1;
    }

#ifdef __STDC
    if(apply_func == (int (*)(void *)) NULL)
#else
    if(apply_func == (int (*)()) NULL)
#endif          /* __STDC__ */
    {
        list->dlist_err = DLERR_NULL;
        return -1;
    }

    /*
    ** Perform the function application
    */

    cur = list->head;
    while(cur != (dlist_node *) NULL)
    {
        rc = apply_func(cur->data);
        if(failsafe && rc)
        {
            list->dlist_err = DLERR_APPLF;
            return rc;
        }
        cur = cur->next;
    }
    list->dlist_err = DLERR_NOERR;
    return 0;
}
