/************************************************************************/
/* SISCO SOFTWARE MODULE HEADER *****************************************/
/************************************************************************/
/*   (c) Copyright Systems Integration Specialists Company, Inc.,	*/
/*              1986-2005  All Rights Reserved                   	*/
/*									*/
/* MODULE NAME : gen_list.c						*/
/* PRODUCT(S)  : general list handling functions			*/
/*									*/
/* MODULE DESCRIPTION : 						*/
/*	This module contains generic que manipulation routines		*/
/*									*/
/* GLOBAL FUNCTIONS DEFINED IN THIS MODULE :				*/
/*									*/
/* MODIFICATION LOG :							*/
/*  Date     Who   Rev			Comments			*/
/* --------  ---  ------   -------------------------------------------	*/
/* 10/04/07  MDE     07    Tweaked LOGCFG_VALUE_GROUP/LOGCFGX_VALUE_MAP	*/
/* 06/30/05  EJV     06    Add genlistDebugMaps, genlistDebugMapCtrl	*/
/*			    for parsing logcfg.xml			*/
/* 04/08/04  JRB     05    list_find_last: if head=NULL, don't log.	*/
/* 12/12/02  JRB     04    list_get_first/last: if *head==NULL, return	*/
/*			   NULL & do NOT log a message.			*/
/* 11/21/01  MDE     03    Changed logging to use SLOGALWAYS 		*/
/* 10/09/98  JRB     02    Add list_find_prev, list_find_last.		*/
/* 06/15/98  MDE     01    Changes to allow compile under C++		*/
/* 04/02/97  DTL   7.00    MMSEASE 7.0 release. See MODL70.DOC for	*/
/*			   history.					*/
/************************************************************************/

#include "glbtypes.h"
#include "sysincs.h"
#define  I_AM_THE_TRUE_GEN_LIST 
#include "gen_list.h"
#include "slog.h"

/************************************************************************/
/************************************************************************/
/* For debug version, use a static pointer to avoid duplication of 	*/
/* __FILE__ strings.							*/
/************************************************************************/

#ifdef DEBUG_SISCO
static ST_CHAR *thisFileName = __FILE__;
#endif

/************************************************************************/
/* The variable below is used in selecting generic queuing debug 	*/
/* checking.  When this variable is set to SD_TRUE all data strucutres 	*/
/* using the list manipulation primitives will under go this checking	*/

ST_BOOLEAN list_debug_sel;

#ifdef DEBUG_SISCO
LOGCFGX_VALUE_MAP genlistDebugMaps[] =
  {
/* Memory debugging */
    {"ListDebugSel",    	0, 		&list_debug_sel,       	_LOGCFG_DATATYPE_BOOLEAN, NULL, NULL}
  };

LOGCFG_VALUE_GROUP genlistDebugMapCtrl =
  {
  {NULL,NULL},
  "GenListLogMasks",	  /* Parent Tag */
  sizeof(genlistDebugMaps)/sizeof(LOGCFGX_VALUE_MAP),
  genlistDebugMaps
  };
#endif  /* DEBUG_SISCO */

/************************************************************************/
/*				list_unlink				*/
/* Primitive to unlink a node out of a circular double linked list	*/
/************************************************************************/

ST_RET list_unlink (DBL_LNK **list_head_ptr, DBL_LNK *node_ptr)
  {
#ifdef DEBUG_SISCO
  if (*list_head_ptr == NULL) 
    {
    SLOGALWAYS0 ("GENLIST: ATTEMPT TO UNLINK A NODE FROM A NULL LIST");
    return (SD_FAILURE);
    }

  if (node_ptr == NULL)
    {
    SLOGALWAYS0 ("GENLIST: ATTEMPT TO UNLINK A NULL ADDRESS FROM A LIST");
    return (SD_FAILURE);
    }

  if (list_debug_sel == SD_TRUE)
    {
    if (list_find_node (*list_head_ptr, node_ptr) == SD_FAILURE)
      {
      SLOGALWAYS0 ("GENLIST: NODE NOT FOUND IN LIST");
      return (SD_FAILURE);
      }
    }
#endif

/* If list checking is enabled and we get to here we have valid 	*/
/* arguments and a valid list						*/

  if ((node_ptr -> next == *list_head_ptr) && 
       (node_ptr == *list_head_ptr))		/* only node in list?	*/
    {
    *list_head_ptr = NULL;
    }
  else 
    {					       
    if (node_ptr == *list_head_ptr)    	/* first node in a multi*/
      *list_head_ptr = node_ptr->next;		/* node list		*/
    (node_ptr->next)->prev = node_ptr->prev;	/* link cur next to prev*/
    (node_ptr->prev)->next = node_ptr->next;	/* link cur prev to next*/
    }
  return (SD_SUCCESS);
  }
   

/************************************************************************/
/*				list_add_first				*/
/* Primitive to add a node as the first node of a circular double 	*/
/* linked list								*/
/************************************************************************/

ST_RET list_add_first (DBL_LNK **list_head_ptr, DBL_LNK *node_ptr)
  {
DBL_LNK *list_tail_ptr;

#ifdef DEBUG_SISCO
  if (node_ptr == NULL)
    {
    SLOGALWAYS0 ("GENLIST: ATTEMPT TO ADD A NULL ADDRESS TO A LIST");
    return (SD_FAILURE);
    }
#endif

/* If list checking is enabled and we get to here we have valid 	*/
/* arguments 								*/

  if (*list_head_ptr == NULL) 	/* will this be the only node?	*/
    {
    node_ptr->next = node_ptr;
    node_ptr->prev = node_ptr;
    }
  else
    {
    list_tail_ptr = (*list_head_ptr)->prev;
    node_ptr->next = *list_head_ptr;
    node_ptr->prev = list_tail_ptr;
    list_tail_ptr->next = node_ptr;
    (*list_head_ptr)->prev = node_ptr;
    }
  *list_head_ptr = node_ptr;		/* assign the new head of list	*/
  return (SD_SUCCESS);
     
  }


/************************************************************************/
/*				list_add_last				*/
/* Primitive to add a node as the last node of a circular double 	*/
/* linked list								*/
/************************************************************************/

ST_RET list_add_last (DBL_LNK **list_head_ptr, DBL_LNK *node_ptr)
  {
DBL_LNK *list_tail_ptr;

#ifdef DEBUG_SISCO
  if (node_ptr == NULL)
    {
    SLOGALWAYS0 ("GENLIST: ATTEMPT TO ADD A NULL ADDRESS TO A LIST");
    return (SD_FAILURE);
    }
#endif

/* If list checking is enabled and we get to here we have valid 	*/
/* arguments 								*/

  if (*list_head_ptr == NULL) 	/* will this be the only node?	*/
    {
    node_ptr->next = node_ptr;
    node_ptr->prev = node_ptr;
    *list_head_ptr = node_ptr;
    }
  else
    {
    list_tail_ptr = (*list_head_ptr)->prev;
    list_tail_ptr->next = node_ptr;
    node_ptr->prev = list_tail_ptr;
    node_ptr->next = *list_head_ptr;
    (*list_head_ptr)->prev = node_ptr;
    }
  return (SD_SUCCESS);
  }

/************************************************************************/
/*				list_get_first				*/
/* Primitive to unlink the first node out of the list and return it's	*/
/* address								*/
/************************************************************************/

#ifndef FASTLIST

ST_VOID *list_get_first (DBL_LNK **list_head_ptr)
  {
DBL_LNK *node_ptr;

  if (*list_head_ptr == NULL)
    return (NULL);

  node_ptr = *list_head_ptr;
  list_unlink (list_head_ptr, node_ptr);
  return (node_ptr);
     
  }
#endif

/************************************************************************/
/*				list_get_last				*/
/* Primitive to unlink the last node out of the list and return it's	*/
/* address								*/
/************************************************************************/


ST_VOID *list_get_last (DBL_LNK **list_head_ptr)
  {
DBL_LNK *node_ptr;

  if (*list_head_ptr == NULL)
    return (NULL);

  node_ptr = *list_head_ptr;
  node_ptr = node_ptr->prev;
  list_unlink (list_head_ptr, node_ptr);
  return (node_ptr);
  }


/************************************************************************/
/*		           list_move_to_first				*/
/* Primitive to unlink the first node out of the list and return it's	*/
/* address								*/
/************************************************************************/

ST_RET list_move_to_first (DBL_LNK **src_list_head_ptr, 
                          DBL_LNK **dest_list_head_ptr, 
			  DBL_LNK *node_ptr)
  {
ST_RET ret_code;
     
  if (!(ret_code = list_unlink (src_list_head_ptr, node_ptr)))
    {
    list_add_first (dest_list_head_ptr, node_ptr);
    }
  return (ret_code);
  }

/************************************************************************/
/*		           list_find_node				*/
/* Primitive to verify that a node is in a list, returns SD_SUCCESS if it	*/
/* is; SD_FAILURE otherwise.						*/
/************************************************************************/

ST_RET list_find_node (DBL_LNK *list_head_ptr, DBL_LNK *node_ptr)
  {
DBL_LNK	  *temp_ptr;
DBL_LNK	  *list_tail_ptr;
ST_RET	ret_code;
     
  if ((list_head_ptr == NULL) || (node_ptr == NULL))
    ret_code = SD_FAILURE;
  else
    {
    temp_ptr = list_head_ptr;
    list_tail_ptr = list_head_ptr->prev;

/* search forward from the begining to the end of the list for our node	   */

    while ((temp_ptr != list_tail_ptr) && (temp_ptr != node_ptr))
      {
      temp_ptr = temp_ptr->next;
      }
    if (temp_ptr == node_ptr)
      ret_code = SD_SUCCESS;
    else
      ret_code = SD_FAILURE;
    }
  return (ret_code);
  }


/************************************************************************/
/*		           list_print_links				*/
/************************************************************************/

#if 0

ST_VOID list_print_links (DBL_LNK *list_head_ptr)
  {
DBL_LNK	*temp_ptr;
DBL_LNK	*list_tail_ptr;
ST_INT i;

  i = 1;
  if (list_head_ptr == NULL)
    {
    printf ("\nThe list is empty");
    }
  else
    {
    temp_ptr = list_head_ptr;
    list_tail_ptr = list_head_ptr->prev;
    printf ("\npointer to the head of the list is: %lx",list_head_ptr);
    printf ("\n");
    printf ("\npointer to node number %d is: %lx",i, temp_ptr);
    printf ("\nnode number:  next ptr is: %lx",temp_ptr->next);
    printf ("\n              prev ptr is: %lx",temp_ptr->prev);
    while ((temp_ptr != list_tail_ptr))
      {
      temp_ptr = temp_ptr->next;
      i++;
      printf ("\npointer to node number %d is: %lx",i, temp_ptr);
      printf ("\nnode number:  next ptr is: %lx",temp_ptr->next);
      printf ("\n              prev ptr is: %lx",temp_ptr->prev);
      }
    }
  }
#endif

/************************************************************************/
/*		           list_add_node_after(list insert node prim)	*/
/* Primitive to add a node to a list after the current node.  This 	*/
/* function assumes that we are working with a non NIL list.  SD_SUCCESS 	*/
/* is returned if the node gets added to the list; SD_FAILURE otherwise.	*/
/************************************************************************/

ST_RET list_add_node_after (DBL_LNK *cur_node, DBL_LNK *new_node)
  {
DBL_LNK	*next_node;
ST_RET	ret_val;

  ret_val = SD_FAILURE;
  if (new_node == NULL)
    {
    SLOGALWAYS0 ("GENLIST: ATTEMPT TO ADD A NULL ADDRESS TO A LIST");
    }
  else if (cur_node == NULL)
    {
    SLOGALWAYS0 ("GENLIST: ATTEMPT TO ADD A NODE TO A NULL LIST");
    }
  else			/* this is a good node and good predecessor	*/
    {
    next_node = cur_node -> next;
    new_node -> next = next_node;
    new_node -> prev = cur_node;
    next_node -> prev = new_node;
    cur_node -> next = new_node;
    ret_val = SD_SUCCESS;
    }
  return (ret_val);
  }    

/************************************************************************/
/*		           list_get_next				*/
/* Primitive to get the node pointed to by the next component of the	*/
/* current node.  This function returns NULL if the list_head_ptr is	*/
/* NULL, the address of the current node is NULL or the next node in the*/
/* list is the list_head_ptr(the list has wrapped around).  This 	*/
/* function is useful for traversing a doubly linked circular list	*/
/* from begining to the end as if the list were NULL terminated.	*/
/************************************************************************/

#ifndef FASTLIST

ST_VOID *list_get_next (DBL_LNK *list_head_ptr, DBL_LNK *cur_node)
  {
DBL_LNK *next_node = NULL;

  if (list_head_ptr == NULL)
    {
    SLOGALWAYS0 ("GENLIST: ATTEMPT TO REFERENCE A NULL LIST");
    }
  else if (cur_node == NULL)
    {
    SLOGALWAYS0 ("GENLIST: ATTEMPT TO REFERENCE THROUGH A NULL PTR");
    }
  else if (cur_node->next != list_head_ptr)
    {
    next_node = cur_node->next;
    }
  return (next_node);
  }
#endif

/************************************************************************/
/*		           list_get_sizeof				*/
/* Primitive to return the number of nodes in this generic list.	*/
/************************************************************************/

ST_INT list_get_sizeof (DBL_LNK *list_head_ptr)
  {
ST_INT count;
DBL_LNK *cur_node;

  count = 0;
  cur_node = list_head_ptr;

  while (cur_node != NULL) 
    {
    count ++;
    cur_node = (DBL_LNK *) list_get_next (list_head_ptr, cur_node);
    }

  return (count);
  }

/************************************************************************/
/*		           list_find_prev				*/
/* Primitive to find the node "before" the current node in the linked	*/
/* list. This function returns NULL if the list_head_ptr is		*/
/* NULL, the current node is NULL, or the current node			*/
/* is the head of the linked list (i.e. does not wrap around).		*/
/************************************************************************/

DBL_LNK *list_find_prev (DBL_LNK *list_head_ptr, DBL_LNK *cur_node)
  {
DBL_LNK *prev_node = NULL;

  if (list_head_ptr == NULL)
    {
    SLOGALWAYS0 ("GENLIST: ATTEMPT TO REFERENCE A NULL LIST");
    }
  else if (cur_node == NULL)
    {
    SLOGALWAYS0 ("GENLIST: ATTEMPT TO REFERENCE THROUGH A NULL PTR");
    }
  else if (cur_node != list_head_ptr)
    {
    prev_node = cur_node->prev;
    }
  return (prev_node);
  }

/************************************************************************/
/*				list_find_last				*/
/* Primitive to find the last node in the linked list and return it's	*/
/* address.								*/
/************************************************************************/

DBL_LNK *list_find_last (DBL_LNK *list_head_ptr)
  {
DBL_LNK *last_node;

  if (list_head_ptr)
    last_node = list_head_ptr->prev;
  else
    {	/* head is NULL (i.e. list is empty) so last is NULL too.	*/
    last_node = NULL;
    }

  return (last_node);
  }

