summaryrefslogtreecommitdiff
path: root/xbase64/xbntx.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'xbase64/xbntx.cpp')
-rwxr-xr-xxbase64/xbntx.cpp2604
1 files changed, 2604 insertions, 0 deletions
diff --git a/xbase64/xbntx.cpp b/xbase64/xbntx.cpp
new file mode 100755
index 0000000..673aa68
--- /dev/null
+++ b/xbase64/xbntx.cpp
@@ -0,0 +1,2604 @@
+/* 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 */