summaryrefslogtreecommitdiff
path: root/src/core/xbdbf.cpp
diff options
context:
space:
mode:
authorJörg Frings-Fürst <debian@jff-webhosting.net>2022-12-07 13:17:14 +0100
committerJörg Frings-Fürst <debian@jff-webhosting.net>2022-12-07 13:17:14 +0100
commit4875a3dd9b183dcd2256e2abfc4ccf7484c233b4 (patch)
tree0abbea881ded030851014ffdd60fbf71fead8f65 /src/core/xbdbf.cpp
parentdaf17154bf13139d9375f48525d19d6aaba08155 (diff)
New upstream version 4.0.2upstream/4.0.2
Diffstat (limited to 'src/core/xbdbf.cpp')
-rwxr-xr-xsrc/core/xbdbf.cpp4220
1 files changed, 4220 insertions, 0 deletions
diff --git a/src/core/xbdbf.cpp b/src/core/xbdbf.cpp
new file mode 100755
index 0000000..8e020fb
--- /dev/null
+++ b/src/core/xbdbf.cpp
@@ -0,0 +1,4220 @@
+/* xbdbf.cpp
+
+XBase64 Software Library
+
+Copyright (c) 1997,2003,2014,2022 Gary A Kunkel
+
+The xb64 software library is covered under the terms of the GPL Version 3, 2007 license.
+
+Email Contact:
+
+ XDB-devel@lists.sourceforge.net
+ XDB-users@lists.sourceforge.net
+
+*/
+
+#include "xbase.h"
+
+
+namespace xb{
+
+/************************************************************************/
+//! @brief Constructor
+/*!
+ \param x Pointer to xbXbase
+*/
+xbDbf::xbDbf( xbXBase * x ) : xbFile( x ){
+ xbase = x;
+ SchemaPtr = NULL;
+ RecBuf = NULL;
+ RecBuf2 = NULL;
+ InitVars();
+}
+/************************************************************************/
+void xbDbf::InitVars()
+{
+ iNoOfFields = 0;
+ iDbfStatus = XB_CLOSED;
+ ulCurRec = 0L;
+ cVersion = 0x00;
+ cUpdateYY = 0x00;
+ cUpdateMM = 0x00;
+ cUpdateDD = 0x00;
+ ulNoOfRecs = 0L;
+ uiHeaderLen = 0x00;
+ uiRecordLen = 0x00;
+ cTransactionFlag = 0x00;
+ cEncryptionFlag = 0x00;
+ cIndexFlag = 0x00;
+ cLangDriver = 0x00;
+ iFileVersion = 0; /* Xbase64 file version */
+ iAutoCommit = -1;
+
+ SetFileName ( "" );
+ sAlias.Set ( "" );
+ SetDirectory ( GetDataDirectory());
+
+ #ifdef XB_LOCKING_SUPPORT
+ iLockFlavor = -1;
+ bTableLocked = xbFalse;
+ bHeaderLocked = xbFalse;
+ ulAppendLocked = 0;
+ SetAutoLock( -1 );
+ lloRecLocks.SetDupKeys( xbFalse );
+ #endif
+
+ #ifdef XB_INDEX_SUPPORT
+ ixList = NULL;
+ pCurIx = NULL;
+ vpCurIxTag = NULL;
+ sCurIxType = "";
+ ClearTagList();
+ #endif
+ #ifdef XB_NDXINF_SUPPORT
+ llNdxInfData.Clear();
+ #endif
+}
+
+/************************************************************************/
+//! @brief Destructor
+xbDbf::~xbDbf(){
+
+ // is there is an uncommited update, discard it.
+ // as we don't know if it is an append or an update
+ if( iDbfStatus == XB_UPDATED )
+ Abort();
+
+ if( iDbfStatus != XB_CLOSED )
+ Close();
+
+ if( SchemaPtr ){
+ free( SchemaPtr );
+ SchemaPtr = NULL;
+ }
+ if( RecBuf ){
+ free( RecBuf );
+ RecBuf = NULL;
+ }
+ if( RecBuf2){
+ free( RecBuf2 );
+ RecBuf2 = NULL;
+ }
+ Close();
+}
+/************************************************************************/
+//! @brief Abort any uncommited changes for the current record buffer.
+/*!
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbDbf::Abort(){
+
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+
+ try{
+ if( iDbfStatus == XB_UPDATED ){
+ #ifdef XB_MEMO_SUPPORT
+ if( MemoFieldsExist()){
+ if(( iRc = Memo->Abort()) != XB_NO_ERROR ){
+ iErrorStop = 10;
+ throw iRc;
+ }
+ }
+ #endif
+ memcpy( RecBuf, RecBuf2, uiRecordLen );
+ iDbfStatus = XB_OPEN;
+ }
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbdbf::Abort() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+/************************************************************************/
+#ifdef XB_INDEX_SUPPORT
+//! @brief Add an index to the internal list of indices for this table.
+/*!
+ The index list is used during any table update process to update any open
+ index file. Index files can contain one or more tags.
+
+ \param ixIn Pointer to index object for a given index file.
+ \param sFmt NDX or MDX.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+
+*/
+
+xbInt16 xbDbf::AddIndex( xbIx * ixIn, const xbString &sFmt ){
+
+ xbIxList *ixt; // this
+ if(( ixt = (xbIxList *) malloc( sizeof( xbIxList ))) == NULL )
+ return XB_NO_ERROR;
+
+ ixt->ix = ixIn;
+ ixt->next = NULL;
+ ixt->sFmt = new xbString( sFmt );
+ ixt->sFmt->ToUpperCase();
+
+ if( ixList ){
+ xbIxList *ixn = ixList; // next
+ while( ixn->next ){
+ ixn = ixn->next;
+ }
+ ixn->next = ixt;
+ } else {
+ ixList = ixt;
+ }
+ return XB_NO_ERROR;
+}
+#endif // XB_INDEX_SUPPORT
+
+
+/************************************************************************/
+//! @brief Append the current record to the data file.
+/*!
+ This method attempts to append the contents of the current record buffer
+ to the end of the DBF file, updates the file date, number of records in the file
+ and updates any open indices associated with this data file.<br>
+
+ To add a record, an application would typically blank the record buffer,
+ update various fields in the record buffer, then append the record.<br>
+
+ The append method performs the following tasks:<br>
+ 1) Create new index key values<br>
+ 2) Lock the table<br>
+ 3) Lock append bytes<br>
+ 4) Lock indices<br>
+ 5) Read the dbf header<br>
+ 6) Check for dup keys<br>
+ 7) Calc last update date, no of recs<br>
+ 8) Add keys<br>
+ 9) Unlock indices<br>
+ 10) Update file header<br>
+ 11) Unlock file header<br>
+ 12) Append record<br>
+ 13) Unlock append bytes<br>
+
+Note: Locking memo files is not needed as the memo file updates are handled outside of the append method.<br>
+
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbDbf::AppendRecord(){
+ xbInt16 iErrorStop = 0;
+ xbInt16 iRc = XB_NO_ERROR;
+ xbUInt32 ulSaveCurRec = 0;
+
+ try{
+ #ifdef XB_INDEX_SUPPORT
+ xbIxList *ixList = GetIxList();
+ // do this step first before anything is locked, reduce lock time as much as possible
+ while( ixList ){
+
+ // std::cout << "xbDbf::CreateKeys(x)\n";
+ if(( iRc = ixList->ix->CreateKeys( 1 )) != XB_NO_ERROR ){
+ iErrorStop = 10;
+ throw iRc;
+ }
+
+ ixList = ixList->next;
+ }
+ #endif // XB_INDEX_SUPPORT
+
+ // lock everything up for an update
+ #ifdef XB_LOCKING_SUPPORT
+ // xbInt16 iAutoLock = GetAutoLock();
+ if( iAutoLock ){
+
+ if(( iRc = LockHeader( XB_LOCK )) != XB_NO_ERROR ){
+ if( iRc == XB_LOCK_FAILED ) {
+ return iRc;
+ } else {
+ iErrorStop = 20;
+ throw iRc;
+ }
+ }
+ if(( iRc = LockAppend( XB_LOCK )) != XB_NO_ERROR ){
+ if( iRc == XB_LOCK_FAILED ){
+ LockHeader( XB_UNLOCK );
+ return iRc;
+ } else {
+ iErrorStop = 30;
+ throw iRc;
+ }
+ }
+
+ #ifdef XB_INDEX_SUPPORT
+ if(( iRc = LockIndices( XB_LOCK )) != XB_NO_ERROR ){
+ iErrorStop = 40;
+ throw iRc;
+ }
+ #endif // XB_INDEX_SUPPORT
+
+ }
+ #endif // XB_LOCKING_SUPPORT
+ if(( iRc = ReadHeader( 1, 1 )) != XB_NO_ERROR ){
+ iErrorStop = 50;
+ throw iRc;
+ }
+ #ifdef XB_INDEX_SUPPORT
+ ixList = GetIxList();
+
+ while( ixList ){
+ if(( iRc = ixList->ix->CheckForDupKeys()) != 0 ){
+ if( iRc < 0 ){
+ iErrorStop = 60;
+ throw iRc;
+ }
+ return XB_KEY_NOT_UNIQUE;
+ }
+ ixList = ixList->next;
+ }
+
+ #endif // XB_INDEX_SUPPORT
+
+ // calculate the latest header information
+ xbDate d;
+ cUpdateYY = (char) d.YearOf() - 1900;
+ cUpdateMM = (char) d.MonthOf();
+ cUpdateDD = (char) d.DayOf( XB_FMT_MONTH );
+ ulSaveCurRec = ulCurRec;
+ ulNoOfRecs++;
+ ulCurRec = ulNoOfRecs;
+
+ #ifdef XB_INDEX_SUPPORT
+
+
+ ixList = GetIxList();
+ while( ixList ){
+ if(( iRc = ixList->ix->AddKeys( ulCurRec )) != XB_NO_ERROR ){
+ iErrorStop = 50;
+ throw iRc;
+ }
+ ixList = ixList->next;
+ }
+
+ #ifdef XB_LOCKING_SUPPORT
+ if( iAutoLock ){
+ if(( iRc = LockIndices( XB_UNLOCK )) != XB_NO_ERROR ){
+ iErrorStop = 60;
+ throw iRc;
+ }
+ }
+ #endif // XB_LOCKING_SUPPORT
+ #endif // XB_INDEX_SUPPORT
+
+ // rewrite the header record
+ if(( iRc = WriteHeader( 1, 1 )) != XB_NO_ERROR ){
+ iErrorStop = 60;
+ throw iRc;
+ }
+
+ #ifdef XB_LOCKING_SUPPORT
+ if( iAutoLock ){
+ if(( iRc = LockHeader( XB_UNLOCK )) != XB_NO_ERROR ){
+ iErrorStop = 70;
+ throw iRc;
+ }
+ }
+ #endif
+
+ // write the last record
+ if(( iRc = xbFseek( (uiHeaderLen+((xbInt64)(ulNoOfRecs-1)*uiRecordLen)), 0 )) != XB_NO_ERROR ){
+ iErrorStop = 80;
+ throw iRc;
+ }
+
+ if(( iRc = xbFwrite( RecBuf, uiRecordLen, 1 )) != XB_NO_ERROR ){
+ iErrorStop = 90;
+ throw iRc;
+ }
+
+ // write the end of file marker
+ if(( iRc = xbFputc( XB_CHAREOF )) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+
+ #ifdef XB_LOCKING_SUPPORT
+ if( iAutoLock ){
+ if(( iRc = LockAppend( XB_UNLOCK )) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ throw( iRc );
+ }
+ }
+ #endif // XB_LOCKING_SUPPORT
+
+ }
+ catch (xbInt16 iRc ){
+ if( ulSaveCurRec != 0 ){
+ ulCurRec = ulSaveCurRec;
+ ulNoOfRecs--;
+ }
+ #ifdef XB_LOCKING_SUPPORT
+ if( iAutoLock ){
+ #ifdef XB_INDEX_SUPPORT
+ LockIndices( XB_UNLOCK );
+ #endif // XB_INDEX_SUPPORT
+ LockAppend( XB_UNLOCK );
+ LockHeader( XB_UNLOCK );
+ }
+ #endif // XB_LOCKING_SUPPORT
+
+ if( iRc != XB_LOCK_FAILED && iRc != XB_KEY_NOT_UNIQUE ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbdbf::Append() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ }
+
+ if( iRc == XB_NO_ERROR )
+ iDbfStatus = XB_OPEN;
+ return iRc;
+}
+
+/************************************************************************/
+#ifdef XB_NDXINF_SUPPORT
+//! @brief Asscoiate a non production index to a DBF file.
+/*!
+
+ The original Dbase (TM) software supported non production indices (NDX) and production indices (MDX).
+ The production indices are opened automatically when the DBF file is opened but the non-production
+ indices are not. This method is specific to the Xbas64 library and providex a means to link non production
+ NDX index files to the DBF file so they will be opened automatically when the DBF file is opened.<br>
+
+ This routine requires NDXINF support be enabled when building the library.<br>
+ This routine creates a file with the same name as the DBF file, but with an extension of INF.<br>
+
+
+ \param sIxType Currently only NDX. Future versions will support additional non prod index types.
+ \param sIxName The index name.
+ \param iOpt 0 - Add index to .INF if not already there<br>
+ 1 - Remove index from .INF if there
+
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+
+*/
+
+xbInt16 xbDbf::AssociateIndex( const xbString &sIxType, const xbString &sIxName, xbInt16 iOpt ){
+
+ xbInt16 iRc = 0;
+ xbInt16 iErrorStop = 0;
+
+ try{
+ xbString sIxTypeIn = sIxType;
+ sIxTypeIn.Trim();
+ xbString sIxNameIn = sIxName;
+ sIxNameIn.Trim();
+
+ if( sIxTypeIn != "NDX" || sIxName == "" )
+ return XB_INVALID_INDEX;
+
+ if(( iRc = LoadNdxInfData()) != XB_NO_ERROR ){
+ iErrorStop = 10;
+ throw iRc;
+ }
+
+ // check if entry exists
+ xbLinkListNode<xbString> * llN = llNdxInfData.GetHeadNode();
+ xbBool bFound = xbFalse;
+ xbString s;
+
+ while( llN && !bFound ){
+ s = llN->GetKey();
+ if( s.Len() > 0 ){
+ if( sIxNameIn == s )
+ bFound = xbTrue;
+ }
+ llN = llN->GetNextNode();
+ }
+
+ xbBool bUpdated = xbFalse;
+ if( iOpt == 0 && !bFound ){
+ s.Sprintf( "%s%c%c", sIxName.Str(), 0x0d, 0x0a );
+ llNdxInfData.InsertAtEnd( s );
+ bUpdated = xbTrue;
+
+ } else if( iOpt == 1 && bFound ){
+ llNdxInfData.RemoveByVal( s );
+ bUpdated = xbTrue;
+ }
+
+ if( bUpdated ){
+ if(( iRc = SaveNdxInfData()) != XB_NO_ERROR ){
+ iErrorStop = 20;
+ throw iRc;
+ }
+ }
+
+ } catch( xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbdbf::AssociateIndex() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+#endif // XB_NDXINF_SUPPORT
+
+/************************************************************************/
+//! @brief Blank the record buffer.
+/*!
+
+ This method would typically be called to initialize the record buffer before
+ updates are applied to append a new record.
+
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbDbf::BlankRecord()
+{
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+
+ try{
+
+ if( iDbfStatus == XB_CLOSED ){
+ iErrorStop = 10;
+ iRc = XB_NOT_OPEN;
+ throw iRc;
+ }
+
+ if( iDbfStatus == XB_UPDATED ){
+ if( GetAutoCommit() == 1 ){
+ if(( iRc = Commit()) != XB_NO_ERROR ){
+ iErrorStop = 20;
+ throw iRc;
+ }
+ } else {
+ if(( iRc = Abort()) != XB_NO_ERROR ){
+ iErrorStop = 30;
+ throw iRc;
+ }
+ }
+ }
+ ulCurRec = 0;
+ memset( RecBuf, 0x20, uiRecordLen );
+ memset( RecBuf2, 0x20, uiRecordLen );
+ }
+
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbdbf::BlankRecord() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+
+ return iRc;
+}
+/************************************************************************/
+#ifdef XB_INDEX_SUPPORT
+
+#ifdef XB_DEBUG_SUPPORT
+
+/*!
+ This method is used to check an index tag's intgerity.
+
+ \param iTagOpt 0 - Check current tag<br>
+ 1 - Check all tag<br>
+
+ \param iOutputOpt Output message destination<br>
+ 0 = stdout<br>
+ 1 = Syslog<br>
+ 2 = Both<br>
+
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbDbf::CheckTagIntegrity( xbInt16 iTagOpt, xbInt16 iOutputOpt ){
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+
+ try{
+
+// std::cout << "CheckTagIntegrity()\n";
+
+ if( iTagOpt == 0 ){
+ if( pCurIx )
+ return pCurIx->CheckTagIntegrity( vpCurIxTag, iOutputOpt );
+ else
+ return XB_INVALID_TAG;
+
+ } else {
+
+ xbLinkListNode<xbTag *> *llN = GetTagList();
+ xbTag *pTag;
+
+ while( llN ){
+ pTag = llN->GetKey();
+ if(( iRc = pTag->GetIx()->CheckTagIntegrity( pTag->GetVpTag(), iOutputOpt )) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+ llN = llN->GetNextNode();
+ }
+ }
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbdbf::CheckTagIntegrity() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+
+ return iRc;
+}
+
+#endif // XB_DEBUG_SUPPORT
+
+/************************************************************************/
+/*!
+ This method is used to reindex / rebuild index tag.
+
+ \param iTagOpt 0 - Reindex current tag<br>
+ 1 - Reindex all tags<br>
+
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbDbf::Reindex( xbInt16 iTagOpt ){
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+ void *vp;
+
+ try{
+ if( iTagOpt == 0 ){
+ if( pCurIx ){
+
+ //
+ // xbString = save current tag name
+ //
+
+ iRc = pCurIx->Reindex( &vpCurIxTag );
+ if( iRc != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+ //
+ // restore current tag name
+ //
+ return iRc;
+
+ } else {
+
+ return XB_INVALID_TAG;
+
+ }
+
+ } else {
+
+ xbLinkListNode<xbTag *> *llN = GetTagList();
+ xbTag *pTag;
+
+ while( llN ){
+ pTag = llN->GetKey();
+ vp = pTag->GetVpTag();
+ if(( iRc = pTag->GetIx()->Reindex( &vp )) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+ llN = llN->GetNextNode();
+ }
+
+
+ }
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbdbf::Reindex() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+
+ return iRc;
+}
+
+/************************************************************************/
+// @brief Clear the index tag list.
+/*
+ Protected method. Clears the list inf index tags.
+ \returns void.
+*/
+void xbDbf::ClearTagList(){
+
+ xbTag *pTag;
+ xbBool bDone = xbFalse;
+ while( llTags.GetNodeCnt() > 0 && !bDone ){
+ if( llTags.RemoveFromFront( pTag ) != XB_NO_ERROR ){
+ bDone = xbTrue;
+ } else {
+ if( pTag )
+ delete pTag;
+ }
+ }
+}
+#endif // XB_INDEX_SUPPORT
+
+/************************************************************************/
+//! @brief Close DBF file/table.
+/*!
+ This routine flushes any remaining updates to disk, closes the DBF file and
+ any associated memo and index files.
+
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbDbf::Close(){
+
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+
+ try{
+
+ if(iDbfStatus == XB_CLOSED)
+ return XB_NO_ERROR;
+
+ else if( iDbfStatus == XB_UPDATED ){
+
+ if( GetAutoCommit() == 1 ){
+ if(( iRc = Commit()) != XB_NO_ERROR ){
+ iErrorStop = 10;
+ throw iRc;
+ }
+ } else {
+ if(( iRc = Abort()) != XB_NO_ERROR ){
+ iErrorStop = 20;
+ throw iRc;
+ }
+ }
+ }
+
+ if(SchemaPtr){
+ free( SchemaPtr );
+ SchemaPtr = NULL;
+ }
+ if(RecBuf){
+ free( RecBuf );
+ RecBuf = NULL;
+ }
+ if(RecBuf2){
+ free( RecBuf2 );
+ RecBuf2 = NULL;
+ }
+
+
+ #ifdef XB_MEMO_SUPPORT
+ if( iMemoFieldCnt > 0 ){
+ Memo->CloseMemoFile();
+ delete Memo;
+ Memo = NULL;
+ }
+ #endif
+
+ // close any open index files, remove from the ix list
+ #ifdef XB_INDEX_SUPPORT
+ while( ixList ){
+ ixList->ix->Close();
+ RemoveIndex( ixList->ix );
+ }
+ #endif
+
+ #ifdef XB_LOCKING_ON
+ if( xblfh ){
+ fclose( xblfh );
+ xblfh = NULL;
+ }
+ #endif
+
+ if(( iRc = xbase->RemoveTblFromTblList( this )) != XB_NO_ERROR ){
+ xbString sMsg;
+ sMsg.Sprintf( "Alias = [%s]", sAlias.Str());
+ xbase->WriteLogMessage( sMsg.Str() );
+ iErrorStop = 30;
+ throw iRc;
+ }
+
+ xbFclose();
+ InitVars();
+
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbdbf::Close() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+
+
+/************************************************************************/
+#ifdef XB_INDEX_SUPPORT
+//! @brief Close an open index file
+/*!
+ All index files are automatically closed when the DBF file is closed.
+ Under normal conditions, it is not necessary to explicitly close an index file
+ with this routine. Any updates posted to a DBF file while an index is closed
+ will not be reflected in the closed index file.
+
+ \param pIx Pointer to index object to close.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbDbf::CloseIndexFile( xbIx *pIx ){
+
+ xbInt16 iErrorStop = 0;
+ xbInt16 iRc = XB_NO_ERROR;
+
+ try{
+
+ // verify index is open and in the list
+ xbBool bFound = xbFalse;
+ xbIxList *p = GetIxList();
+ while( p && !bFound ){
+ if( pIx == p->ix )
+ bFound = xbTrue;
+ p = p->next;
+ }
+ if( !bFound ){
+ iErrorStop = 10;
+ iRc = XB_NOT_OPEN;
+ throw iRc;
+ }
+
+ // close it
+ if(( iRc = pIx->Close()) != XB_NO_ERROR ){
+ iErrorStop = 20;
+ throw iRc;
+ }
+
+ // remove it from the list
+ if(( iRc = RemoveIndex( pIx )) != XB_NO_ERROR ){
+ iErrorStop = 30;
+ throw iRc;
+ }
+
+ // refresh the tag list
+ if(( iRc = UpdateTagList()) != XB_NO_ERROR ){
+ iErrorStop = 40;
+ throw iRc;
+ }
+
+ if( pIx == pCurIx ){
+ pCurIx = NULL;
+ vpCurIxTag = NULL;
+ sCurIxType = "";
+ }
+
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbdbf::CloseIndexFile() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+#endif // XB_INDEX_SUPPORT
+
+/************************************************************************/
+//! @brief Commit updates to disk
+/*!
+
+ This routine commits any pending updates to disk.
+
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbDbf::Commit(){
+
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+ try{
+
+ if( iDbfStatus == XB_UPDATED ){
+ if( ulCurRec == 0 ){
+ if(( iRc = AppendRecord()) != XB_NO_ERROR ){
+ iErrorStop = 20;
+ throw iRc;
+ }
+ } else {
+ if(( iRc = PutRecord( ulCurRec )) != XB_NO_ERROR ){
+ iErrorStop = 30;
+ throw iRc;
+ }
+ }
+ }
+ }
+ catch (xbInt16 iRc ){
+ if( iRc != XB_LOCK_FAILED ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbdbf::Commit() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ }
+ return iRc;
+}
+
+
+/************************************************************************/
+//! @brief Copy table (dbf) file structure.
+/*!
+
+ This routine will copy the structure of a dbf file and if successful
+ return a pointer to the new table in an open state.
+
+ \param dNewTable Reference to new table object.
+ \param sNewTableName New table (dbf) name.
+ \param sNewTableAlias Alias name of new table.
+ \param iOverlay xbTrue - Overlay existing file.<br>
+ xbFalse - Don't overlay existing file.
+ \param iShareMode XB_SINGLE_USER<br>
+ XB_MULTI_USER
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+//! Copy DBF structure
+/*!
+*/
+xbInt16 xbDbf::CopyDbfStructure( xbDbf * dNewTable, const xbString &sNewTableName,
+ const xbString & sNewTableAlias, xbInt16 iOverlay, xbInt16 iShareMode ) {
+
+// If successful, the table is returned in an open state after executing this method
+
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+ xbSchema *newTableSchema = NULL;
+
+ try{
+
+ if( iDbfStatus == XB_CLOSED ){
+ iErrorStop = 10;
+ iRc = XB_DBF_FILE_NOT_OPEN;
+ throw iRc;
+ }
+
+ if( !dNewTable ){
+ iErrorStop = 20;
+ iRc = XB_INVALID_OBJECT;
+ throw iRc;
+ }
+
+ // Get the number of schema entries for this table
+ xbInt32 lSchemaRecCnt = GetFieldCnt() + 1;
+
+ // Allocate a Schema = No Of Fields + 1
+ if((newTableSchema=(xbSchema *)malloc( (size_t) lSchemaRecCnt * sizeof(xbSchema)))==NULL){
+ iErrorStop = 30;
+ iRc = XB_NO_MEMORY;
+ throw iRc;
+ }
+
+ // Populate the Schema
+ xbInt32 l;
+ for( l = 0; l < lSchemaRecCnt-1; l++ ){
+ memset( newTableSchema[l].cFieldName, 0x00, 11 );
+ for( int x = 0; x < 10 && SchemaPtr[l].cFieldName[x]; x++ )
+ newTableSchema[l].cFieldName[x] = SchemaPtr[l].cFieldName[x];
+ newTableSchema[l].cType = SchemaPtr[l].cType;
+ newTableSchema[l].iFieldLen = SchemaPtr[l].cFieldLen;
+ newTableSchema[l].iNoOfDecs = SchemaPtr[l].cNoOfDecs;
+ }
+
+ // set the last one to zeroes
+ memset( newTableSchema[l].cFieldName, 0x00, 11 );
+ newTableSchema[l].cType = 0;
+ newTableSchema[l].iFieldLen = 0;
+ newTableSchema[l].iNoOfDecs = 0;
+
+ dNewTable->SetVersion();
+ #ifdef XB_MEMO_SUPPORT
+ if( MemoFieldsExist())
+ dNewTable->SetCreateMemoBlockSize( Memo->GetBlockSize() );
+ #endif
+
+ // Call the create a table function
+ if(( iRc = dNewTable->CreateTable( sNewTableName, sNewTableAlias, newTableSchema, iOverlay, iShareMode )) != XB_NO_ERROR ){
+ iErrorStop = 40;
+ throw iRc;
+ }
+ }
+
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbDbf::CopyDbfStructure() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+
+ if( newTableSchema )
+ free( newTableSchema );
+
+ return iRc;
+}
+
+
+/************************************************************************/
+#ifdef XB_INDEX_SUPPORT
+//! @brief Create a new tag (index) for this dbf file (table).
+/*!
+ This routine creates a new tag (index) on a dbf file. The library currently supports NDX and MDX
+ indices. If you don't have a specific need for an NDX file, use MDX.
+
+ \param sIxType "MDX" or "NDX".
+ \param sName Index or tag name.
+ \param sKey Index key expression,
+ \param sFilter Filter expression. Not applicable for NDX indices.
+ \param iDescending xbTrue for descending. Not available for NDX indices.<br>
+ xbFalse - ascending
+ \param iUnique xbTrue - Unique index<br>xbFalse - Not unique index.
+ \param iOverLay xbTrue - Overlay if exists<br>
+ xbFalse - Don't overlay if it exists.
+ \param pIxOut Pointer to pointer of output index object.
+ \param vpTagOut Pointer to pointer of newly created tag,
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbDbf::CreateTag( const xbString &sIxType, const xbString &sName, const xbString &sKey, const xbString &sFilter,
+ xbInt16 iDescending, xbInt16 iUnique, xbInt16 iOverLay, xbIx **pIxOut, void **vpTagOut ){
+
+ // this routine is used to open indices and link to files
+
+ xbInt16 iErrorStop = 0;
+ xbInt16 iRc = XB_NO_ERROR;
+ xbBool bLocked = xbFalse;
+
+ try{
+ xbString sType = sIxType;
+ sType.ToUpperCase();
+
+ #ifdef XB_LOCKING_SUPPORT
+ if( !bTableLocked ){
+ if(( iRc = LockTable( XB_LOCK )) != XB_NO_ERROR ){
+ iErrorStop = 10;
+ throw( iRc );
+ }
+ bLocked = xbTrue;
+ }
+ #endif // XB_LOCKING_SUPPORT
+
+ if( sIxType == "" ){
+ iErrorStop = 100;
+ iRc = XB_INVALID_OPTION;
+ throw iRc;
+
+ #ifdef XB_NDX_SUPPORT
+ } else if( sIxType == "NDX" ){
+ xbIxNdx *ixNdx = new xbIxNdx( this );
+
+ if(( iRc = ixNdx->CreateTag( sName, sKey, sFilter, iDescending, iUnique, iOverLay, vpTagOut )) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ throw iRc;
+ }
+
+ if(( iRc = AddIndex( ixNdx, sIxType )) != XB_NO_ERROR ){
+ iErrorStop = 120;
+ throw iRc;
+ }
+ *pIxOut = ixNdx;
+
+ // Set the current tag if one not already set
+ if( sCurIxType == "" ){
+ sCurIxType = "NDX";
+ pCurIx = ixNdx;
+ vpCurIxTag = ixNdx->GetTag(0);
+ }
+
+ #endif
+
+ #ifdef XB_MDX_SUPPORT
+ } else if( sIxType == "MDX" ){
+
+ if( GetVersion() == 3 ){ // MDX indexes were version 4 and higher
+ iErrorStop = 200;
+ iRc = XB_INVALID_INDEX;
+ throw iRc;
+ }
+
+ xbIxMdx *ixMdx;
+ xbString s;
+ // look through the index list and see if there is an mdx pointer we can grab
+ xbBool bMdxFound = xbFalse;
+ xbIxList *ixList = GetIxList();
+ while( ixList && !bMdxFound ){
+ s = ixList->sFmt->Str();
+ if( s == "MDX" ){
+ ixMdx = (xbIxMdx *) ixList->ix;
+ bMdxFound = xbTrue;
+ }
+ }
+
+ if( !bMdxFound )
+ ixMdx = new xbIxMdx( this );
+
+ if(( iRc = ixMdx->CreateTag( sName, sKey, sFilter, iDescending, iUnique, iOverLay, vpTagOut )) != XB_NO_ERROR ){
+ iErrorStop = 210;
+ throw iRc;
+ }
+
+ if( !bMdxFound ){
+ if(( iRc = AddIndex( ixMdx, "MDX" )) != XB_NO_ERROR ){
+ iErrorStop = 220;
+ throw iRc;
+ }
+
+ cIndexFlag = 0x01;
+ if(( iRc = WriteHeader( 1, 0 )) != XB_NO_ERROR ){
+ iErrorStop = 230;
+ throw iRc;
+ }
+ }
+ *pIxOut = ixMdx;
+
+ // set the current tag if one not already set
+ if( sCurIxType == "" ){
+ sCurIxType = "MDX";
+ pCurIx = ixMdx;
+ vpCurIxTag = ixMdx->GetTag(0);
+ }
+
+ #endif
+
+ } else {
+ iErrorStop = 200;
+ iRc = XB_INVALID_OPTION;
+ throw iRc;
+ }
+
+ if(( iRc = UpdateTagList()) != XB_NO_ERROR ){
+ iErrorStop = 300;
+ throw iRc;
+ }
+
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbdbf::CreateTag() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+
+ #ifdef XB_LOCKING_SUPPORT
+ if( bLocked )
+ LockTable( XB_UNLOCK );
+ #endif // XB_LOCKING_SUPPORT
+
+
+ return iRc;
+}
+#endif // XB_INDEX_SUPPORT
+
+/************************************************************************/
+//! @brief Delete or undelete all records in a dbf file (table).
+/*!
+ This routine deletes or un-deletes all records. The xbase file format contains
+ a leading one byte character used for flagging a record as deleted. When a record
+ is deleted, it's not physically removed from the file, the first byte is flagged as deleted.
+
+ \param iOption 0 - Delete all records.<br>
+ 1 - Un-delete all deleted records.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbDbf::DeleteAll( xbInt16 iOption )
+{
+ xbInt16 iErrorStop = 0;
+ xbInt16 iRc = XB_NO_ERROR;
+ xbUInt32 ulRecCnt;
+
+ try{
+ if(( iRc = GetRecordCnt( ulRecCnt )) != XB_NO_ERROR ){
+ iErrorStop = 10;
+ throw iRc;
+ }
+ if( ulRecCnt == 0 )
+ return XB_NO_ERROR;
+ for( xbUInt32 ul = 0; ul < ulRecCnt; ul++ ){
+ if(( iRc = GetRecord( ul+1 )) != XB_NO_ERROR ){
+ iErrorStop = 20;
+ throw iRc;
+ }
+ if( iOption == 0 ){ /* delete all option */
+ if( !RecordDeleted()){
+ if(( iRc = DeleteRecord()) != XB_NO_ERROR ){
+ iErrorStop = 30;
+ throw iRc;
+ }
+ }
+ } else { /* undelete all option */
+ if( RecordDeleted()){
+ if(( iRc = UndeleteRecord()) != XB_NO_ERROR ){
+ iErrorStop = 40;
+ throw iRc;
+ }
+ }
+ }
+ }
+ }
+ catch (xbInt16 iRc ){
+ if( iRc != XB_LOCK_FAILED ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbDbf::DeleteAll() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ }
+ return iRc;
+}
+
+/************************************************************************/
+#ifdef XB_INDEX_SUPPORT
+//! @brief
+/*!
+ This routine deletes all indices associated with the dbf file.
+
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbDbf::DeleteAllIndexFiles(){
+
+ xbInt16 iRc = 0;
+ xbInt16 iErrorStop = 0;
+ xbBool bLocked = xbFalse;
+
+ #ifdef XB_NDXINF_SUPPORT
+ xbString sIxName;
+ #endif // XB_NDXINF_SUPPORT
+
+ try{
+ #ifdef XB_LOCKING_SUPPORT
+ if( !bTableLocked ){
+ if(( iRc = LockTable( XB_LOCK )) != XB_NO_ERROR ){
+ iErrorStop = 10;
+ throw( iRc );
+ }
+ bLocked = xbTrue;
+ }
+ #endif // XB_LOCKING_SUPPORT
+
+ // close any open index files, delete it, remove from the ix list
+ while( ixList ){
+ ixList->ix->Close();
+ ixList->ix->xbRemove();
+ #ifdef XB_NDXINF_SUPPORT
+ // if XB_NDXINF_SUPPORT is enabled, all open non prod indices should be in here
+ if( *ixList->sFmt != "MDX" ){ // production indices not stored in .INF dataset
+ if(( iRc = ixList->ix->GetFileNamePart( sIxName )) != XB_NO_ERROR ){
+ iErrorStop = 20;
+ throw iRc;
+ }
+ if(( iRc = AssociateIndex( *ixList->sFmt, sIxName, 1 )) != XB_NO_ERROR ){
+ iErrorStop = 30;
+ throw iRc;
+ }
+ }
+ #endif
+ RemoveIndex( ixList->ix );
+ }
+ } catch( xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbdbf::DeleteAllIndexFiles() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ #ifdef XB_LOCKING_SUPPORT
+ if( bLocked )
+ LockTable( XB_UNLOCK );
+ #endif // XB_LOCKING_SUPPORT
+ return iRc;
+}
+#endif // XB_INDEX_SUPPORT
+
+/************************************************************************/
+//! @brief Delete all records.
+/*!
+ This routine deletes all the records in a table / dbf file.
+
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbDbf::DeleteAllRecords(){
+ return DeleteAll( 0 );
+}
+
+/************************************************************************/
+#ifdef XB_NDXINF_SUPPORT
+//! @brief Delete .INF File
+/*!
+ The original Dbase (TM) software supported non production indices (NDX) and production indices (MDX).
+ The production indices are opened automatically when the DBF file is opened but the non-production
+ indices are not. This method is specific to the Xbas64 library and providex a means to link non production
+ NDX index files to the DBF file so they will be opened automatically when the DBF file is opened.<br>
+
+ This routine requires NDXINF support be enabled when building the library.<br>
+ This routine deletes the .INF file.<br>
+
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbDbf::DeleteNdxInfData(){
+ xbInt16 iRc = 0;
+ xbInt16 iErrorStop = 0;
+
+ try{
+ xbString sNdxInfFileName;
+ if(( iRc = GetNdxInfFileName( sNdxInfFileName )) != XB_NO_ERROR ){
+ iErrorStop = 10;
+ throw iRc;
+ }
+ xbFile f( xbase );
+ f.SetFileName( sNdxInfFileName );
+ if( f.FileExists()){
+ if(( iRc = f.xbRemove()) != XB_NO_ERROR ){
+ iErrorStop = 20;
+ throw iRc;
+ }
+ }
+ } catch( xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbdbf::DeleteNdxInfData() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+#endif // XB_NDXINF_SUPPORT
+
+/************************************************************************/
+//! @brief Delete the current record.
+/*!
+ This routine flags the current record for deletion if it's not already flagged.
+
+
+ \returns XB_NO_ERROR<br>
+ XB_INVALID_RECORD
+*/
+
+xbInt16 xbDbf::DeleteRecord(){
+ if( RecBuf && ulCurRec > 0 ){
+ if( RecBuf[0] != 0x2a){
+ if( iDbfStatus != XB_UPDATED ){
+ iDbfStatus = XB_UPDATED;
+ memcpy( RecBuf2, RecBuf, uiRecordLen ); // save off original before making any updates
+ }
+ RecBuf[0] = 0x2a;
+ }
+ return XB_NO_ERROR;
+ }
+ else
+ return XB_INVALID_RECORD;
+}
+/************************************************************************/
+//! @brief Delete a table.
+/*!
+ This routine deletes a given table, associated index files if any, the
+ memo file if any and the .INF file if any.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbDbf::DeleteTable(){
+
+ xbInt16 iRc = 0;
+ xbInt16 iErrorStop = 0;
+
+ try{
+
+ #ifdef XB_LOCKING_SUPPORT
+ if(( iRc = LockTable( XB_LOCK )) != XB_NO_ERROR ){
+ iErrorStop = 10;
+ throw iRc;
+ }
+ #endif // XB_LOCKING_SUPPORT
+
+ #ifdef XB_INDEX_SUPPORT
+ if(( iRc = DeleteAllIndexFiles()) != XB_NO_ERROR ){
+ iErrorStop = 20;
+ throw iRc;
+ }
+
+ #ifdef XB_NDXINF_SUPPORT
+ if(( iRc = DeleteNdxInfData()) != XB_NO_ERROR ){
+ iErrorStop = 30;
+ throw iRc;
+ }
+ #endif // XB_NDXINF_SUPPORT
+ #endif // XB_INDEX_SUPPORT
+
+ #ifdef XB_MEMO_SUPPORT
+ xbInt16 iMemoFldCnt = GetMemoFieldCnt();
+ xbString sMemoFileName;
+ if(iMemoFldCnt > 0 ){
+ sMemoFileName = Memo->GetFqFileName();
+ }
+ #endif // XB_MEMO_SUPPORT
+
+ if(( iRc = Close()) != XB_NO_ERROR ){
+ iErrorStop = 40;
+ throw iRc;
+ }
+
+ if(( iRc = xbRemove()) != XB_NO_ERROR ){
+ iErrorStop = 50;
+ throw iRc;
+ }
+
+ #ifdef XB_MEMO_SUPPORT
+ if( iMemoFieldCnt > 0 ){
+ xbFile f( xbase );
+ if(( iRc = f.xbRemove( sMemoFileName )) != XB_NO_ERROR ){
+ iErrorStop = 60;
+ throw iRc;
+ }
+ }
+ #endif // XB_MEMO_SUPPORT
+
+ } catch( xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbdbf::DeleteTable() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ #ifdef XB_LOCKING_SUPPORT
+ LockTable( XB_UNLOCK );
+ #endif // XB_LOCKING_SUPPORT
+ return iRc;
+}
+
+/************************************************************************/
+//! @brief Delete an index tag.
+
+/*!
+ This routine deletes an index tag
+ \param sIxType Either "NDX" or "MDX".<br>
+ \param sName Tag name to delete.<br>
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbDbf::DeleteTag( const xbString &sIxType, const xbString &sName ){
+
+ xbInt16 iErrorStop = 0;
+ xbInt16 iRc = XB_NO_ERROR;
+ xbIx *pIx = NULL;
+
+ try{
+
+ #ifdef XB_LOCKING_SUPPORT
+ // xbInt16 iAutoLock = GetAutoLock();
+ if( iAutoLock && !GetTableLocked() ){
+ if(( iRc = LockTable( XB_LOCK )) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+ }
+ #endif
+
+ if( sIxType == "" ){
+ iErrorStop = 110;
+ iRc = XB_INVALID_OPTION;
+ throw iRc;
+
+ #ifdef XB_NDX_SUPPORT
+ } else if( sIxType == "NDX" ){
+
+ xbIxList *ixl = ixList;
+ xbBool bDone = xbFalse;
+ while( ixl && !bDone ){
+
+ if( ixl->ix->GetTagName( NULL ) == sName ){
+ bDone = xbTrue;
+
+ // remove from .INF if it's there
+ #ifdef XB_NDXINF_SUPPORT
+ if(( iRc = AssociateIndex( "NDX", sName, 1 )) != XB_NO_ERROR ){
+ iErrorStop = 120;
+ throw iRc;
+ }
+ #endif // XB_NDXINF_SUPPORT
+
+ if(( iRc = ixl->ix->DeleteTag( ixl->ix->GetTag( 0 ))) != XB_NO_ERROR ){
+ iErrorStop = 130;
+ throw iRc;
+ }
+
+ if(( iRc = RemoveIndex( ixl->ix )) != XB_NO_ERROR ){
+ iErrorStop = 140;
+ throw iRc;
+ }
+
+ if( ixl->ix == pCurIx )
+ SetCurTag( "", NULL, NULL );
+
+ }
+ ixl = ixl->next;
+ }
+ #endif
+
+ #ifdef XB_MDX_SUPPORT
+ } else if( sIxType == "MDX" ){
+ xbIxList *ixl = ixList;
+ xbIxList *ixlNext;
+ xbIxList *ixlPrev = NULL;
+ xbBool bDone = xbFalse;
+ xbIxMdx *pMdx;
+ xbMdxTag *pMdxTag;
+ xbInt16 iTagCnt = 0;
+
+ while( ixl && !bDone ){
+ ixlNext = ixl->next;
+ pMdx = (xbIxMdx *) ixl->ix;
+ iTagCnt = pMdx->GetTagCount();
+ for( xbInt16 i = 0; i < iTagCnt && !bDone; i++ ){
+ pMdxTag = (xbMdxTag *) pMdx->GetTag( i );
+ if( pMdx->GetTagName( pMdxTag ) == sName ){
+ bDone = xbTrue;
+ iRc = pMdx->DeleteTag( pMdxTag );
+ if( iRc > 0 ){
+ // Successful delete of only tag in production mdx file - need to remove it from the list, update the dbf header
+ cIndexFlag = 0x00;
+ if(( iRc = WriteHeader( 1, 0 )) != XB_NO_ERROR ){
+ iErrorStop = 150;
+ throw iRc;
+ }
+ if(( iRc = RemoveIndex( ixl->ix )) != XB_NO_ERROR ){
+ iErrorStop = 160;
+ throw iRc;
+ }
+ if( ixlPrev == NULL ){
+ // std::cout << "setting ixList to null or should be\n";
+ ixList = ixlNext;
+ } else {
+ ixlPrev = ixlNext;
+ }
+ } else if( iRc < 0 ){
+ iErrorStop = 170;
+ throw iRc;
+ }
+ if( ixl->ix == pCurIx )
+ SetCurTag( "", NULL, NULL );
+ }
+ }
+ ixlPrev = ixl;
+ ixl = ixlNext;
+ }
+
+ if( !bDone )
+ return XB_INVALID_TAG;
+
+ #endif
+
+ } else {
+ iErrorStop = 180;
+ iRc = XB_INVALID_OPTION;
+ throw iRc;
+ }
+
+ if(( iRc = UpdateTagList()) != XB_NO_ERROR ){
+ iErrorStop = 190;
+ throw iRc;
+ }
+
+ // unlock as necessary
+ #ifdef XB_LOCKING_SUPPORT
+ if( iAutoLock && GetTableLocked() ){
+ if(( iRc = LockTable( XB_UNLOCK )) != XB_NO_ERROR ){
+ iErrorStop = 200;
+ throw iRc;
+ }
+ }
+ #endif
+
+ }
+ catch (xbInt16 iRc ){
+ if( pIx ) delete pIx;
+ xbString sMsg;
+ sMsg.Sprintf( "xbdbf::DeleteTag() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+
+ #ifdef XB_LOCKING_SUPPORT
+ if( iAutoLock && GetTableLocked() ){
+ LockTable( XB_UNLOCK );
+ }
+ #endif
+
+ }
+ return iRc;
+}
+
+/************************************************************************/
+#ifdef XB_DEBUG_SUPPORT
+//! @brief Dump dbf file header.
+/*!
+ This routine dumps dbf header information to the console.
+
+ \param iOption 1 = Print header only<br>
+ 2 = Field data only<br>
+ 3 = Header and Field data<br>
+ 4 = Header, Field and Memo header data if applicable
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbDbf::DumpHeader( xbInt16 iOption ) const {
+ int i;
+ int iMemoCtr = 0;
+
+ if( iOption < 1 || iOption > 4 )
+ return XB_INVALID_OPTION;
+
+ if( iDbfStatus == XB_CLOSED )
+ return XB_NOT_OPEN;
+
+ std::cout << "\nDatabase file " << GetFqFileName() << std::endl << std::endl;
+
+ if( iOption != 2 ){
+ std::cout << "File header data:" << std::endl;
+
+ xbInt16 sVer = DetermineXbaseTableVersion( cVersion );
+
+ if( sVer == 3 )
+ std::cout << "Dbase III file" << std::endl;
+ else if ( sVer == 4 )
+ std::cout << "Dbase IV file" << std::endl << std::endl;
+ else if ( sVer == 5 )
+ std::cout << "Dbase V file" << std::endl << std::endl;
+ else if ( sVer == 7 )
+ std::cout << "Dbase VII file" << std::endl << std::endl;
+ else
+ std::cout << "Unknown Version" << std::endl;
+
+ /* display the bit stream */
+ unsigned char c, tfv, displayMask = 1 << 7;
+ tfv = cVersion;
+ std::cout << "File descriptor bits = ";
+ for( c = 1; c<= 8; c++ ){
+ std::cout << (tfv & displayMask ? '1' : '0');
+ tfv <<= 1;
+ }
+ std::cout << std::endl;
+
+ std::cout << "Descriptor bits legend:" << std::endl;
+ std::cout << " 0-2 = version number" << std::endl;
+ std::cout << " 3 = presence of dBASE IV memo" << std::endl;
+ std::cout << " 4-6 = SQL table presence" << std::endl;
+ std::cout << " 7 = Presence of any memo file (dBASE III PLUS or dBASE IV)" << std::endl << std::endl;
+
+ std::cout << "Last update date = "
+ << (int) cUpdateMM << "/" << (int) cUpdateDD << "/" << (int) cUpdateYY % 100 << std::endl;
+
+ std::cout << "Header length = " << uiHeaderLen << std::endl;
+ std::cout << "Record length = " << uiRecordLen << std::endl;
+ std::cout << "Records in file = " << ulNoOfRecs << std::endl << std::endl << std::endl;
+
+ std::cout << "Transaction Flag = ";
+ xbase->BitDump( cTransactionFlag );
+ std::cout << std::endl;
+
+ std::cout << "Encryption Flag = ";
+ xbase->BitDump( cEncryptionFlag );
+ std::cout << std::endl;
+
+ std::cout << "Index Flag = ";
+ xbase->BitDump( cIndexFlag );
+ std::cout << std::endl;
+
+ std::cout << "Lang Driver = " << (int) cIndexFlag << " - ";
+ xbase->BitDump( cIndexFlag );
+ std::cout << std::endl;
+
+ std::cout << "Open Index Files = " << GetPhysicalIxCnt() << std::endl;
+ }
+
+ if( iOption != 1 ){
+ char c;
+ std::cout << "Field Name Type Length Decimals IxFlag" << std::endl;
+ std::cout << "---------- ---- ------ -------- ------" << std::endl;
+ for( i = 0; i < iNoOfFields; i++ ){
+
+ SchemaPtr[i].cIxFlag ? c = 'Y' : c = ' ';
+
+ if( SchemaPtr[i].cType == 'C' && SchemaPtr[i].cNoOfDecs > 0 )
+ printf( "%10s %1c %4d %4d %c\n", SchemaPtr[i].cFieldName,
+ SchemaPtr[i].cType, SchemaPtr[i].cFieldLen, 0, c );
+ else
+ printf( "%10s %1c %4d %4d %c\n", SchemaPtr[i].cFieldName,
+ SchemaPtr[i].cType, SchemaPtr[i].cFieldLen, SchemaPtr[i].cNoOfDecs, c );
+
+ if( SchemaPtr[i].cType == 'M' )
+ iMemoCtr++;
+ }
+ }
+ std::cout << std::endl;
+
+#ifdef XB_MEMO_SUPPORT
+ if( iOption > 3 && iMemoCtr > 0 )
+ Memo->DumpMemoHeader();
+#endif
+
+ return XB_NO_ERROR;
+}
+
+/************************************************************************/
+//! Dump record
+/*!
+ Dump the contents of the specified record
+
+
+ \param ulRecNo Record number of record to be dumped.
+ \param iOutputDest 0 = stdout<br>
+ 1 = Syslog<br>
+ 2 = Both<br>
+
+ \param iOutputFmt 0 = with field names<br>
+ 1 = 1 line per rec, no field names<br>
+ 2 = 1 line per rec, first line is a list of field names.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbDbf::DumpRecord( xbUInt32 ulRecNo, xbInt16 iOutputDest, xbInt16 iOutputFmt ) {
+ int i, iRc = XB_NO_ERROR;
+
+ xbString sTemp;
+ xbString s2;
+ if( ulRecNo == 0 || ulRecNo > ulNoOfRecs )
+ return XB_INVALID_RECORD;
+
+ if( ulCurRec != ulRecNo ){
+ iRc = GetRecord( ulRecNo );
+ if( iRc != XB_NO_ERROR )
+ return iRc;
+ }
+
+ if( iOutputFmt >= 1 ){
+ if( iOutputFmt == 2 ){
+ sTemp = "RecNo";
+ for( i = 0; i < iNoOfFields; i++ ){
+ s2 = SchemaPtr[i].cFieldName;
+ s2.Trim();
+ sTemp += ",";
+ sTemp += s2;
+ }
+ xbase->WriteLogMessage( sTemp.Str(), iOutputDest );
+ }
+
+ if( RecordDeleted() )
+ s2.Sprintf( "%ld Deleted", ulRecNo );
+
+ else{
+ s2.Sprintf( "%ld ", ulRecNo );
+ //xbString s3;
+ for( i = 0; i < iNoOfFields; i++ ){
+ GetField( i, sTemp );
+ sTemp.Trim();
+ s2.Sprintf( "%s,'%s'", s2.Str(), sTemp.Str());
+ }
+ xbase->WriteLogMessage( s2.Str(),iOutputDest );
+ }
+
+ return XB_NO_ERROR;
+ }
+
+ sTemp.Sprintf( "\nRec Number: %ld", ulRecNo );
+ xbase->WriteLogMessage( sTemp.Str(),iOutputDest);
+
+ if( RecordDeleted())
+ xbase->WriteLogMessage( "Record flagged as deleted", iOutputDest );
+
+
+ #ifdef XB_MEMO_SUPPORT
+ xbString sMemo;
+ #endif
+
+ for( i = 0; i < iNoOfFields; i++ ){
+
+ #ifdef XB_MEMO_SUPPORT
+ GetField( i, sTemp );
+ sTemp.Trim();
+
+ if(SchemaPtr[i].cType == 'M'){
+ GetMemoField( i, sMemo );
+ if( sMemo.Len() > 70 )
+ sMemo.Resize( 70 );
+ s2.Sprintf ( "%c %s = '%s'", SchemaPtr[i].cType, SchemaPtr[i].cFieldName, sMemo.Str());
+ xbase->WriteLogMessage( s2.Str(), iOutputDest);
+
+ /*
+ xbUInt32 ulMlen;
+ if( MemoFieldExists( i )){
+ Memo->GetMemoFieldLen( i, ulMlen );
+ s2.Sprintf( " Len = %d", ulMlen );
+ }
+ xbase->WriteLogMessage( s2.Str(), iOutputDest);
+ */
+
+ }
+ else{
+ s2.Sprintf ( "%c %s = '%s'", SchemaPtr[i].cType, SchemaPtr[i].cFieldName, sTemp.Str());
+ xbase->WriteLogMessage( s2.Str(), iOutputDest);
+ }
+ #else
+ GetField( i, sTemp );
+ sTemp.Trim();
+ s2.Sprintf( "%s = '%s'", SchemaPtr[i].cFieldName, sTemp.Str());
+ xbase->WriteLogMessage( s2.Str(), iOutputDest);
+ #endif
+
+ }
+ return XB_NO_ERROR;
+}
+
+
+/************************************************************************/
+#ifdef XB_LOCKING_SUPPORT
+//! @brief Dump the table lock status
+/*!
+ Debugging routine. Dumps the table lock status to the console.
+ \returns void
+*/
+
+void xbDbf::DumpTableLockStatus() const {
+
+ std::cout << "File Lock Retry Count = [" << GetLockRetryCount() << "]" << std::endl;
+ std::cout << "File Lock Flavor = [";
+ switch (GetLockFlavor()){
+ case 1:
+ std::cout << "Dbase]" << std::endl;
+ break;
+ case 2:
+ std::cout << "Clipper]" << std::endl;
+ break;
+ case 3:
+ std::cout << "Fox]" << std::endl;
+ break;
+ case 9:
+ std::cout << "Xbase64]" << std::endl;
+ break;
+ default:
+ std::cout << "Unknown]" << std::endl;
+ break;
+ }
+ std::cout << "File Auto Lock = [";
+
+ if( GetAutoLock())
+ std::cout << "ON]" << std::endl;
+ else
+ std::cout << "OFF]" << std::endl;
+ if( GetHeaderLocked())
+ std::cout << "Header Locked = [TRUE]\n";
+ else
+ std::cout << "Header Locked = [FALSE]\n";
+
+ if( GetTableLocked())
+ std::cout << "Table Locked = [TRUE]\n";
+ else
+ std::cout << "Table Locked = [FALSE]\n";
+
+ if( GetAppendLocked() > 0 )
+ std::cout << "Append Locked = [" << GetAppendLocked() << "]\n";
+ else
+ std::cout << "Append Locked = [FALSE]\n";
+
+ #ifdef XB_MEMO_SUPPORT
+ if( GetMemoLocked())
+ std::cout << "Memo Locked = [TRUE]\n";
+ else
+ std::cout << "Memo Locked = [FALSE]\n";
+ #endif // XB_MEMO_SUPPORT
+
+ xbLinkListNode<xbUInt32> * llN = GetFirstRecLock();
+ if( llN ){
+ while( llN ){
+ std::cout << "Record Locked = [" << llN->GetKey() << "]\n";
+ llN = llN->GetNextNode();
+ }
+ } else {
+ std::cout << "Record Locked = [None]\n";
+ }
+}
+#endif // XB_LOCKING_SUPPORT
+#endif // XB_DEBUG_SUPPORT
+
+/************************************************************************/
+#ifdef XB_LOCKING_SUPPORT
+//! @brief Get the append locked bytes status
+/*!
+ \returns The record number of the new record for the append lock operation.
+*/
+
+xbUInt32 xbDbf::GetAppendLocked() const {
+ return this->ulAppendLocked;
+}
+
+#endif // XB_LOCKING_SUPPORT
+
+/************************************************************************/
+//! @brief Get auto commit setting.
+/*!
+
+ This routine returns the table setting if set, otherwise returns the system
+ level setting.
+
+ \returns Not 0 - Auto commit on for this table.<br>
+ 0 - Auto commit off for this table.
+*/
+
+xbInt16 xbDbf::GetAutoCommit() const {
+ return GetAutoCommit( 1 );
+}
+
+/************************************************************************/
+//! @brief Get auto commit setting.
+/*!
+
+ \param iOption 0 - Specific setting for this table<br>
+ 1 - If this table should be auto updated (takes DBMS setting into account)
+ \returns Not 0 - Auto commit on for this table.<br>
+ 0 - Auto commit off for this table.
+*/
+
+xbInt16 xbDbf::GetAutoCommit( xbInt16 iOption ) const {
+ if( iOption == 1 && iAutoCommit == -1 )
+ return xbase->GetDefaultAutoCommit();
+ else
+ return iAutoCommit;
+}
+
+
+/************************************************************************/
+//! @brief Get Auto Lock setting.
+/*!
+ \returns Auto lock setting.
+*/
+#ifdef XB_LOCKING_SUPPORT
+xbInt16 xbDbf::GetAutoLock() const{
+ return iAutoLock;
+}
+#endif // XB_LOCKING_SUPPORT
+
+/************************************************************************/
+#ifdef XB_MEMO_SUPPORT
+//! @brief Get the memo file block size used when creating a memo file.
+/*!
+ \returns Memo block size.
+*/
+xbUInt32 xbDbf::GetCreateMemoBlockSize() const {
+ return ulCreateMemoBlockSize;
+}
+#endif
+
+/************************************************************************/
+//! @brief Get a pointer to the current index object.
+/*!
+ \returns Pointer to current index.
+*/
+#ifdef XB_INDEX_SUPPORT
+xbIx *xbDbf::GetCurIx() const {
+ return pCurIx;
+}
+/************************************************************************/
+//! @brief Get pointer to current tag for the current index.
+/*!
+ An index file can have one or more tags. An NDX index has one tag.
+ An MDX file can have up to 47 tags.
+
+ \returns Pointer to current tag.
+*/
+void *xbDbf::GetCurTag() const {
+ return vpCurIxTag;
+}
+/************************************************************************/
+//! @brief Get the current index type.
+/*!
+ \returns NDX for single tag index.<br>
+ MDX for production multi tag index.
+*/
+const xbString &xbDbf::GetCurIxType() const {
+ return sCurIxType;
+}
+
+/************************************************************************/
+//! @brief Get the current tag name.
+/*!
+ \returns Current Tag Name.<br>
+*/
+
+const xbString &xbDbf::GetCurTagName() const {
+
+ if( pCurIx )
+ return pCurIx->GetTagName( vpCurIxTag );
+ else
+ return sNullString;
+}
+
+/************************************************************************/
+//! @brief GetFirstKey for tag.
+/*!
+
+ Position to the first key for the current tag
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+
+*/
+xbInt16 xbDbf::GetFirstKey(){
+ if( pCurIx )
+ return pCurIx->GetFirstKey( vpCurIxTag, 1 );
+ else
+ return XB_INVALID_TAG;
+}
+
+/************************************************************************/
+//! @brief GetLastKey for tag.
+/*!
+
+ Position to the last key for the current tag
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+
+*/
+xbInt16 xbDbf::GetLastKey(){
+ if( pCurIx )
+ return pCurIx->GetLastKey( vpCurIxTag, 1 );
+ else
+ return XB_INVALID_TAG;
+}
+
+/************************************************************************/
+//! @brief GetNextKey for tag.
+/*!
+
+ Position to the next key for the current tag
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+
+*/
+xbInt16 xbDbf::GetNextKey(){
+ if( pCurIx )
+ return pCurIx->GetNextKey( vpCurIxTag, 1 );
+ else
+ return XB_INVALID_TAG;
+}
+
+/************************************************************************/
+//! @brief GetPrevKey for tag.
+/*!
+
+ Position to the previous key for the current tag
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+
+*/
+xbInt16 xbDbf::GetPrevKey(){
+ if( pCurIx )
+ return pCurIx->GetPrevKey( vpCurIxTag, 1 );
+ else
+ return XB_INVALID_TAG;
+}
+
+/************************************************************************/
+//! @brief Find record for key.
+/*!
+
+ Find a key and position to record if key found
+
+ \param sKey String key to find
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+
+*/
+xbInt16 xbDbf::Find( xbString &sKey ){
+ if( pCurIx )
+ return pCurIx->FindKey( vpCurIxTag, sKey.Str(), (xbInt32) sKey.Len(), 1 );
+ else
+ return XB_INVALID_TAG;
+}
+
+/************************************************************************/
+//! @brief Find record for key.
+/*!
+
+ Find a key and position to record if key found
+
+ \param dtKey Date key to find
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+
+*/
+xbInt16 xbDbf::Find( xbDate &dtKey ){
+
+ if( pCurIx )
+ return pCurIx->FindKey( vpCurIxTag, dtKey, 1 );
+ else
+ return XB_INVALID_TAG;
+
+}
+/************************************************************************/
+//! @brief Find record for key.
+/*!
+
+ Find a key and position to record if key found
+
+ \param dtKey Date key to find
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+
+*/
+xbInt16 xbDbf::Find( xbDouble &dKey ){
+
+ if( pCurIx )
+ return pCurIx->FindKey( vpCurIxTag, dKey, 1 );
+ else
+ return XB_INVALID_TAG;
+
+}
+
+
+#endif // XB_INDEX_SUPPORT
+
+/************************************************************************/
+//! @brief Return the current record number.
+/*!
+ \returns Returns the current record number.
+*/
+xbUInt32 xbDbf::GetCurRecNo() const {
+ return ulCurRec;
+}
+
+/************************************************************************/
+//! @brief Return the current dbf status.
+/*!
+ \returns 0 = closed<br>
+ 1 = open<br>
+ 2 = updates pending<br>
+*/
+xbInt16 xbDbf::GetDbfStatus() const {
+ return iDbfStatus;
+}
+/************************************************************************/
+//! @brief Return the number of fields in the table.
+/*!
+ \returns The number of fields in the table.
+*/
+xbInt32 xbDbf::GetFieldCnt() const {
+ return iNoOfFields;
+}
+
+/************************************************************************/
+#ifdef XB_LOCKING_SUPPORT
+//! @brief Get the first first record lock.
+/*!
+ Get the first record lock from a linked list of record locks.
+
+ \returns First record lock.
+*/
+xbLinkListNode<xbUInt32> * xbDbf::GetFirstRecLock() const {
+ return lloRecLocks.GetHeadNode();
+}
+#endif // XB_LOCKING_SUPPORT
+/************************************************************************/
+//! @brief Get the first record.
+/*!
+ Get the first not deleted record. This routines skips over any deleted records.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbDbf::GetFirstRecord()
+{
+ return GetFirstRecord( XB_ACTIVE_RECS );
+}
+
+/************************************************************************/
+//! @brief Get the first record.
+/*!
+
+ \param iOption XB_ALL_RECS - Get the first record, deleted or not.<br>
+ XB_ACTIVE_RECS - Get the first active record.<br>
+ XB_DELETED_RECS - Get the first deleted record.<br>
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbDbf::GetFirstRecord( xbInt16 iOption )
+{
+ if( ulNoOfRecs == 0 )
+ return XB_EMPTY;
+
+ xbInt16 iRc = GetRecord( 1L );
+ while( iRc == XB_NO_ERROR &&
+ ((RecordDeleted() && iOption == XB_ACTIVE_RECS) ||
+ (!RecordDeleted() && iOption == XB_DELETED_RECS)))
+ if( ulCurRec < ulNoOfRecs )
+ iRc = GetRecord( ulCurRec + 1 );
+ else
+ return XB_EOF;
+
+ return iRc;
+}
+
+/************************************************************************/
+#ifdef XB_LOCKING_SUPPORT
+//! @brief Return lock status of the table header
+/*! \returns DBF header lock status
+*/
+
+xbBool xbDbf::GetHeaderLocked() const {
+ return this->bHeaderLocked;
+}
+#endif
+
+#ifdef XB_INDEX_SUPPORT
+//! @brief Return pointer to list of index files for the table.
+/*!
+ \returns Returns an xbIxList * pointer to list of open index files.
+*/
+
+xbIxList *xbDbf::GetIxList() const{
+ return ixList;
+}
+#endif // XB_INDEX_SUPPORT
+
+
+/************************************************************************/
+//! @brief Get the last record.
+/*!
+ Get the last not deleted record. This routines skips over any deleted records.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbDbf::GetLastRecord()
+{
+ return GetLastRecord( XB_ACTIVE_RECS );
+}
+/************************************************************************/
+//! @brief Get the last record.
+/*!
+
+ \param iOption XB_ALL_RECS - Get the last record, deleted or not.<br>
+ XB_ACTIVE_RECS - Get the last active record.<br>
+ XB_DELETED_RECS - Get the last deleted record.<br>
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbDbf::GetLastRecord( xbInt16 iOption )
+{
+ if( ulNoOfRecs == 0 )
+ return XB_EMPTY;
+
+ xbInt16 iRc = GetRecord( ulNoOfRecs );
+ while( iRc == XB_NO_ERROR &&
+ ((RecordDeleted() && iOption == XB_ACTIVE_RECS) ||
+ (!RecordDeleted() && iOption == XB_DELETED_RECS)))
+ if( ulCurRec > 1 )
+ iRc = GetRecord( ulCurRec - 1 );
+ else
+ return XB_EOF;
+
+ return iRc;
+}
+
+
+/************************************************************************/
+//! @brief Get lock flavor.
+/*!
+ This routine is currently in place to provide structure for future locking
+ schemes that may differ from the legacy DBase (TM) locking scheme.
+ \returns Always 1.
+*/
+
+xbInt16 xbDbf::GetLockFlavor() const{
+ if( iLockFlavor == -1 )
+ return xbase->GetDefaultLockFlavor();
+ else
+ return iLockFlavor;
+}
+
+/************************************************************************/
+#ifdef XB_MEMO_SUPPORT
+/************************************************************************/
+//! @brief Get the lock status of the memo file.
+/*!
+ \returns Lock status of memo file.
+*/
+xbBool xbDbf::GetMemoLocked() const {
+ if( MemoFieldsExist())
+ return Memo->GetMemoLocked();
+ else
+ return xbFalse;
+}
+/************************************************************************/
+//! @brief Get pointer to Memo object.
+/*!
+ \returns This routine returns the pointer to the memo object.
+*/
+
+xbMemo * xbDbf::GetMemoPtr(){
+ return Memo;
+}
+#endif
+
+
+/************************************************************************/
+#ifdef XB_NDXINF_SUPPORT
+//! @brief Return the .INF file name
+/*!
+ If NDXIDX support is enabled in the library, and a non production (ndx)
+ has been associated with the dbf file, the .INF file name can be retrieved
+ with this routine.
+
+ \param sNdxInfFileName Output string containing .INF file name.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbDbf::GetNdxInfFileName( xbString &sNdxInfFileName ){
+
+ sNdxInfFileName = GetFqFileName();
+ xbUInt32 lLen = sNdxInfFileName.Len();
+ if( lLen < 5 )
+ return XB_FILE_NOT_FOUND;
+ sNdxInfFileName.PutAt(lLen-2, 'I');
+ sNdxInfFileName.PutAt(lLen-1, 'N');
+ sNdxInfFileName.PutAt(lLen, 'F');
+ return XB_NO_ERROR;
+}
+
+/************************************************************************/
+//! @brief Return first node of linked list of .INF items.
+/*!
+ \returns List of .INF entries.
+*/
+
+xbLinkListNode<xbString> * xbDbf::GetNdxInfList() const{
+ return llNdxInfData.GetHeadNode();
+}
+#endif // XB_NDXINF_SUPPORT
+
+
+/************************************************************************/
+//! @brief Get the next record.
+/*!
+ Get the next not deleted record. This routines skips over any deleted records.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbDbf::GetNextRecord(){
+ return GetNextRecord( XB_ACTIVE_RECS );
+}
+
+/************************************************************************/
+//! @brief Get the next record.
+/*!
+ \param iOption XB_ALL_RECS - Get the next record, deleted or not.<br>
+ XB_ACTIVE_RECS - Get the next active record.<br>
+ XB_DELETED_RECS - Get the next deleted record.<br>
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbDbf::GetNextRecord( xbInt16 iOption ){
+ if( ulNoOfRecs == 0 )
+ return XB_EMPTY;
+ else if( ulCurRec >= ulNoOfRecs )
+ return XB_EOF;
+ xbInt16 iRc = GetRecord( ulCurRec + 1 );
+ while( iRc == XB_NO_ERROR &&
+ ((RecordDeleted() && iOption == XB_ACTIVE_RECS) ||
+ (!RecordDeleted() && iOption == XB_DELETED_RECS)))
+ if( ulCurRec < ulNoOfRecs )
+ iRc = GetRecord( ulCurRec + 1 );
+ else
+ return XB_EOF;
+ return iRc;
+}
+/************************************************************************/
+//! @brief Get the next record.
+/*!
+
+ \param iOption XB_ALL_RECS - Get the next record, deleted or not.<br>
+ XB_ACTIVE_RECS - Get the next active record.<br>
+ XB_DELETED_RECS - Get the next deleted record.<br>
+ \param ulStartRec Get next record, starting from ulStartRec.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbDbf::GetNextRecord( xbInt16 iOption , xbUInt32 ulStartRec ){
+
+ if( iOption == 0 )
+ return GetNextRecord();
+ else if( iOption == 1 ){
+ if( ulStartRec > 0 )
+ ulCurRec = ulStartRec;
+ xbInt16 iRc = GetNextRecord();
+ while( iRc == XB_NO_ERROR && RecordDeleted())
+ iRc = GetNextRecord();
+ return iRc;
+ }
+ else
+ return XB_INVALID_OPTION;
+}
+
+
+/************************************************************************/
+#ifdef XB_INDEX_SUPPORT
+//! @brief Physical count of open index files.
+/*!
+
+ Returns a physical count of open index files for the dbf file. An index file
+ can contain one or more tags.
+ \returns Count of open index files.
+*/
+
+xbInt32 xbDbf::GetPhysicalIxCnt() const {
+
+ xbInt32 lCnt = 0;
+ #ifdef XB_INDEX_SUPPORT
+ xbIxList *p = ixList;
+ while( p ){
+ lCnt++;
+ p = p->next;
+ }
+ #endif
+ return lCnt;
+}
+#endif
+
+
+/************************************************************************/
+//! @brief Get the previous record.
+/*!
+ Get the previous not deleted record. This routine skips over any deleted records.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbDbf::GetPrevRecord()
+{
+ return GetPrevRecord( XB_ACTIVE_RECS );
+}
+
+/************************************************************************/
+//! @brief Get the previous record.
+/*!
+
+ \param iOption XB_ALL_RECS - Get the previous record, deleted or not.<br>
+ XB_ACTIVE_RECS - Get the previous active record.<br>
+ XB_DELETED_RECS - Get the previous deleted record.<br>
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbDbf::GetPrevRecord( xbInt16 iOption ){
+ if( ulNoOfRecs == 0 )
+ return XB_EMPTY;
+ else if( ulCurRec <= 1L )
+ return XB_BOF;
+ xbInt16 iRc = GetRecord( ulCurRec - 1 );
+ while( iRc == XB_NO_ERROR &&
+ ((RecordDeleted() && iOption == XB_ACTIVE_RECS) ||
+ (!RecordDeleted() && iOption == XB_DELETED_RECS)))
+ if( ulCurRec > 1 )
+ iRc = GetRecord( ulCurRec - 1 );
+ else
+ return XB_BOF;
+
+ return iRc;
+}
+
+
+/************************************************************************/
+//! @brief Get record for specified record number.
+/*!
+ Retrieve a record from disk and load it into the record buffer. If auto commit
+ is enabled and there are pending updates, this routine will flush the updates
+ to disk before proceeding to ulRecNo.
+
+ \param ulRecNo - Record number to retrieve.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbDbf::GetRecord( xbUInt32 ulRecNo ){
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+
+ try{
+ /* verify the file is open */
+ if( iDbfStatus == XB_CLOSED ){
+ iErrorStop = 10;
+ iRc = XB_NOT_OPEN;
+ throw iRc;
+ }
+ if( iDbfStatus == XB_UPDATED ){
+ if( GetAutoCommit() == 1 ){
+ if(( iRc = Commit()) != XB_NO_ERROR ){
+ iErrorStop = 20;
+ throw iRc;
+ }
+ } else {
+ if(( iRc = Abort()) != XB_NO_ERROR ){
+ iErrorStop = 30;
+ throw iRc;
+ }
+ }
+ }
+
+ if( ulRecNo > ulNoOfRecs || ulRecNo == 0L ){
+ iErrorStop = 40;
+ iRc = XB_INVALID_RECORD;
+ throw iRc;
+ }
+
+ if(( xbFseek( (uiHeaderLen+(( (xbInt64) ulRecNo-1L ) * uiRecordLen )), SEEK_SET )) != XB_NO_ERROR ){
+ iErrorStop = 50;
+ iRc = XB_SEEK_ERROR;
+ throw iRc;
+ }
+
+ if( xbFread( RecBuf, uiRecordLen, 1 ) != XB_NO_ERROR ){
+ iErrorStop = 60;
+ iRc = XB_READ_ERROR;
+ throw iRc;
+ }
+ ulCurRec = ulRecNo;
+ }
+
+ catch (xbInt16 iRc ){
+ if( iRc != XB_LOCK_FAILED ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbDbf::GetRecord() Exception Caught. Error Stop = [%d] iRc = [%d] record = [%d]", iErrorStop, iRc, ulRecNo );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ }
+ return iRc;
+}
+/************************************************************************/
+//! @brief Get pointer to record buffer
+/*!
+ \param iOpt 0 for RecBuf (current) or 1 for RecBuf2 (original contents)
+
+ \returns Pointer to record buffer.
+*/
+char * xbDbf::GetRecordBuf( xbInt16 iOpt ) const {
+ if( iOpt )
+ return RecBuf2;
+ else
+ return RecBuf;
+}
+
+/************************************************************************/
+//! @brief Get the current number of records in the dbf data file.
+/*!
+ \param ulRecCnt Output number of records in file.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbDbf::GetRecordCnt( xbUInt32 & ulRecCnt )
+{
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+ xbBool bIlocked = xbFalse;
+ try{
+ #ifdef XB_LOCKING_SUPPORT
+ if( iAutoLock && !bTableLocked ){
+ if(( iRc = LockHeader( XB_LOCK )) != XB_NO_ERROR ){
+ iErrorStop = 10;
+ throw iRc;
+ } else
+ bIlocked = xbTrue;
+ }
+
+ if((iRc = ReadHeader(1,1)) != XB_NO_ERROR){
+ LockHeader( XB_UNLOCK );
+ iErrorStop = 20;
+ throw iRc;
+ }
+
+ if( iAutoLock && bIlocked ){
+ if(( iRc = LockHeader( XB_UNLOCK )) != XB_NO_ERROR ){
+ iErrorStop = 30;
+ throw iRc;
+ }
+ }
+ #endif
+ }
+ catch( xbInt16 iRc ){
+ if( iRc != XB_LOCK_FAILED ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbDbf::GetRecordCnt() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ }
+ ulRecCnt = ulNoOfRecs;
+ return iRc;
+}
+/************************************************************************/
+//! @brief Get the dbf record length.
+/*!
+ \returns Record length.
+*/
+xbUInt16 xbDbf::GetRecordLen() const {
+ return uiRecordLen;
+}
+/************************************************************************/
+//! @brief Get table locked status
+/*!
+ \returns Table lock status.
+*/
+
+xbBool xbDbf::GetTableLocked() const {
+ return this->bTableLocked;
+}
+/************************************************************************/
+//! @brief Get tag list for dbf file.
+/*!
+ This routine returns a list of tags for the file.<br>
+
+ The library is structured to support one or more files of the same or differing
+ index types (NDX/MDX), with each file supporting one or more index tags.<br>
+
+ \returns Tag list for the file/table.
+*/
+xbLinkListNode<xbTag *> *xbDbf::GetTagList() const {
+ return llTags.GetHeadNode();
+}
+/************************************************************************/
+//! @brief Get the table alias.
+/*!
+ This routine returns the table alias.
+ \returns Table alias
+*/
+const xbString & xbDbf::GetTblAlias() const {
+ return this->sAlias;
+}
+
+/************************************************************************/
+//! @brief Get the pointer to the xbXbase structure,
+/*!
+ \returns Pointer to xbXbase structure.
+*/
+xbXBase * xbDbf::GetXbasePtr() const {
+ return xbase;
+}
+/************************************************************************/
+#ifdef XB_NDXINF_SUPPORT
+//! @brief Load .INF data file,
+/*!
+ Protected method. This routine loads the ndx inf file.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbDbf::LoadNdxInfData(){
+
+ xbInt16 iRc = 0;
+ xbInt16 iErrorStop = 0;
+
+ try{
+ // create file name
+ xbString sNdxInfFileName;
+ if(( iRc = GetNdxInfFileName( sNdxInfFileName )) != XB_NO_ERROR ){
+ iErrorStop = 10;
+ throw iRc;
+ }
+ // if file does not exist, return no error
+ xbFile fMd( xbase );
+ if( !fMd.FileExists( sNdxInfFileName ))
+ return XB_NO_ERROR;
+
+ // open file file in read only mode
+ if(( iRc = fMd.xbFopen( "r", sNdxInfFileName, GetShareMode())) != XB_NO_ERROR ){
+ iErrorStop = 20;
+ throw iRc;
+ }
+ // clear the linked list
+ llNdxInfData.Clear();
+
+ // for each entry in the file, add a linked list item
+ xbString sRec;
+ xbString sLeft3;
+ xbString sFn;
+
+ while( iRc == XB_NO_ERROR ){
+ sRec = "";
+ if(( iRc = fMd.xbFgets( 132, sRec )) == XB_NO_ERROR ){
+ sLeft3 = sRec;
+ sLeft3.Left( 3 );
+ sLeft3.ToUpperCase();
+ if( sLeft3 == "NDX"){
+ sFn.ExtractElement(sRec.Str(), '=', 2 );
+ sFn.ZapChar( 0x0d );
+ sFn.ZapChar( 0x0a );
+ llNdxInfData.InsertAtEnd( sFn );
+ }
+ }
+ }
+ // close the file
+ if(( iRc = fMd.xbFclose()) != XB_NO_ERROR ){
+ iErrorStop = 40;
+ throw iRc;
+ }
+
+ } catch( xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbdbf::LoadNdxInfData() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+#endif // XB_NDXINF_SUPPORT
+
+/************************************************************************/
+#ifdef XB_LOCKING_SUPPORT
+//! @brief Lock append bytes.
+/*!
+ This routine locks the append bytes and is used by the AppendRecord function.
+
+ \param iLockFunction XB_LOCK<br>XB_UNLOCK
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbDbf::LockAppend( xbInt16 iLockFunction )
+{
+ xbInt16 iRc = 0;
+ xbInt16 iErrorStop = 0;
+ xbUInt32 ulAppendRec;
+
+ try{
+ if( iLockFunction == XB_LOCK ){
+ iErrorStop = 100;
+ if( ulAppendLocked > 0 ) /* already have an append lock */
+ return XB_NO_ERROR;
+
+ ulAppendRec = ulNoOfRecs + 1; /* record number needing to be locked */
+ if( GetLockFlavor() == LK_DBASE ){
+ iRc = xbLock( XB_LOCK, LK4026531839, 1 );
+ if( iRc != XB_NO_ERROR ){
+ if( iRc == XB_LOCK_FAILED )
+ return iRc;
+ else{
+ iErrorStop = 120;
+ throw iRc;
+ }
+ }
+
+ xbInt64 llAppendRecLockByte = (xbInt64) LK4026531838 - ulAppendRec;
+ iRc = xbLock( XB_LOCK, llAppendRecLockByte, 1 );
+ if( iRc != XB_NO_ERROR ){
+ xbLock( XB_UNLOCK, LK4026531839, 1 );
+ if( iRc == XB_LOCK_FAILED ){
+ return iRc;
+ } else {
+ iErrorStop = 130;
+ throw iRc;
+ }
+ }
+ ulAppendLocked = ulAppendRec; /* set the append lock switch */
+
+
+ // } else { - other lock-table flavor options go here Clipper, Fox, etc - }
+
+ } else {
+ iErrorStop = 190;
+ iRc = XB_INVALID_OPTION;
+ throw iRc;
+ }
+
+ } else if( iLockFunction == XB_UNLOCK ){
+
+ iErrorStop = 200;
+
+ if( ulAppendLocked == 0 ) /* verify we have an active append lock */
+ return XB_NO_ERROR;
+
+ if( GetLockFlavor() == LK_DBASE ){
+ xbInt64 llAppendRecLockByte =(xbInt64) LK4026531838 - ulAppendLocked;
+ iRc = xbLock( XB_UNLOCK, llAppendRecLockByte, 1 );
+ if( iRc != XB_NO_ERROR ){
+ xbLock( XB_UNLOCK, LK4026531839, 1 );
+ iErrorStop = 220;
+ throw iRc;
+ }
+ iRc = xbLock( XB_UNLOCK, LK4026531839, 1 );
+ if( iRc != XB_NO_ERROR ){
+ iErrorStop = 230;
+ throw iRc;
+ }
+
+ ulAppendLocked = 0; /* release the append lock switch */
+
+ // } else { - other unlock-table flavor options go here Clipper, Fox, etc - }
+
+ } else {
+ iErrorStop = 290;
+ iRc = XB_INVALID_OPTION;
+ throw iRc;
+ }
+ } else {
+ iErrorStop = 300;
+ iRc = XB_INVALID_OPTION;
+ throw iRc;
+ }
+ } catch( xbInt16 iRc ){
+ if( iRc != XB_LOCK_FAILED ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbdbf::LockAppendBytes() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ }
+
+ return iRc;
+}
+
+
+/************************************************************************/
+//! @brief Lock Header
+/*!
+ This routine locks the file header.
+ \param iLockFunction XB_LOCK<br>XB_UNLOCK
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbDbf::LockHeader( xbInt16 iLockFunction )
+{
+ xbInt16 iRc = 0;
+ xbInt16 iErrorStop = 0;
+ try{
+
+ if( iLockFunction == XB_LOCK ){
+ iErrorStop = 100;
+ if( GetHeaderLocked())
+ return XB_NO_ERROR;
+
+ iErrorStop = 110;
+ if( GetLockFlavor() == LK_DBASE ){
+
+ iRc = xbLock( XB_LOCK, LK4026531838, 1 );
+
+ if( iRc != XB_NO_ERROR ){
+ if( iRc == XB_LOCK_FAILED )
+ return iRc;
+ else{
+ iErrorStop = 120;
+ throw iRc;
+ }
+ }
+
+ } else {
+ iErrorStop = 190;
+ iRc = XB_INVALID_OPTION;
+ throw iRc;
+ }
+
+ SetHeaderLocked( xbTrue );
+
+ } else if( iLockFunction == XB_UNLOCK ){
+
+ iErrorStop = 200;
+ if( !GetHeaderLocked())
+ return XB_NO_ERROR;
+
+ iErrorStop = 210;
+ if( GetLockFlavor() == LK_DBASE ){
+ iRc = xbLock( XB_UNLOCK, LK4026531838, 1 );
+ if( iRc != XB_NO_ERROR ){
+ iErrorStop = 220;
+ throw iRc;
+ }
+ } else {
+ iErrorStop = 290;
+ iRc = XB_INVALID_OPTION;
+ throw iRc;
+ }
+ SetHeaderLocked( xbFalse );
+ } else {
+ iErrorStop = 300;
+ iRc = XB_INVALID_OPTION;
+ throw iRc;
+ }
+
+ } catch (xbInt16 iRc ){
+ if( iRc != XB_LOCK_FAILED ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbdbf::LockHeader() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ }
+ return iRc;
+}
+/************************************************************************/
+#ifdef XB_INDEX_SUPPORT
+//! @brief Lock Index files.
+/*!
+ This routine locks all the index files.
+ \param iLockFunction XB_LOCK<br>XB_UNLOCK
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbDbf::LockIndices( xbInt16 iLockFunction )
+{
+ // this function doesn't take into account any Lack Flavors other than DBASE,
+ // would need updated to supprot other lock flavors - Clipper, FoxPro etc
+
+ xbInt16 iRc = 0;
+ xbInt16 iErrorStop = 0;
+
+ try{
+ xbIxList *ixLI = GetIxList(); // index list item
+
+ while( ixLI ){
+ if( iLockFunction == XB_LOCK ){
+
+ #ifdef XB_NDX_SUPPORT
+ if( *ixLI->sFmt == "NDX" ){
+ if( !ixLI->ix->GetLocked()){
+ if(( iRc = ixLI->ix->xbLock( XB_LOCK, LK4026531838, 1 )) != XB_NO_ERROR ){
+ ixLI->ix->xbLock( XB_UNLOCK, LK4026531838, 1 );
+ iErrorStop = 100;
+ throw iRc;
+ }
+ ixLI->ix->SetLocked( xbTrue );
+ }
+ }
+ #endif
+
+ } else if( iLockFunction == XB_UNLOCK ){
+
+ #ifdef XB_NDX_SUPPORT
+ if( *ixLI->sFmt == "NDX" ){
+ if( ixLI->ix->GetLocked()){
+ if(( iRc = ixLI->ix->xbLock( XB_UNLOCK, LK4026531838, 1 )) != XB_NO_ERROR ){
+ ixLI->ix->xbLock( XB_UNLOCK, LK4026531838, 1 );
+ iErrorStop = 100;
+ throw iRc;
+ }
+ ixLI->ix->SetLocked( xbFalse );
+ }
+ }
+ #endif
+ }
+ ixLI = ixLI->next;
+ }
+
+ } catch( xbInt16 iRc ){
+ if( iRc != XB_LOCK_FAILED ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbdbf::LockIndices() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ }
+ return iRc;
+}
+#endif // XB_INDEX_SUPPORT
+/************************************************************************/
+#ifdef XB_MEMO_SUPPORT
+//! @brief Lock Memo file.
+/*!
+ This routine locks the memo file for updates.
+ \param iLockFunction XB_LOCK<br>XB_UNLOCK
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbDbf::LockMemo( xbInt16 iLockFunction ){
+ if( MemoFieldsExist())
+ return Memo->LockMemo( iLockFunction );
+ else
+ return XB_NO_ERROR;
+}
+#endif // XB_MEMO_SUPPORT
+
+
+
+/************************************************************************/
+//! @brief Loc Record
+/*!
+ This routine locks a record for update.
+ \param iLockFunction XB_LOCK<br>XB_UNLOCK
+ \param ulRecNo Record number to lock
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbDbf::LockRecord( xbInt16 iLockFunction, xbUInt32 ulRecNo )
+{
+ xbInt16 iRc = 0;
+ xbInt16 iErrorStop = 0;
+
+ try{
+
+ if( ulRecNo > ulNoOfRecs )
+ return XB_INVALID_RECORD;
+
+ if( iLockFunction == XB_LOCK ){
+ iErrorStop = 100;
+
+ if( lloRecLocks.KeyExists( ulRecNo ))
+ return XB_NO_ERROR;
+
+ if( GetLockFlavor() == LK_DBASE ){
+
+ iRc = xbLock( XB_LOCK, LK4026531838 - ulRecNo, 1 );
+ if( iRc != XB_NO_ERROR ){
+ if( iRc == XB_LOCK_FAILED ){
+ return iRc;
+ } else {
+ iErrorStop = 130;
+ throw iRc;
+ }
+ }
+ // other lock-table flavor options go here Clipper, Fox, etc
+
+ } else {
+ iErrorStop = 190;
+ iRc = XB_INVALID_OPTION;
+ throw iRc;
+ }
+
+ // add the record lock info to the linked list chain of record locks
+ iRc = lloRecLocks.InsertKey( ulRecNo );
+ if( iRc != XB_NO_ERROR ){
+ xbLock( XB_UNLOCK, LK4026531838 - ulRecNo, 1 );
+ iErrorStop = 140;
+ throw iRc;
+ }
+
+ } else if( iLockFunction == XB_UNLOCK ){
+
+ iErrorStop = 200;
+
+ if( !lloRecLocks.KeyExists( ulRecNo ) )
+ return XB_NO_ERROR;
+
+ if( GetLockFlavor() == LK_DBASE ){
+ iRc = xbLock( XB_UNLOCK, LK4026531838 - ulRecNo, 1 );
+ if( iRc != XB_NO_ERROR ){
+ iErrorStop = 220;
+ throw iRc;
+ }
+ } else {
+ iErrorStop = 290;
+ iRc = XB_INVALID_OPTION;
+ throw iRc;
+ }
+
+ // remove the record lock info to the linked list chain of record locks
+ // next line is crashing
+ iRc = lloRecLocks.RemoveKey( ulRecNo );
+ if( iRc != XB_NO_ERROR ){
+ iErrorStop = 230;
+ throw iRc;
+ }
+
+ } else {
+ iErrorStop = 300;
+ iRc = XB_INVALID_OPTION;
+ throw iRc;
+ }
+
+ } catch( xbInt16 iRc ){
+ if( iRc != XB_LOCK_FAILED ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbdbf::LockRecord() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ }
+
+ return iRc;
+}
+
+
+/************************************************************************/
+//! @brief Lock table.
+/*!
+ This routine locks the table for updates.
+
+ \param iLockFunction XB_LOCK<br>XB_UNLOCK
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbDbf::LockTable( xbInt16 iLockFunction )
+{
+ xbInt16 iRc = 0;
+ xbInt16 iErrorStop = 0;
+
+ try{
+ if( iLockFunction == XB_LOCK ){
+ iErrorStop = 100;
+ if( GetTableLocked())
+ return XB_NO_ERROR; // table already locked
+
+ iErrorStop = 110;
+ if( GetLockFlavor() == LK_DBASE ){
+
+ // lOffset = LK4026531838;
+ // iLen = 2;
+ iRc = xbLock( XB_LOCK, LK4026531838, 2 );
+ if( iRc != XB_NO_ERROR ){
+ if( iRc == XB_LOCK_FAILED )
+ return iRc;
+ else{
+ iErrorStop = 120;
+ throw iRc;
+ }
+ }
+
+ // lOffset = LK3026531838;
+ // iLen = LK1000000000;
+ iRc = xbLock( XB_LOCK, LK3026531838, LK1000000000);
+ if( iRc != XB_NO_ERROR ){
+
+ // lOffset = LK4026531838;
+ // iLen = 2;
+ xbLock( XB_UNLOCK, LK4026531838, 2 );
+ if( iRc == XB_LOCK_FAILED ){
+ return iRc;
+ } else {
+ iErrorStop = 130;
+ throw iRc;
+ }
+ }
+
+ // iRc = xbLock( XB_UNLOCK, lOffset, iLen );
+ iRc = xbLock( XB_UNLOCK, LK3026531838, LK1000000000);
+ if( iRc != XB_NO_ERROR ){
+ // lOffset = LK4026531838;
+ // iLen = 2;
+ xbLock( XB_UNLOCK, LK4026531838, 2 );
+ iErrorStop = 140;
+ throw iRc;
+ }
+
+ // other lock-table flavor options go here Clipper, Fox, etc
+
+ } else {
+ iErrorStop = 190;
+ iRc = XB_INVALID_OPTION;
+ throw iRc;
+ }
+ SetTableLocked( xbTrue );
+
+ } else if( iLockFunction == XB_UNLOCK ){
+
+ iErrorStop = 200;
+ if( !GetTableLocked())
+ return XB_NO_ERROR; // table already unlocked
+
+ iErrorStop = 210;
+ if( GetLockFlavor() == LK_DBASE ){
+
+ // lOffset = LK4026531838;
+ // iLen = 2;
+ iRc = xbLock( XB_UNLOCK, LK4026531838, 2 );
+ if( iRc != XB_NO_ERROR ){
+ iErrorStop = 220;
+ throw iRc;
+ }
+ } else {
+ iErrorStop = 290;
+ iRc = XB_INVALID_OPTION;
+ throw iRc;
+ }
+ SetTableLocked( xbFalse );
+
+ } else {
+ iErrorStop = 300;
+ iRc = XB_INVALID_OPTION;
+ throw iRc;
+ }
+
+ } catch (xbInt16 iRc ){
+ if( iRc != XB_LOCK_FAILED ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbdbf::LockFile() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ }
+ return iRc;
+
+}
+
+
+/************************************************************************/
+//! @brief Check for existence of any memo fields.
+/*!
+ \returns xbTrue - Memo fields exist.<br>xbFalse - Memo fields don't exist.
+*/
+xbBool xbDbf::MemoFieldsExist() const {
+#ifdef XB_MEMO_SUPPORT
+ if( iMemoFieldCnt > 0 )
+ return xbTrue;
+#endif
+ return xbFalse;
+}
+
+/************************************************************************/
+//! @brief Open a table/dbf file.
+/*!
+ This routine sets the alias name to the same as the table name.
+
+ \param sTableName Table name to open, Include the .dbf or .DBF extension.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbDbf::Open( const xbString & sTableName ) {
+ return Open( sTableName, sTableName );
+}
+
+/************************************************************************/
+//! @brief Open a table/dbf file.
+/*!
+ \param sTableName Table name to open, Include the .dbf or .DBF extension.
+ \param sAlias Alias name to assign to this entry.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbDbf::Open( const xbString & sTableName, const xbString & sAlias ){
+
+ xbInt16 iRc = 0;
+ xbInt16 iErrorStop = 0;
+
+
+ try{
+
+ if(( iRc = Open( sTableName, sAlias, XB_READ_WRITE, XB_MULTI_USER )) != XB_NO_ERROR ){
+ iErrorStop = 10;
+ throw iRc;
+ }
+
+ // do any .INF data things on the file, like open indices
+ #ifdef XB_NDXINF_SUPPORT
+ if(( iRc = LoadNdxInfData()) != XB_NO_ERROR ){
+ iErrorStop = 20;
+ throw iRc;
+ }
+ xbUInt32 llNodeCnt = llNdxInfData.GetNodeCnt();
+ if( llNodeCnt > 0 ){
+ xbString s2;
+ xbLinkListNode<xbString> * llN = llNdxInfData.GetHeadNode();
+ for( xbUInt32 i = 0; i < llNodeCnt; i++ ){
+ s2 = llN->GetKey();
+ if(( iRc = OpenIndex( "NDX", s2 )) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc ;
+ }
+ llN = llN->GetNextNode();
+ }
+ }
+ #endif // XB_NDXINF_SUPPORT
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbdbf::Open() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+#endif // XB_LOCKING_SUPPORT
+
+/************************************************************************/
+#ifdef XB_INDEX_SUPPORT
+//! @brief Open an index.
+/*!
+ Open an index file for the dbf file.
+
+ \param sIxType - "NDX" or "MDX"
+ \param sFileName - File name of index,
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbDbf::OpenIndex( const xbString &sIxType, const xbString &sFileName ){
+
+ // this routine is used to open indices and set up linkages
+
+ xbInt16 iErrorStop = 0;
+ xbInt16 iRc = XB_NO_ERROR;
+ xbIx *pIx = NULL;
+
+ try{
+ xbString sType = sIxType;
+ sType.ToUpperCase();
+
+ if( sType == "" ){
+ iErrorStop = 10;
+ iRc = XB_INVALID_OPTION;
+ throw iRc;
+
+ #ifdef XB_NDX_SUPPORT
+ } else if( sType == "NDX" ){
+ pIx = new xbIxNdx( this );
+ if(( iRc = pIx->Open( sFileName )) != XB_NO_ERROR ){
+ iErrorStop = 20;
+ throw iRc;
+ }
+ #endif
+
+ #ifdef XB_MDX_SUPPORT
+ } else if( sType == "MDX" ){
+
+ pIx = new xbIxMdx( this );
+ if(( iRc = pIx->Open( sFileName )) != XB_NO_ERROR ){
+ iErrorStop = 30;
+ throw iRc;
+ }
+
+ #endif
+
+ } else {
+ iErrorStop = 40;
+ iRc = XB_INVALID_OPTION;
+ throw iRc;
+ }
+
+ if(( iRc = AddIndex( pIx, sIxType )) != XB_NO_ERROR ){
+ iErrorStop = 50;
+ throw iRc;
+ }
+ if(( iRc = UpdateTagList()) != XB_NO_ERROR ){
+ iErrorStop = 60;
+ throw iRc;
+ }
+ pCurIx = pIx;
+ sCurIxType = sIxType;
+ vpCurIxTag = pIx->GetTag( 0 );
+ }
+ catch (xbInt16 iRc ){
+ if( pIx ) delete pIx;
+ xbString sMsg;
+ sMsg.Sprintf( "xbdbf::OpenIndex() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+#endif // XB_INDEX_SUPPORT
+
+
+
+/************************************************************************/
+//! @brief Pack dbf file.
+/*!
+ This routine eliminates all deleted records from the file.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbDbf::Pack()
+{
+ xbUInt32 ulDeletedRecCnt;
+ return Pack( ulDeletedRecCnt );
+}
+
+
+/************************************************************************/
+//! @brief Pack dbf file.
+/*!
+ This routine eliminates all deleted records from the file and clears
+ out any unused blocks in the memo file if one exists.
+ \param ulDeletedRecCnt - Output - number of recrods removed from the file.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbDbf::Pack( xbUInt32 &ulDeletedRecCnt )
+{
+ xbInt16 iRc = 0;
+ xbInt16 iErrorStop = 0;
+ xbUInt32 ulLastMovedRec = 0;
+ xbUInt32 ulStartPos = 0;
+ xbUInt32 ulLastPackedRec = 0;
+ xbUInt32 ulMoveRec = 0;
+ xbUInt32 ulRecCnt = 0;
+ ulDeletedRecCnt = 0;
+
+ try{
+ if( !FileIsOpen() ){
+ iErrorStop = 10;
+ iRc = XB_DBF_FILE_NOT_OPEN;
+ throw iRc;
+ }
+
+ #ifdef XB_LOCKING_SUPPORT
+ if( iAutoLock ){
+ if(( iRc = LockTable( XB_LOCK )) != XB_NO_ERROR ){
+ iErrorStop = 20;
+ throw iRc;
+ }
+ }
+ #endif
+
+ if(( iRc = GetRecordCnt( ulRecCnt )) != XB_NO_ERROR ){
+ iErrorStop = 30;
+ throw iRc;
+ }
+
+ xbBool bDone = xbFalse;
+ for( xbUInt32 ulI = 1; ulI <= ulRecCnt && !bDone; ulI++ ){
+
+
+ if(( iRc = GetRecord( ulI )) != XB_NO_ERROR ){
+ iErrorStop = 40;
+ throw iRc;
+ }
+
+ if( RecordDeleted()){
+
+ ulDeletedRecCnt++;
+ if( ulI > ulLastMovedRec )
+ ulStartPos = ulI;
+ else
+ ulStartPos = ulLastMovedRec;
+
+ iRc = GetNextRecord( 1, ulStartPos );
+
+ if( iRc == XB_NO_ERROR ){
+ ulMoveRec = ulCurRec;
+ }
+ else if( iRc == XB_EOF ){
+ ulMoveRec = 0;
+ bDone = xbTrue;
+ }
+ else{
+ iErrorStop = 50;
+ throw iRc;
+ }
+ if( ulMoveRec > 0 ){
+ if(( iRc = DeleteRecord()) != XB_NO_ERROR ){
+ iErrorStop = 60;
+ throw iRc;
+ }
+ if(( iRc = PutRecord( ulMoveRec )) != XB_NO_ERROR ){
+ iErrorStop = 70;
+ throw iRc;
+ }
+ if(( iRc = UndeleteRecord()) != XB_NO_ERROR ){
+ iErrorStop = 80;
+ throw iRc;
+ }
+ if(( iRc = PutRecord( ulI )) != XB_NO_ERROR ){
+ iErrorStop = 90;
+ throw iRc;
+ }
+ ulLastPackedRec = ulI;
+ }
+
+ } else {
+ ulLastPackedRec = ulI;
+ }
+ }
+
+ if( ulLastPackedRec < ulRecCnt ){
+ // update header record count
+
+ xbDate d;
+ cUpdateYY = (char) d.YearOf() - 1900;
+ cUpdateMM = (char) d.MonthOf();
+ cUpdateDD = (char) d.DayOf( XB_FMT_MONTH );
+ ulNoOfRecs = ulLastPackedRec;
+
+ // rewrite the header record
+ if(( iRc = WriteHeader( 1, 1 )) != XB_NO_ERROR ){
+ iErrorStop = 200;
+ throw iRc;
+ }
+
+ // truncate the file to the new size
+ if(( iRc = xbTruncate( uiHeaderLen + uiRecordLen * ulLastPackedRec )) != XB_NO_ERROR ){
+ iErrorStop = 210;
+ throw iRc;
+ }
+ }
+
+ if( ulNoOfRecs > 0 ){
+ if(( iRc = GetRecord( 1 )) != XB_NO_ERROR ){
+ iErrorStop = 220;
+ throw iRc;
+ }
+ } else {
+ BlankRecord();
+ ulCurRec = 0;
+ }
+
+ #ifdef XB_MEMO_SUPPORT
+ if( iMemoFieldCnt > 0 ){
+ if(( iRc = Memo->PackMemo( 0 )) != XB_NO_ERROR ){
+ iErrorStop = 230;
+ throw iRc;
+ }
+ }
+ #endif // XB_MEMO_SUPPORT
+
+ #ifdef XB_LOCKING_SUPPORT
+ if( iAutoLock ){
+ if(( iRc = LockTable( XB_UNLOCK )) != XB_NO_ERROR ){
+ iErrorStop = 240;
+ throw iRc;
+ }
+ }
+
+ #endif
+ }
+ catch (xbInt16 iRc ){
+ if( iRc != XB_LOCK_FAILED ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbdbf::Pack() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ }
+ return iRc;
+}
+
+/************************************************************************/
+//! @brief Write the current record to disk.
+/*!
+ This routine is used to write any updates to the current record buffer to disk.
+
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbDbf::PutRecord() {
+ return PutRecord(ulCurRec);
+}
+
+/************************************************************************/
+//! @brief Write record to disk.
+/*!
+ This routine is used to write a copy of the current record buffer to disk
+ for a given record number.
+
+ \param ulRecNo Record number to update.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbDbf::PutRecord(xbUInt32 ulRecNo)
+{
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+ try{
+
+ if( ulRecNo < 1 ){
+ iErrorStop = 100;
+ throw XB_INVALID_RECORD;
+ }
+
+ xbUInt32 ulRecCnt;
+ if(( iRc = GetRecordCnt( ulRecCnt )) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ throw iRc;
+ }
+
+ if( ulRecNo > ulRecCnt ){
+ iErrorStop = 120;
+ throw XB_INVALID_RECORD;
+ }
+
+ if( iDbfStatus == XB_CLOSED ){
+ iErrorStop = 130;
+ iRc = XB_NOT_OPEN;
+ throw iRc;
+ }
+ /* lock the database */
+ #ifdef XB_LOCKING_SUPPORT
+ // xbInt16 iAutoLock = GetAutoLock();
+ if( iAutoLock && !bTableLocked ){
+ if(( iRc = LockHeader( XB_LOCK )) != XB_NO_ERROR ){
+ if( iRc == XB_LOCK_FAILED )
+ return iRc;
+ else{
+ iErrorStop = 140;
+ throw iRc;
+ }
+ }
+
+ if(( iRc = ReadHeader( 1, 1 )) != XB_NO_ERROR ){
+ iErrorStop = 150;
+ LockHeader( XB_UNLOCK );
+ throw iRc;
+ }
+ }
+ #endif
+
+ // verify valid record number request
+ if( ulRecNo > ulNoOfRecs || ulRecNo == 0L ){
+ iErrorStop = 160;
+ iRc = XB_INVALID_RECORD;
+ throw iRc;
+ }
+
+ #ifdef XB_LOCKING_SUPPORT
+ if( iAutoLock ){
+ if( !bTableLocked ){
+ if(( iRc = LockRecord( XB_LOCK, ulRecNo )) != XB_NO_ERROR ){
+ if( iRc == XB_LOCK_FAILED )
+ return iRc;
+ else{
+ iErrorStop = 170;
+ throw iRc;
+ }
+ }
+ }
+
+ #ifdef XB_INDEX_SUPPORT
+ if(( iRc = LockIndices( XB_LOCK )) != XB_NO_ERROR ){
+ if( iRc == XB_LOCK_FAILED )
+ return iRc;
+ else{
+ iErrorStop = 180;
+ throw iRc;
+ }
+ }
+ #endif
+ }
+ #endif
+
+ // build keys, check for duplicate keys, add keys
+ #ifdef XB_INDEX_SUPPORT
+ xbIxList *ixList = GetIxList();
+
+ while( ixList ){
+ if(( iRc = ixList->ix->CreateKeys( 2 )) != XB_NO_ERROR ){
+ iErrorStop = 190;
+ throw iRc;
+ }
+ iRc = ixList->ix->CheckForDupKeys();
+ if( iRc != 0 ){
+ if( iRc < 0 ){
+ iErrorStop = 200;
+ throw iRc;
+ }
+ return XB_KEY_NOT_UNIQUE;
+ }
+ ixList = ixList->next;
+ }
+
+ ixList = GetIxList();
+ while( ixList ){
+
+ if(( iRc = ixList->ix->AddKeys( ulCurRec )) != XB_NO_ERROR ){
+ iErrorStop = 210;
+ throw iRc;
+ }
+ ixList = ixList->next;
+ }
+
+ ixList = GetIxList();
+ while( ixList ){
+
+// if(( iRc = ixList->ix->DeleteKeys( ulCurRec )) != XB_NO_ERROR ){
+
+ if(( iRc = ixList->ix->DeleteKeys()) != XB_NO_ERROR ){
+ iErrorStop = 220;
+ throw iRc;
+ }
+ ixList = ixList->next;
+ }
+ #endif // XB_INDEX_SUPPORT
+
+ // update latest header date if changed
+ xbDate d;
+ if( (cUpdateYY != (char)(d.YearOf() - 1900)) || (cUpdateMM != (char) d.MonthOf()) || (cUpdateDD != (char)d.DayOf( XB_FMT_MONTH))){
+ cUpdateYY = (char) d.YearOf() - 1900;
+ cUpdateMM = (char) d.MonthOf();
+ cUpdateDD = (char) d.DayOf( XB_FMT_MONTH );
+ // rewrite the header record - first 8 bytes
+ if(( iRc = WriteHeader( 1, 1 )) != XB_NO_ERROR ){
+ iErrorStop = 70;
+ throw iRc;
+ }
+ }
+
+ #ifdef XB_LOCKING_SUPPORT
+ if( iAutoLock && !bTableLocked ){
+ if(( iRc = LockHeader( XB_UNLOCK )) != XB_NO_ERROR ){
+ if( iRc == XB_LOCK_FAILED )
+ return iRc;
+ else{
+ iErrorStop = 230;
+ throw iRc;
+ }
+ }
+ }
+ #endif
+
+ // update record
+ iRc = xbFseek( (uiHeaderLen+(( (xbInt64) ulRecNo-1L ) * uiRecordLen )),0 );
+ if( iRc != XB_NO_ERROR ){
+ iErrorStop = 240;
+ throw iRc;
+ }
+
+ if( xbFwrite( RecBuf, uiRecordLen, 1 ) != XB_NO_ERROR ){
+ iErrorStop = 250;
+ iRc = XB_WRITE_ERROR;
+ throw iRc;
+ }
+
+ #ifdef XB_MEMO_SUPPORT
+ if( MemoFieldsExist() ){
+ if(( iRc = Memo->Commit()) != XB_NO_ERROR ){
+ iErrorStop = 260;
+ throw iRc;
+ }
+ }
+ #endif
+
+ // unlock record
+ #ifdef XB_LOCKING_SUPPORT
+ if( iAutoLock ){
+ #ifdef XB_INDEX_SUPPORT
+ LockIndices( XB_UNLOCK );
+ #endif // XB_INDEX_SUPPORT
+ if( !bTableLocked ){
+ if(( iRc = LockRecord( XB_UNLOCK, ulRecNo )) != XB_NO_ERROR ){
+ if( iRc == XB_LOCK_FAILED )
+ return iRc;
+ else{
+ iErrorStop = 270;
+ throw iRc;
+ }
+ }
+ }
+ }
+
+ #endif
+ ulCurRec = ulRecNo;
+ iDbfStatus = XB_OPEN;
+ }
+
+ catch (xbInt16 iRc ){
+
+ #ifdef XB_LOCKING_SUPPORT
+ if( iAutoLock ){
+ LockHeader( XB_UNLOCK );
+ LockAppend( XB_UNLOCK );
+ LockRecord( XB_UNLOCK, ulRecNo );
+ LockIndices( XB_UNLOCK );
+ }
+ #endif // XB_LOCKING_SUPPORT
+
+ if( iRc != XB_LOCK_FAILED ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbdbf::PutRecord() Exception Caught. Error Stop = [%d] iRc = [%d] record = [%d]", iErrorStop, iRc, ulRecNo );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ }
+ return iRc;
+}
+
+/************************************************************************/
+//! @brief Read dbf file header information.
+/*!
+ This method assumes the header has been locked appropriately
+ in a multi user environment
+
+
+ \param iPositionOption 0 - Don't fseek to beginning of file before read.<br>
+ 1 - Start from beginning of file.
+
+ \param iReadOption 0 - Read entire 32 byte header<br>
+ 1 - Read first eight bytes which includes the last update date and number of records.
+
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbDbf::ReadHeader( xbInt16 iPositionOption, xbInt16 iReadOption ){
+
+ char buf[32];
+ size_t iReadSize;
+
+ if(iPositionOption)
+ xbRewind();
+ if( iReadOption == 1 )
+ iReadSize = 8;
+ else
+ iReadSize = 32;
+
+ if(xbFread(buf, iReadSize, 1) != XB_NO_ERROR)
+ return XB_READ_ERROR;
+ memcpy(&cVersion, buf, 4);
+ ulNoOfRecs = eGetUInt32(&buf[4]);
+ if( iReadOption == 1 )
+ return XB_NO_ERROR;
+
+ uiHeaderLen = eGetUInt16(&buf[8]);
+ uiRecordLen = eGetUInt16(&buf[10]);
+ cTransactionFlag = buf[14];
+ cEncryptionFlag = buf[15];
+ cIndexFlag = buf[28];
+ cLangDriver = buf[29];
+ return XB_NO_ERROR;
+}
+
+/************************************************************************/
+//! @brief Return record deletion status.
+/*!
+ This routine returns the record deletion status.
+ \param iOpt 0 = Current record buffer, 1 = Original record buffer
+ \returns xbTrue - Record deleted.<br>xbFalse - Record not deleted.
+*/
+xbInt16 xbDbf::RecordDeleted( xbInt16 iOpt ) const {
+ if( !iOpt && RecBuf && RecBuf[0] == 0x2a )
+ return xbTrue;
+ else if( iOpt && RecBuf2 && RecBuf2[0] == 0x2a )
+ return xbTrue;
+ else
+ return xbFalse;
+}
+
+/************************************************************************/
+#ifdef XB_INDEX_SUPPORT
+//! @brief Remove an index from the internal list of indices for this table
+/*
+ The index list is used during any table update process to update any open
+ index file. Index files can contain one or more tags.
+
+ \param ixIn Pointer to index object for a given index file.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbDbf::RemoveIndex( xbIx * ixIn ){
+
+ xbIxList *p = ixList;
+ // if index is the first entry in the list
+ if( ixList->ix == ixIn ){
+ ixList = ixList->next;
+ delete p->sFmt;
+ delete p->ix;
+ free( p );
+ return XB_NO_ERROR;
+ }
+
+ // spin down to the correct ix
+ xbIxList *p2 = NULL;
+ while( p && p->ix != ixIn ){
+ p2 = p;
+ p = p->next;
+ }
+ if( p ){
+ p2->next = p->next;
+ delete p->sFmt;
+ delete p->ix;
+ free( p );
+ }
+ return XB_NO_ERROR;
+}
+#endif // XB_INDEX_SUPPORT
+
+/************************************************************************/
+// @brief Reset number of records.
+/*
+ Protected method. Resets number of records to 0.
+ \returns void
+*/
+void xbDbf::ResetNoOfRecords() {
+ ulNoOfRecs = 0UL;
+}
+
+/************************************************************************/
+#ifdef XB_NDXINF_SUPPORT
+// @brief Update .INF data file.
+/*
+ Protected method.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbDbf::SaveNdxInfData(){
+
+ xbInt16 iRc = 0;
+ xbInt16 iErrorStop = 0;
+ xbFile fMd( xbase );
+
+ try{
+
+ xbUInt32 llNodeCnt = llNdxInfData.GetNodeCnt();
+
+ xbString sNdxInfFileName;
+ if(( iRc = GetNdxInfFileName( sNdxInfFileName )) != XB_NO_ERROR ){
+ iErrorStop = 10;
+ throw iRc;
+ }
+
+ // open the file
+ if(( iRc = fMd.xbFopen( "w", sNdxInfFileName, GetShareMode())) != XB_NO_ERROR ){
+ iErrorStop = 20;
+ throw iRc;
+ }
+
+ xbString s1;
+ xbString s2;
+ s2.Sprintf( "[dbase]%c%c", 0x0d, 0x0a );
+ if(( iRc = fMd.xbFputs( s2 )) != XB_NO_ERROR ){
+ iErrorStop = 30;
+ throw iRc;
+ }
+
+
+ // for each entry in the linked list, write a line
+ xbLinkListNode<xbString> * llN = llNdxInfData.GetHeadNode();
+ for( xbUInt32 i = 0; i < llNodeCnt; i++ ){
+ s2 = llN->GetKey();
+ if( i > 0 )
+ s1.Sprintf( "NDX%d=%s%c%c", i, s2.Str(), 0x0d, 0x0a );
+ else
+ s1.Sprintf( "NDX=%s%c%c", s2.Str(), 0x0d, 0x0a );
+
+ if(( iRc = fMd.xbFputs( s1 )) != XB_NO_ERROR ){
+ iErrorStop = 40;
+ throw iRc;
+ }
+ llN = llN->GetNextNode();
+ }
+
+ // close the file
+ if(( iRc = fMd.xbFclose()) != XB_NO_ERROR ){
+ iErrorStop = 50;
+ throw iRc;
+ }
+
+ } catch( xbInt16 iRc ){
+ if( fMd.FileIsOpen())
+ fMd.xbFclose();
+ xbString sMsg;
+ sMsg.Sprintf( "xbdbf::SaveNdxInfData() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+#endif // XB_NDXINF_SUPPORT
+/************************************************************************/
+//! @brief Set auto commit.
+/*!
+ This routine sets the auto commit setting for this table.
+ \returns XB_NO_ERROR;
+*/
+xbInt16 xbDbf::SetAutoCommit( xbBool iAutoCommit ) {
+ this->iAutoCommit = iAutoCommit;
+ return XB_NO_ERROR;
+}
+
+
+/************************************************************************/
+//! @brief Set auto lock.
+/*!
+ This routine sets the auto lock setting for this table.
+ There is an overall system level auto lock default setting and each table
+ can have it's own autolock setting. This method controls the table level
+ auto lock setting.
+
+ \param iAutoLock 1 - Use auto lock for this table.<br>
+ 0 - Don't use auto lock for this table.<br>
+ -1 - (minus one) Use system default.<br>
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+void xbDbf::SetAutoLock( xbInt16 iAutoLock ){
+ if( iAutoLock == -1 )
+ this->iAutoLock = xbase->GetDefaultAutoLock();
+ else
+ this->iAutoLock = iAutoLock;
+}
+
+
+
+/************************************************************************/
+#ifdef XB_INDEX_SUPPORT
+//! @brief Set the current tag for the dbf file.
+/*!
+ \param sTagName - Tag Name
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbDbf::SetCurTag( const xbString &sTagName ){
+
+ if( sTagName == "" ){
+ SetCurTag( "", 0, 0 );
+ return XB_NO_ERROR;
+
+ } else {
+ xbLinkListNode<xbTag *> *llN = GetTagList();
+ xbTag *pTag;
+ while( llN ){
+ pTag = llN->GetKey();
+ if( pTag->GetTagName() == sTagName ){
+ SetCurTag( pTag->GetType(), pTag->GetIx(), pTag->GetVpTag());
+ return XB_NO_ERROR;
+ }
+ llN = llN->GetNextNode();
+ }
+ }
+
+ return XB_INVALID_TAG;
+}
+
+/************************************************************************/
+//! @brief Set the current tag for the dbf file.
+/*!
+
+ \param sIxType - One of "NDX" or MDX",
+ \param pIx - Pointer to index object.
+ \param vpTag - Pointer to tag object.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+void xbDbf::SetCurTag( const xbString &sIxType, xbIx *pIx, void *vpTag ){
+ pCurIx = pIx;
+ vpCurIxTag = vpTag;
+ sCurIxType.Set( sIxType );
+}
+#endif // XB_INDEX_SUPPORT
+
+/************************************************************************/
+//! @brief Set the header locked status.
+/*!
+ \param bHeaderLocked xbTrue - Locked<br>xbFalse - Not locked.
+ \returns void
+*/
+void xbDbf::SetHeaderLocked( xbBool bHeaderLocked ){
+ this->bHeaderLocked = bHeaderLocked;
+}
+
+/************************************************************************/
+//! @brief Set lock flavor.
+/*!
+ This routine is for future expansion.
+ \param iLockFlavor 1 - Use Dbase (tm) style locking.
+ \returns void
+*/
+
+void xbDbf::SetLockFlavor( xbInt16 iLockFlavor ){
+ this->iLockFlavor = iLockFlavor;
+}
+
+/************************************************************************/
+//! @brief Set table locked status.
+/*!
+ \param bTableLocked - xbTrue Table locked.<br>xbFalse Table unlocked.
+ \returns void
+*/
+
+void xbDbf::SetTableLocked( xbBool bTableLocked ){
+ this->bTableLocked = bTableLocked;
+}
+/************************************************************************/
+//! @brief Undelete all records.
+/*!
+ This routine will remove the deletion flag on any deleted records in the table.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbDbf::UndeleteAllRecords(){
+ return DeleteAll( 1 );
+}
+
+/************************************************************************/
+//! @brief Undelete one record.
+/*!
+ This routine will undelete the current record, if it is deleted.
+ \returns XB_NO_ERROR<br>XB_INVALID_RECORD
+*/
+xbInt16 xbDbf::UndeleteRecord()
+{
+ if( RecBuf && ulCurRec > 0 ){
+ if( RecBuf[0] != 0x20 ){
+ if( iDbfStatus != XB_UPDATED ){
+ iDbfStatus = XB_UPDATED;
+ memcpy( RecBuf2, RecBuf, uiRecordLen ); // save off original before making updates
+ }
+ RecBuf[0] = 0x20;
+ }
+ return XB_NO_ERROR;
+ }
+ else
+ return XB_INVALID_RECORD;
+}
+
+/************************************************************************/
+#ifdef XB_MEMO_SUPPORT
+//! @brief Update memo field
+/*!
+ This routine updates a memo field.
+ \param iFieldNo - Memo field number to update.
+ \param sMemoData - Memo data for update.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbDbf::UpdateMemoField( xbInt16 iFieldNo, const xbString &sMemoData ){
+ return Memo->UpdateMemoField( iFieldNo, sMemoData );
+}
+/************************************************************************/
+//! @brief Update memo field
+/*!
+ This routine updates a memo field.
+ \param sFieldName - Memo field name to update.
+ \param sMemoData - Memo data for update.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbDbf::UpdateMemoField( const xbString & sFieldName, const xbString & sMemoData ){
+ return Memo->UpdateMemoField( GetFieldNo( sFieldName ), sMemoData );
+}
+#endif
+
+
+/************************************************************************/
+#ifdef XB_INDEX_SUPPORT
+//! @brief Update SchemaIxFlag
+/*!
+ This routine can be called from the DeleteTag routine if a tag has been deleted and the flag needs reset
+ \param iFldNo - Which field the index flag needs changed on
+ \param cVal - Value to change it to
+*/
+
+void xbDbf::UpdateSchemaIxFlag( xbInt16 iFldNo, unsigned char cVal ){
+ if( cVal != 0x00 || cVal != 0x01 )
+ SchemaPtr[iFldNo].cIxFlag = cVal;
+}
+
+#endif // XB_INDEX_SUPPORT
+/************************************************************************/
+//! @brief Update tag list.
+/*!
+ This routine updates the internal tag list of open index tags.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbDbf::UpdateTagList(){
+
+ xbInt16 iErrorStop = 0;
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt32 lTagCnt;
+
+ try{
+ ClearTagList();
+
+ // For each active index
+ xbIxList *p = GetIxList();
+ xbIx *ixp;
+ while( p ){
+ ixp = p->ix;
+ // for each tag within the file
+ lTagCnt = ixp->GetTagCount();
+ for( xbInt32 l = 0; l < lTagCnt; l++ ){
+ xbTag *pTag = new xbTag( ixp, ixp->GetTag( l ), *p->sFmt, ixp->GetTagName( ixp->GetTag( l )),
+ ixp->GetKeyExpression( ixp->GetTag( l )), ixp->GetKeyFilter( ixp->GetTag( l )),
+ ixp->GetUnique( ixp->GetTag( l )), ixp->GetSortOrder( ixp->GetTag( l )));
+
+ // append it to the llTags list
+ llTags.InsertAtEnd( pTag );
+ }
+ p = p->next;
+ }
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbdbf::UpdateTagList() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+
+/************************************************************************/
+// @brief Write Header
+/*
+ Protected method.
+
+ \param iPositionOption 0 - Don't fseek to beginning of file before read.<br>
+ 1 - Go to beginning of file before read.
+ \param iWriteOption 0 - Write entire 32 byte header.<br>
+ 1 - Write first eight bytes needed for table updates - last update date and number of records.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbDbf::WriteHeader( xbInt16 iPositionOption, xbInt16 iWriteOption )
+{
+ char buf[32];
+ xbInt32 lWriteLen;
+
+ if(iPositionOption)
+ xbRewind();
+
+ memset(buf, 0, 32);
+ if( iWriteOption == 1 )
+ lWriteLen = 8;
+ else{
+ lWriteLen = 32;
+ ePutUInt16( &buf[8], uiHeaderLen );
+ ePutUInt16( &buf[10], uiRecordLen );
+ buf[14] = cTransactionFlag;
+ buf[15] = cEncryptionFlag;
+ buf[28] = cIndexFlag;
+ buf[29] = cLangDriver;
+ }
+ memcpy(&buf[0], &cVersion, 4);
+ ePutUInt32( &buf[4], ulNoOfRecs);
+ if(xbFwrite(buf, (size_t) lWriteLen, 1) != XB_NO_ERROR)
+ return XB_WRITE_ERROR;
+
+ return XB_NO_ERROR;
+}
+
+/************************************************************************/
+//! @brief Zap (remove) everything from the file,
+/*!
+ This routine eliminates everything from the dbf file and dbt memo file.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbDbf::Zap(){
+
+ xbInt16 iRc = 0;
+ xbInt16 iErrorStop = 0;
+
+ try{
+ if( iDbfStatus != XB_OPEN ){
+ iErrorStop = 10;
+ iRc = XB_DBF_FILE_NOT_OPEN;
+ throw iRc;
+ }
+ #ifdef XB_LOCKING_SUPPORT
+ if( iAutoLock ){
+ if(( iRc = LockTable( XB_LOCK )) != XB_NO_ERROR ){
+ iErrorStop = 20;
+ throw iRc;
+ }
+ }
+ #endif
+
+ xbDate d;
+ cUpdateYY = (char) d.YearOf() - 1900;
+ cUpdateMM = (char) d.MonthOf();
+ cUpdateDD = (char) d.DayOf( XB_FMT_MONTH );
+ ulNoOfRecs = 0;
+
+ // rewrite the header record
+ if(( iRc = WriteHeader( 1, 1 )) != XB_NO_ERROR ){
+ iErrorStop = 30;
+ throw iRc;
+ }
+
+ // truncate the file to the new size
+ if(( iRc = xbTruncate( uiHeaderLen )) != XB_NO_ERROR ){
+ iErrorStop = 40;
+ throw iRc;
+ }
+ BlankRecord();
+ ulCurRec = 0;
+
+ #ifdef XB_MEMO_SUPPORT
+ if( iMemoFieldCnt ){
+ if(( iRc = Memo->Zap()) != XB_NO_ERROR ){
+ iErrorStop = 50;
+ throw iRc;
+ }
+ }
+ #endif
+ #ifdef XB_INDEX_SUPPORT
+ xbLinkListNode<xbTag *> *llN = GetTagList();
+ xbTag *pTag;
+ xbIx *pIx;
+ void *vpTag;
+ while( llN ){
+ pTag = llN->GetKey();
+ pIx = pTag->GetIx();
+ vpTag = pTag->GetVpTag();
+ if(( iRc = pIx->Reindex( &vpTag )) != XB_NO_ERROR ){
+ iErrorStop = 60;
+ throw iRc;
+ }
+ llN = llN->GetNextNode();
+ }
+
+ #endif // XB_INDEX_SUPPORT
+ #ifdef XB_LOCKING_SUPPORT
+ if( iAutoLock ){
+ if(( iRc = LockTable( XB_UNLOCK )) != XB_NO_ERROR ){
+ iErrorStop = 70;
+ throw iRc;
+ }
+ }
+ #endif // XB_LOCKING_SUPPORT
+ }
+ catch (xbInt16 iRc ){
+ if( iRc != XB_LOCK_FAILED ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbdbf::Zap() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ }
+ return iRc;
+}
+/************************************************************************/
+} /* namespace */ \ No newline at end of file