/* 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 */