// ************************************************************************
//
//                      Microsoft Developer Support
//             Copyright (c) 1992, 1993 Microsoft Corporation
//
// ************************************************************************
// MODULE    : LinkList.C - Ordered Double Linked List Package
// PURPOSE   : Provide a general, sorted, double linked list package
// FUNCTIONS :
//   CreateList        - allocate memory for a new list, registers the
//                       ordering function
//   CreateNode        - allocate memory fpr a new node that can be put
//                       into the linked list
//   InsertNode        - insert a new node into the list
//   SetCurrentNode    - finds the 1st occurence of a node that matches
//                       a key(s) according to the match function
//                       and sets it as the current node
//   SetCurrentNodeEx  - finds the Nth occurence of a node that matches
//                       a key(s) according to the match function
//                       and sets it as the current node
//   GetCurrentNode    - get the current node
//   GetFirstNode      - get the first (left-most) node in the list
//   GetLastNode       - get the last (right-most) node in the list
//   GetNextNode       - get the next (right) node from the current
//   GetPrevNode       - get the previous (left) node from the current
//   DeleteCurrentNode - deletes the current node from the list and
//                       frees the memory associated with it
//   DestroyNode       - deallocates the memory associated with a node
//   DestroyList       - deallocates the memory associated with a list,
//                       does not delete any of the nodes
//   GetListError      - gets the current list error
//
// COMMENTS  : The application must serialize access to the linked list
//
//   Format of the Ordering and Matching functions is as follows:
//   -----------------------------------------------------------
//
//   int OrderFunc( PNODE pNodeOne, PNODE pNodeTwo )
//   {
//     if( (pNodeOne->pNodeData).SortKey <  (pNodeTwo->pNodeData).SortKey )
//       return( LIST_LEFT_OF  );
//     if( (pNodeOne->pNodeData).SortKey == (pNodeTwo->pNodeData).SortKey )
//       return( LIST_MATCH    );
//     if( (pNodeOne->pNodeData).SortKey  > (pNodeTwo->pNodeData).SortKey )
//       return( LIST_RIGHT_OF );
//   }
//
// ************************************************************************
#include <StdLib.H>     // malloc(), free()
#include "LinkList.H"

//-- NULL pointer dereferencing checking macro
#define CHECK_POINTER( ppNode )                                       \
          if( ppNode == NULL ) {                                      \
            return( pList->ListError = LIST_ERROR_DEREFERENCE_NULL ); \
          }


// ************************************************************************
// FUNCTION : CreateList( PLIST* ppList, int (*)(PNODE, PNODE) ) )
// PURPOSE  : allocate a new list, registers the ordering function
// COMMENTS : sets all pointers to NULL
// ************************************************************************
BOOL
CreateList( PLIST* ppList, int (*OrderFunction)( PNODE pNodeOne, PNODE pNodeTwo ) )
{
  *ppList = (PLIST) malloc( sizeof(LIST) );
  if( ppList == NULL ) {
    (*ppList)->ListError = LIST_NO_ERROR;
    return( FALSE );
  }

  //-- register the link list data ordering function
  (*ppList)->OrderFunction = OrderFunction;

  //-- initialize the general linked list node pointers to NULL
  (*ppList)->pFirstNode   = NULL;
  (*ppList)->pCurrentNode = NULL;
  (*ppList)->pLastNode    = NULL;

  //-- indicate no error
  (*ppList)->ListError  = LIST_NO_ERROR;

  return( TRUE );
}


// ************************************************************************
// FUNCTION : CreateNode( PNODE* )
// PURPOSE  : create/allocate a new node that can be put into list
// COMMENTS :
// ************************************************************************
BOOL
CreateNode( PNODE* ppNewNode )
{
  *ppNewNode = (PNODE) malloc( sizeof(NODE) );
  if( *ppNewNode == NULL ) {
    return( FALSE );
  }

  //-- initialize the node pointers to NULL
  (*ppNewNode)->pRightLink = NULL;
  (*ppNewNode)->pLeftLink  = NULL;

  return( TRUE );
}


// ************************************************************************
// FUNCTION : InsertNode( PLIST pList, PNODE )
// PURPOSE  : insert a new node into the list
// COMMENTS :
//   All nodes must have a unique key entry (from order function), if a
//   match is found during an insert then the old node gets replaced with
//   the new node.
//
//  NOTE: changes pList->pCurrentNode
// ************************************************************************
BOOL
InsertNode( PLIST pList, PNODE pNewNode )
{
  int Position;
  int LastPosition;

  //-- if this is the first node in a new list
  if( pList->pFirstNode == NULL ) {
    pList->pFirstNode   = pNewNode;
    pList->pLastNode    = pNewNode;
    pList->pCurrentNode = pNewNode;
    pList->ListError    = LIST_NO_ERROR;
    return( TRUE );
  }

  Position = (*(pList->OrderFunction))( pNewNode, pList->pCurrentNode );

  //-- search for insertion point
  while( pList->pCurrentNode != NULL ) {

    LastPosition = Position;
    Position = (*(pList->OrderFunction))(pNewNode, pList->pCurrentNode);

    if( pList->pFirstNode == pList->pLastNode )
      break;

    if( Position != LastPosition )
      break;

    if( pList->pCurrentNode == pList->pFirstNode ) {
      if( Position == LIST_RIGHT_OF ) {
        pList->pCurrentNode = pList->pCurrentNode->pRightLink;
        continue;
      }
      break;
    }

    if( pList->pCurrentNode == pList->pLastNode ) {
      if( Position == LIST_LEFT_OF ) {
        pList->pCurrentNode = pList->pCurrentNode->pLeftLink;
        continue;
      }
      break;
    }

    if( Position == LastPosition ) {
      if( Position == LIST_LEFT_OF) {
        pList->pCurrentNode = pList->pCurrentNode->pLeftLink;
        continue;
      }
      if( Position == LIST_RIGHT_OF ) {
        pList->pCurrentNode = pList->pCurrentNode->pRightLink;
        continue;
      }
      break;
    }

  }

  //-- now, insert the pNewNode
  switch( Position ) {

    case LIST_LEFT_OF:
      pNewNode->pRightLink = pList->pCurrentNode;
      pNewNode->pLeftLink  = pList->pCurrentNode->pLeftLink;
      if( pList->pCurrentNode == pList->pFirstNode )
        pList->pFirstNode = pNewNode;
      else
        pList->pCurrentNode->pLeftLink->pRightLink = pNewNode;
      pList->pCurrentNode->pLeftLink = pNewNode;
      break;

  #if 1  // replace duplicates with new data

    case LIST_MATCH:
      pNewNode->pLeftLink  = pList->pCurrentNode->pLeftLink;
      pNewNode->pRightLink = pList->pCurrentNode->pRightLink;
      if( pList->pCurrentNode == pList->pLastNode )
        pList->pLastNode = pNewNode;
      else
        pList->pCurrentNode->pRightLink->pLeftLink = pNewNode;
      if( pList->pCurrentNode == pList->pFirstNode )
        pList->pFirstNode = pNewNode;
      else
        pList->pCurrentNode->pLeftLink->pRightLink = pNewNode;
      // note: doesn't destroy extra data associated with the node
      DestroyNode( pList->pCurrentNode );
      break;

  #else // or allow duplicates

    case LIST_MATCH:

  #endif

    case LIST_RIGHT_OF:
      pNewNode->pLeftLink  = pList->pCurrentNode;
      pNewNode->pRightLink = pList->pCurrentNode->pRightLink;
      if( pList->pCurrentNode == pList->pLastNode )
        pList->pLastNode = pNewNode;
      else
        pList->pCurrentNode->pRightLink->pLeftLink = pNewNode;
      pList->pCurrentNode->pRightLink = pNewNode;
      break;

  }
  pList->pCurrentNode = pNewNode;
  pList->ListError    = LIST_NO_ERROR;

  return( TRUE );
}


// ************************************************************************
// FUNCTION : SetCurrentNode( PLIST pList, PNODE, int (*)(PNODE, PNODE) )
// PURPOSE  : finds the first occurence of a node from the list that matches
//            a key(s) according to the match function.
// COMMENTS :
//   May use the ordering function or a new matching function if not
//   searching for a match based on the primary ordering key.
//   NOTE: the matching and ordering functions have the same definition.
//   NOTE: changes pList->pCurrentNode.
// ************************************************************************
BOOL
SetCurrentNode( PLIST pList, PNODE pKeyNode,
         int (*MatchFunction)( PNODE pNodeOne, PNODE pNodeTwo ) )
{
  return( SetCurrentNodeEx( pList, pKeyNode, MatchFunction, 1 ) );
}


// ************************************************************************
// FUNCTION : SetCurrentNodeEx( PLIST pList, PNODE, int (*)(PNODE, PNODE), int )
// PURPOSE  : finds the Nth occurence of a node from the list that matches
//            a key(s) according to the match function.
// COMMENTS :
//   May use the ordering function or a new matching function if not
//   searching for a match based on the primary ordering key.
//   NOTE: the matching and ordering functions have the same definition.
//   NOTE: changes pList->pCurrentNode.
// ************************************************************************
BOOL
SetCurrentNodeEx( PLIST pList, PNODE pKeyNode,
  int (*MatchFunction)( PNODE pNodeOne, PNODE pNodeTwo ),
  int Occurence )
{
  int Position;

  //-- if list is empty, exit
  if( pList->pCurrentNode == NULL ) {
    pList->ListError = LIST_ERROR_NO_NODE;
    return( FALSE );
  }

  //-- if match and order are same function
  if( MatchFunction == (pList->OrderFunction) ) {
    int  LastPosition;

    LastPosition = Position = (*MatchFunction)(pKeyNode, pList->pCurrentNode);

    while( Occurence ) {
      if( ( Position == LIST_LEFT_OF ) && ( LastPosition == LIST_LEFT_OF )
          && ( pList->pCurrentNode != pList->pFirstNode ) )
        pList->pCurrentNode = pList->pCurrentNode->pLeftLink;
      else if( ( Position == LIST_RIGHT_OF ) && ( LastPosition == LIST_RIGHT_OF )
            && ( pList->pCurrentNode != pList->pLastNode ) )
        pList->pCurrentNode = pList->pCurrentNode->pRightLink;
      else {
        Occurence--;
        continue;
      }
      LastPosition = Position;
      Position = (*MatchFunction)(pKeyNode, pList->pCurrentNode);
    }

  }
  //-- match and order are not the same function, thus start
  //   the search at the front of the list
  else {
    pList->pCurrentNode = pList->pFirstNode;
    while( (Occurence > 0) && ( (pList->pCurrentNode) != NULL ) ) {
      Position = (*MatchFunction)(pKeyNode, pList->pCurrentNode);
      if( Position == LIST_MATCH )
        Occurence--;
      if( Occurence > 0 )
        pList->pCurrentNode = pList->pCurrentNode->pRightLink;
    }
  }

  if( ( Position == LIST_MATCH ) && ( Occurence == 0 ) ) {
    pList->ListError = LIST_NO_ERROR;
    return( TRUE );
  }
  pList->ListError = LIST_ERROR_NO_MATCH;

  return( FALSE );
}


// ************************************************************************
// FUNCTION : GetCurrentNode( PLIST pList, PNODE* )
// PURPOSE  : gets the current node from the list
// COMMENTS :
//   Does not change pList->pCurrentNode.
//   Changes ppNode, thus do not pass in a ppNode which has additional
//   memory associated with it.
// ************************************************************************
BOOL
GetCurrentNode( PLIST pList, PNODE* ppNode )
{
  CHECK_POINTER( ppNode );
  if( pList->pCurrentNode == NULL ) {
    pList->ListError = LIST_ERROR_NO_NODE;
    return( FALSE );
  }
  *ppNode = pList->pCurrentNode;
  pList->ListError = LIST_NO_ERROR;

  return( TRUE );
}


// ************************************************************************
// FUNCTION : GetFirstNode( PLIST pList, PNODE* )
// PURPOSE  : gets the first (left-most) node from the list
// COMMENTS :
//   Does not change pList->pCurrentNode.
//   Changes ppNode, thus do not pass in a ppNode which has additional
//   memory associated with it.
// ************************************************************************
BOOL
GetFirstNode( PLIST pList, PNODE* ppNode )
{
  CHECK_POINTER( ppNode );
  if( pList->pFirstNode == NULL ) {
    pList->ListError = LIST_ERROR_NO_NODE;
    return( FALSE );
  }
  *ppNode = pList->pFirstNode;
  pList->ListError = LIST_NO_ERROR;

  return( TRUE );
}


// ************************************************************************
// FUNCTION : GetLastNode( PLIST pList, PNODE* )
// PURPOSE  : get the last (right-most) node from the List
// COMMENTS :
//   Does not change pList->pCurrentNode.
//   Changes ppNode, thus do not pass in a ppNode which has additional
//   memory associated with it.
// ************************************************************************
BOOL
GetLastNode( PLIST pList, PNODE* ppNode )
{
  CHECK_POINTER( ppNode );
  if( pList->pLastNode == NULL ) {
    pList->ListError = LIST_ERROR_NO_NODE;
    return( FALSE );
  }
  *ppNode = pList->pLastNode;
  pList->ListError = LIST_NO_ERROR;

  return( TRUE );
}


// ************************************************************************
// FUNCTION : GetNextNode( PLIST pList, PNODE* )
// PURPOSE  : get the next (right) node from the pList->pCurrentNode
// COMMENTS :
//   Does not change pList->pCurrentNode.
//   Changes ppNode, thus do not pass in a ppNode which has additional
//   memory associated with it.
// ************************************************************************
BOOL
GetNextNode( PLIST pList, PNODE* ppNode )
{
  CHECK_POINTER( ppNode );
  if( (*ppNode)->pRightLink == NULL ) {
    pList->ListError = LIST_ERROR_NO_NODE;
    return( FALSE );
  }
  *ppNode = (*ppNode)->pRightLink;
  pList->ListError = LIST_NO_ERROR;

  return( TRUE );
}


// ************************************************************************
// FUNCTION : GetPrevNode( PLIST pList, PNODE* )
// PURPOSE  : get the previous (left) node from the pList->pCurrentNode
// COMMENTS :
//   Does not change pList->pCurrentNode.
//   Changes ppNode, thus do not pass in a ppNode which has additional
//   memory associated with it.
// ************************************************************************
BOOL
GetPrevNode( PLIST pList, PNODE* ppNode )
{
  CHECK_POINTER( ppNode );
  if( (*ppNode)->pLeftLink == NULL ) {
    pList->ListError = LIST_ERROR_NO_NODE;
    return( FALSE );
  }
  *ppNode = (*ppNode)->pLeftLink;
  pList->ListError = LIST_NO_ERROR;

  return( TRUE );
}


// ************************************************************************
// FUNCTION : DeleteCurrentNode( PLIST pList )
// PURPOSE  : deletes the current node from the list and frees the memory
//            associated with it
// COMMENTS :
//   Changes pList->pCurrentNode.
//
//   Typically, SetCurrentNode (or SetCurrentNodeEx) is called first to set
//   pList->pCurrentNode.  Also, any addtional memory associated with this
//   node should be freed first before calling this function.
// ************************************************************************
BOOL
DeleteCurrentNode( PLIST pList )
{
  PNODE pOldCurrentNode;

  if( pList->pCurrentNode != NULL ) {
    pOldCurrentNode = pList->pCurrentNode;

    if( pOldCurrentNode == pList->pFirstNode ) {
      pList->pFirstNode   = pOldCurrentNode->pRightLink;
      pList->pCurrentNode = pOldCurrentNode->pRightLink;
    }
    else {
      pOldCurrentNode->pLeftLink->pRightLink = pOldCurrentNode->pRightLink;
      pList->pCurrentNode = pOldCurrentNode->pLeftLink;
    }

    if( pOldCurrentNode == pList->pLastNode )
      pList->pLastNode = pOldCurrentNode->pLeftLink;
    else
      pOldCurrentNode->pRightLink->pLeftLink = pOldCurrentNode->pLeftLink;

    DestroyNode( pOldCurrentNode );
    pList->ListError = LIST_NO_ERROR;
    return( TRUE );
  }
  pList->ListError = LIST_NO_ERROR;

  return( TRUE );
}


// ************************************************************************
// FUNCTION : DestroyNode( PNODE pNode )
// PURPOSE  : deallocates a node
// COMMENTS :
// ************************************************************************
BOOL
DestroyNode( PNODE pNode )
{
  free( pNode );

  return( TRUE );
}


// ************************************************************************
// FUNCTION : DestroyList( PLIST pList )
// PURPOSE  : deallocates a list, does not free any nodes, if present
// COMMENTS :
// ************************************************************************
BOOL
DestroyList( PLIST pList )
{
  free( pList );
  pList->ListError = LIST_NO_ERROR;

  return( TRUE );
}


// ************************************************************************
// FUNCTION : GetListError( PLIST pList )
// PURPOSE  : get the last linked list error
// COMMENTS :
// ************************************************************************
int
GetListError( PLIST pList )
{
  return( pList->ListError );
}
