From 4875a3dd9b183dcd2256e2abfc4ccf7484c233b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Frings-F=C3=BCrst?= Date: Wed, 7 Dec 2022 13:17:14 +0100 Subject: New upstream version 4.0.2 --- src/core/xbdbf.cpp | 4220 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 4220 insertions(+) create mode 100755 src/core/xbdbf.cpp (limited to 'src/core/xbdbf.cpp') 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 Return Codes +*/ +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 Return Codes + +*/ + +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.
+ + To add a record, an application would typically blank the record buffer, + update various fields in the record buffer, then append the record.
+ + The append method performs the following tasks:
+ 1) Create new index key values
+ 2) Lock the table
+ 3) Lock append bytes
+ 4) Lock indices
+ 5) Read the dbf header
+ 6) Check for dup keys
+ 7) Calc last update date, no of recs
+ 8) Add keys
+ 9) Unlock indices
+ 10) Update file header
+ 11) Unlock file header
+ 12) Append record
+ 13) Unlock append bytes
+ +Note: Locking memo files is not needed as the memo file updates are handled outside of the append method.
+ + \returns Return Codes +*/ + +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.
+ + This routine requires NDXINF support be enabled when building the library.
+ This routine creates a file with the same name as the DBF file, but with an extension of INF.
+ + + \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
+ 1 - Remove index from .INF if there + + \returns Return Codes + +*/ + +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 * 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 Return Codes +*/ + +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
+ 1 - Check all tag
+ + \param iOutputOpt Output message destination
+ 0 = stdout
+ 1 = Syslog
+ 2 = Both
+ + \returns Return Codes +*/ + +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 *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
+ 1 - Reindex all tags
+ + \returns Return Codes +*/ + +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 *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 Return Codes +*/ + +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 Return Codes +*/ +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 Return Codes +*/ + +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.
+ xbFalse - Don't overlay existing file. + \param iShareMode XB_SINGLE_USER
+ XB_MULTI_USER + \returns Return Codes +*/ + +//! 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.
+ xbFalse - ascending + \param iUnique xbTrue - Unique index
xbFalse - Not unique index. + \param iOverLay xbTrue - Overlay if exists
+ 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 Return Codes +*/ + +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.
+ 1 - Un-delete all deleted records. + \returns Return Codes +*/ + +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 Return Codes +*/ +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 Return Codes +*/ + +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.
+ + This routine requires NDXINF support be enabled when building the library.
+ This routine deletes the .INF file.
+ + \returns Return Codes +*/ +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
+ 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 Return Codes +*/ + +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".
+ \param sName Tag name to delete.
+ \returns Return Codes +*/ + +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
+ 2 = Field data only
+ 3 = Header and Field data
+ 4 = Header, Field and Memo header data if applicable + \returns Return Codes +*/ + +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
+ 1 = Syslog
+ 2 = Both
+ + \param iOutputFmt 0 = with field names
+ 1 = 1 line per rec, no field names
+ 2 = 1 line per rec, first line is a list of field names. + \returns Return Codes +*/ +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 * 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.
+ 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
+ 1 - If this table should be auto updated (takes DBMS setting into account) + \returns Not 0 - Auto commit on for this table.
+ 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.
+ MDX for production multi tag index. +*/ +const xbString &xbDbf::GetCurIxType() const { + return sCurIxType; +} + +/************************************************************************/ +//! @brief Get the current tag name. +/*! + \returns Current Tag Name.
+*/ + +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 Return Codes + +*/ +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 Return Codes + +*/ +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 Return Codes + +*/ +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 Return Codes + +*/ +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 Return Codes + +*/ +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 Return Codes + +*/ +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 Return Codes + +*/ +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
+ 1 = open
+ 2 = updates pending
+*/ +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 * 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 Return Codes +*/ +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.
+ XB_ACTIVE_RECS - Get the first active record.
+ XB_DELETED_RECS - Get the first deleted record.
+ \returns Return Codes +*/ +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 Return Codes +*/ +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.
+ XB_ACTIVE_RECS - Get the last active record.
+ XB_DELETED_RECS - Get the last deleted record.
+ \returns Return Codes +*/ +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 Return Codes +*/ + +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 * 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 Return Codes +*/ +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.
+ XB_ACTIVE_RECS - Get the next active record.
+ XB_DELETED_RECS - Get the next deleted record.
+ \returns Return Codes +*/ +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.
+ XB_ACTIVE_RECS - Get the next active record.
+ XB_DELETED_RECS - Get the next deleted record.
+ \param ulStartRec Get next record, starting from ulStartRec. + \returns Return Codes +*/ + +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 Return Codes +*/ + +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.
+ XB_ACTIVE_RECS - Get the previous active record.
+ XB_DELETED_RECS - Get the previous deleted record.
+ \returns Return Codes +*/ +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 Return Codes +*/ +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 Return Codes +*/ +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.
+ + 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.
+ + \returns Tag list for the file/table. +*/ +xbLinkListNode *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 Return Codes +*/ + +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
XB_UNLOCK + \returns Return Codes +*/ +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
XB_UNLOCK + \returns Return Codes +*/ +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
XB_UNLOCK + \returns Return Codes +*/ +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
XB_UNLOCK + \returns Return Codes +*/ +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
XB_UNLOCK + \param ulRecNo Record number to lock + \returns Return Codes +*/ + +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
XB_UNLOCK + \returns Return Codes +*/ + +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.
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 Return Codes +*/ +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 Return Codes +*/ + +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 * 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 Return Codes +*/ + +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 Return Codes +*/ +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 Return Codes +*/ + +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 Return Codes +*/ +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 Return Codes +*/ + +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.
+ 1 - Start from beginning of file. + + \param iReadOption 0 - Read entire 32 byte header
+ 1 - Read first eight bytes which includes the last update date and number of records. + + \returns Return Codes +*/ +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.
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 Return Codes +*/ + +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 Return Codes +*/ + +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 * 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.
+ 0 - Don't use auto lock for this table.
+ -1 - (minus one) Use system default.
+ \returns Return Codes +*/ + +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 Return Codes +*/ +xbInt16 xbDbf::SetCurTag( const xbString &sTagName ){ + + if( sTagName == "" ){ + SetCurTag( "", 0, 0 ); + return XB_NO_ERROR; + + } else { + xbLinkListNode *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 Return Codes +*/ + +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
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.
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 Return Codes +*/ +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
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 Return Codes +*/ +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 Return Codes +*/ +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 Return Codes +*/ +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.
+ 1 - Go to beginning of file before read. + \param iWriteOption 0 - Write entire 32 byte header.
+ 1 - Write first eight bytes needed for table updates - last update date and number of records. + \returns Return Codes +*/ +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 Return Codes +*/ +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 *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 -- cgit v1.2.3