/*
 *========================================================================
 * $Id: llists.c 82 2002-04-04 00:08:03Z rgb $
 *
 * See copyright in copyright.h and the accompanying file COPYING
 *========================================================================
 */


#include "xmlsysd.h"

/*
 * This is the working/public interface to manipulation of the
 * linked lists used in xmlsysd.  The routines are pretty simple.
 *
 * List *new_list();
 *   allocates a List struct at the list address.
 * void add_list(List *list,void *data,int size)
 *   allocates a list element (including room for the data element
 *   it must hold) and adds it to the tail of newlist. Updates list->size;
 * void rm_list(List *list,void *data,int size);
 *   searches the list for an element containing data, puts its
 *   tail address into the tail address of the preceding element,
 *   frees the data memory, then frees the element memory.
 * delete_list(List *list);
 *   walks the list from the tail to the head, removing each element
 *   it finds.  Does NOT deallocate the list -- this must be done
 *   with free if desired.
 *
 * We can easily add commands but this is probably enough four our
 * purposes.
 */

List *newlist()
{

 List *ret;

 if((verbose == D_ALL) || (verbose == D_LLISTS)){
   printf("D_LLISTS: Starting newlist().  Use -v %d to focus.\n",D_LLISTS);
 }

 ret = (List *)malloc((size_t) sizeof(List));
 if(ret == NULL){
   fprintf(stderr,"Error: Out of memory in newlist()\n");
   exit(0);
 }
 /* zero the list contents */
 ret->size = 0;
 ret->head =  NULL;
 ret->tail =  NULL;

 if((verbose == D_ALL) || (verbose == D_LLISTS)){
   printf("D_LLISTS: Made a new List at %0x\n",ret);
   printf("D_LLISTS: size = %d, head = %x, tail = %x\n",ret->size,ret->head,ret->tail);
 }

 return ret;

}

void add_list(List *list,void *data,int size)
{

 ListElement *new,*cur;

 if((verbose == D_ALL) || (verbose == D_LLISTS)){
   printf("D_LLISTS: Starting add_list().  Use -v %d to focus.\n",D_LLISTS);
 }

 /*
  * First allocate a new element.
  */
 new = (ListElement *) malloc((size_t) sizeof(ListElement));
 if(new == NULL){
   fprintf(stderr,"Error: Out of memory in add_list()\n");
   exit(0);
 }
 if((verbose == D_ALL) || (verbose == D_LLISTS)){
   printf("D_LLISTS: Made a new element at %0x\n",new);
 }

 /*
  * Allocate room for data in the new element.  Include room for
  * a terminating null (always).
  */
 new->data = (void *)malloc((size_t) size);
 if(new->data == NULL){
   fprintf(stderr,"Error: Out of memory in add_list()\n");
   exit(0);
 }
 if((verbose == D_ALL) || (verbose == D_LLISTS)){
   printf("D_LLISTS: Made a new->data element of length %d at %0x\n",size,new->data);
 }
 memcpy(new->data,data,size);

 if((verbose == D_ALL) || (verbose == D_LLISTS)){
   printf("D_LLISTS: put data = %s in element\n",(char *)new->data);
 }
 /*
  * new now is an element containing the data.  Time to link it into
  * the list.
  */
 if((verbose == D_ALL) || (verbose == D_LLISTS)){
   printf("D_LLISTS: About to put it in list at %x\n",list);
 }

 /*
  * Is the list empty?  If so we put it at the head (and tail)
  * and cause the element's prev and next to point to NULL.
  */
 if(list->size == 0){
  list->head = new;
  list->tail = new;
  list->size = 1;
  new->prev = NULL;
  new->next = NULL;
 } else {
   /*
    * Otherwise we simply add the new element at the end.
    */
   list->tail->next = new;	/* next of prev element */
   new->prev = list->tail;	/* prev of new element */
   list->tail = new;		/* tail of list */
   new->next = NULL;		/* next of new element */
   list->size++;			/* one more element all done. */
 }

 if((verbose == D_ALL) || (verbose == D_LLISTS)){
     printf("D_LLISTS: Data is safe and accessible in list->tail->data = %s\n",(char *)list->tail->data);
 }

}


/*
 * We want to remove only ONE instance of any given entry from the
 * list, I think -- if we can add more than one in the first place
 * (one at a time) we should have to remove them one at a time as well,
 * or we should uniqueify the additions.  We'll therefore break out
 * of the remove process instead of completing the loop.
 */
void rm_list(List *list,void *data,int size)
{

 int i;
 ListElement *element;

 if((verbose == D_ALL) || (verbose == D_LLISTS)){
   printf("D_LLISTS: Starting rm_list().  Use -v %d to focus.\n",D_LLISTS);
   printf("D_LLISTS: Should be removing %s\n",(char *)data);
 }

 i = 0;
 element = list->head;
 while (element != NULL) {
   if((verbose == D_ALL) || (verbose == D_LLISTS)){
     printf("D_LLISTS: checking list[%d]=%s\n", i, element->data);
   }
   /* 
    * Have to figure out where we are going before dumping element.
    * We do this here as element might not exist at the bottom.
    */
   if(strncmp((char *)element->data,(char *)data,size) == 0){
     /* 
      * They match! Remove this element from the list.  First free
      * the data.
      */
     if((verbose == D_ALL) || (verbose == D_LLISTS)){
       printf("D_LLISTS: list[%d]=%s matched %s, removing.\n", i, (char *)element->data, (char *) data);
     }
     free(element->data);
     /*
      * We now have a nice little logic problem.  The element we're
      * freeing could be the only element in the list (so its next and
      * prev are both null and it is both head and tail).  It could
      * be head (prev is null).  It could be tail (next is null).
      * OR it could be in the middle: neither head nor tail, with
      * a non-null prev and next.
      *
      * Each of these has its own peculiar set of actions we have to
      * take to not break the linkage on removal.
      */

     /* only element left -- NULL list head and tail */
     if(element == list->head && element == list->tail) {
       list->head = NULL;
       list->tail = NULL;
     } else
     /* head of non-empty list -- set head to next, set prev of next to null */
     if(element == list->head){
       list->head = element->next;
       element->next->prev = NULL;
     } else
     /* tail of non-empty list -- set tail to prev, set next of prev to null */
     if(element == list->tail){
       list->tail = element->prev;
       element->prev->next = NULL;
     } else {
       /* 
        * If we get here, we're not the head OR the tail OR the
        * head AND tail, so we must be a mid-element of non-empty 
        * list -- we therefore join prev->next and next->prev 
        */
       element->prev->next = element->next;
       element->next->prev = element->prev;
     }
     /* Decrement the count of list */
     list->size--;
     /* FINALLY, free the element */
     free(element);
     if((verbose == D_ALL) || (verbose == D_LLISTS)){
       printf("D_LLISTS: element removed, we hope.\n");
     }
     break;
   }
   /* Go on to the next element */
   element = element->next;
   i++;
 }

}

/*
 * This doesn't de-initialize the list, only clear all its elements
 */
void delete_list(List *list)
{

 int i;
 ListElement *element,*previous_element;

 if((verbose == D_ALL) || (verbose == D_LLISTS)){
   printf("D_LLISTS: Starting delete_list().  Use -v %d to focus.\n",D_LLISTS);
 }

 /*
  * We'll start at the end and work backwards -- why not?
  */
 i = list->size;
 element = list->tail;
 while (element != NULL) {
   if((verbose == D_ALL) || (verbose == D_LLISTS)){
     printf("D_LLISTS: deleting list[%d]=%s\n", i, element->data);
   }
   free(element->data);
   previous_element = element->prev;
   free(element);
   if((verbose == D_ALL) || (verbose == D_LLISTS)){
     printf("D_LLISTS: element removed, we hope.\n");
   }
   /* Go on to the next element */
   element = previous_element;
   i--;
 }
 /* Now reset the list itself to be properly empty */
 list->size = 0;
 list->head = NULL;
 list->tail = NULL;

}

