/*  xbntx.xpp

    Xbase64 project source code

    NTX (Clipper) indexing routines for X-Base

    Copyright (C) 1999 SynXis Corp., Bob Cotton
    email - bob@synxis.com

    Copyright (C) 1997,2003  Gary A Kunkel
    
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA


    Contact:
    
     Email:
    
      xdb-devel@lists.sourceforge.net
      xdb-users@lists.sourceforge.net
      
      
     Regular Mail:
     
       XBase Support
       149C South Main St
       Keller Texas, 76248     
       USA
*/

#ifdef __GNU LesserG__
  #pragma implementation "xbntx.h"
#endif

#ifdef __WIN32__
#include <xbase64/xbwincfg.h>
#else
#include <xbase64/xbconfig.h>
#endif

#include <xbase64/xbase64.h>

#ifdef XB_INDEX_NTX

#ifdef HAVE_IO_H
#include <io.h>
#endif

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <ctype.h>
#include <sys/stat.h>

//#include <xbase64/xbexcept.h>

/*! \file xbntx.cpp
*/

/***********************************************************************/
//! Short description.
/*!
*/
/* This routine dumps the node chain to stdout                         */
#ifdef XBASE_DEBUG
void xbNtx::DumpNodeChain( void )
{
  xbNodeLink  *n;
  std::cout << "*************************"         << std::endl;
  std::cout << "NodeLinkCtr = " << NodeLinkCtr     << std::endl;
  std::cout << "Reused      = " << ReusedNodeLinks << std::endl;

  n = NodeChain;
  while(n){
    std::cout << "xbNodeLink Chain" << n->NodeNo << std::endl;
    n = n->NextNode;
  }
  n = FreeNodeChain;
  while(n){
    std::cout << "FreeNodeLink Chain" << n->NodeNo << std::endl;
    n = n->NextNode;
  }
  n = DeleteChain;
  while(n){
    std::cout << "DeleteLink Chain" << n->NodeNo << std::endl;
    n = n->NextNode;
  }
}
#endif
/***********************************************************************/
//! Short description.
/*!
  \param n
*/
/* This routine returns a chain of one or more index nodes back to the */
/* free node chain                                                     */

void xbNtx::ReleaseNodeMemory( xbNodeLink * n, xbBool doFree )
{
  xbNodeLink *temp;

  if(doFree){
    while(n){
      temp = n->NextNode;
      if(n->offsets)
        free(n->offsets);
      free(n);
      n = temp;
    }
  } else {
    if(!FreeNodeChain )
      FreeNodeChain = n;
    else {   /* put this list at the end */
      temp = FreeNodeChain;
      while( temp->NextNode )
        temp = temp->NextNode;
      temp->NextNode = n;
    }
  }
}
/***********************************************************************/
//! Short description.
/*!
*/
/* This routine returns a node from the free chain if available,       */
/* otherwise it allocates new memory for the requested node             */

xbNodeLink * xbNtx::GetNodeMemory( void )
{
  xbNodeLink * temp;
  if( FreeNodeChain ){
    temp = FreeNodeChain;
    temp->offsets = FreeNodeChain->offsets;
    FreeNodeChain = temp->NextNode;
    ReusedNodeLinks++;
    memset( temp->Leaf.KeyRecs, 0x00, XB_NTX_NODE_SIZE );
    temp->Leaf.NoOfKeysThisNode = 0;
    temp->PrevNode = 0x00;
    temp->NextNode = 0x00;
    temp->CurKeyNo = 0L;
    temp->NodeNo = 0L;

    for (int i = 0; i < HeadNode.KeysPerNode + 1; i++){
       temp->offsets[i] = 2 + ((HeadNode.KeysPerNode + 1) * 2) + (HeadNode.KeySize * i);
    }
  } else {
    temp = (xbNodeLink *) malloc( sizeof( xbNodeLink ));
    if(temp==NULL) return NULL;
    memset( temp, 0x00, sizeof( xbNodeLink ));
    temp->offsets = (xbUShort *)malloc( (HeadNode.KeysPerNode + 1) * sizeof(xbUShort));
    if (temp->offsets==NULL) {
      free(temp);
      return NULL;
    };
    NodeLinkCtr++;
  }
  return temp;
}
/***********************************************************************/
//! Short description.
/*!
*/
#ifdef XBASE_DEBUG
void xbNtx::DumpHdrNode( xbShort Option )
{
  if( Option == 0 ){
    std::cout << "Signature       = " << HeadNode.Signature       << std::endl;
    std::cout << "Version         = " << HeadNode.Version         << std::endl;
    std::cout << "StartPahe       = " << HeadNode.StartNode       << std::endl;
    std::cout << "UnusedOffset    = " << HeadNode.UnusedOffset    << std::endl;
    std::cout << "KeySize         = " << HeadNode.KeySize         << std::endl;
    std::cout << "KeyLen          = " << HeadNode.KeyLen          << std::endl;
    std::cout << "DecimalCount    = " << HeadNode.DecimalCount    << std::endl;
    std::cout << "KeysPerNode     = " << HeadNode.KeysPerNode     << std::endl;
    std::cout << "HalfKeysPerPage = " << HeadNode.HalfKeysPerNode << std::endl;
    std::cout << "KeyExpression   = " << HeadNode.KeyExpression   << std::endl;
    std::cout << "Unique          = " << HeadNode.Unique          << std::endl;
  } else
    std::cout << "Print Hdr Node option not implemented yet" << std::endl;
}
#endif
/***********************************************************************/
//! Constructor
/*!
*/
xbNtx::xbNtx() : xbIndex()
{
}
/***********************************************************************/
//! Constructor
/*!
  \param pdbf
*/
xbNtx::xbNtx( xbDbf * pdbf )  : xbIndex (pdbf)
{
  memset( Node, 0x00, XB_NTX_NODE_SIZE );
  memset( &HeadNode, 0x00, sizeof( NtxHeadNode ));
  NodeChain       = NULL;
//  CloneChain      = NULL;
  FreeNodeChain   = NULL;
  DeleteChain     = NULL;
  CurNode         = NULL;
  NodeLinkCtr     = 0L;
  ReusedNodeLinks = 0L;
}
/***********************************************************************/
//! Destructor
/*!
*/
xbNtx::~xbNtx()
{
  CloseIndex();
}
/***********************************************************************/
//! Short description.
/*!
*/
xbShort xbNtx::GetHeadNode( void )
{
  char *p;
  if( !IsOpen() )
    return XB_NOT_OPEN;
  if( _fseek( indexfp, 0, SEEK_SET ))
    return XB_SEEK_ERROR;
  if(( fread( Node, XB_NTX_NODE_SIZE, 1, indexfp )) != 1 )
    return XB_READ_ERROR;

  /* load the head node structure */
  p = Node;
  HeadNode.Signature = dbf->xbase->GetShort( p ); p += sizeof(xbUShort);
  HeadNode.Version = dbf->xbase->GetShort( p ); p += sizeof(xbUShort);
  HeadNode.StartNode = dbf->xbase->GetULong( p ); p += sizeof(xbULong);
  HeadNode.UnusedOffset = dbf->xbase->GetULong( p ); p += sizeof(xbULong);
  HeadNode.KeySize = dbf->xbase->GetShort( p ); p += sizeof(xbUShort);
  HeadNode.KeyLen = dbf->xbase->GetShort( p ); p += sizeof(xbUShort);
  HeadNode.DecimalCount = dbf->xbase->GetShort( p ); p += sizeof(xbUShort);
  HeadNode.KeysPerNode = dbf->xbase->GetShort( p ); p += sizeof(xbUShort);
  HeadNode.HalfKeysPerNode = dbf->xbase->GetShort( p ); p += sizeof(xbUShort);
  strncpy(HeadNode.KeyExpression, p, 256); p+= 256;
//   HeadNode.Unique = *p++;    ++ is unused code 8/19/03 - gkunkel
  HeadNode.Unique = *p;

  p = HeadNode.KeyExpression;
  while (*p){
    *p = toupper(*p);
     p++;
  }
  return 0;
}
/***********************************************************************/
//! Short description.
/*!
  \param NodeNo
  \param SetNodeChain
*/
/* This routine reads a leaf node from disk                            */
/*                                                                     */
/*  If SetNodeChain 2, then the node is not appended to the node chain */
/*                     but the CurNode pointer points to the node read */
/*  If SetNodeChain 1, then the node is appended to the node chain     */
/*  If SetNodeChain 0, then record is only read to Node memory         */

xbShort xbNtx::GetLeafNode( xbLong NodeNo, xbShort SetNodeChain )
{
  xbNodeLink *n;
  char *p;

  if( !IsOpen() )
    return XB_NOT_OPEN;

  if( _fseek( indexfp, (xbOffT)NodeNo, SEEK_SET ))
    return XB_SEEK_ERROR;

  if(( fread( Node, XB_NTX_NODE_SIZE, 1, indexfp )) != 1 )
    return XB_READ_ERROR;

  if( !SetNodeChain ) return 0;

  if(( n = GetNodeMemory()) == NULL )
    return XB_NO_MEMORY;

  n->NodeNo = NodeNo;
  n->CurKeyNo = 0L;
  n->NextNode = NULL;

  // The offsets at the head of each leaf are not necessarly in order.
  p = Node + 2;
  for( int i = 0; i < HeadNode.KeysPerNode + 1; i++){
    n->offsets[i] = dbf->xbase->GetShort( p );
    p += 2;
  }
   
  // Do the edian translation correctly
  n->Leaf.NoOfKeysThisNode = dbf->xbase->GetShort( Node );
  memcpy( n->Leaf.KeyRecs, Node, XB_NTX_NODE_SIZE );

  /* put the node in the chain */
  if( SetNodeChain == 1 ){
    if( NodeChain == NULL ){      /* first one ? */
      NodeChain = n;
      CurNode = n;
      CurNode->PrevNode = NULL;
    } else {
      n->PrevNode = CurNode;
      CurNode->NextNode = n;
      CurNode = n;
    }
  } 
  else
    CurNode = n;

  return 0;
}
/***********************************************************************/
//! Short description.
/*!
  \param n
*/
#ifdef XBASE_DEBUG
void xbNtx::DumpNodeRec( xbLong n )
{
  char *p;
  xbShort NoOfKeys;
  xbLong LeftBranch, RecNo;
  xbShort i,j;

  GetLeafNode( n, 0 );
  NoOfKeys = dbf->xbase->GetShort( Node );
  p = Node + 4;        /* go past no of keys */
std::cout << "-----------------------------------------------" << std::endl;
  std::cout << "Node # " << n;
  std::cout << "Number of keys = " << NoOfKeys    << std::endl;
  std::cout << " Key     Left     Rec      Key"   << std::endl;
  std::cout << "Number  Branch   Number    Data"  << std::endl;

  for( i = 0; i < GetKeysPerNode()+1 /*NoOfKeys*/; i++ ){
    LeftBranch = dbf->xbase->GetLong( p );
    p+=4;
    RecNo = dbf->xbase->GetLong( p );
    p+=4;
    std::cout << i          << "         "
              << LeftBranch << "         "
              << RecNo      << "         " << std::endl;
    for( j = 0; j < HeadNode.KeyLen; j++ ) std::cout << *p++;
  }
}
#endif
/***********************************************************************/
//! Short description.
/*!
  \param RecNo
  \param n
*/
xbLong xbNtx::GetDbfNo( xbShort RecNo, xbNodeLink * n )
{
  NtxLeafNode *temp;
  char *p;
  xbUShort itemOffset;
  
  if( !n ) return 0L;
  temp = &n->Leaf;
  p = temp->KeyRecs;
  if( RecNo < 0 || RecNo > ( temp->NoOfKeysThisNode  )) return 0L;
  itemOffset = GetItemOffset(RecNo, n, 0);
  // ItemOffset is from the beginning of the record.
  p += itemOffset;
  p += 4;
  return( dbf->xbase->GetLong( p ));
}
/***********************************************************************/
//! Short description.
/*!
  \param RecNo
  \param n
*/
xbLong xbNtx::GetLeftNodeNo( xbShort RecNo, xbNodeLink * n )
{
  NtxLeafNode *temp;
  char *p;
  xbUShort itemOffset;
   
  if( !n ) return 0L;
  temp = &n->Leaf;
  p = temp->KeyRecs;
  if( RecNo < 0 || RecNo > temp->NoOfKeysThisNode  ) return 0L;
  itemOffset = GetItemOffset(RecNo, n, 0);
  // ItemOffset is from the beginning of the record.
  p += itemOffset;
  return( dbf->xbase->GetULong( p ));
}

/***********************************************************************/
//! Short description.
/*!
  \param RecNo
  \param n
*/
char * xbNtx::GetKeyData( xbShort RecNo, xbNodeLink * n )
{
  NtxLeafNode *temp;
  char *p;
  xbUShort itemOffset;
  if( !n ) return 0L;
  temp = &n->Leaf;
  p = temp->KeyRecs;
  if( RecNo < 0 || RecNo > ( temp->NoOfKeysThisNode )) return 0L;
  itemOffset = GetItemOffset(RecNo, n, 0);
  // ItemOffset is from the beginning of the record.
  p += itemOffset + 8;
  return( p );
}
/***********************************************************************/
//! Short description.
/*!
  \param RecNo
  \param n
  \param
*/
xbUShort
xbNtx::GetItemOffset(xbShort RecNo, xbNodeLink *n, xbShort) {
  if( RecNo > (this->HeadNode.KeysPerNode + 1) ){
    std::cout << "RecNo = " << RecNo << std::endl;
    std::cout << "this->HeadNode.KeysPerNode = "
              << this->HeadNode.KeysPerNode << std::endl;
    std::cout << "********************* BUG ***********************"
              << std::endl;
    // ;-)
    exit(1);
  }
    
  return n->offsets[RecNo];
}

//! Short description.
/*!
  \param pos
  \param n
*/
xbUShort
xbNtx::InsertKeyOffset(xbShort pos, xbNodeLink *n)
{
  xbUShort temp;

  // save the new offset
  temp  = n->offsets[n->Leaf.NoOfKeysThisNode + 1];

  for( int i = n->Leaf.NoOfKeysThisNode + 1; i > pos; i-- ){
    n->offsets[i] = n->offsets[i-1];
  }
  n->offsets[pos] = temp;
    
  return n->offsets[pos];
}

//! Short description.
/*!
  \param pos
  \param n
*/
xbUShort
xbNtx::DeleteKeyOffset(xbShort pos, xbNodeLink *n)
{
  xbUShort temp;
  xbShort i;
  // save the old offset
  temp  = n->offsets[pos];

  for( i = pos; i < n->Leaf.NoOfKeysThisNode; i++ ){
    n->offsets[i] = n->offsets[i+1];
  }
  n->offsets[i] = temp;
  return n->offsets[i];
}
/***********************************************************************/
//! Short description.
/*!
*/
xbLong xbNtx::GetTotalNodes( void ) 
{
//   if( &HeadNode )
//      return HeadNode.TotalNodes;
//   else
  return 0L;
}
/***********************************************************************/
//! Short description.
/*!
*/
xbUShort xbNtx::GetKeysPerNode( void ) 
{
  if( &HeadNode )
    return HeadNode.KeysPerNode;
  else
    return 0L;
}
/***********************************************************************/
//! Short description.
/*!
  \param RetrieveSw
*/
xbShort xbNtx::GetFirstKey( xbShort RetrieveSw )
{
/* This routine returns 0 on success and sets CurDbfRec to the record  */
/* corresponding to the first index pointer                            */

  xbLong TempNodeNo;
  xbShort rc;
   
#ifdef XB_LOCKING_ON
//   if( dbf->GetAutoLock() )
//      if((rc = LockIndex( XB_LOCK )) != 0)
//         return rc;
#endif
       
  /* initialize the node chain */
  if( NodeChain ){
    ReleaseNodeMemory( NodeChain );
    NodeChain = NULL;
  }

  if(( rc = GetHeadNode()) != 0 ){
    CurDbfRec = 0L;
#ifdef XB_LOCKING_ON
//    if( dbf->GetAutoLock() )
//         LockIndex( XB_UNLOCK );
#endif
    return rc;
  }

  /* get a node and add it to the link */

  if(( rc = GetLeafNode( HeadNode.StartNode, 1 )) != 0 ){
#ifdef XB_LOCKING_ON
//      if( dbf->GetAutoLock() )
//         LockIndex( XB_UNLOCK );
#endif
    return rc;
  }

/* traverse down the left side of the tree */
  while( GetLeftNodeNo( 0, CurNode )){
    TempNodeNo = GetLeftNodeNo( 0, CurNode );     
    if(( rc = GetLeafNode( TempNodeNo, 1 )) != 0 ){
#ifdef XB_LOCKING_ON
//         if( dbf->GetAutoLock() )
//            LockIndex( XB_UNLOCK );
#endif
      CurDbfRec = 0L;
      return rc;     
    }
    CurNode->CurKeyNo = 0;
  }
  CurDbfRec = GetDbfNo( 0, CurNode );
#ifdef XB_LOCKING_ON
//   if( dbf->GetAutoLock() )
//      LockIndex( XB_UNLOCK );
#endif
  if( RetrieveSw )
    return dbf->GetRecord( CurDbfRec );
  else
    return XB_NO_ERROR;
}
/***********************************************************************/
//! Short description.
/*!
  \param RetrieveSw
*/
xbShort xbNtx::GetNextKey( xbShort RetrieveSw )
{
/* This routine returns 0 on success and sets CurDbfRec to the record  */
/* corresponding to the next index pointer                             */

  xbNodeLink * TempNodeLink;
  xbLong TempNodeNo;
  xbShort rc;
    
#ifdef XB_LOCKING_ON
//   if( dbf->GetAutoLock() )
  //    if((rc = LockIndex( XB_LOCK )) != 0)
    //     return rc;
#endif

  if( !IsOpen() ){
#ifdef XB_LOCKING_ON
//      if( dbf->GetAutoLock() )
//         LockIndex( XB_UNLOCK );
#endif
    CurDbfRec = 0L;
    return XB_NOT_OPEN;
  }

  if( !CurNode ){
    rc = GetFirstKey( RetrieveSw );
#ifdef XB_LOCKING_ON
//    if( dbf->GetAutoLock() )
//         LockIndex( XB_UNLOCK );
#endif
    return rc;
  }
     
  /* more keys on this node ? */
  if(( CurNode->Leaf.NoOfKeysThisNode -1 ) > CurNode->CurKeyNo ){
    CurNode->CurKeyNo++;
    CurDbfRec = GetDbfNo( CurNode->CurKeyNo, CurNode );
#ifdef XB_LOCKING_ON
//      if( dbf->GetAutoLock() )
//         LockIndex( XB_UNLOCK );
#endif
    if( RetrieveSw )
      return dbf->GetRecord( CurDbfRec );
    else
      return XB_NO_ERROR;
  }

  /* if head node we are at eof */
  if( CurNode->NodeNo == HeadNode.StartNode ){
#ifdef XB_LOCKING_ON
//      if( dbf->GetAutoLock() )
//         LockIndex( XB_UNLOCK );
#endif
    return XB_EOF;
  }

  /* this logic assumes that interior nodes have n+1 left node no's where */
  /* n is the number of keys in the node                                  */
  /* pop up one node to the interior node level & free the leaf node      */
  TempNodeLink = CurNode;
  CurNode = CurNode->PrevNode;
  CurNode->NextNode = NULL;
  ReleaseNodeMemory( TempNodeLink );

  /* while no more right keys && not head node, pop up one node */
  while(( CurNode->CurKeyNo >= CurNode->Leaf.NoOfKeysThisNode ) &&
        ( CurNode->NodeNo != HeadNode.StartNode )){
    TempNodeLink = CurNode;
    CurNode = CurNode->PrevNode;
    CurNode->NextNode = NULL;
    ReleaseNodeMemory( TempNodeLink );
  }

  /* if head node && right most key, return end-of-file */
  if(( HeadNode.StartNode == CurNode->NodeNo ) &&
     ( CurNode->CurKeyNo >= CurNode->Leaf.NoOfKeysThisNode )){
#ifdef XB_LOCKING_ON
//      if( dbf->GetAutoLock() )
//         LockIndex( XB_UNLOCK );
#endif
      return XB_EOF;
  }

  /* move one to the right */
  CurNode->CurKeyNo++;
  TempNodeNo = GetLeftNodeNo( CurNode->CurKeyNo, CurNode );

  if(( rc = GetLeafNode( TempNodeNo, 1 )) != 0 ){
#ifdef XB_LOCKING_ON
//      if( dbf->GetAutoLock() )
//         LockIndex( XB_UNLOCK );
#endif
    return rc;
  }

/* traverse down the left side of the tree */
  while( GetLeftNodeNo( 0, CurNode )){
    TempNodeNo = GetLeftNodeNo( 0, CurNode );     
    if(( rc = GetLeafNode( TempNodeNo, 1 )) != 0 ){
#ifdef XB_LOCKING_ON
//         if( dbf->GetAutoLock() )
//            LockIndex( XB_UNLOCK );
#endif
      CurDbfRec = 0L;
      return rc;     
    }
    CurNode->CurKeyNo = 0;
  }
  CurDbfRec = GetDbfNo( 0, CurNode );
#ifdef XB_LOCKING_ON
//   if( dbf->GetAutoLock() )
//      LockIndex( XB_UNLOCK );
#endif
  if( RetrieveSw )
    return dbf->GetRecord( CurDbfRec );
  else
    return XB_NO_ERROR;
}
/***********************************************************************/
//! Short description.
/*!
  \param NodeNo
  \param RetrieveSw
*/
xbShort xbNtx::GetLastKey( xbLong NodeNo, xbShort RetrieveSw )
{
/* This routine returns 0 on success and sets CurDbfRec to the record  */
/* corresponding to the last index pointer                             */

/* If NodeNo = 0, start at head node, otherwise start at NodeNo        */

  xbLong TempNodeNo;
  xbShort rc;

// TODO
// NTX files keep no TotalNode count.   
//   if( NodeNo < 0 || NodeNo > HeadNode.TotalNodes )
//      return XB_INVALID_NODE_NO;

#ifdef XB_LOCKING_ON
//  if( dbf->GetAutoLock() )
//      if((rc = LockIndex( XB_LOCK )) != 0)
//         return rc;
#endif

  /* initialize the node chain */
  if( NodeChain ){
    ReleaseNodeMemory( NodeChain );
    NodeChain = NULL;
  }
  if( NodeNo == 0L )
    if(( rc = GetHeadNode()) != 0 ){ 
#ifdef XB_LOCKING_ON
//         if( dbf->GetAutoLock() )
//            LockIndex( XB_UNLOCK );
#endif
      CurDbfRec = 0L;
      return rc;
    }

  /* get a node and add it to the link */
  if( NodeNo == 0L ){
    if(( rc = GetLeafNode( HeadNode.StartNode, 1 )) != 0 ){
#ifdef XB_LOCKING_ON
//         if( dbf->GetAutoLock() )
//            LockIndex( XB_UNLOCK);
#endif
      CurDbfRec = 0L;
      return rc;
    }
  } else {
    if(( rc = GetLeafNode( NodeNo, 1 )) != 0 ){
#ifdef XB_LOCKING_ON
//         if( dbf->GetAutoLock() )
//            LockIndex( XB_UNLOCK );
#endif
      CurDbfRec = 0L;
      return rc;
    }
  }
  CurNode->CurKeyNo = CurNode->Leaf.NoOfKeysThisNode;

/* traverse down the right side of the tree */
  while( GetLeftNodeNo( CurNode->Leaf.NoOfKeysThisNode, CurNode )){
    TempNodeNo = GetLeftNodeNo( CurNode->Leaf.NoOfKeysThisNode, CurNode );     
    if(( rc = GetLeafNode( TempNodeNo, 1 )) != 0 ){
#ifdef XB_LOCKING_ON
//         if( dbf->GetAutoLock() )
//            LockIndex( XB_UNLOCK );
#endif
      CurDbfRec = 0L;
      return rc;     
    }
    CurNode->CurKeyNo = CurNode->Leaf.NoOfKeysThisNode;
  }
  CurNode->CurKeyNo--;           /* leaf node has one fewer ix recs */
  CurDbfRec = GetDbfNo( CurNode->Leaf.NoOfKeysThisNode-1, CurNode );
#ifdef XB_LOCKING_ON
//   if( dbf->GetAutoLock() )
//      LockIndex( XB_UNLOCK );
#endif
  if( RetrieveSw )
    return dbf->GetRecord( CurDbfRec );
  else
    return XB_NO_ERROR;
}
/***********************************************************************/
//! Short description.
/*!
  \param RetrieveSw
*/
xbShort xbNtx::GetPrevKey( xbShort RetrieveSw )
{
/* This routine returns 0 on success and sets CurDbfRec to the record  */
/* corresponding to the previous index pointer                         */

  xbNodeLink * TempNodeLink;
  xbLong TempNodeNo;
  xbShort rc;
    
#ifdef XB_LOCKING_ON
//   if( dbf->GetAutoLock() )
//      if((rc = LockIndex( XB_LOCK )) != 0)
//        return rc;
#endif

  if( !IsOpen() ){
#ifdef XB_LOCKING_ON
//    if( dbf->GetAutoLock() )
//       LockIndex( XB_UNLOCK );
#endif
    CurDbfRec = 0L;
    return XB_NOT_OPEN;
  }

  if( !CurNode ){
#ifdef XB_LOCKING_ON
//      if( dbf->GetAutoLock() )
//         LockIndex( XB_UNLOCK );
#endif
    CurDbfRec = 0L;
    return GetFirstKey( RetrieveSw );
  }

  /* more keys on this node ? */
  if( CurNode->CurKeyNo > 0 ){
    CurNode->CurKeyNo--;
    CurDbfRec = GetDbfNo( CurNode->CurKeyNo, CurNode );
#ifdef XB_LOCKING_ON
//      if( dbf->GetAutoLock() )
//         LockIndex( XB_UNLOCK );
#endif
    if( RetrieveSw )
      return dbf->GetRecord( CurDbfRec );
    else
      return XB_NO_ERROR;
  }

  /* this logic assumes that interior nodes have n+1 left node no's where */
  /* n is the number of keys in the node                                  */
  /* pop up one node to the interior node level & free the leaf node      */

  if( !CurNode->PrevNode ){       /* michael - make sure prev node exists */
#ifdef XB_LOCKING_ON
//      if( dbf->GetAutoLock() )
//         LockIndex( XB_UNLOCK );
#endif
    return XB_EOF;
  }

  TempNodeLink = CurNode;
  CurNode = CurNode->PrevNode;
  CurNode->NextNode = NULL;
  ReleaseNodeMemory( TempNodeLink );

  /* while no more left keys && not head node, pop up one node */
  while(( CurNode->CurKeyNo == 0 ) && 
        ( CurNode->NodeNo != HeadNode.StartNode )){
    TempNodeLink = CurNode;
    CurNode = CurNode->PrevNode;
    CurNode->NextNode = NULL;
    ReleaseNodeMemory( TempNodeLink );
  }

  /* if head node && left most key, return end-of-file */
  if(( HeadNode.StartNode == CurNode->NodeNo ) &&
     ( CurNode->CurKeyNo == 0 )){
#ifdef XB_LOCKING_ON
//     if( dbf->GetAutoLock() )
//         LockIndex( XB_UNLOCK );
#endif
    return XB_EOF;
   }

  /* move one to the left */
  CurNode->CurKeyNo--;
  TempNodeNo = GetLeftNodeNo( CurNode->CurKeyNo, CurNode );
  if(( rc = GetLeafNode( TempNodeNo, 1 )) != 0 ){
#ifdef XB_LOCKING_ON
//      if( dbf->GetAutoLock() )
//         LockIndex( XB_UNLOCK );
#endif
    return rc;
  }

  if( GetLeftNodeNo( 0, CurNode )) /* if interior node */
    CurNode->CurKeyNo = CurNode->Leaf.NoOfKeysThisNode;
  else              /* leaf node */
    CurNode->CurKeyNo = CurNode->Leaf.NoOfKeysThisNode -1;

/* traverse down the right side of the tree */
  while( GetLeftNodeNo( 0, CurNode )){    /* while interior node */
    TempNodeNo = GetLeftNodeNo( CurNode->Leaf.NoOfKeysThisNode, CurNode );     
    if(( rc = GetLeafNode( TempNodeNo, 1 )) != 0 ){
#ifdef XB_LOCKING_ON
//         if( dbf->GetAutoLock() )
//            LockIndex( XB_UNLOCK );
#endif
      CurDbfRec = 0L;
      return rc;     
    }
    if( GetLeftNodeNo( 0, CurNode )) /* if interior node */
      CurNode->CurKeyNo = CurNode->Leaf.NoOfKeysThisNode;
    else              /* leaf node */
      CurNode->CurKeyNo = CurNode->Leaf.NoOfKeysThisNode -1;
  }
  CurDbfRec = GetDbfNo( CurNode->Leaf.NoOfKeysThisNode -1, CurNode );
#ifdef XB_LOCKING_ON
//   if( dbf->GetAutoLock() )
//      LockIndex( XB_UNLOCK );
#endif
  if( RetrieveSw )
    return dbf->GetRecord( CurDbfRec );
  else
    return XB_NO_ERROR;
}
/***********************************************************************/
//! Short description.
/*!
  \param Key1
  \param Key2
  \param Klen
*/
xbShort xbNtx::CompareKey( const char * Key1, const char * Key2, xbShort Klen )
{
/*   if key1 = key2  --> return 0      */
/*   if key1 > key2  --> return 1      */
/*   if key1 < key2  --> return 2      */

  const char   *k1, *k2;
  xbShort  i;

  if( Klen > HeadNode.KeyLen ) Klen = HeadNode.KeyLen;
  k1 = Key1;
  k2 = Key2;
  for( i = 0; i < Klen; i++ ){
    if( *k1 > *k2 ) return 1;
    if( *k1 < *k2 ) return 2;
    k1++;
    k2++;
  }
  return 0;
}
/***********************************************************************/
//! Short description.
/*!
  \param Key1
  \param Key2
*/
xbShort xbNtx::CompareKey( const char * Key1, const char * Key2)
{
/*   if key1 = key2  --> return 0      */
/*   if key1 > key2  --> return 1      */
/*   if key1 < key2  --> return 2      */

  int rc;
  rc = strcmp(Key1, Key2);
  if( rc < 0 )
    return 2;
  else if( rc > 0 )
    return 1;
  else
    return 0;
}
/***********************************************************************/
//! Short description.
/*!
  \param Tkey
  \param 
*/
xbULong xbNtx::GetLeafFromInteriorNode( const char * Tkey, xbShort )
{
  /* This function scans an interior node for a key and returns the   */
  /* correct interior leaf node no                                    */

  xbShort rc, p;

  /* if Tkey > any keys in node, return right most key */
  p = CurNode->Leaf.NoOfKeysThisNode -1 ;
  if( CompareKey( Tkey, GetKeyData( p, CurNode )) == 1 ){
    CurNode->CurKeyNo = CurNode->Leaf.NoOfKeysThisNode;
    return GetLeftNodeNo( CurNode->Leaf.NoOfKeysThisNode, CurNode );
  }
   
  /* otherwise, start at the beginning and scan up */
  p = 0;
  while( p < CurNode->Leaf.NoOfKeysThisNode){
    rc = CompareKey( Tkey, GetKeyData( p, CurNode ) );
    if (rc == 2) break;
    else if (rc == 0){
      CurNode->CurKeyNo = p;
      CurDbfRec = GetDbfNo( p, CurNode );
      return 0;
    }
    p++;
  }

  CurNode->CurKeyNo = p;
  return GetLeftNodeNo( p, CurNode );
}
/***********************************************************************/
//! Short description.
/*!
  \param d
*/
xbShort xbNtx::KeyExists( xbDouble d )
{
  char buf[9];
  memset( buf, 0x00, 9 );
  dbf->xbase->PutDouble( buf, d );
  return FindKey( buf, 8, 0 );
}
/***********************************************************************/
//! Short description.
/*!
  \param d
*/
xbShort xbNtx::FindKey( xbDouble d )
{
  char buf[9];
  memset( buf, 0x00, 9 );
  dbf->xbase->PutDouble( buf, d );
  return FindKey( buf, 8, 1 );
}
/***********************************************************************/
//! Short description.
/*!
  \param Key
*/
xbShort xbNtx::FindKey( const char * Key )
{
  return FindKey( Key, strlen( Key ), 1 );
}
/***********************************************************************/
//! Short description.
/*!
  \param Tkey
  \param DbfRec
*/
xbShort xbNtx::FindKey( const char * Tkey, xbLong DbfRec )
{
  /* find a key with a specifc xbDbf record number */
  xbShort rc;
  xbLong CurDbfRecNo;
  xbLong CurNtxDbfNo;

#ifdef XB_LOCKING_ON
//   if( dbf->GetAutoLock() )
//      if((rc = LockIndex( XB_LOCK )) != 0)
//        return rc;
#endif

  /* if we are already on the correct key, return XB_FOUND */
  if( CurNode ){
    CurDbfRecNo = dbf->GetCurRecNo();
    CurNtxDbfNo = GetDbfNo( CurNode->CurKeyNo, CurNode );
    if( CurDbfRecNo == CurNtxDbfNo ){
#ifdef XB_LOCKING_ON
//         if( dbf->GetAutoLock() )
//            LockIndex( XB_UNLOCK );
#endif
      return XB_FOUND;
    }
  }

  rc =  FindKey( Tkey, HeadNode.KeyLen, 0 );

  while( rc == 0 || rc == XB_FOUND ){
    if( strncmp( Tkey, GetKeyData( CurNode->CurKeyNo, CurNode ),
         HeadNode.KeyLen ) == 0 ){
      if( DbfRec == GetDbfNo( CurNode->CurKeyNo, CurNode )){
#ifdef XB_LOCKING_ON
//            if( dbf->GetAutoLock() )
  //             LockIndex( XB_UNLOCK );
#endif
        return XB_FOUND;
       } else
         rc = GetNextKey( 0 );
     } else {
#ifdef XB_LOCKING_ON
//         if( dbf->GetAutoLock() )
//            LockIndex( XB_UNLOCK );
#endif
       return XB_NOT_FOUND;
     }
   }
#ifdef XB_LOCKING_ON
//   if( dbf->GetAutoLock() )
//      LockIndex( XB_UNLOCK );
#endif
  return XB_NOT_FOUND;
}
/***********************************************************************/
//! Short description.
/*!
*/
xbShort xbNtx::FindKey( void )
{
  /* if no paramaters given, use KeyBuf */
  return( FindKey( KeyBuf, HeadNode.KeyLen, 0 ));
}
/***********************************************************************/
//! Short description.
/*!
  \param Tkey
  \param Klen
  \param RetrieveSw
*/
xbShort xbNtx::FindKey( const char * Tkey, xbShort Klen, xbShort RetrieveSw )
{
  /* This routine sets the current key to the found key */
  /* if RetrieveSw is true, the method positions the dbf record */

  xbShort rc,i;
  xbLong TempNodeNo;

#ifdef XB_LOCKING_ON
//   if( dbf->GetAutoLock() )
//      if((rc = LockIndex( XB_LOCK )) != 0)
//         return rc;
#endif
  if( NodeChain ){
    ReleaseNodeMemory( NodeChain );
    NodeChain = NULL;
  }

  if(( rc = GetHeadNode()) != 0 ){
#ifdef XB_LOCKING_ON
//      if( dbf->GetAutoLock() )
//         LockIndex( XB_UNLOCK );
#endif
    CurDbfRec = 0L;
    return rc;
  }

  // If the index is empty
  if( HeadNode.StartNode == 0){
#ifdef XB_LOCKING_ON
//      if( dbf->GetAutoLock() )
//         LockIndex( XB_UNLOCK );
#endif
    return XB_NOT_FOUND;
  }
           
  /* load first node */
  if(( rc = GetLeafNode( HeadNode.StartNode, 1 )) != 0 ){
    CurDbfRec = 0L;
#ifdef XB_LOCKING_ON
//      if( dbf->GetAutoLock() )
//         LockIndex( XB_UNLOCK );
#endif
    return rc;
  }

  /* traverse down the tree until it hits a leaf */
  while( GetLeftNodeNo( 0, CurNode )){ /* while interior node */   
    TempNodeNo = GetLeafFromInteriorNode( Tkey, Klen );

#if 1
  // GetLeafFromInteriorNode will return 0 if the key is found on
  // an inode. But the leftNodeNo will not be 0. 
    if (TempNodeNo == 0 && GetLeftNodeNo( 0, CurNode ) != 0){
#ifdef XB_LOCKING_ON
//          if( dbf->GetAutoLock() )
//              LockIndex( XB_UNLOCK );
#endif
      if( RetrieveSw ) dbf->GetRecord( CurDbfRec );
        return XB_FOUND;
    }
#endif      
      
    if(( rc = GetLeafNode( TempNodeNo, 1 )) != 0 ){
#ifdef XB_LOCKING_ON
//         if( dbf->GetAutoLock() )
//            LockIndex( XB_UNLOCK );
#endif
      CurDbfRec = 0L;
      return rc;
    }
  }

  /* leaf level */
  for( i = 0; i < CurNode->Leaf.NoOfKeysThisNode; i++ ){
    rc = CompareKey(  Tkey, GetKeyData( i, CurNode ) );
    if( rc == 0 ){
      CurNode->CurKeyNo = i;
      CurDbfRec = GetDbfNo( i, CurNode );
#ifdef XB_LOCKING_ON
//         if( dbf->GetAutoLock() )
//            LockIndex( XB_UNLOCK );
#endif
      if( RetrieveSw ) dbf->GetRecord( CurDbfRec );
        return XB_FOUND;
    } else if( rc == 2 ) {
      CurNode->CurKeyNo = i;
      CurDbfRec = GetDbfNo( i, CurNode );
      if( RetrieveSw ) dbf->GetRecord( CurDbfRec );
      // If key is lessthan, without length involved,
      // Check to see if the substring match
#ifdef XB_LOCKING_ON
//         if( dbf->GetAutoLock() )
//            LockIndex( XB_UNLOCK );
#endif
      if(CompareKey(  Tkey, GetKeyData( i, CurNode ), Klen ) == 0)
        return XB_FOUND;
      else
        return XB_NOT_FOUND;
     }
   }
   CurNode->CurKeyNo = i;
   CurDbfRec = GetDbfNo( i, CurNode );
#ifdef XB_LOCKING_ON
//   if( dbf->GetAutoLock() )
//      LockIndex( XB_UNLOCK );
#endif
  if( RetrieveSw ) dbf->GetRecord( CurDbfRec );
  return XB_NOT_FOUND;
}
/***********************************************************************/
//! Short description.
/*!
*/
xbShort xbNtx::CalcKeyLen( void )
{
  xbShort rc;
  xbExpNode * TempNode;
  char FieldName[11];
  char Type;
  TempNode = IxExp->GetFirstTreeNode();

  if( !TempNode ) return 0;
  if( TempNode->Type == 'd' ) return TempNode->ResultLen;
  if( TempNode->Type == 'D' ) {
    memset( FieldName, 0x00, 11 );
    memcpy( FieldName, TempNode->NodeText, TempNode->Len );
    Type = dbf->GetFieldType( dbf->GetFieldNo( FieldName ));
    if( Type == 'N' || Type == 'F' )
      return TempNode->ResultLen;
  }

  if(( rc = IxExp->ProcessExpression()) != XB_NO_ERROR )
    return 0;

  TempNode = (xbExpNode *) IxExp->Pop();
  if( !TempNode ) return 0;
  rc = TempNode->DataLen;

//   if( !TempNode->InTree ) dbf->xbase->FreeExpNode( TempNode );
  if( !TempNode->InTree ) delete TempNode;
  return rc;
}
/***********************************************************************/
//! Short description.
/*!
  \param IxName
  \param Exp
  \param Unique
  \param Overlay
*/
xbShort xbNtx::CreateIndex(const char * IxName, const char * Exp, xbShort Unique, xbShort Overlay )
{
  xbShort i, KeyLen, rc;

  if( IsOpen()) CloseIndex();
  if( strlen( Exp ) > 255 ) return XB_INVALID_KEY_EXPRESSION;
  if( dbf->GetDbfStatus() == 0 ) return XB_NOT_OPEN;

  /* Get the index file name and store it in the class */
  SetFileName(IxName);

  /* check if the file already exists */
  if(((indexfp = fopen( GetFileName(), "r" )) != NULL ) && !Overlay ){
    fclose( indexfp );
    return XB_FILE_EXISTS;
  }
  else if( indexfp ) fclose( indexfp );

  if(( indexfp = fopen( GetFileName(), "w+b" )) == NULL ){
    return XB_OPEN_ERROR;
  }

#ifdef XB_LOCKING_ON   
   /*
   **  Must turn off buffering when multiple programs may be accessing
   **  index files.
   */
  setbuf( indexfp, NULL );
#endif   

#ifdef XB_LOCKING_ON
//   if( dbf->GetAutoLock() )
//      if((rc = LockIndex( XB_LOCK )) != 0)
//         return rc;
#endif

  /* parse the expression */
  IxExp = new xbExpn( dbf->xbase );
  if(( rc = IxExp->BuildExpressionTree( Exp, strlen( Exp ), dbf )) != XB_NO_ERROR )
    return rc;
    
//  ExpressionTree = dbf->xbase->GetTree();
//  dbf->xbase->SetTreeToNull(); 

  /* build the header record */
  memset( &HeadNode, 0x00, sizeof( NtxHeadNode ));
  HeadNode.Signature = 0x6;   // Clipper 5.x
  HeadNode.Version = 1;
  HeadNode.StartNode  = 1024L;
  KeyLen = CalcKeyLen();

  // TODO
  // What is the Clipper key length limit?
  if( KeyLen == 0 || KeyLen > 100 ){       /* 100 byte key length limit */

#ifdef XB_LOCKING_ON
//      if( dbf->GetAutoLock() )
//         LockIndex( XB_UNLOCK );
#endif
    return XB_INVALID_KEY;
  } else { 
    HeadNode.KeyLen = KeyLen; 
  }

  // This is not the algorithm that Clipper uses. I cant figure out
  // what they use from looking at the examples.
  // This is correct tho.
  HeadNode.KeysPerNode = (xbUShort)
    (( XB_NTX_NODE_SIZE - (2 * sizeof( xbUShort ))) / (HeadNode.KeyLen + 10 )) - 1;
  if( HeadNode.KeysPerNode % 2 )
      HeadNode.KeysPerNode--;

  HeadNode.HalfKeysPerNode = (xbUShort) HeadNode.KeysPerNode / 2;
  HeadNode.KeySize = HeadNode.KeyLen + 8;
//   while(( HeadNode.KeySize % 4 ) != 0 ) HeadNode.KeySize++;  /* multiple of 4*/
  HeadNode.Unique = Unique;
  strncpy( HeadNode.KeyExpression, Exp, 255 );

  rc=AllocKeyBufs();
  if(rc) {
    fclose(indexfp);
    return rc;     
  };

  if(( rc = PutHeadNode( &HeadNode, indexfp, 0 )) != 0 ){
#ifdef XB_LOCKING_ON
//      if( dbf->GetAutoLock() )
//         LockIndex( XB_UNLOCK );
#endif
    return rc;
  }
  /* write node #1 all 0x00 */
  for( i = 0; i < XB_NTX_NODE_SIZE; i++ ){
    if(( fwrite( "\x00", 1, 1, indexfp )) != 1 ){
#ifdef XB_LOCKING_ON
//         if( dbf->GetAutoLock() )
//            LockIndex( XB_UNLOCK );
#endif
      fclose( indexfp );
        return XB_WRITE_ERROR;
    }
  }

  if((rc = GetLeafNode(HeadNode.StartNode, 1)) != 0){
#ifdef XB_LOCKING_ON
//    if( dbf->GetAutoLock() )
//       LockIndex( XB_UNLOCK );
#endif
    return rc;
  }
   
  for( i = 0; i < HeadNode.KeysPerNode + 1; i++ )
    CurNode->offsets[i] = (i * HeadNode.KeySize) +
       2 + (2 * (HeadNode.KeysPerNode + 1));

  if((rc = PutLeafNode(HeadNode.StartNode, CurNode )) != 0){
#ifdef XB_LOCKING_ON
//      if( dbf->GetAutoLock() )
//         LockIndex( XB_UNLOCK );
#endif
    return rc;
  }

#ifdef XB_LOCKING_ON
//   if( dbf->GetAutoLock() )
//      LockIndex( XB_UNLOCK );
#endif
  return dbf->AddIndexToIxList( index, GetFileName());  
}
/***********************************************************************/
//! Short description.
/*!
  \param RecNo
  \param n
  \param NodeNo
*/
xbShort xbNtx::PutLeftNodeNo( xbShort RecNo, xbNodeLink *n, xbLong NodeNo )
{
  /* This routine sets n node's leftnode number */
  NtxLeafNode *temp;
  char *p;
  xbUShort itemOffset;
  if( !n ) return XB_INVALID_NODELINK;
  temp = &n->Leaf;
  if( RecNo < 0 || RecNo > HeadNode.KeysPerNode)
    return XB_INVALID_KEY;
  p = temp->KeyRecs;
  itemOffset = GetItemOffset(RecNo, n, 1);
  p += itemOffset;
  dbf->xbase->PutLong( p, NodeNo );
  return XB_NO_ERROR;
}
/***********************************************************************/
//! Short description.
/*!
  \param RecNo
  \param n
  \param DbfNo
*/
xbShort xbNtx::PutDbfNo( xbShort RecNo, xbNodeLink *n, xbLong DbfNo )
{
  /* This routine sets n node's dbf number */

  NtxLeafNode *temp;
  char *p;
  xbUShort itemOffset;
  if( !n ) return XB_INVALID_NODELINK;
  temp = &n->Leaf;
  if( RecNo < 0 || RecNo > (HeadNode.KeysPerNode))
    return XB_INVALID_KEY;
  itemOffset = GetItemOffset(RecNo, n, 1);
  p = temp->KeyRecs;
  p += itemOffset;
  p += 4;
  dbf->xbase->PutLong( p, DbfNo );
  return XB_NO_ERROR;
}
/************************************************************************/
//! Short description.
/*!
  \param l
  \param n
*/
xbShort xbNtx::PutLeafNode( xbLong l, xbNodeLink *n )
{
  NtxLeafNode *temp;
  char *p;

  if(( _fseek( indexfp, (xbOffT)l , SEEK_SET )) != 0 ){
    fclose( indexfp );
    return XB_SEEK_ERROR;
  }

  temp = &n->Leaf;
  p = temp->KeyRecs;
  dbf->xbase->PutShort( p, temp->NoOfKeysThisNode );

  // The offsets at the head of each leaf are not necessarly in order.
  p += 2;
  for( int i = 0; i < HeadNode.KeysPerNode + 1; i++){
    dbf->xbase->PutShort( p, n->offsets[i] );
    p += 2;
  }
   
  if(( fwrite( &n->Leaf.KeyRecs, XB_NTX_NODE_SIZE, 1, indexfp )) != 1 ){
    fclose( indexfp );
    return XB_WRITE_ERROR;
  }

  PutHeadNode(&HeadNode, indexfp, 1);
  return 0;   
}
/************************************************************************/
//! Short description.
/*!
  \param Head
  \param f
  \param UpdateOnly
*/
xbShort xbNtx::PutHeadNode( NtxHeadNode * Head, FILE * f, xbShort UpdateOnly )
{
  char buf[4];
  char *p;
 
  if(( _fseek( f, 0L, SEEK_SET )) != 0 ){
    fclose( f );
    return XB_SEEK_ERROR;
  }

  memset( buf, 0x00, 2 );
  dbf->xbase->PutUShort( buf, Head->Signature );
  if(( fwrite( &buf, 2, 1, f )) != 1 ){
    fclose( f );
    return XB_WRITE_ERROR;
  }

  memset( buf, 0x00, 2 );
  dbf->xbase->PutUShort( buf, Head->Version );
  if(( fwrite( &buf, 2, 1, f )) != 1 ){
    fclose( f );
    return XB_WRITE_ERROR;
  }

  memset( buf, 0x00, 4 );
  dbf->xbase->PutULong( buf, Head->StartNode );
  if(( fwrite( &buf, 4, 1, f )) != 1 ){
    fclose( f );
    return XB_WRITE_ERROR;
  }
    
  memset( buf, 0x00, 4 );
  dbf->xbase->PutULong( buf, Head->UnusedOffset );
  if(( fwrite( &buf, 4, 1, f )) != 1 ){
    fclose( f );
    return XB_WRITE_ERROR;
  }

  if( UpdateOnly ){
    fflush(indexfp);
    return XB_NO_ERROR;
  }

  memset( buf, 0x00, 2 );
  dbf->xbase->PutUShort( buf, Head->KeySize );
  if(( fwrite( &buf, 2, 1, f )) != 1 ){
    fclose( f );
    return XB_WRITE_ERROR;
  }

  memset( buf, 0x00, 2 );
  dbf->xbase->PutUShort( buf, Head->KeyLen );
  if(( fwrite( &buf, 2, 1, f )) != 1 ){
    fclose( f );
    return XB_WRITE_ERROR;
  }

  memset( buf, 0x00, 2 );
  dbf->xbase->PutUShort( buf, Head->DecimalCount );
  if(( fwrite( &buf, 2, 1, f )) != 1 ){
    fclose( f );
    return XB_WRITE_ERROR;
  }

  memset( buf, 0x00, 2 );
  dbf->xbase->PutUShort( buf, Head->KeysPerNode );
  if(( fwrite( &buf, 2, 1, f )) != 1 ){
    fclose( f );
    return XB_WRITE_ERROR;
  }

  memset( buf, 0x00, 2 );
  dbf->xbase->PutUShort( buf, Head->HalfKeysPerNode );
  if(( fwrite( &buf, 2, 1, f )) != 1 ){
    fclose( f );
    return XB_WRITE_ERROR;
  }

  p = HeadNode.KeyExpression;
  while(*p){
    *p = tolower(*p);
    p++;
  }

  if(( fwrite( &Head->KeyExpression, 256, 1, f )) != 1 ){
    fclose( f );
    return XB_WRITE_ERROR;
  }
    
  memset( buf, 0x00, 1 );
  buf[0] = Head->Unique;
  if(( fwrite( &buf, 1, 1, f )) != 1 ){
    fclose( f );
    return XB_WRITE_ERROR;
  }

  if(( fwrite( &Head->NotUsed, 745, 1, f )) != 1 ){
    fclose( f );
    return XB_WRITE_ERROR;
  }
  return 0;
}

xbShort xbNtx::TouchIndex( void )
{
  xbShort rc;
  if (( rc = GetHeadNode()) != XB_NO_ERROR) return rc;
  HeadNode.Version++;
  if (( rc = PutHeadNode(&HeadNode, indexfp, 1)) != XB_NO_ERROR) return rc;
  return XB_NO_ERROR;
}
/************************************************************************/
//! Short description.
/*!
  \param RecNo
  \param n
*/
xbShort xbNtx::PutKeyData( xbShort RecNo, xbNodeLink *n )
{
  /* This routine copies the KeyBuf data into xbNodeLink n */
  NtxLeafNode *temp;
  char *p;
  xbShort i;
  xbUShort itemOffset;
  if( !n ) return XB_INVALID_NODELINK;
  temp = &n->Leaf;
  if( RecNo < 0 || RecNo > (HeadNode.KeysPerNode))
    return XB_INVALID_KEY;
  itemOffset = GetItemOffset(RecNo, n, 1);
  p = temp->KeyRecs;
  p += itemOffset;
  p += 8;
  for( i = 0; i < HeadNode.KeyLen; i++ ){
    *p = KeyBuf[i];
    p++;
  }
  return XB_NO_ERROR;
}
/************************************************************************/
//! Short description.
/*!
  \param n
  \param pos
  \param d
  \param l
  \param w
*/
xbShort xbNtx::PutKeyInNode( xbNodeLink * n, xbShort pos, xbLong d, xbLong l, xbShort w )
{
  /* check the node */
  if( !n ) return XB_INVALID_NODELINK;
  if( pos < 0 || pos > HeadNode.KeysPerNode ) return XB_INVALID_RECORD;
  if( n->Leaf.NoOfKeysThisNode >= HeadNode.KeysPerNode ) return XB_NODE_FULL;

  InsertKeyOffset(pos, n);
  PutKeyData( pos, n );
  PutDbfNo( pos, n, d );
  PutLeftNodeNo( pos, n, l );
  n->Leaf.NoOfKeysThisNode++;
  if( w )
    return PutLeafNode( n->NodeNo, n );
  else
    return 0;
}
/************************************************************************/
//! Short description.
/*!
  \param n1
  \param n2
  \param pos
  \param d
*/
xbShort xbNtx::SplitLeafNode( xbNodeLink *n1, xbNodeLink *n2, xbShort pos, xbLong d )
{
  xbShort i,j,rc;
  xbShort temp;
  xbShort start;
  xbShort end;
//   xbShort length;

  if( !n1 || !n2 ) return XB_INVALID_NODELINK;
  if( pos < 0 || pos > HeadNode.KeysPerNode ) return XB_INVALID_RECORD;

//   length = strlen(KeyBuf);

  // If the new key goes in the first node.
  if( pos < HeadNode.HalfKeysPerNode ){
    // Setup key to insert into parent
    memcpy(PushItem.Key,
    GetKeyData(HeadNode.HalfKeysPerNode -1, n1), HeadNode.KeyLen);
    PushItem.RecordNumber = GetDbfNo(HeadNode.HalfKeysPerNode -1, n1);
    PushItem.Node =  0L; 
    start = pos;
    end = HeadNode.HalfKeysPerNode - 1;
    temp  = n1->offsets[end];
    for( i = end; i > start; i--)
      n1->offsets[i] = n1->offsets[i-1];
       
    n1->offsets[start] = temp;
    // Insert new key
    PutKeyData( start , n1 );
    PutDbfNo  ( start , n1, d );
  } else {
    // If the passed-in key IS median key, just copy it.
    if( pos == HeadNode.HalfKeysPerNode ){
      memcpy(PushItem.Key, KeyBuf, HeadNode.KeyLen);
      PushItem.RecordNumber = d;
      start = pos;
      end = pos;
    } else {
      // Otherwise, the median key will be middle key because the
      // new key will be inserted somewhere above the middle.
      memcpy( PushItem.Key,
              GetKeyData(HeadNode.HalfKeysPerNode,  n1),
              HeadNode.KeyLen);
      PushItem.RecordNumber = GetDbfNo(HeadNode.HalfKeysPerNode, n1);
      start = HeadNode.HalfKeysPerNode ;
      end = pos -1;
    }
    temp  = n1->offsets[start];
    for( i = start; i < end; i++)
      n1->offsets[i] = n1->offsets[i+1];

    n1->offsets[end] = temp;

    // Insert new key
    PutKeyData( pos -1 , n1 );
    PutDbfNo  ( pos -1 , n1, d );
  }
   
  // Dup the node data
  memcpy(n2->Leaf.KeyRecs, n1->Leaf.KeyRecs, XB_NTX_NODE_SIZE);

  // Dup the offsets
  for( i = 0; i < HeadNode.KeysPerNode +1; i++)
    n2->offsets[i] = n1->offsets[i];
  
  // Setup the second node
  for(j=0, i=HeadNode.HalfKeysPerNode; i < HeadNode.KeysPerNode; i++, j++ ){
    temp = n2->offsets[j];
    n2->offsets[j] = n2->offsets[i];
    n2->offsets[i] = temp;
  }

  // Get the last offset for both nodes
  temp = n2->offsets[j];
  n2->offsets[j] = n2->offsets[HeadNode.KeysPerNode];
  n2->offsets[HeadNode.KeysPerNode] = temp;

  // Set the new count of both nodes
  n2->Leaf.NoOfKeysThisNode = HeadNode.HalfKeysPerNode;
  n1->Leaf.NoOfKeysThisNode = HeadNode.HalfKeysPerNode;

  if(( rc = PutLeafNode( n1->NodeNo, n1 )) != 0 )
    return rc;
  if(( rc = PutLeafNode( n2->NodeNo, n2 )) != 0 )
    return rc;
  return 0;
}
/************************************************************************/
//! Short description.
/*!
  \param n1
  \param n2
  \param
*/
xbShort xbNtx::SplitINode( xbNodeLink *n1, xbNodeLink *n2, xbLong )
                   /* parent, tempnode, tempnodeno */
{
  xbShort i,j,rc;
  xbShort temp;
  xbShort pos = n1->CurKeyNo;
  xbShort start;
  xbShort end;
  xbLong n1LastNodeNo = 0;

  NtxItem oldPushItem;
  oldPushItem.Node = PushItem.Node;
  oldPushItem.RecordNumber = PushItem.RecordNumber;
  memcpy(oldPushItem.Key, PushItem.Key, sizeof(PushItem.Key));
   
  //   n2->NodeNo = HeadNode.TotalNodes++;
  n2->NodeNo = GetNextNodeNo();

  // If the new key goes in the first node.
  if( pos < HeadNode.HalfKeysPerNode ){
    // Setup key to insert into parent
    memcpy(PushItem.Key,
    GetKeyData(HeadNode.HalfKeysPerNode -1, n1), HeadNode.KeyLen);
    PushItem.RecordNumber = GetDbfNo(HeadNode.HalfKeysPerNode -1, n1);
    PushItem.Node =  n2->NodeNo;
    n1LastNodeNo = GetLeftNodeNo(HeadNode.HalfKeysPerNode -1, n1);
    start = pos;
    end = HeadNode.HalfKeysPerNode - 1;

    // Insert the new key.
    temp  = n1->offsets[end];
    for( i = end; i > start; i--)
      n1->offsets[i] = n1->offsets[i-1];
    n1->offsets[start] = temp;
  } else {
    // If the passed-in key IS median key, just copy it.
    if( pos == HeadNode.HalfKeysPerNode ){
      PutLeftNodeNo(0, n2, oldPushItem.Node);
      // PushItem should remain the same, except for its left pointer
      PushItem.Node = n2->NodeNo;
//           start = pos;    unused code
//           end = pos;      unused code
    } else {
      // Otherwise, the median key will be middle key becasue the
      // new key will be inserted somewhere above the middle.
      memcpy( PushItem.Key, 
              GetKeyData(HeadNode.HalfKeysPerNode,  n1),
              HeadNode.KeyLen);
      PushItem.RecordNumber = GetDbfNo(HeadNode.HalfKeysPerNode, n1);
      PushItem.Node = n2->NodeNo;
      n1LastNodeNo = GetLeftNodeNo(HeadNode.HalfKeysPerNode, n1);
           
//    start = HeadNode.HalfKeysPerNode + 1;
      start = HeadNode.HalfKeysPerNode;
      end = pos -1;

      // Insert the new key.
      temp  = n1->offsets[start];
      for( i = start; i < end; i++)
        n1->offsets[i] = n1->offsets[i+1];
      n1->offsets[end] = temp;
      pos--;
    }
  }

  /* restore original key */
  memcpy( KeyBuf, oldPushItem.Key, HeadNode.KeyLen + 1);

  // Insert new key
  PutKeyData( pos, n1 );
  PutDbfNo  ( pos, n1, oldPushItem.RecordNumber);
  PutLeftNodeNo( pos, n1, GetLeftNodeNo (pos + 1, n1));
  PutLeftNodeNo( pos + 1  /* +1 ?*/, n1, oldPushItem.Node /* t */ );
   
  // Dup the node data into the new page
  memcpy(n2->Leaf.KeyRecs, n1->Leaf.KeyRecs, XB_NTX_NODE_SIZE);

  // Dup the offsets
  for( i = 0; i < HeadNode.KeysPerNode +1; i++){
    n2->offsets[i] = n1->offsets[i];
  }

  // Setup the second node
  for( j = 0, i = HeadNode.HalfKeysPerNode; i<HeadNode.KeysPerNode; i++,j++ ){
    temp = n2->offsets[j];
    n2->offsets[j] = n2->offsets[i];
    n2->offsets[i] = temp;
  }

  // Get the last offset for both nodes
  temp = n2->offsets[j];
  n2->offsets[j] = n2->offsets[HeadNode.KeysPerNode];
  n2->offsets[HeadNode.KeysPerNode] = temp;
  PutLeftNodeNo(HeadNode.HalfKeysPerNode, n1, n1LastNodeNo);

  // Set the new count of both nodes
  n2->Leaf.NoOfKeysThisNode = HeadNode.HalfKeysPerNode;
  n1->Leaf.NoOfKeysThisNode = HeadNode.HalfKeysPerNode;

  if((rc = PutLeafNode( n1->NodeNo,n1 )) != 0) return rc;
  if((rc = PutLeafNode( n2->NodeNo,n2 )) != 0) return rc;
  return 0;
}
/************************************************************************/
//! Short description.
/*!
  \param RecBufSw
  \param KeyBufSw
*/
xbShort xbNtx::CreateKey( xbShort RecBufSw, xbShort KeyBufSw )
{ 
  /* RecBufSw   0   Use RecBuf    */
  /*            1   Use RecBuf2   */
  /* KeyBufSw   0   Use KeyBuf    */
  /*            1   Use KeyBuf2   */

  xbShort rc;
  xbExpNode * TempNode;

  if(( rc = IxExp->ProcessExpression( RecBufSw )) != XB_NO_ERROR )
    return rc;
  TempNode = (xbExpNode *) IxExp->Pop();
  if( !TempNode ) return XB_INVALID_KEY;

  if( KeyBufSw ){
    memset( KeyBuf2, 0x00, HeadNode.KeyLen + 1 );
    memcpy( KeyBuf2, TempNode->StringResult, XB_MIN(HeadNode.KeyLen + 1, TempNode->DataLen) );
  } else {
    memset( KeyBuf, 0x00, HeadNode.KeyLen + 1 );
    memcpy( KeyBuf, TempNode->StringResult, XB_MIN(HeadNode.KeyLen + 1, TempNode->DataLen) );
  }
//   if( !TempNode->InTree ) dbf->xbase->FreeExpNode( TempNode );
  if( !TempNode->InTree ) delete TempNode;
  return 0;
}
/************************************************************************/
//! Short description.
/*!
  \param key
*/
xbShort
xbNtx::GetCurrentKey(char *key)
{
  CreateKey(0, 0);
  memcpy(key, KeyBuf, HeadNode.KeyLen + 1);  
  return 0;
}
/************************************************************************/
//! Short description.
/*!
  \param DbfRec
*/
xbShort xbNtx::AddKey( xbLong DbfRec )
{
 /* This routine assumes KeyBuf contains the contents of the index to key */

  xbShort i,rc;
  xbNodeLink * TempNode;   
  xbNodeLink * Tparent;
  xbLong TempNodeNo;          /* new, unattached leaf node no */

  /* find node key belongs in */
  rc = FindKey( KeyBuf, HeadNode.KeyLen, 0 );
  if( rc == XB_FOUND && HeadNode.Unique )
    return XB_KEY_NOT_UNIQUE;

  /************************************************/
  /* section A - if room in node, add key to node */
  /************************************************/

  if( CurNode->Leaf.NoOfKeysThisNode < HeadNode.KeysPerNode ){
    if(( rc = PutKeyInNode( CurNode,CurNode->CurKeyNo,DbfRec,0L,1)) != 0)
      return rc;
    if(( rc = PutHeadNode( &HeadNode, indexfp, 1 )) != 0)
      return rc;
    return XB_NO_ERROR;
  }   

  /***********************************************************************/
  /* section B - split leaf node if full and put key in correct position */
  /***********************************************************************/

  TempNode = GetNodeMemory();
  // Create a new page
  TempNode->NodeNo = GetNextNodeNo();

  rc = SplitLeafNode( CurNode, TempNode, CurNode->CurKeyNo, DbfRec );
  if( rc ) return rc;

  /* TempNode is on disk, now we have to point someone above to
     that node. Keep the NodeNo of the on disk new node.
  */
  TempNodeNo = TempNode->NodeNo;
  ReleaseNodeMemory( TempNode );

  /*
    PushItem also contains the key to put into the parent
    PushItem should point at TempNode
   */
  PushItem.Node = TempNodeNo;

  /*****************************************************/
  /* section C go up tree splitting nodes as necessary */
  /*****************************************************/

  Tparent = CurNode->PrevNode;

  while( Tparent && Tparent->Leaf.NoOfKeysThisNode >= HeadNode.KeysPerNode ){
    TempNode = GetNodeMemory();
    if( !TempNode )
      return XB_NO_MEMORY;

    rc = SplitINode( Tparent, TempNode, TempNodeNo );
    if( rc ) return rc;
    TempNodeNo = TempNode->NodeNo;
    ReleaseNodeMemory( TempNode );
    ReleaseNodeMemory( CurNode );
    CurNode = Tparent;
    CurNode->NextNode = NULL;
    Tparent = CurNode->PrevNode;
  }

  /************************************************************/
  /* Section D  if CurNode is split root, create new root     */
  /************************************************************/

  /* at this point
      CurNode = The node that was just split
      TempNodeNo = The new node split off from CurNode */

  if(CurNode->NodeNo == HeadNode.StartNode ){
    TempNode = GetNodeMemory();
    if( !TempNode )
      return XB_NO_MEMORY;

    memcpy( KeyBuf, PushItem.Key, HeadNode.KeyLen );
    PutKeyData( 0, TempNode );
    PutDbfNo ( 0, TempNode, PushItem.RecordNumber );
    PutLeftNodeNo( 0, TempNode, CurNode->NodeNo );
    PutLeftNodeNo( 1, TempNode, PushItem.Node );
    TempNode->NodeNo = GetNextNodeNo();
    TempNode->Leaf.NoOfKeysThisNode++;
    HeadNode.StartNode = TempNode->NodeNo;
    rc = PutLeafNode( TempNode->NodeNo, TempNode );
    if( rc ) return rc;
    rc = PutHeadNode( &HeadNode, indexfp, 1 );
    if( rc ) return rc;
    ReleaseNodeMemory( TempNode );
    return XB_NO_ERROR;
  }
  /**********************************/
  /* Section E  make room in parent */
  /**********************************/
  InsertKeyOffset(Tparent->CurKeyNo, Tparent);
  
  /* put key in parent */

  i = Tparent->CurKeyNo;
  memcpy( KeyBuf, PushItem.Key, HeadNode.KeyLen);
  PutKeyData( i, Tparent );

  PutDbfNo( i, Tparent, PushItem.RecordNumber);
  PutLeftNodeNo( i , Tparent, CurNode->NodeNo );
  PutLeftNodeNo( i + 1 , Tparent, TempNodeNo );
  Tparent->Leaf.NoOfKeysThisNode++;
   
  rc = PutLeafNode( Tparent->NodeNo, Tparent );
  if( rc ) return rc;
  rc = PutHeadNode( &HeadNode, indexfp, 1 );
  if( rc ) return rc;

  return XB_NO_ERROR;
}
/***********************************************************************/
//! Short description.
/*!
  \param n
*/
xbShort xbNtx::UpdateParentKey( xbNodeLink * n )
{
/* this routine goes backwards thru the node chain looking for a parent
   node to update */

  xbNodeLink * TempNode;
   
  if( !n ) return XB_INVALID_NODELINK;
  if( !GetDbfNo( 0, n )){
    std::cout << "Fatal index error - Not a leaf node" << n->NodeNo << std::endl;
//  exit(0);
    return XB_NOT_LEAFNODE;
  }
  TempNode = n->PrevNode;

  while( TempNode ){
    if( TempNode->CurKeyNo < TempNode->Leaf.NoOfKeysThisNode ){
      memcpy(KeyBuf,GetKeyData(n->Leaf.NoOfKeysThisNode-1,n),HeadNode.KeyLen);
      PutKeyData( TempNode->CurKeyNo, TempNode );
      return PutLeafNode( TempNode->NodeNo, TempNode );
    }
    TempNode = TempNode->PrevNode;
  }
  return XB_NO_ERROR;
}
/***********************************************************************/
//! Short description.
/*!
  \param n
*/
/* This routine queues up a list of nodes which have been emptied      */
void xbNtx::UpdateDeleteList( xbNodeLink *n )
{
  n->NextNode = DeleteChain;
  DeleteChain = n;
}
/***********************************************************************/
//! Short description.
/*!
*/
/* Delete nodes from the node list - for now we leave the empty nodes  */
/* dangling in the file. Eventually we will remove nodes from the file */

void xbNtx::ProcessDeleteList( void )
{
  if( DeleteChain ){
    ReleaseNodeMemory( DeleteChain );
    DeleteChain = NULL;
  }
}
/***********************************************************************/
//! Short description.
/*!
*/
xbShort xbNtx::KeyWasChanged( void )
{
  CreateKey( 0, 0 );            /* use KeyBuf,  RecBuf    */
  CreateKey( 1, 1 );            /* use KeyBuf2, RecBuf2   */
  if( CompareKey( KeyBuf, KeyBuf2, HeadNode.KeyLen ) != 0 )
    return 1;
  else
    return 0;
}
/***********************************************************************/
//! Short description.
/*!
  \param DbfRec
*/
xbShort xbNtx::DeleteKey( xbLong DbfRec )
{
/* this routine assumes the key to be deleted is in KeyBuf */

  xbShort rc;

  // FindKey will set CurNodeNo on evey page down to the
  // key being deleted. This is important. Plus we
  // need to be able to find the key to delete it.
  CurNode = NULL;
  if(( rc = FindKey( KeyBuf, DbfRec )) != XB_FOUND )
    return rc;

  // Then delete it
  // next sentence modified 8/20/03 - gkunkel
  if(( rc = DeleteKeyFromNode( CurNode->CurKeyNo, CurNode )) != XB_NO_ERROR )
    return rc;
   
  CurDbfRec = GetDbfNo( CurNode->CurKeyNo, CurNode );
  if(( rc = PutHeadNode( &HeadNode, indexfp, 1 )) != 0 )
    return rc;
  return XB_NO_ERROR;
}

//! Short description.
/*!
  \param pos
  \param n
*/
xbShort
xbNtx::DeleteKeyFromNode(xbShort pos, xbNodeLink *n )
{
  xbNodeLink *TempNode;
  xbShort rc;
    
  // Check to see if this is an inode
  if( GetLeftNodeNo( 0 , n ) != 0 ){
    // Copy the rightmost key from the left node.
    TempNode = n;
    GetLeafNode ( GetLeftNodeNo (n->CurKeyNo, n), 1);
    while(( rc = GetLeftNodeNo( 0, CurNode )) != 0 )
      GetLeafNode ( GetLeftNodeNo (CurNode->Leaf.NoOfKeysThisNode, CurNode), 1);
      
    // Get the key Data
    strcpy (KeyBuf , GetKeyData( CurNode->Leaf.NoOfKeysThisNode -1, CurNode));
    PutKeyData( pos, TempNode );

    // Get the xbDbf no
    PutDbfNo (pos, TempNode, GetDbfNo( CurNode->Leaf.NoOfKeysThisNode -1, CurNode) );

    // We don't change the LeftNodeNo. determined later
    // Write the changed node
    PutLeafNode( TempNode->NodeNo, TempNode );

    // Now delete the key from the child
    TempNode = CurNode;

    if((rc = PutLeafNode( n->NodeNo,n )) != 0) return rc;       
  
    return DeleteKeyFromNode( TempNode->Leaf.NoOfKeysThisNode -1, TempNode);
  } else {
    return RemoveKeyFromNode(pos, n);
  }
}

//! Short description.
/*!
  \param pos
  \param n
*/
xbShort xbNtx::RemoveKeyFromNode( xbShort pos, xbNodeLink *n )
{
  xbNodeLink *TempNode;
  xbNodeLink *sibling;
  xbNodeLink *parent;
  xbShort rc;
  xbLong newHeadNode = 0;
  xbBool harvest = false;
 
  // Here we are a leaf node..

  if( n->NodeNo == HeadNode.StartNode && n->Leaf.NoOfKeysThisNode == 1)
    // we are about to delete the last node from the head node.
    newHeadNode = GetLeftNodeNo( 0 , n );
  
  // Remove the key from the current node.
  DeleteKeyOffset(pos, n);
  n->Leaf.NoOfKeysThisNode--;
   
  // Check to see if the number of keys left is less then
  // 1/2 KeysPerNode
  if( ! ( n->NodeNo == HeadNode.StartNode )
       && n->Leaf.NoOfKeysThisNode < HeadNode.HalfKeysPerNode){
    // This observed clipper behavior.
    // If less then 1/2 keys per node, then merge with right sibling.
    // If no right sibling, merge with left sibling.
    parent = n->PrevNode;

    // If the parents cur key is the last key, then take the left node
    if( parent->CurKeyNo == parent->Leaf.NoOfKeysThisNode ){
      TempNode = CurNode;
      GetLeafNode( GetLeftNodeNo(parent->CurKeyNo -1, parent), 2 );
      sibling = CurNode;
      CurNode = TempNode;

      rc = JoinSiblings(parent, parent->CurKeyNo -1, sibling, n);
            
      // Harvest the empty node, if necessary Clipper keeps the old key
      // count on the node, to we can't set it to 0
      if( rc == XB_HARVEST_NODE )
         harvest = true;

      if((rc = PutLeafNode( n->NodeNo,n )) != 0) return rc;
      if((rc = PutLeafNode( sibling->NodeNo,sibling )) != 0) return rc;
      if((rc = PutLeafNode( parent->NodeNo,parent )) != 0) return rc;
            
      if(harvest){
        HeadNode.UnusedOffset = n->NodeNo;
        // Save the empty xbNodeLink
        // ReleaseNodeMemory(n);
        // We may have to delete a node from the parent
        return RemoveKeyFromNode( parent->CurKeyNo, parent);
      }
    } else {
      // Take the right node
      TempNode = CurNode;
      GetLeafNode( GetLeftNodeNo(parent->CurKeyNo + 1, parent), 2 );
      sibling = CurNode;
      CurNode = TempNode;
            
      rc = JoinSiblings(parent, parent->CurKeyNo, n, sibling);
      // Harvest the empty node, if necessary Clipper keeps the old key
      // count on the node, to we can't set it to 0
      if( rc == XB_HARVEST_NODE )
        harvest = true;

      if((rc = PutLeafNode( n->NodeNo,n )) != 0) return rc;
      if((rc = PutLeafNode( sibling->NodeNo,sibling )) != 0) return rc;
      if((rc = PutLeafNode( parent->NodeNo,parent )) != 0) return rc;
            
      if( harvest ){
        HeadNode.UnusedOffset = sibling->NodeNo;
        // Save the empty xbNodeLink
        ReleaseNodeMemory( sibling );

        // Now the parents->CurKeyNo+1 left pointer is empty, and
        // we are about to delete the parent. So move the left node no
        // from the parents->CurKeyNo+1 to the parent->CurNodeNo
        PutLeftNodeNo( parent->CurKeyNo +1 , parent,
                       GetLeftNodeNo( parent->CurKeyNo, parent ));
        // We may have to delete a node from the parent
        return RemoveKeyFromNode( parent->CurKeyNo, parent);  
      }
    }
  } else {
    if( n->NodeNo == HeadNode.StartNode && n->Leaf.NoOfKeysThisNode == 0 ){
      // we are about to delete the last node from the head node.
      HeadNode.UnusedOffset = HeadNode.StartNode;
      HeadNode.StartNode = newHeadNode;
    }

    if((rc = PutLeafNode( n->NodeNo,n )) != 0) return rc;
      // If more then 1/2 keys per node -> done.
      return XB_NO_ERROR;
  }
  return XB_NO_ERROR;
}

//! Short description.
/*!
  \param parent
  \param parentPos
  \param n1
  \param n2
*/
xbShort
xbNtx::JoinSiblings(xbNodeLink *parent, xbShort parentPos, xbNodeLink *n1, xbNodeLink* n2)
{
  // ASSUMES: keys in n1 are less then keys in n2
  //
  // Here, the contents of n1 need to be merged with n2. If n1 + parent_key
  // + n2 can all fit in n1, then leave n2 empty, and remove the key from the
  // parent.
  // Otherwise evenly distribute the keys from n1 and n2 over both, resetting
  // the parent.

  xbShort i, j;
  int totalKeys;
  int median;

  // if n1 has exactly (it will never have less) 1/2 keys per node
  // then put everything into n1.
  if((n1->Leaf.NoOfKeysThisNode + n2->Leaf.NoOfKeysThisNode + 1) <= HeadNode.KeysPerNode){
    int n1LastNodeNo = GetLeftNodeNo(n2->Leaf.NoOfKeysThisNode, n2);        
    // Bring down the parent
    strcpy(KeyBuf, GetKeyData( parentPos, parent ));
    PutKeyData( n1->Leaf.NoOfKeysThisNode , n1);
    PutDbfNo ( n1->Leaf.NoOfKeysThisNode, n1, GetDbfNo( parentPos, parent ) );
    n1->Leaf.NoOfKeysThisNode++;

    // Copy over the rest of the keys
    for(i = n1->Leaf.NoOfKeysThisNode, j = 0; j < n2->Leaf.NoOfKeysThisNode; i++, j++){
      strcpy(KeyBuf, GetKeyData( j, n2 ));
      PutKeyData( i, n1);
      PutLeftNodeNo( i, n1, GetLeftNodeNo( j, n2) );
      PutDbfNo ( i , n1, GetDbfNo( j, n2 ) );
    }
    n1->Leaf.NoOfKeysThisNode += j;
    PutLeftNodeNo(n1->Leaf.NoOfKeysThisNode, n1, n1LastNodeNo);

    // We need a way to signal that this node will be harvested.
    // Clipper keeps the KeyCount on harvested nodes, it does NOT
    // set them to 0.
    return XB_HARVEST_NODE;
  } else {
    // Distribute the keys evenly. Of off by one, the extra
    // goes to n1.

    // If n1 contains the greater than keys, then at this point we
    // know that n1 has more than 1/2MKPN. therefore we copy
    // over untill we get to  median. All the while removing
    // keys from n2. Then 
        
    totalKeys = n1->Leaf.NoOfKeysThisNode + n2->Leaf.NoOfKeysThisNode + 1;
    median = (int) totalKeys/2;

    // If n1 has more keys then n2, then we need to copy the last keys
    // of n1 to the beginning of n2.
    // Leave HalfKeysPerNode+1 keys in n1, then the last key will
    // be copied up to the parent.
    if( n1->Leaf.NoOfKeysThisNode > HeadNode.HalfKeysPerNode ){
      // Bring down the parent
      InsertKeyOffset(0, n2);
      strcpy(KeyBuf, GetKeyData( parentPos, parent ));
      PutKeyData( 0 , n2);
      PutDbfNo ( 0, n2, GetDbfNo( parentPos, parent ) );
      n2->Leaf.NoOfKeysThisNode++;
      PutLeftNodeNo(0, n2, GetLeftNodeNo(n1->Leaf.NoOfKeysThisNode, n1));
      for( i = n1->Leaf.NoOfKeysThisNode -1; i > median;  i-- ){
        // Put the key in n2
        InsertKeyOffset(0, n2);
        strcpy(KeyBuf, GetKeyData( i, n1 ));
        PutKeyData( 0, n2);
        PutLeftNodeNo( 0, n2, GetLeftNodeNo( i, n1) );
        PutDbfNo ( 0 , n2, GetDbfNo( i, n1 ) );

        // Remove the key from the current node.
        n1->Leaf.NoOfKeysThisNode--;
        n2->Leaf.NoOfKeysThisNode++;
      }
      // Copy up the last key from n1, that will become the new parent key.
      strcpy(KeyBuf, GetKeyData( n1->Leaf.NoOfKeysThisNode -1 , n1 ));
      PutKeyData( parentPos, parent);
      PutDbfNo ( parentPos , parent, GetDbfNo( n1->Leaf.NoOfKeysThisNode -1, n1) );
      n1->Leaf.NoOfKeysThisNode--;
    } else {
      xbLong n1LastLeftNodeNo;
      xbShort medianOffset = n2->Leaf.NoOfKeysThisNode - median -1;
      // Bring down the parent
      strcpy(KeyBuf, GetKeyData( parentPos, parent ));
      PutKeyData( n1->Leaf.NoOfKeysThisNode , n1);
      PutDbfNo ( n1->Leaf.NoOfKeysThisNode, n1, GetDbfNo( parentPos, parent ) );
      n1->Leaf.NoOfKeysThisNode++;

// 8/20/03 gkunkel n1LastLeftNodeNo = GetLeftNodeNo(medianOffset, n2);
      PutLeftNodeNo( n1->Leaf.NoOfKeysThisNode, n1, GetLeftNodeNo(medianOffset, n2));

      // Moving the median to the parent may have to occur
      // before moving the other keys to n1. This we would have
      // to calcualte the correct offset from the median
      // Copy up the first key from n2 (the median),
      // that will become the new parent key.
      strcpy(KeyBuf, GetKeyData( medianOffset, n2 ));
      PutKeyData( parentPos, parent);
      PutDbfNo ( parentPos , parent, GetDbfNo(medianOffset, n2 ) );
      n1LastLeftNodeNo = GetLeftNodeNo(medianOffset, n2);

// Still investigating the -1 thing with clipper, If anyone has clues, 
// please let me know - bob@synxis.com            
//             if ( n1->Leaf.NoOfKeysThisNode  >=  (median - 1))
//             {
//                 // Clipper, don't know why
//                 PutLeftNodeNo(0, n2 , -1 );
//                 std::cout << "Clipper hack" << std::endl;
//             }

      DeleteKeyOffset(medianOffset, n2);
      n2->Leaf.NoOfKeysThisNode--;

//            xbShort clipperMessedUpIndex = n1->Leaf.NoOfKeysThisNode;            
      for( i = n1->Leaf.NoOfKeysThisNode, j = 0; j < medianOffset; i++, j++ ){
        strcpy(KeyBuf, GetKeyData( 0, n2 ));
        PutKeyData( i, n1);
        PutLeftNodeNo( i, n1, GetLeftNodeNo( 0, n2) );
        PutDbfNo ( i , n1, GetDbfNo( 0, n2 ) );

//      if( i == clipperMessedUpIndex){
//        // Clipper, don't know why
//        PutLeftNodeNo(0, n2 , -1 );
//        std::cout << "Clipper hack in loop i = " <<  i << std::endl;
//      }
            
        // Remove the key from the current node.
        DeleteKeyOffset(0, n2);
        n2->Leaf.NoOfKeysThisNode--;
        n1->Leaf.NoOfKeysThisNode++;
      }
      PutLeftNodeNo(n1->Leaf.NoOfKeysThisNode, n1, n1LastLeftNodeNo);

    }
  }
  return XB_NO_ERROR;
}
/************************************************************************/
//! Short description.
/*!
  \param option
*/
#ifdef XBASE_DEBUG
xbShort xbNtx::CheckIndexIntegrity( const xbShort option )
{
  /* if option = 1, print out some stats */

  xbShort rc;
  xbLong ctr = 1L;

  if ( option ) std::cout << "Checking NTX " << GetFileName() << std::endl;
  rc = dbf->GetRecord( ctr );
  while( ctr < dbf->NoOfRecords() ){
    ctr++;
    if( option ) std::cout << "Checking Record " << ctr << std::endl;
    if( !dbf->RecordDeleted() ){
      CreateKey( 0, 0 );
      rc = FindKey( KeyBuf, dbf->GetCurRecNo());
      if( rc != XB_FOUND ){
        if( option ){
          std::cout << "Record number " << dbf->GetCurRecNo()
                    <<  " Not Found" << std::endl;
          std::cout << "Key = " << KeyBuf << std::endl;
        }
        return rc;
      }
    }
    if(( rc = dbf->GetRecord( ctr )) != XB_NO_ERROR )
      return rc;
  }

  if( option )
    std::cout << "Exiting with rc = " << rc << std::endl;
  return XB_NO_ERROR;
}
#endif
/***********************************************************************/
//! Short description.
/*!
  \param statusFunc
*/
xbShort xbNtx::ReIndex(void (*statusFunc)(xbLong itemNum, xbLong numItems))
{
  /* this method assumes the index has been locked in exclusive mode */
  xbLong l;
  xbShort rc, i, saveAutoLock;
  NtxHeadNode TempHead;
  FILE *t, *temp;
  xbString TempName;

  memcpy( &TempHead, &HeadNode, sizeof( struct NtxHeadNode ));
  TempHead.StartNode = 1024L;
  rc = dbf->xbase->DirectoryExistsInName( GetFileName() );
  if( rc ) {
    TempName.assign(GetFileName(), 0, rc);
    TempName += "TEMPFILE.NTX";
  } else
    TempName = "TEMPFILE.NTX";

  if(( t = fopen( TempName, "w+b" )) == NULL )
    return XB_OPEN_ERROR;

  if(( rc = PutHeadNode( &TempHead, t, 0 )) != 0 ){
    fclose( t );
    remove( TempName );
    return rc;
  }

  for( i = 0; i < XB_NTX_NODE_SIZE; i++ ){
    if(( fwrite( "\x00", 1, 1, t )) != 1 ){
      fclose( t );
      remove( TempName );
      return XB_WRITE_ERROR;
    }
  }

  temp = indexfp;
  indexfp = t;

  if(( rc = GetLeafNode(TempHead.StartNode, 1)) != 0 )
    return rc;

  for(i = 0; i < TempHead.KeysPerNode + 1; i++)
    CurNode->offsets[i] = (i * HeadNode.KeySize) +
           2 + (2 * (HeadNode.KeysPerNode + 1));
   
  HeadNode.StartNode = TempHead.StartNode;
  if((rc = PutLeafNode(TempHead.StartNode, CurNode )) != 0)
    return rc;

  indexfp = temp;

  if( fclose( indexfp ) != 0 )
    return XB_CLOSE_ERROR;

  if( fclose( t ) != 0 )
    return XB_CLOSE_ERROR;

  if( remove( GetFileName() ) != 0 )
    return XB_CLOSE_ERROR;

  if( rename( TempName,  GetFileName()) != 0 )
    return XB_WRITE_ERROR;

  if(( indexfp = fopen( GetFileName(), "r+b" )) == NULL )
    return XB_OPEN_ERROR;

  saveAutoLock = dbf->GetAutoLock();
  dbf->AutoLockOff();

  for( l = 1; l <= dbf->NoOfRecords(); l++ ){
    if(statusFunc)
      statusFunc(l, dbf->NoOfRecords());

    if(( rc = dbf->GetRecord(l)) != XB_NO_ERROR )
      return rc;

    if(!dbf->GetRealDelete() || !dbf->RecordDeleted()){
      /* Create the key */
      CreateKey( 0, 0 );

      /* add key to index */
      if(( rc = AddKey( l )) != XB_NO_ERROR )
        return rc;
    }
  }
  if(saveAutoLock)
     dbf->AutoLockOn();

  return XB_NO_ERROR;
}

//! Short description.
/*!
*/
xbLong
xbNtx::GetNextNodeNo()
{
  struct stat FileStat;
  int rc;
  xbULong FileSize;

  if( HeadNode.UnusedOffset != 0){
    FileSize = HeadNode.UnusedOffset;
    HeadNode.UnusedOffset = 0;
    PutHeadNode(&HeadNode, indexfp, 1);
    return FileSize;
  }

  rc = fstat(fileno(indexfp), &FileStat);
  if( rc != 0 )
    return 0;
   

  FileSize = (xbULong)FileStat.st_size;
  // File offset is zero based, so the file size will be the
  // offset of the next page.
  return FileSize;
}

//! Short description.
/*!
  \param buf
  \param len
*/
void xbNtx::GetExpression(char *buf, int len)
{
  memcpy(buf, HeadNode.KeyExpression, len < 256 ? len : 256);
}

const char* xbNtx::GetExtWithDot(bool lower)
{
  return lower? ".ntx": ".NTX";
}

xbUShort xbNtx::GetKeyLen()
{
  return HeadNode.KeyLen;
}

const char* xbNtx::GetKeyExpression()
{
  return HeadNode.KeyExpression;
}

void xbNtx::FreeNodesMemory()
{
  ReleaseNodeMemory(NodeChain, true);
  NodeChain = 0;
//  ReleaseNodeMemory(CloneChain, true);
//  CloneChain = 0;
  ReleaseNodeMemory(FreeNodeChain, true);
  FreeNodeChain = 0;
  ReleaseNodeMemory(DeleteChain, true);
  DeleteChain = 0;
}

#endif     /* XB_INDEX_NTX */
