From c894a7cdd8686ea695602a23a511a3f1b0d047be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Frings-F=C3=BCrst?= Date: Mon, 14 Aug 2023 21:07:46 +0200 Subject: New upstream version 4.1.4 --- src/core/xbbcd.cpp | 301 +++ src/core/xbblockread.cpp | 279 +++ src/core/xbdate.cpp | 867 +++++++++ src/core/xbdbf.cpp | 4533 +++++++++++++++++++++++++++++++++++++++++++ src/core/xbdbf3.cpp | 768 ++++++++ src/core/xbdbf4.cpp | 885 +++++++++ src/core/xbexp.cpp | 2721 ++++++++++++++++++++++++++ src/core/xbexpnode.cpp | 562 ++++++ src/core/xbfields.cpp | 1189 ++++++++++++ src/core/xbfile.cpp | 2217 +++++++++++++++++++++ src/core/xbfilter.cpp | 544 ++++++ src/core/xbfuncs.cpp | 851 ++++++++ src/core/xbixbase.cpp | 789 ++++++++ src/core/xbixmdx.cpp | 4844 ++++++++++++++++++++++++++++++++++++++++++++++ src/core/xbixndx.cpp | 2834 +++++++++++++++++++++++++++ src/core/xbixtdx.cpp | 661 +++++++ src/core/xblog.cpp | 227 +++ src/core/xbmemo.cpp | 219 +++ src/core/xbmemo3.cpp | 585 ++++++ src/core/xbmemo4.cpp | 1336 +++++++++++++ src/core/xbssv.cpp | 658 +++++++ src/core/xbstring.cpp | 2000 +++++++++++++++++++ src/core/xbtag.cpp | 121 ++ src/core/xbtblmgr.cpp | 312 +++ src/core/xbuda.cpp | 78 + src/core/xbxbase.cpp | 803 ++++++++ 26 files changed, 31184 insertions(+) create mode 100755 src/core/xbbcd.cpp create mode 100755 src/core/xbblockread.cpp create mode 100755 src/core/xbdate.cpp create mode 100755 src/core/xbdbf.cpp create mode 100755 src/core/xbdbf3.cpp create mode 100755 src/core/xbdbf4.cpp create mode 100755 src/core/xbexp.cpp create mode 100755 src/core/xbexpnode.cpp create mode 100755 src/core/xbfields.cpp create mode 100755 src/core/xbfile.cpp create mode 100755 src/core/xbfilter.cpp create mode 100755 src/core/xbfuncs.cpp create mode 100755 src/core/xbixbase.cpp create mode 100755 src/core/xbixmdx.cpp create mode 100755 src/core/xbixndx.cpp create mode 100755 src/core/xbixtdx.cpp create mode 100755 src/core/xblog.cpp create mode 100755 src/core/xbmemo.cpp create mode 100755 src/core/xbmemo3.cpp create mode 100755 src/core/xbmemo4.cpp create mode 100755 src/core/xbssv.cpp create mode 100755 src/core/xbstring.cpp create mode 100755 src/core/xbtag.cpp create mode 100755 src/core/xbtblmgr.cpp create mode 100755 src/core/xbuda.cpp create mode 100755 src/core/xbxbase.cpp (limited to 'src/core') diff --git a/src/core/xbbcd.cpp b/src/core/xbbcd.cpp new file mode 100755 index 0000000..f86e74f --- /dev/null +++ b/src/core/xbbcd.cpp @@ -0,0 +1,301 @@ +/* xbbcd.cpp + +XBase64 Software Library + +Copyright (c) 1997,2003,2014,2022,2023 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 + + BCD class + +*/ + +#include "xbase.h" + +#ifdef XB_INDEX_SUPPORT +///@cond DOXYOFF +namespace xb{ + + + +xbBool bcdBitSet( unsigned char c, xbInt16 iBitNo ){ + return c & 1 << iBitNo; +} +void bcdBitDump( unsigned char c ){ + for( int i = 7; i >= 0; i-- ) + std::cout << (bcdBitSet( c, i ) ? 1 : 0); +} +void bcdBitDump( char c ){ + bcdBitDump( (unsigned char) c ); +} + + +/***********************************************************************/ +void xbBcd::ctor(){ + memset( &bcd, 0x00, sizeof( xbBcdStruct )); +} +/***********************************************************************/ +xbBcd::xbBcd( xbDouble d ) { + Set( d ); +} +/***********************************************************************/ +xbBcd::xbBcd( const xbString &sNumIn ) { + Set( sNumIn ); +} +/***********************************************************************/ +xbBcd::xbBcd( const void *vBcdIn ) { + memcpy( &bcd, vBcdIn, sizeof( xbBcdStruct )); +} +/***********************************************************************/ +void xbBcd::Set( xbDouble d ) { + ctor(); + xbString s( d ); + StringToBcd( s ); +} +/***********************************************************************/ +void xbBcd::Set( const xbString &sNumIn ) { + ctor(); + StringToBcd( sNumIn ); +} +/***********************************************************************/ +void xbBcd::Set( const void *vBcdIn ) { + memcpy( &bcd, vBcdIn, sizeof( xbBcdStruct )); +} +/***********************************************************************/ +void xbBcd::StringToBcd( const xbString &sIn ) +{ + + unsigned char cEdc = 0; // encoded digit count + xbUInt32 iPos; // current position in source string + xbBool bDecHit = xbFalse; // decimal position hit? + unsigned char *p = bcd.cData; // pointer to cData + xbInt16 iBytePos = 0; // next load position in xbs structure + xbInt16 iBcdDataPos = 0; // current position in output structure + xbByteSplit xbs; // combiner + + + ctor(); + xbString sNum( sIn ); + xbBool bSign = xbFalse; + + sNum.Trim(); + if( sNum[1] == '-' ){ + bSign = 1; + sNum.ZapLeadingChar( '-' ); + } + sNum.ZapLeadingChar( '0' ); + + xbInt16 iDecPos = sNum.Pos( '.' ); + if( iDecPos > 0 ){ + sNum.ZapTrailingChar( '0' ); + cEdc = (unsigned char) (sNum.Len() - 1); + } else { + cEdc = (unsigned char) (sNum.Len()); + } + if( cEdc > 31 ) cEdc = 31; // max 5 bit number + + + if( sNum[1] == '.' ){ + iPos = 2; + bDecHit = xbTrue; + while( sNum[iPos] == '0' && iPos <= sNum.Len()){ + bcd.cSigDigits--; + iPos++; + } + } else { + iPos = 1; + } + + while( iPos <= sNum.Len() ){ + if( sNum[iPos] == '.' ) + bDecHit = true; + else{ + if( !bDecHit ){ + bcd.cSigDigits++; + } + if( iBytePos++ == 0 ){ + xbs.c2 = (unsigned) sNum[iPos] - 0x30; + } else { + xbs.c1 = (unsigned) sNum[iPos] - 0x30; + iBytePos = 0; + if( iBcdDataPos++ < 10 ){ + memcpy( p++, &xbs, 1 ); + xbs.c1 = 0x00; + } + } + } + iPos++; + } + + if( iBytePos == 1 && iBcdDataPos < 10 ){ + memcpy( p, &xbs, 1 ); + } + + bcd.cSigDigits += 52; + bcd.cEncDigits = cEdc << 2; + bcd.cEncDigits = bcd.cEncDigits | 0x01; + if( bSign ) + bcd.cEncDigits = bcd.cEncDigits | 0x80; + +} +/***********************************************************************/ +void xbBcd::ToChar( char * cOut ){ + + memcpy( cOut, &bcd, sizeof( xbBcdStruct )); +} +/***********************************************************************/ +void xbBcd::ToDouble( xbDouble &d ){ + xbString s; + ToString( s ); + d = atof( s.Str()); +} +/***********************************************************************/ +void xbBcd::ToString( xbString &sStringOut ){ + +// printf( "\n\n\nToString " ); +// printf( "Sig digits [%d] EncodedDigits [%d] sign [%d] sizeof struct [%d]\n", +// bcd.cSigDigits - 52 , bcd.EncDigits, bcd.Sign, sizeof( xbBcdStruct )); + +// this routine converts a bcd numeric key value to a base 10 number in a string + + + xbBool bHasDot = false; + xbInt16 iSig = bcd.cSigDigits - 52; + xbByteSplit xbs; + unsigned char *p = bcd.cData; + unsigned char c; + +// xbInt16 iEnc = bcd.cEncDigits; + xbInt16 iEnc = GetEncDigitsNoSign(); + + // set the sign + // if( bcd.Sign ) + if( bcd.cEncDigits >> 7 ) + sStringOut = "-"; + else + sStringOut = ""; + + // do add any needed zeroes after the decimal + if( iSig <= 0 ){ + if( iEnc > 0 ){ + sStringOut.Append( "." ); + bHasDot = true; + } + for( xbInt16 i = iSig; i < 0; i++ ) + sStringOut.Append( "0" ); + } + + // do the encoded digits + while( iEnc > 0 ){ + if( iSig == 0 && !bHasDot ) + sStringOut.Append( "." ); + c = *p++; + memcpy( &xbs, &c, 1 ); + c = xbs.c2 + 0x30; + sStringOut.Append((char) c ); + iSig--; + iEnc--; + if( iEnc > 0 ){ + if( iSig == 0 && !bHasDot ) + sStringOut.Append( "." ); + c = xbs.c1 + 0x30; + sStringOut.Append((char) c ); + iSig--; + iEnc--; + } + } + // do the trailing zeroes + while( iSig-- > 0 ) + sStringOut.Append( "0" ); + if( sStringOut == "" ) + sStringOut = "0"; + +} + +/***********************************************************************/ +xbInt16 xbBcd::Compare( xbDouble d ){ + xbBcd bcdIn( d ); + + xbString s; + bcdIn.ToString( s ); + return Compare( bcdIn ); +} + +/***********************************************************************/ +xbInt16 xbBcd::Compare( const xbBcd &bcdIn ){ + // if this == bcdIn return 0 + // if this < bcdIn return -1 + // if this > bcdIn return 1 + + + xbInt16 iRc = 0; +// if( bcd.Sign != bcdIn.GetSign() ){ +// bcd.Sign > 0 ? iRc = -1 : iRc = 1; + + if( (unsigned)(bcd.cEncDigits >> 7 ) != bcdIn.GetSign() ){ + (bcd.cEncDigits >> 7 ) > 0 ? iRc = -1 : iRc = 1; + return iRc; + } + + if( (unsigned) bcd.cSigDigits != bcdIn.GetSigDigits()){ + // if( !bcd.Sign ){ // positive numbers + if( !(bcd.cEncDigits >> 7 )){ // positive numbers + if( (unsigned) bcd.cSigDigits > bcdIn.GetSigDigits()) + return 1; + else + return -1; + } else { // negative numbers + if( (unsigned) bcd.cSigDigits > bcdIn.GetSigDigits()) + return -1; + else + return 1; + } + } + +// iRc = xbXBase::xbMemcmp( bcd.cData, bcdIn.GetData(), (size_t)((bcd.cEncDigits + 1) / 2) ); + iRc = xbXBase::xbMemcmp( bcd.cData, bcdIn.GetData(), (size_t)((GetEncDigitsNoSign() + 1) / 2) ); + if( iRc == 0 ) + return 0; + // else if((!bcd.Sign && iRc > 0) || (bcd.Sign && iRc < 0 )) + else if((!(bcd.cEncDigits >> 7) && iRc > 0) || ((bcd.cEncDigits >> 7) && iRc < 0 )) + return 1; + else + return -1; +} + +/***********************************************************************/ +unsigned char xbBcd::GetEncDigitsNoSign() const { + unsigned char c = bcd.cEncDigits << 1; + return c >> 3; +} +/***********************************************************************/ +unsigned xbBcd::GetSign() const { + //return bcd.Sign; + return (unsigned) bcd.cEncDigits >> 7; +} +/***********************************************************************/ +unsigned xbBcd::GetSigDigits() const { + return bcd.cSigDigits; +} +/***********************************************************************/ +unsigned xbBcd::GetActualSigDigits() const { + return bcd.cSigDigits - (xbUInt32) 52; +} +/***********************************************************************/ +const unsigned char * xbBcd::GetData() const { + const unsigned char *p = bcd.cData; + return p; +} +/***********************************************************************/ +const void * xbBcd::GetBcd() const { + return &bcd; +} +} /* namespace */ +///@endcond DOXYOFF +#endif /* XB_INDEX_SUPPORT */ + diff --git a/src/core/xbblockread.cpp b/src/core/xbblockread.cpp new file mode 100755 index 0000000..7e2c5fc --- /dev/null +++ b/src/core/xbblockread.cpp @@ -0,0 +1,279 @@ +/* xbblockread.cpp + +XBase64 Software Library + +Copyright (c) 1997,2003,2014,2022,2023 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 + +This module handles block read methods. Block reading is used for performance improvement +during sequential access processing. + +*/ + +#include "xbase.h" + +#ifdef XB_BLOCKREAD_SUPPORT + +namespace xb{ + +/************************************************************************/ +xbBlockRead::xbBlockRead( xbDbf * d ) { + pBlock = NULL; + ulBlkSize = 0; + ulFirstBlkRec = 0; + ulRecCnt = 0; + ulMaxRecs = 0; + bEof = xbFalse; + this->dbf = d; + tFmTime = 0; +} +/************************************************************************/ +xbBlockRead::~xbBlockRead(){ + if( pBlock ){ + free( pBlock ); + pBlock = NULL; + } +} + +/************************************************************************/ +//! @brief Dump read block internals to stdout. +/*! + Dump the current read block internals to stdout. +*/ + + +#ifdef XB_DEBUG_SUPPORT +void xbBlockRead::DumpReadBlockInternals(){ + + xbUInt32 ulRecCnt; + dbf->GetRecordCnt( ulRecCnt ); + + std::cout << "------- DumpBlockInternals ---------" << std::endl; + std::cout << "Dflt Blk Size = [" << dbf->GetXbasePtr()->GetDefaultBlockReadSize() + << "]" << std::endl; + std::cout << "Dbf Record Count = [" << ulRecCnt << "]" << std::endl; + std::cout << "Dbf Record Len = [" << dbf->GetRecordLen() << "]" << std::endl; + std::cout << "ulBlkSize = [" << ulBlkSize << "]" << std::endl; + std::cout << "ulMaxRecs = [" << ulMaxRecs << "]" << std::endl; + std::cout << "ulFirstBlkRec = [" << ulFirstBlkRec << "]" << std::endl; + std::cout << "ulRecCnt = [" << ulRecCnt << "]" << std::endl; + std::cout << "bEof = [" << bEof << "]" << std::endl; +} +#endif // XB_DEBUG_SUPPORT +/************************************************************************/ +//! @brief Get the first record number in the current block. +/*! + Retrieve the first record numer in the current block.
+ + \returns First record number in the current block. +*/ + +xbUInt32 xbBlockRead::GetBlockFirstRecNo() const{ + return ulFirstBlkRec; +} +/************************************************************************/ +//! @brief Get record for specified record number. +/*! + Retrieve a record from read block buffer and copy it into the record buffer. + If the current record is not in the current block, the routine calls + GetBlockForRecNo to load the currect block from disk.

+ For performance reasons, this method assumes a valid record number has been + passed.

+ + \param ulRecNo - Record number to retrieve. + \returns Return Codes +*/ + +xbInt16 xbBlockRead::GetRecord( xbUInt32 ulRecNo ){ + xbInt16 iRc = 0; + xbInt16 iErrorStop = 0; + + // std::cout << "xbBlockRead::GetRecord( " << ulRecNo << ")\n"; + + try{ + if( !( ulRecNo >= ulFirstBlkRec && ulRecNo < (ulFirstBlkRec + ulRecCnt))){ + if(( iRc = GetBlockForRecNo( ulRecNo )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + } + char *s = pBlock; + s += (ulRecNo - ulFirstBlkRec) * dbf->GetRecordLen(); + char *t = dbf->RecBuf; + xbUInt32 ulRecLen = dbf->GetRecordLen(); + for( xbUInt32 l = 0; l < ulRecLen; l++ ){ + *t = *s; + t++; + s++; + } + dbf->ulCurRec = ulRecNo; + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbBlockRead::GetBlockForRecNo() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + dbf->GetXbasePtr()->WriteLogMessage( sMsg.Str() ); + dbf->GetXbasePtr()->WriteLogMessage( dbf->GetXbasePtr()->GetErrorMessage( iRc )); + } + return iRc; +} + +/************************************************************************/ +//! @brief Get record for specified record number. +/*! + Retrieve a block containing specified record. This routine calculates the + correct block in the DBF file, updates the internal block fields and retrieves + the block of records from disk and loads into the block buffer.

+ + \param ulRecNo - Record number to retrieve. + \returns Return Codes +*/ + +xbInt16 xbBlockRead::GetBlockForRecNo( xbUInt32 ulRecNo ){ + + xbInt16 iRc = 0; + xbInt16 iErrorStop = 0; + + try{ + xbUInt32 ulDbfRecCnt; + + if(( iRc = dbf->GetRecordCnt( ulDbfRecCnt )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + + // calc to determine block number for the requested record, 0 based offset + xbUInt32 ulBlockNo = (xbUInt32)(ulRecNo / ulMaxRecs); + if( ulRecNo % ulMaxRecs == 0 ) ulBlockNo--; + + // calc the first record + ulFirstBlkRec = (ulBlockNo * ulMaxRecs); + + // calc the record count + if(( ulFirstBlkRec + ulMaxRecs) > ulDbfRecCnt ){ + ulRecCnt = ulDbfRecCnt - ulFirstBlkRec; + bEof = xbTrue; + } else { + ulRecCnt = ulMaxRecs; + bEof = xbFalse; + } + + // position accordingly + xbInt64 ulStartPos = dbf->GetHeaderLen() + ((xbInt64) ulFirstBlkRec * dbf->GetRecordLen()); + if(( dbf->xbFseek( ulStartPos, SEEK_SET )) != XB_NO_ERROR ){ + iErrorStop = 110; + iRc = XB_SEEK_ERROR; + throw iRc; + } + + if(( dbf->GetFileMtime( tFmTime )) != XB_NO_ERROR ){ + iErrorStop = 120; + throw iRc; + } + // read it + if(( dbf->xbFread( (void *) pBlock, ulRecCnt * dbf->GetRecordLen(), 1 )) != XB_NO_ERROR ){ + iErrorStop = 130; + throw iRc; + } + + ulFirstBlkRec++; // zero offset in the routine, regular record number from ths point forward + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbBlockRead::GetBlockForRecNo() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + dbf->GetXbasePtr()->WriteLogMessage( sMsg.Str() ); + dbf->GetXbasePtr()->WriteLogMessage( dbf->GetXbasePtr()->GetErrorMessage( iRc )); + } + return iRc; +} + +/************************************************************************/ +//! @brief Get the current block size. +/*! + Retrieve the current block size.

+ + \returns Current Block Size. +*/ + +xbUInt32 xbBlockRead::GetBlockSize() const{ + return ulBlkSize; +} + +/************************************************************************/ +//! @brief Get the current block record count. +/*! + Retrieve the current number of records loaded in the block.

+ + \returns Current Block Size. +*/ + +xbUInt32 xbBlockRead::GetBlockRecCnt() const { + return ulRecCnt; +} + +/************************************************************************/ +//! @brief Init the block processing for a iven DBF file. +/*! + Initialize the settings for a given DBF file.
+ This routine may adjust the block size as needed to eliminate unused + memory or adjust it bigger if too small. + + \param ulRecNo - ulBlockSize - Block size to allocate. If 0 or missing, it uses default block size of 32K. + \returns Return Codes +*/ + +xbInt16 xbBlockRead::Init(xbUInt32 ulBlockSize ){ + + xbInt16 iRc = 0; + xbInt16 iErrorStop = 0; + + try{ + + // calculate the block size + if( ulBlockSize == 0 ) + ulBlkSize = dbf->GetXbasePtr()->GetDefaultBlockReadSize(); + + // if not big enough to handle more than one record, bump it up to something meaningful + if( ulBlkSize < (xbUInt32)(dbf->GetRecordLen() * 2 )) + ulBlkSize = (xbUInt32) dbf->GetRecordLen() * 10; + + ulMaxRecs = (xbUInt32) ulBlkSize / dbf->GetRecordLen(); + ulBlkSize = ulMaxRecs * dbf->GetRecordLen(); + + // allocate memory for the block + if(( pBlock = (char *) calloc( 1, ulBlkSize )) == NULL ){ + iErrorStop = 100; + throw iRc; + } + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbBlockRead::Init() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + dbf->GetXbasePtr()->WriteLogMessage( sMsg.Str() ); + dbf->GetXbasePtr()->WriteLogMessage( dbf->GetXbasePtr()->GetErrorMessage( iRc )); + } + return iRc; +} + +/************************************************************************/ +//! @brief Set the block size. +/*! + St block size for this DBF file.
. + + \param ulBlkSize - Block Size. + \returns XB_NO_ERROR +*/ + +xbInt16 xbBlockRead::SetBlockSize( xbUInt32 ulBlkSize ){ + this->ulBlkSize = ulBlkSize; + return XB_NO_ERROR; +} + +/************************************************************************/ +} /* namespace */ +#endif /* XB_BLOCKREAD_SUPPORT */ \ No newline at end of file diff --git a/src/core/xbdate.cpp b/src/core/xbdate.cpp new file mode 100755 index 0000000..54834ac --- /dev/null +++ b/src/core/xbdate.cpp @@ -0,0 +1,867 @@ +/* xbdate.cpp + +XBase64 Software Library + +Copyright (c) 1997,2003,2014,2022,2023 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" +#include + +namespace xb{ + +int xbDate::iDaysInMonths[2][13]; +int xbDate::iAggregatedDaysInMonths[2][13]; + +/*************************************************************************/ +//! @brief Constructor. + +xbDate::xbDate() { + sDate8.Set( "" ); + SetDateTables(); +} + +/*************************************************************************/ +//! @brief Constructor. +/*! + \param iInitOpt - Constructor to use to initialize date static variables + Called by the main xbXbase::xbXBase constructor +*/ + +xbDate::xbDate( xbUInt16 ) { + + SetDateTables(); + Sysdate(); +} +/*************************************************************************/ +//! @brief Constructor. +/*! + \param sDate8In - Input date. +*/ + +xbDate::xbDate( const xbString & sDate8In ) { + + if( DateIsValid( sDate8In )) + sDate8.Set( sDate8In ); + else + sDate8.Set( "" ); + + // SetDateTables(); +} + +/*************************************************************************/ +//! @brief Constructor. +/*! + \param sDate8In - Input date. +*/ +xbDate::xbDate( const char * sDate8In ) { + + if( DateIsValid( sDate8In )) + sDate8.Set( sDate8In ); + else + sDate8.Set( "" ); + + // SetDateTables(); +} + +/*************************************************************************/ +//! @brief Constructor. +/*! + \param lJulDate - Input julian date. +*/ +xbDate::xbDate( xbInt32 lJulDate ) { + // SetDateTables(); + JulToDate8( lJulDate ); +} + +/*************************************************************************/ +//! @brief Destructor. +xbDate::~xbDate(){} +/*************************************************************************/ +//! @brief Set operator= +/*! + \param dt - Date value for set operation. +*/ +void xbDate::operator=( const xbDate & dt ){ + sDate8.Set( dt.Str()); +} +/*************************************************************************/ +//! @brief operator += +/*! + This routine adds lDays to the date if the date is not null. + \param lDays - Number of days to add to the date. +*/ +void xbDate::operator+=( xbInt32 lDays ){ + if( !IsNull() ) + JulToDate8( JulianDays() + lDays ); +} +/*************************************************************************/ +//! @brief operator -= +/*! + This routine subtracts lDays from the date if the date is not null. + \param lDays - Number of days to subtract from the date. +*/ +void xbDate::operator-=( xbInt32 lDays ){ + if( !IsNull() ) + JulToDate8( JulianDays() - lDays ); +} +/*************************************************************************/ +//! @brief operator ++ +/*! + This routine adds one day to the date field if the date is not null. +*/ +void xbDate::operator++(xbInt32){ + if( !IsNull() ) + *this+=1; +} +/*************************************************************************/ +//! @brief operator -- +/*! + This routine subtracts one day from the date field if the date is not null. +*/ +void xbDate::operator--(xbInt32){ + if( !IsNull()) + *this-=1; +} +/*************************************************************************/ +//! @brief operator - +/*! + This routine subtracts one date from another date returning the difference. + \param dt - Date to subtract + \returns Number of days difference or zero if one of the dates is null. +*/ +xbInt32 xbDate::operator-( const xbDate &dt ) const{ + if( !IsNull() && !dt.IsNull() ) + return JulianDays() - dt.JulianDays(); + else + return 0; +} +/*************************************************************************/ +//! @brief operator + +/*! + This routine adds additional days to a valid date field. + \param lCount - Number of days to add. + \returns New date in CCYYMMDD format. +*/ +const char *xbDate::operator+( xbInt32 lCount ){ + if( !IsNull() ) + JulToDate8( JulianDays() + lCount ); + return sDate8.Str(); +} +/*************************************************************************/ +//! @brief operator - +/*! + This routine subtracts days from a valid date field. + \param lCount - Number of days to subtract. + \returns New date in CCYYMMDD format. +*/ +const char *xbDate::operator-( xbInt32 lCount ){ + if( !IsNull() ) + JulToDate8( JulianDays() - lCount ); + return sDate8; +} +/*************************************************************************/ +//! @brief operator == +/*! + This routine compares two dates for equality. + \param dt - Date to compare. + \returns xbTrue - Dates match.
xbFalse - Dates don't match. +*/ +xbBool xbDate::operator==( const xbDate &dt ) const{ + if( JulianDays() == dt.JulianDays() ) + return xbTrue; + else + return xbFalse; +} +/*************************************************************************/ +//! @brief operator != +/*! + This routine compares two dates for inequality. + \param dt - Date to compare. + \returns xbTrue - Dates don't match.
xbFalse - Dates match. +*/ +xbBool xbDate::operator!=( const xbDate &dt ) const{ + if( JulianDays() != dt.JulianDays() ) + return xbTrue; + else + return xbFalse; +} +/*************************************************************************/ +//! @brief operator < +/*! + This routine compares two dates + \param dt - Date to compare. + \returns xbTrue - Left date is less than right date.
+ xbFalse - Left date is not less than right date. +*/ +xbBool xbDate::operator<( const xbDate &dt ) const { + if( JulianDays() < dt.JulianDays() ) + return xbTrue; + else + return xbFalse; +} +/*************************************************************************/ +//! @brief > +/*! + This routine compares two dates + \param dt - Date to compare. + \returns xbTrue - Left date is greater than right date.
+ xbFalse - Left date is not greater than right date. +*/ +xbBool xbDate::operator>( const xbDate &dt ) const { + if( JulianDays() > dt.JulianDays() ) + return xbTrue; + else + return xbFalse; +} +/*************************************************************************/ +//! @brief operator <= +/*! + This routine compares two dates + \param dt - Date to compare. + \returns xbTrue - Left date is less than or equal to right date.
+ xbFalse - Left date is not less than or equal to right date. +*/ +xbBool xbDate::operator<=( const xbDate &dt ) const { + if( JulianDays() <= dt.JulianDays() ) + return xbTrue; + else + return xbFalse; +} +/*************************************************************************/ +//! @brief operator >= +/*! + This routine compares two dates + \param dt - Date to compare. + \returns xbTrue - Left date is greater than or equal to right date.
+ xbFalse - Left date is not greater than or equal to right date. +*/ +xbBool xbDate::operator>=( const xbDate &dt ) const { + if( JulianDays() >= dt.JulianDays() ) + return xbTrue; + else + return xbFalse; +} +/*************************************************************************/ +//! @brief Calculate century for a given year. +/*! + This routine calculates a century for a given year. It uses an 80/20 + rolling date window to calculate the century. + + \param iCalcYear - Two digit year to calculate a century for. + \returns Century calculated for the two digit year. +*/ +xbInt16 xbDate::CalcRollingCenturyForYear( xbInt16 iCalcYear ) const { + + xbDate d; + d.Sysdate(); + xbInt16 iThisYear = d.YearOf(); + xbInt16 iThisCentury = d.CenturyOf(); + iThisYear -= (iThisCentury * 100); + if( iThisYear < 80 && iCalcYear < (iThisYear+20) ) + return iThisCentury; + else if( iThisYear >= 80 && + iCalcYear < iThisYear && + iCalcYear >= (iThisYear-80)) + return iThisCentury; + else + return iThisCentury - 1; +} +/*************************************************************************/ +//! @brief Get century for date. +/*! + \returns the century from the valid date.\ or 0 for a null date. +*/ +xbInt16 xbDate::CenturyOf() const { + if( !IsNull() ){ + char Century[3]; + Century[0] = sDate8[1]; + Century[1] = sDate8[2]; + Century[2] = 0x00; + return( atoi( Century )); + } else { + return 0; + } +} +/*************************************************************************/ +//! @brief Get the day of the week. +/*! + \param sOutCharDay - Output character day of week (Sun-Sat). + \returns XB_INVALID_DATE
XB_NO_ERROR +*/ +xbInt16 xbDate::CharDayOf( xbString &sOutCharDay ) { + + if( !IsNull()){ + struct tm tblock; + char buf[25]; + + tblock.tm_year = YearOf() - 1900; + tblock.tm_mon = MonthOf() - 1; + tblock.tm_mday = DayOf( XB_FMT_MONTH ); + tblock.tm_hour = 0; + tblock.tm_min = 0; + tblock.tm_sec = 1; + tblock.tm_isdst = -1; + if( mktime( &tblock ) == -1 ){ + sOutCharDay = "" ; + return XB_INVALID_DATE; + } else { + strftime( buf, 25, "%A", &tblock ); + sOutCharDay = buf; + } + } + return XB_NO_ERROR;; +} +/*************************************************************************/ +//! @brief Get the month from the date. +/*! + \param sOutCharMonth - Output character month. + \returns XB_INVALID_DATE
XB_NO_ERROR +*/ +xbInt16 xbDate::CharMonthOf( xbString &sOutCharMonth ) { + + if( !IsNull()){ + struct tm tblock; + char buf[25]; + tblock.tm_year = YearOf() - 1900; + tblock.tm_mon = MonthOf() - 1; + tblock.tm_mday = DayOf( XB_FMT_MONTH ); + tblock.tm_hour = 0; + tblock.tm_min = 0; + tblock.tm_sec = 1; + tblock.tm_isdst = -1; + if( mktime( &tblock ) == -1 ){ + sOutCharMonth = ""; + return XB_INVALID_DATE; + } else { + strftime( buf, 25, "%B", &tblock ); + sOutCharMonth = buf; + } + } + return XB_NO_ERROR; +} +/*************************************************************************/ +//! @brief Check a date for valid data. +/*! + \param sDateIn - Date to check for valid formaat of CCYYMMDD. + \returns xbTrue - Valid date.
xbFalse - Not a valid date. +*/ +xbBool xbDate::DateIsValid( const xbString &sDateIn ) const { + + xbInt16 iYear, iMonth, iDay; + char sYear[5]; + char sMonth[3]; + char sDay[3]; + + if( sDateIn.Len() != 8 ) + return xbFalse; + + if(!isdigit( sDateIn[1] ) || !isdigit( sDateIn[2] ) || !isdigit( sDateIn[3] ) || + !isdigit( sDateIn[4] ) || !isdigit( sDateIn[5] ) || !isdigit( sDateIn[6] ) || + !isdigit( sDateIn[7] ) || !isdigit( sDateIn[8] ) ) + return xbFalse; + + sDay[0] = sDateIn[7]; + sDay[1] = sDateIn[8]; + sDay[2] = 0x00; + iDay = atoi( sDay ); + + sMonth[0] = sDateIn[5]; + sMonth[1] = sDateIn[6]; + sMonth[2] = 0x00; + iMonth = atoi( sMonth ); + + sYear[0] = sDateIn[1]; + sYear[1] = sDateIn[2]; + sYear[2] = sDateIn[3]; + sYear[3] = sDateIn[4]; + sYear[4] = 0x00; + iYear = atoi( sYear ); + + // valid years are 0001 thru 9999 + if( iYear < 1 || iYear > 9999 || iMonth < 1 || iMonth > 12 || iDay < 1 || iDay > 31 ) + return xbFalse; + + // April, June, September and November have 30 days + if(( iMonth==4 || iMonth==6 || iMonth==9 || iMonth==11 )&& iDay > 30 ) + return xbFalse; + + // check for February with leap year + if( iMonth == 2 ){ + if(( iYear % 4 == 0 && iYear % 100 != 0 ) || iYear % 400 == 0 ){ + if( iDay > 29 ){ + return xbFalse; + } + } else if( iDay > 28 ){ + return xbFalse; + } + } + return xbTrue; +} + +/*************************************************************************/ +//! @brief +/*! + This routine returns the numeric day. + \param iFormat + XB_FMT_WEEK Number of day in WEEK 0-6 ( Sat - Fri )
+ XB_FMT_MONTH Number of day in MONTH 1-31
+ XB_FMT_YEAR Number of day in YEAR 1-366 + \returns XB_INVALID_OPTION
XB_NO_ERROR +*/ + +xbInt16 xbDate::DayOf( xbInt16 iFormat ) const { + + if( !IsNull()){ + xbInt16 iOutDay = 0; + char sDay[3]; + xbInt16 iDay, iMonth, iYear, iDay2; + + // check for valid format switch + if( iFormat!=XB_FMT_WEEK && iFormat!=XB_FMT_MONTH && iFormat!=XB_FMT_YEAR ) + return XB_INVALID_OPTION; + + if( iFormat == XB_FMT_WEEK ){ + //DayOf( XB_FMT_MONTH, iDay ); + iDay = DayOf( XB_FMT_MONTH ); + iMonth = MonthOf(); + iYear = YearOf(); + + // The following formula uses Zeller's Congruence to determine the day of the week + if( iMonth > 2 ) // init to February + iMonth -= 2; + else { + iMonth += 10; + iYear--; + } + iDay2 = ((13 * iMonth - 1) / 5) + iDay + ( iYear % 100 ) + + (( iYear % 100 ) / 4) + ((iYear /100 ) / 4 ) - 2 * + ( iYear / 100 ) + 77 ; + + iOutDay = iDay2 - 7 * ( iDay2 / 7 ); + iOutDay == 6 ? iOutDay = 0 : iOutDay++; + } + else if( iFormat == XB_FMT_MONTH ){ + sDay[0] = sDate8[7]; + sDay[1] = sDate8[8]; + sDay[2] = 0x00; + iOutDay = atoi( sDay ); + } else { + iOutDay = iAggregatedDaysInMonths[IsLeapYear()][MonthOf()-1] + DayOf( XB_FMT_MONTH ); + } + return iOutDay; + } else { + return 0; + } +} +/*************************************************************************/ +#ifdef XB_DEBUG_SUPPORT +//! @brief Dump date information to stdout. +/*! + \param sTitle - Title for output. + \returns Return Codes +*/ +void xbDate::Dump( const char *sTitle ){ + fprintf( stdout, "%s\n sDate = [%s]\n", sTitle, sDate8.Str() ); +} + +/*************************************************************************/ +//! @brief Dump the date tables. +/*! + This dumps the internal date structures to stdout. + \returns void +*/ + +void xbDate::DumpDateTables(){ + fprintf( stdout, "Date Tables\n" ); + fprintf( stdout, "Month *-Aggragated Days-* *--Days In Month--*\n" ); + fprintf( stdout, " *-NonLeap Leap-* *--NonLeap Leap--*\n" ); + for( int i = 1; i < 13; i++ ) + fprintf( stdout, " %2d %3d %3d %3d %3d\n", i, + iAggregatedDaysInMonths[0][i],iAggregatedDaysInMonths[1][i], + iDaysInMonths[0][i], iDaysInMonths[1][i]); +} +#endif + +/*************************************************************************/ +//! @brief Format MM/DD/YY date +/*! + This routine takes an MM/DD/YY format date as input and populates a + date class with the appropriate YYYYMMDD data. + + \param sCtodInDate - MM/DD/YY formatted date as input. + \returns XB_INVALID_OPTION
XB_NO_ERROR +*/ +xbInt16 xbDate::CTOD( const xbString &sCtodInDate ){ + + if( sCtodInDate[1] != ' ' && ( sCtodInDate[3] == '\\' || sCtodInDate[3] == '/') ){ + char yy[3]; + yy[0] = sCtodInDate[7]; + yy[1] = sCtodInDate[8]; + yy[2] = 0x00; + sDate8.Sprintf( "%02d%c%c%c%c%c%c", CalcRollingCenturyForYear( atoi( yy )), + sCtodInDate[7], sCtodInDate[8], sCtodInDate[1], sCtodInDate[2], sCtodInDate[4], sCtodInDate[5] ); + return XB_NO_ERROR; + } + else{ + return XB_INVALID_DATE; + } +} +/*************************************************************************/ +//! @brief +/*! + This routine will reformat a date based on the format specifiers entered + in sFmtIn. If no input format is specified, the routine will use the + system default date format. + + \param sFmtIn - A format specifier with the following paramaters:
+ + 1) YYDDD - A julian date format + 2) YY or YYYY will print a 2 or 4 digit year + 3) M,MM,MMM or MMMM + M - one digit month if no leading zero + MM - two digit month, contains leading zero + MMM - Jan through Dec + MMMM - January through December + 4) D,DD,DDD or DDDD + D - one digit dayif no leading zero + DD - two digit day, contains leading zero + DDD - Sun through Sat (or julian if YYDDD) + DDDD - Sunday through Saturday + + \param sOutFmtDate - Reformatted output date. + \returns XB_NO_ERROR +

+ Format Examples:
+ MM/DD/YY
+ YYYY-MM-DD
+ DDDDDDDDDDD MMMMMMMMMMM DD,YYYY +*/ +xbInt16 xbDate::FormatDate( const xbString &sFmtIn, xbString &sOutFmtDate ){ + xbUInt32 FmtCtr; + char type; + xbUInt32 iTypeCtr; + xbString ws; + xbString sWrkFmt; + sOutFmtDate = ""; + + if( IsNull()) + return XB_NO_ERROR; + + /* use format for this specific string if available, else use default format */ + if( strlen( sFmtIn ) > 0 ) + sWrkFmt = sFmtIn; + else + sWrkFmt = GetDefaultDateFormat(); + + if( strstr( sWrkFmt.Str(), "YYDDD" )){ + sOutFmtDate.Sprintf( "%c%c%03d", sDate8[3], sDate8[4], DayOf( XB_FMT_YEAR )); + } else { + FmtCtr = 1; + while( FmtCtr <= sWrkFmt.Len() ){ + if( sWrkFmt[FmtCtr] != 'D' && sWrkFmt[FmtCtr] != 'M' && sWrkFmt[FmtCtr] != 'Y' ){ + sOutFmtDate += sWrkFmt[FmtCtr]; + FmtCtr++; + iTypeCtr = 0; + } else { + type = sWrkFmt[FmtCtr]; + iTypeCtr = 0; + while( sWrkFmt[FmtCtr] == type ) { + iTypeCtr++; + FmtCtr++; + } + switch( type ){ + case 'D': + + if( iTypeCtr == 1 ){ + sOutFmtDate += ws.Sprintf( "%d", DayOf( XB_FMT_MONTH )); + } + else if( iTypeCtr == 2 ){ + sOutFmtDate += ws.Sprintf( "%c%c", sDate8[7], sDate8[8] ); + } else { + xbString sCDO; + CharDayOf( sCDO ); + ws.Assign( sCDO, 1, iTypeCtr ); + sOutFmtDate += ws.Str(); + } + break; + + case 'M': + if( iTypeCtr == 1 ){ + sOutFmtDate += ws.Sprintf( "%d", MonthOf()); + } + else if( iTypeCtr == 2 ){ + sOutFmtDate += ws.Sprintf( "%c%c", sDate8[5], sDate8[6] ); + } else { + xbString sCMO; + CharMonthOf( sCMO ); + ws.Assign( sCMO, 1, iTypeCtr ); + sOutFmtDate += ws.Str(); + } + break; + + case 'Y': + if( iTypeCtr == 2 ){ + sOutFmtDate += ws.Sprintf( "%c%c", sDate8[3], sDate8[4] ); + } + else if( iTypeCtr == 4 ){ + sOutFmtDate += ws.Sprintf( "%c%c%c%c", sDate8[1], sDate8[2], sDate8[3], sDate8[4] ); + } + break; + default: + break; + } + } + } + } + return XB_NO_ERROR; +} +/*************************************************************************/ +//! @brief Return the date value. +/*! + \returns char ptr to date value. +*/ +const char * xbDate::Str() const{ + return sDate8.Str(); +}; +/*************************************************************************/ +//! @brief Determine if date is a leap year. +/*! + \returns xbTrue - Is leapyear.
xbFalse - Not a leap year. +*/ +xbBool xbDate::IsLeapYear() const { + if( IsNull() ) + return xbFalse; + xbInt16 iYear = YearOf(); + if(( iYear % 4 == 0 && iYear % 100 != 0 ) || iYear % 400 == 0 ) + return xbTrue; + else + return xbFalse; +} +/*************************************************************************/ +//! @brief Determine if date is a leap year. +/*! + \param iYear - Year to check for leap year status. + \returns xbTrue - Is leapyear.
xbFalse - Not a leap year. +*/ +xbBool xbDate::IsLeapYear( xbInt16 iYear ) const { + if(( iYear % 4 == 0 && iYear % 100 != 0 ) || iYear % 400 == 0 ) + return xbTrue; + else + return xbFalse; +} +/*************************************************************************/ +//! @brief Determine if date is null date +/*! + \returns xbTrue - If null date.
xbFalse - Not a null date. +*/ +xbBool xbDate::IsNull() const { + if( sDate8.Len() < 8 ) + return xbTrue; + else + return xbFalse; +} +/*************************************************************************/ +//! @brief Calculate julian days for a given date. +/*! + \returns The number of days since 01/01/0001 + JUL_OFFSET. +*/ +xbInt32 xbDate::JulianDays() const{ + if( !IsNull()){ + xbInt32 ly = YearOf() - 1; + xbInt32 lDays = ly * 365L + ly / 4L - ly / 100L + ly / 400L; + lDays += DayOf( XB_FMT_YEAR ); + return lDays + JUL_OFFSET; + } else { + return 0; + } +} +/*************************************************************************/ +//! @brief Convert the number of julian days to gregorian date. +/*! + \param lJulDays - Julian days. + \returns XB_NO_ERROR +*/ +xbInt16 xbDate::JulToDate8( xbInt32 lJulDays ) +{ + lJulDays -= JUL_OFFSET; + // calculate the year + xbInt16 iYear = (xbInt16)(lJulDays / 365.24 ); + lJulDays -= (iYear * 365L) + (iYear / 4L) - (iYear / 100L) + (iYear / 400L); + iYear++; + while( lJulDays <= 0 ){ + iYear--; + lJulDays += (365L + IsLeapYear( iYear )); + } + // this for loop calculates the month by comparing the number of days remaining to one of the tables + xbInt16 iIsLeap = IsLeapYear(iYear); + xbInt16 iMonth = 1; + while( ((xbInt16) lJulDays > iAggregatedDaysInMonths[iIsLeap][iMonth]) && (iMonth < 12) ) + iMonth++; + lJulDays -= iAggregatedDaysInMonths[iIsLeap][iMonth-1]; + sDate8.Sprintf( "%04d%02d%02ld", iYear, iMonth, lJulDays ); + return XB_NO_ERROR; +} +/*************************************************************************/ +//! @brief Set the date to the last day of month for a given date. +/*! + This routine sets the last date of the month. + \returns XB_NO_ERROR +*/ +xbInt16 xbDate::LastDayOfMonth(){ + if( !IsNull()) + sDate8.Sprintf( "%4.4d%2.2d%2.2d", YearOf(), MonthOf(), iDaysInMonths[IsLeapYear()][MonthOf()]); + return XB_NO_ERROR; +}; +/*************************************************************************/ +//! @brief Return the month for the date. +/*! + \returns The month of the date. +*/ +xbInt16 xbDate::MonthOf() const { + if( !IsNull()){ + xbInt16 iOutMonth; + char month[3]; + month[0] = sDate8[5]; + month[1] = sDate8[6]; + month[2] = 0x00; + iOutMonth = atoi( month ); + return iOutMonth; + } else { + return 0; + } +} + +/*************************************************************************/ +//! @brief Set the date. +/*! + \param sDateIn - Input date. + \returns XB_NO_ERROR +*/ +xbInt16 xbDate::Set( const xbString & sDateIn ){ + + if( DateIsValid( sDateIn )){ + sDate8 = sDateIn; + } else { + sDate8 = ""; // set to null date if invalid date + } + return XB_NO_ERROR; +} +/*************************************************************************/ +//! @brief This routine sets up static data tables on startup. +/*! + \returns void +*/ +void xbDate::SetDateTables() { + if( iAggregatedDaysInMonths[1][12] != 366 ){ /* first time called ? */ + + iAggregatedDaysInMonths[0][0] = 0; + iAggregatedDaysInMonths[0][1] = 31; + iAggregatedDaysInMonths[0][2] = 59; + iAggregatedDaysInMonths[0][3] = 90; + iAggregatedDaysInMonths[0][4] = 120; + iAggregatedDaysInMonths[0][5] = 151; + iAggregatedDaysInMonths[0][6] = 181; + iAggregatedDaysInMonths[0][7] = 212; + iAggregatedDaysInMonths[0][8] = 243; + iAggregatedDaysInMonths[0][9] = 273; + iAggregatedDaysInMonths[0][10] = 304; + iAggregatedDaysInMonths[0][11] = 334; + iAggregatedDaysInMonths[0][12] = 365; + iAggregatedDaysInMonths[1][0] = 0; + iAggregatedDaysInMonths[1][1] = 31; + iAggregatedDaysInMonths[1][2] = 60; + iAggregatedDaysInMonths[1][3] = 91; + iAggregatedDaysInMonths[1][4] = 121; + iAggregatedDaysInMonths[1][5] = 152; + iAggregatedDaysInMonths[1][6] = 182; + iAggregatedDaysInMonths[1][7] = 213; + iAggregatedDaysInMonths[1][8] = 244; + iAggregatedDaysInMonths[1][9] = 274; + iAggregatedDaysInMonths[1][10] = 305; + iAggregatedDaysInMonths[1][11] = 335; + iAggregatedDaysInMonths[1][12] = 366; + + iDaysInMonths[0][0] = 0; + iDaysInMonths[0][1] = 31; + iDaysInMonths[0][2] = 28; + iDaysInMonths[0][3] = 31; + iDaysInMonths[0][4] = 30; + iDaysInMonths[0][5] = 31; + iDaysInMonths[0][6] = 30; + iDaysInMonths[0][7] = 31; + iDaysInMonths[0][8] = 31; + iDaysInMonths[0][9] = 30; + iDaysInMonths[0][10] = 31; + iDaysInMonths[0][11] = 30; + iDaysInMonths[0][12] = 31; + iDaysInMonths[1][0] = 0; + iDaysInMonths[1][1] = 31; + iDaysInMonths[1][2] = 29; + iDaysInMonths[1][3] = 31; + iDaysInMonths[1][4] = 30; + iDaysInMonths[1][5] = 31; + iDaysInMonths[1][6] = 30; + iDaysInMonths[1][7] = 31; + iDaysInMonths[1][8] = 31; + iDaysInMonths[1][9] = 30; + iDaysInMonths[1][10] = 31; + iDaysInMonths[1][11] = 30; + iDaysInMonths[1][12] = 31; + } +} +/*************************************************************************/ +//! @brief Set the date equal to the system date. +/*! + \returns XB_NO_ERROR +*/ +xbInt16 xbDate::Sysdate(){ + + #ifdef HAVE__LOCALTIME64_S_F + __time64_t timer; + _time64( &timer ); + struct tm tblock; + _localtime64_s( &tblock, &timer ); + tblock.tm_year += 1900; + tblock.tm_mon++; + sDate8.Sprintf( "%4d%02d%02d", tblock.tm_year, tblock.tm_mon, tblock.tm_mday ); + #else + time_t timer; + timer = time( &timer ); + struct tm *tblock; + tblock = localtime( &timer ); + tblock->tm_year += 1900; + tblock->tm_mon++; + sDate8.Sprintf( "%4d%02d%02d",tblock->tm_year,tblock->tm_mon,tblock->tm_mday ); + #endif + + return XB_NO_ERROR; +} +/*************************************************************************/ +//! @brief Returns the year of the date. +/*! + \returns The year of the date. +*/ +xbInt16 xbDate::YearOf() const { + if( !IsNull()){ + char year[5]; + year[0] = sDate8[1]; + year[1] = sDate8[2]; + year[2] = sDate8[3]; + year[3] = sDate8[4]; + year[4] = 0x00; + xbInt16 iOutYear = atoi( year ); + return iOutYear; + } else { + return 0; + } +}; +} /* namespace */ \ No newline at end of file diff --git a/src/core/xbdbf.cpp b/src/core/xbdbf.cpp new file mode 100755 index 0000000..8904a6d --- /dev/null +++ b/src/core/xbdbf.cpp @@ -0,0 +1,4533 @@ +/* xbdbf.cpp + +XBase64 Software Library + +Copyright (c) 1997,2003,2014,2022,2023 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; + + #ifdef XB_BLOCKREAD_SUPPORT + pRb = NULL; + bBlockReadEnabled = xbFalse; // batch read switch, if xbTrue, then ON + #endif // XB_BLOCKREAD_SUPPORT + + 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 // XB_LOCKING_SUPPORT + + #ifdef XB_INDEX_SUPPORT + ixList = NULL; + pCurIx = NULL; + vpCurIxTag = NULL; + sCurIxType = ""; + ClearTagList(); + #endif // XB_INDEX_SUPPORT + + #ifdef XB_MEMO_SUPPORT + Memo = NULL; + #endif // XB_MEMO_SUPPORT + + #ifdef XB_INF_SUPPORT + llInfData.Clear(); + #endif // XB_INF_SUPPORT +} + +/************************************************************************/ +//! @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; + } + if( RecBuf ){ + free( RecBuf ); + RecBuf = NULL; + } + + #ifdef XB_BLOCKREAD_SUPPORT + if( bBlockReadEnabled ) + DisableBlockReadProcessing(); + #endif // XB_BLOCKREAD_SUPPORT + + 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 = 100; + 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. Temporary tags + are not included here because they are created after a table is open + and will be deleted when the table is closed. + + \param ixIn Pointer to index object for a given index file. + \param sFmt NDX, MDX or TDX. + \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 = 100; + throw iRc; + } + + ixList = ixList->next; + } + #endif // XB_INDEX_SUPPORT + + // lock everything up for an update + #ifdef XB_LOCKING_SUPPORT + if( iAutoLock && !bTableLocked ){ + if(( iRc = LockHeader( XB_LOCK )) != XB_NO_ERROR ){ + if( iRc == XB_LOCK_FAILED ) { + return iRc; + } else { + iErrorStop = 110; + throw iRc; + } + } + if(( iRc = LockAppend( XB_LOCK )) != XB_NO_ERROR ){ + if( iRc == XB_LOCK_FAILED ){ + LockHeader( XB_UNLOCK ); + return iRc; + } else { + iErrorStop = 120; + throw iRc; + } + } + + #ifdef XB_INDEX_SUPPORT + if(( iRc = LockIndices( XB_LOCK )) != XB_NO_ERROR ){ + iErrorStop = 130; + throw iRc; + } + #endif // XB_INDEX_SUPPORT + + } + #endif // XB_LOCKING_SUPPORT + if(( iRc = ReadHeader( 1, 1 )) != XB_NO_ERROR ){ + iErrorStop = 140; + throw iRc; + } + #ifdef XB_INDEX_SUPPORT + ixList = GetIxList(); + + while( ixList ){ + if(( iRc = ixList->ix->CheckForDupKeys()) != 0 ){ + if( iRc < 0 ){ + iErrorStop = 150; + throw iRc; + } + return XB_KEY_NOT_UNIQUE; + } + ixList = ixList->next; + } + + #endif // XB_INDEX_SUPPORT + + // calculate the latest header information + xbDate d; + d.Sysdate(); + 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 = 160; + throw iRc; + } + ixList = ixList->next; + } + + #ifdef XB_LOCKING_SUPPORT + if( iAutoLock ){ + if(( iRc = LockIndices( XB_UNLOCK )) != XB_NO_ERROR ){ + iErrorStop = 170; + throw iRc; + } + } + #endif // XB_LOCKING_SUPPORT + #endif // XB_INDEX_SUPPORT + + // rewrite the header record + if(( iRc = WriteHeader( 1, 1 )) != XB_NO_ERROR ){ + iErrorStop = 180; + throw iRc; + } + + #ifdef XB_LOCKING_SUPPORT + if( iAutoLock ){ + if(( iRc = LockHeader( XB_UNLOCK )) != XB_NO_ERROR ){ + iErrorStop = 190; + throw iRc; + } + } + #endif + + // write the last record + if(( iRc = xbFseek( (uiHeaderLen+((xbInt64)(ulNoOfRecs-1)*uiRecordLen)), 0 )) != XB_NO_ERROR ){ + iErrorStop = 200; + throw iRc; + } + + if(( iRc = xbFwrite( RecBuf, uiRecordLen, 1 )) != XB_NO_ERROR ){ + iErrorStop = 210; + throw iRc; + } + + // write the end of file marker + if(( iRc = xbFputc( XB_CHAREOF )) != XB_NO_ERROR ){ + iErrorStop = 220; + throw iRc; + } + + #ifdef XB_LOCKING_SUPPORT + if( iAutoLock ){ + if(( iRc = LockAppend( XB_UNLOCK )) != XB_NO_ERROR ){ + iErrorStop = 230; + 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_INF_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 INF 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 exists + + \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 = LoadInfData()) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + + // check if entry exists + xbLinkListNode * llN = llInfData.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 ); + llInfData.InsertAtEnd( s ); + bUpdated = xbTrue; + + } else if( iOpt == 1 && bFound ){ + llInfData.RemoveByVal( s ); + bUpdated = xbTrue; + } + + if( bUpdated ){ + if(( iRc = SaveInfData()) != XB_NO_ERROR ){ + iErrorStop = 110; + 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_INF_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 = 100; + iRc = XB_NOT_OPEN; + throw iRc; + } + + if( iDbfStatus == XB_UPDATED ){ + if( GetAutoCommit() == 1 ){ + if(( iRc = Commit()) != XB_NO_ERROR ){ + iErrorStop = 110; + throw iRc; + } + } else { + if(( iRc = Abort()) != XB_NO_ERROR ){ + iErrorStop = 120; + 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 +/*! + This method is used to check an index tag's intgerity. + + \param iTagOpt 0 - Check current tag
+ 1 - Check all tags
+ + \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{ + 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 = 110; + 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; +} + +/************************************************************************/ +/*! + This method is used to reindex / rebuild index tag. + + \param iTagOpt 0 - Reindex current tag
+ 1 - Reindex all tags
+ 2 - Reindex for tag identified by vpTag + \param iErrorOpt 0 - Don't delete tag on reindex failure
+ 1 - Delete tag on reindex failure + \param vpTag if option 2 used, pointer to tag to reindex + + \returns Return Codes +*/ + +xbInt16 xbDbf::Reindex( xbInt16 iTagOpt, xbInt16 iErrorOpt, xbIx **ppIx, void **vppTag ){ + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + void *vp; + + xbString sType; + xbString sTagName; + + if( iTagOpt < 0 || iTagOpt > 2 || (iTagOpt == 2 && (!ppIx || !vppTag ))) + return XB_INVALID_OPTION; + + + #ifdef XB_BLOCKREAD_SUPPORT + xbBool bOriginalBlockReadSts = GetBlockReadStatus(); + #endif + + try{ + + #ifdef XB_BLOCKREAD_SUPPORT + if( !bOriginalBlockReadSts ) + EnableBlockReadProcessing(); + #endif + + if( iTagOpt == 0 ){ + if( pCurIx ){ + + if(( iRc = pCurIx->Reindex( &vpCurIxTag )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + return iRc; + } else { + return XB_INVALID_TAG; + } + + } else if( iTagOpt == 1 ) { + + xbLinkListNode *llN = GetTagList(); + xbTag *pTag; + + while( llN ){ + pTag = llN->GetKey(); + vp = pTag->GetVpTag(); + if(( iRc = pTag->GetIx()->Reindex( &vp )) != XB_NO_ERROR ){ + iErrorStop = 110; + throw iRc; + } + llN = llN->GetNextNode(); + } + } else if( iTagOpt == 2 ){ + + // xbIx *pIx; + // pIx = *ppIx; + xbIx *pIx = *ppIx; + // void *vpTag; + // vpTag = *vppTag; + void *vpTag = *vppTag; + + if(( iRc = pIx->Reindex( &vpTag )) != XB_NO_ERROR ){ + iErrorStop = 120; + throw iRc; + } + } + } + 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 )); + } + + #ifdef XB_BLOCKREAD_SUPPORT + if( !bOriginalBlockReadSts ) + DisableBlockReadProcessing(); + #endif + + 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 = 100; + throw iRc; + } + } else { + if(( iRc = Abort()) != XB_NO_ERROR ){ + iErrorStop = 110; + 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 + + if(( iRc = xbase->RemoveTblFromTblList( this )) != XB_NO_ERROR ){ + xbString sMsg; + sMsg.Sprintf( "Alias = [%s]", sAlias.Str()); + xbase->WriteLogMessage( sMsg.Str() ); + iErrorStop = 120; + 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 = 100; + iRc = XB_NOT_OPEN; + throw iRc; + } + // close it + if(( iRc = pIx->Close()) != XB_NO_ERROR ){ + iErrorStop = 110; + throw iRc; + } + // remove it from the list + if(( iRc = RemoveIndex( pIx )) != XB_NO_ERROR ){ + iErrorStop = 120; + throw iRc; + } + // refresh the tag list + if(( iRc = UpdateTagList()) != XB_NO_ERROR ){ + iErrorStop = 130; + 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 = 100; + throw iRc; + } + } else { + if(( iRc = PutRecord( ulCurRec )) != XB_NO_ERROR ){ + iErrorStop = 110; + 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 = 100; + iRc = XB_DBF_FILE_NOT_OPEN; + throw iRc; + } + + if( !dNewTable ){ + iErrorStop = 110; + 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 = 120; + 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 = 130; + 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, MDX ans TDX. + indices. If you don't have a specific need for an NDX file, use MDX. + + \param sIxType "MDX", "NDX" or "NTX". + \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; + + #ifdef XB_LOCKING_SUPPORT + xbBool bLocked = xbFalse; + #endif + + try{ + xbString sType = sIxType; + sType.ToUpperCase(); + + #ifdef XB_LOCKING_SUPPORT + if( iAutoLock && !bTableLocked ){ + if(( iRc = LockTable( XB_LOCK )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw( iRc ); + } + bLocked = xbTrue; + } + #endif // XB_LOCKING_SUPPORT + + if( sIxType == "" ){ + iErrorStop = 110; + 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 = 120; + throw iRc; + } + + if(( iRc = AddIndex( ixNdx, sIxType )) != XB_NO_ERROR ){ + iErrorStop = 130; + 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 = 140; + 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; + } + ixList = ixList->next; + } + + if( !bMdxFound ) + ixMdx = new xbIxMdx( this ); + + if(( iRc = ixMdx->CreateTag( sName, sKey, sFilter, iDescending, iUnique, iOverLay, vpTagOut )) != XB_NO_ERROR ){ + iErrorStop = 150; + throw iRc; + } + + if( !bMdxFound ){ + if(( iRc = AddIndex( ixMdx, "MDX" )) != XB_NO_ERROR ){ + iErrorStop = 160; + throw iRc; + } + + cIndexFlag = 0x01; + if(( iRc = WriteHeader( 1, 0 )) != XB_NO_ERROR ){ + iErrorStop = 170; + throw iRc; + } + } + *pIxOut = ixMdx; + + // set the current tag if one not already set + if( sCurIxType == "" ){ + sCurIxType = "MDX"; + pCurIx = ixMdx; + vpCurIxTag = ixMdx->GetTag(0); + } + #endif + + #ifdef XB_TDX_SUPPORT + } else if( sIxType == "TDX" ){ + + if( GetVersion() == 3 ){ // TDX indexes were version 4 and higher + iErrorStop = 140; + iRc = XB_INVALID_INDEX; + throw iRc; + } + + xbIxTdx *ixTdx; + xbString s; + // look through the index list and see if there is an mdx pointer we can grab + xbBool bTdxFound = xbFalse; + xbIxList *ixList = GetIxList(); + while( ixList && !bTdxFound ){ + s = ixList->sFmt->Str(); + if( s == "TDX" ){ + ixTdx = (xbIxTdx *) ixList->ix; + bTdxFound = xbTrue; + } + ixList = ixList->next; + } + if( !bTdxFound ) + ixTdx = new xbIxTdx( this ); + + if(( iRc = ixTdx->CreateTag( sName, sKey, sFilter, iDescending, iUnique, iOverLay, vpTagOut )) != XB_NO_ERROR ){ + iErrorStop = 150; + throw iRc; + } + if( !bTdxFound ){ + if(( iRc = AddIndex( ixTdx, "TDX" )) != XB_NO_ERROR ){ + iErrorStop = 160; + throw iRc; + } + } + *pIxOut = ixTdx; + + // set the current tag if one not already set + if( sCurIxType == "" ){ + + sCurIxType = "TDX"; + pCurIx = ixTdx; + vpCurIxTag = ixTdx->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 = 100; + 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 = 110; + throw iRc; + } + if( iOption == 0 ){ /* delete all option */ + if( !RecordDeleted()){ + if(( iRc = DeleteRecord()) != XB_NO_ERROR ){ + iErrorStop = 120; + throw iRc; + } + if(( iRc = Commit()) != XB_NO_ERROR ){ + iErrorStop = 130; + throw iRc; + } + } + } else { /* undelete all option */ + if( RecordDeleted()){ + if(( iRc = UndeleteRecord()) != XB_NO_ERROR ){ + iErrorStop = 140; + throw iRc; + } + if(( iRc = Commit()) != XB_NO_ERROR ){ + iErrorStop = 150; + 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; + + #ifdef XB_LOCKING_SUPPORT + xbBool bLocked = xbFalse; + #endif // XB_LOCKING_SUPPORT + + #ifdef XB_INF_SUPPORT + xbString sIxName; + #endif // XB_INF_SUPPORT + + try{ + #ifdef XB_LOCKING_SUPPORT + if( iAutoLock && !bTableLocked ){ + if(( iRc = LockTable( XB_LOCK )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw( iRc ); + } + bLocked = xbTrue; + } + #endif // XB_LOCKING_SUPPORT + + // close any open index files, delete it, remove from the ix list + while( ixList ){ + + // next two lines for debugging + ixList->ix->GetFileNamePart( sIxName ); + ixList->ix->Close(); + if(( iRc = ixList->ix->xbRemove()) != XB_NO_ERROR ){ + iErrorStop = 110; + throw iRc; + } + #ifdef XB_INF_SUPPORT + // if XB_INF_SUPPORT is enabled, all open non prod indices should be in here + if( *ixList->sFmt != "MDX" && *ixList->sFmt != "TDX" ){ // production and temp indices not stored in .INF dataset + if(( iRc = ixList->ix->GetFileNamePart( sIxName )) != XB_NO_ERROR ){ + iErrorStop = 120; + throw iRc; + } + if(( iRc = AssociateIndex( *ixList->sFmt, sIxName, 1 )) != XB_NO_ERROR ){ + iErrorStop = 130; + 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_INF_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 INF support be enabled when building the library.
+ This routine deletes the .INF file.
+ + \returns Return Codes +*/ +xbInt16 xbDbf::DeleteInfData(){ + xbInt16 iRc = 0; + xbInt16 iErrorStop = 0; + + try{ + xbString sInfFileName; + if(( iRc = GetInfFileName( sInfFileName )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + xbFile f( xbase ); + f.SetFileName( sInfFileName ); + if( f.FileExists()){ + if(( iRc = f.xbRemove()) != XB_NO_ERROR ){ + iErrorStop = 110; + throw iRc; + } + } + } catch( xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbdbf::DeleteInfData() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return iRc; +} +#endif // XB_INF_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; + + #ifdef XB_LOCKING_SUPPORT + xbBool bTableLocked = xbFalse; + #endif + + try{ + + #ifdef XB_LOCKING_SUPPORT + if( iAutoLock && !bTableLocked ){ + if(( iRc = LockTable( XB_LOCK )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } else { + bTableLocked = xbTrue; + } + } + #endif // XB_LOCKING_SUPPORT + + #ifdef XB_INDEX_SUPPORT + if(( iRc = DeleteAllIndexFiles()) != XB_NO_ERROR ){ + iErrorStop = 110; + throw iRc; + } + + #ifdef XB_INF_SUPPORT + if(( iRc = DeleteInfData()) != XB_NO_ERROR ){ + iErrorStop = 120; + throw iRc; + } + #endif // XB_INF_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 = 130; + throw iRc; + } + + if(( iRc = xbRemove()) != XB_NO_ERROR ){ + iErrorStop = 140; + throw iRc; + } + + #ifdef XB_MEMO_SUPPORT + if( iMemoFieldCnt > 0 ){ + xbFile f( xbase ); + if(( iRc = f.xbRemove( sMemoFileName )) != XB_NO_ERROR ){ + iErrorStop = 150; + 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 + if( bTableLocked ) + LockTable( XB_UNLOCK ); + #endif // XB_LOCKING_SUPPORT + return iRc; +} + +/************************************************************************/ +#ifdef XB_INDEX_SUPPORT +//! @brief Delete an index tag. + +/*! + This routine deletes an index tag + \param sIxType Either "NDX", "MDX" or "TDX".
+ \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; + + #ifdef XB_LOCKING_SUPPORT + xbBool bTableLocked = xbFalse; + #endif + + try{ + + #ifdef XB_LOCKING_SUPPORT + if( iAutoLock && !GetTableLocked() ){ + if(( iRc = LockTable( XB_LOCK )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } else { + bTableLocked = xbTrue; + } + } + #endif // XB_LOCKING_SUPPORT + + 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_INF_SUPPORT + if(( iRc = AssociateIndex( "NDX", sName, 1 )) != XB_NO_ERROR ){ + iErrorStop = 120; + throw iRc; + } + #endif // XB_INF_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( !ixList || 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( !ixList || ixl->ix == pCurIx ) + SetCurTag( "", NULL, NULL ); + } + } + ixlPrev = ixl; + ixl = ixlNext; + } + + if( !bDone ) + return XB_INVALID_TAG; + #endif + + #ifdef XB_TDX_SUPPORT + } else if( sIxType == "TDX" ){ + xbIxList *ixl = ixList; + xbIxList *ixlNext; + xbIxList *ixlPrev = NULL; + xbBool bDone = xbFalse; + xbIxTdx *pTdx; + xbMdxTag *pMdxTag; + xbInt16 iTagCnt = 0; + + while( ixl && !bDone ){ + ixlNext = ixl->next; + pTdx = (xbIxTdx *) ixl->ix; + iTagCnt = pTdx->GetTagCount(); + for( xbInt16 i = 0; i < iTagCnt && !bDone; i++ ){ + pMdxTag = (xbMdxTag *) pTdx->GetTag( i ); + if( pTdx->GetTagName( pMdxTag ) == sName ){ + bDone = xbTrue; + iRc = pTdx->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( ixList ) + std::cout << "ixlist not null\n"; + else + std::cout << "ixlist null\n"; + + if( !ixList || 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; + } + + + } + 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 // XB_LOCKING_SUPPORT + } + + #ifdef XB_LOCKING_SUPPORT + if( bTableLocked ){ + LockTable( XB_UNLOCK ); + } + #endif // XB_LOCKING_SUPPORT + + + return iRc; +} +#endif // XB_INDEX_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 ){ + int i; + int iMemoCtr = 0; + + if( iOption < 1 || iOption > 4 ) + return XB_INVALID_OPTION; + + xbInt16 iRc = ReadHeader( xbTrue, 0 ); + if( iRc != XB_NO_ERROR ) + return iRc; + +// 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; + #ifdef XB_INDEX_SUPPORT + std::cout << "Open Index Files = " << GetPhysicalIxCnt() << std::endl; + #endif // XB_INDEX_SUPPORT + } + + 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,DEL"; + 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,DEL", ulRecNo ); + else + s2.Sprintf( "%ld,", ulRecNo ); + + 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_DEBUG_SUPPORT + + +#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; +} + + +/************************************************************************/ +#ifdef XB_LOCKING_SUPPORT +//! @brief Get Auto Lock setting. +/*! + \returns Auto lock setting. +*/ +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 // XB_MEMO_SUPPORT + +/************************************************************************/ +//! @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 + NDX index files have one tag per file. + MDX index files can can have up to 47 tags per file. + TDX index files can can have up to 47 tags per file. + + \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.
+ TDX for temporary 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 GetHeaderLen for dbf +/*! + + Returns the length of the header portion of the dbf file + \returns Length of header protion of dbf file + +*/ +xbUInt16 xbDbf::GetHeaderLen() const { + return uiHeaderLen; +} + +/************************************************************************/ +//! @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 true if dbf file empty or positioned to the first record +/*! + \returns Returns true if dbf file is empty or positioned on the first record. +*/ +xbBool xbDbf::GetBof() { +/* + if( GetRecordCount() == 0 || GetCurRecNo() == 1 ) + return xbTrue; + else + */ + return xbFalse; +} +/************************************************************************/ +//! @brief Return true if dbf file empty or positioned to the last record +/*! + \returns Returns true if error, dbf file is empty or positioned on the last record. +*/ +xbBool xbDbf::GetEof() { + + // xbUInt32 ulRecCnt = GetRecordCount(); + + xbUInt32 ulRecCnt; + xbInt16 iRc = GetRecordCnt( ulRecCnt ); + + if( iRc != XB_NO_ERROR || ulRecCnt == 0 || GetCurRecNo() == ulRecCnt ) + return xbTrue; + else + return xbFalse; +} +/************************************************************************/ +//! @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 // XB_LOCKING_SUPPORT + +#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; +} + + +/************************************************************************/ +#ifdef XB_LOCKING_SUPPORT +//! @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; +} +#endif // XB_LOCKING_SUPPORT + +/************************************************************************/ +#ifdef XB_MEMO_SUPPORT +/************************************************************************/ +#ifdef XB_LOCKING_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; +} +#endif // XB_LOCKING_SUPPORT + +/************************************************************************/ +//! @brief Get pointer to Memo object. +/*! + \returns This routine returns the pointer to the memo object. +*/ + +xbMemo * xbDbf::GetMemoPtr(){ + return Memo; +} + +#endif // XB_MEMO_SUPPORT + + +/************************************************************************/ +#ifdef XB_INF_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 sInfFileName Output string containing .INF file name. + \returns Return Codes +*/ + +xbInt16 xbDbf::GetInfFileName( xbString &sInfFileName ){ + + sInfFileName = GetFqFileName(); + xbUInt32 lLen = sInfFileName.Len(); + if( lLen < 5 ) + return XB_FILE_NOT_FOUND; + sInfFileName.PutAt(lLen-2, 'I'); + sInfFileName.PutAt(lLen-1, 'N'); + sInfFileName.PutAt(lLen, 'F'); + return XB_NO_ERROR; +} + +/************************************************************************/ +//! @brief Return first node of linked list of .INF items. +/*! + \returns List of .INF entries. +*/ + +xbLinkListNode * xbDbf::GetInfList() const{ + return llInfData.GetHeadNode(); +} +#endif // XB_INF_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 // XB_INDEX_SUPPORT + + +/************************************************************************/ +//! @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 = 100; + iRc = XB_NOT_OPEN; + throw iRc; + } + if( iDbfStatus == XB_UPDATED ){ + if( GetAutoCommit() == 1 ){ + if(( iRc = Commit()) != XB_NO_ERROR ){ + iErrorStop = 110; + throw iRc; + } + } else { + if(( iRc = Abort()) != XB_NO_ERROR ){ + iErrorStop = 120; + throw iRc; + } + } + } + + + // std::cout << "xbdbf::GetRecord: " << ulRecNo << " " << ulNoOfRecs << "\n"; + if( ulRecNo > ulNoOfRecs || ulRecNo == 0L ){ + iErrorStop = 130; + iRc = XB_INVALID_RECORD; + throw iRc; + } + + #ifdef XB_BLOCKREAD_SUPPORT + if( bBlockReadEnabled ) + return pRb->GetRecord( ulRecNo ); + #endif // XB_BLOCK_READ_SUPPORT + + + if(( xbFseek( (uiHeaderLen+(( (xbInt64) ulRecNo-1L ) * uiRecordLen )), SEEK_SET )) != XB_NO_ERROR ){ + iErrorStop = 140; + iRc = XB_SEEK_ERROR; + throw iRc; + } + + if( xbFread( RecBuf, uiRecordLen, 1 ) != XB_NO_ERROR ){ + iErrorStop = 150; + 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. +/*! + \returns Record count or Return Codes +*/ +/* +xbUInt32 xbDbf::GetRecordCount(){ + + xbUInt32 ulCnt; + xbInt16 iRc = GetRecordCnt( ulCnt ); + if( iRc < 0 ) + return (xbUInt32) iRc; + else + return ulCnt; +} +*/ +/************************************************************************/ +//! @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; + + #ifdef XB_LOCKING_SUPPORT + xbBool bLocked = xbFalse; + #endif // XB_LOCKING_SUPPORT + + try{ + #ifdef XB_LOCKING_SUPPORT + if( iAutoLock && !bTableLocked ){ + if(( iRc = LockHeader( XB_LOCK )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } else + bLocked = xbTrue; + } + #endif // XB_LOCKING_SUPPORT + + if((iRc = ReadHeader(1,1)) != XB_NO_ERROR){ + iErrorStop = 110; + throw iRc; + } + } + 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 )); + } + } + + #ifdef XB_LOCKING_SUPPORT + if( bLocked ){ + LockHeader( XB_UNLOCK ); + } + #endif + + ulRecCnt = ulNoOfRecs; + return iRc; +} +/************************************************************************/ +//! @brief Get the dbf record length. +/*! + \returns Record length. +*/ +xbUInt16 xbDbf::GetRecordLen() const { + return uiRecordLen; +} +/************************************************************************/ +#ifdef XB_LOCKING_SUPPORT +//! @brief Get table locked status +/*! + \returns Table lock status. +*/ +xbBool xbDbf::GetTableLocked() const { + return this->bTableLocked; +} +#endif // XB_LOCKING_SUPPORT +/************************************************************************/ +#ifdef XB_INDEX_SUPPORT +//! @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/TDX), with each file supporting one or more index tags.
+ + \returns Tag list for the file/table. +*/ +xbLinkListNode *xbDbf::GetTagList() const { + return llTags.GetHeadNode(); +} +#endif // XB_INDEX_SUPPORT +/************************************************************************/ +//! @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_INF_SUPPORT +//! @brief Load .INF data file, +/*! + Protected method. This routine loads the ndx inf file. + \returns Return Codes +*/ + +xbInt16 xbDbf::LoadInfData(){ + + xbInt16 iRc = 0; + xbInt16 iErrorStop = 0; + + try{ + // create file name + xbString sInfFileName; + if(( iRc = GetInfFileName( sInfFileName )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + // if file does not exist, return no error + xbFile fMd( xbase ); + if( !fMd.FileExists( sInfFileName )) + return XB_NO_ERROR; + + // open file file in read only mode + if(( iRc = fMd.xbFopen( "r", sInfFileName, GetShareMode())) != XB_NO_ERROR ){ + iErrorStop = 110; + throw iRc; + } + // clear the linked list + llInfData.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 ); + llInfData.InsertAtEnd( sFn ); + } + } + } + // close the file + if(( iRc = fMd.xbFclose()) != XB_NO_ERROR ){ + iErrorStop = 120; + throw iRc; + } + + } catch( xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbdbf::LoadInfData() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return iRc; +} +#endif // XB_INF_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 = 110; + 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 = 120; + throw iRc; + } + } + ulAppendLocked = ulAppendRec; /* set the append lock switch */ + + + // } else { - other lock-table flavor options go here Clipper, Fox, etc - } + + } else { + iErrorStop = 130; + 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 = 130; + 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 = 230; + 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 + + #ifdef XB_MDX_SUPPORT + if( *ixLI->sFmt == "MDX" ){ + 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 + + #ifdef XB_TDX_SUPPORT + if( *ixLI->sFmt == "TDX" ){ + 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 ){ + iErrorStop = 100; + throw iRc; + } + ixLI->ix->SetLocked( xbFalse ); + } + } + #endif + + #ifdef XB_MDX_SUPPORT + if( *ixLI->sFmt == "MDX" ){ + if( ixLI->ix->GetLocked()){ + if(( iRc = ixLI->ix->xbLock( XB_UNLOCK, LK4026531838, 1 )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + ixLI->ix->SetLocked( xbFalse ); + } + } + #endif + + #ifdef XB_TDX_SUPPORT + if( *ixLI->sFmt == "MDX" ){ + if( ixLI->ix->GetLocked()){ + if(( iRc = ixLI->ix->xbLock( XB_UNLOCK, LK4026531838, 1 )) != XB_NO_ERROR ){ + 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 = 110; + throw iRc; + } + } + // other lock-table flavor options go here Clipper, Fox, etc + + } else { + iErrorStop = 120; + 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 = 130; + 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 = 210; + throw iRc; + } + } else { + iErrorStop = 220; + 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; + +} +#endif // XB_LOCKING_SUPPORT + + +/************************************************************************/ +//! @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 = 100; + throw iRc; + } + + // do any .INF data things on the file, like open indices + #ifdef XB_INF_SUPPORT + if(( iRc = LoadInfData()) != XB_NO_ERROR ){ + iErrorStop = 110; + throw iRc; + } + xbUInt32 llNodeCnt = llInfData.GetNodeCnt(); + if( llNodeCnt > 0 ){ + xbString s2; + xbLinkListNode * llN = llInfData.GetHeadNode(); + for( xbUInt32 i = 0; i < llNodeCnt; i++ ){ + s2 = llN->GetKey(); + #ifdef XB_NDX_SUPPORT + if(( iRc = OpenIndex( "NDX", s2 )) != XB_NO_ERROR ){ + iErrorStop = 120; + throw iRc ; + } + #endif // XB_NDX_SUPPORT + llN = llN->GetNextNode(); + } + } + #endif // XB_INF_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; +} + +/************************************************************************/ +#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 = 100; + 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 = 110; + throw iRc; + } + #endif + + #ifdef XB_MDX_SUPPORT + } else if( sType == "MDX" ){ + pIx = new xbIxMdx( this ); + if(( iRc = pIx->Open( sFileName )) != XB_NO_ERROR ){ + iErrorStop = 120; + throw iRc; + } + + #endif + + } else { + iErrorStop = 130; + iRc = XB_INVALID_OPTION; + throw iRc; + } + + if(( iRc = AddIndex( pIx, sIxType )) != XB_NO_ERROR ){ + iErrorStop = 140; + throw iRc; + } + if(( iRc = UpdateTagList()) != XB_NO_ERROR ){ + iErrorStop = 150; + 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; + + #ifdef XB_LOCKING_SUPPORT + xbBool bLocked = xbFalse; + #endif // XB_LOCKING_SUPPORT + + try{ + if( !FileIsOpen() ){ + iErrorStop = 100; + iRc = XB_DBF_FILE_NOT_OPEN; + throw iRc; + } + + #ifdef XB_LOCKING_SUPPORT + if( iAutoLock && !bTableLocked ){ + if(( iRc = LockTable( XB_LOCK )) != XB_NO_ERROR ){ + iErrorStop = 110; + throw iRc; + } else { + bLocked = xbTrue; + } + } + #endif + + if(( iRc = GetRecordCnt( ulRecCnt )) != XB_NO_ERROR ){ + iErrorStop = 120; + throw iRc; + } + + xbBool bDone = xbFalse; + for( xbUInt32 ulI = 1; ulI <= ulRecCnt && !bDone; ulI++ ){ + + if(( iRc = GetRecord( ulI )) != XB_NO_ERROR ){ + iErrorStop = 130; + 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 = 140; + throw iRc; + } + if( ulMoveRec > 0 ){ + if(( iRc = DeleteRecord()) != XB_NO_ERROR ){ + iErrorStop = 150; + throw iRc; + } + if(( iRc = PutRecord( ulMoveRec )) != XB_NO_ERROR ){ + iErrorStop = 160; + throw iRc; + } + if(( iRc = UndeleteRecord()) != XB_NO_ERROR ){ + iErrorStop = 170; + throw iRc; + } + if(( iRc = PutRecord( ulI )) != XB_NO_ERROR ){ + iErrorStop = 180; + throw iRc; + } + ulLastPackedRec = ulI; + } + + } else { + ulLastPackedRec = ulI; + } + } + + if( ulLastPackedRec < ulRecCnt ){ + // update header record count + + xbDate d; + d.Sysdate(); + 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 = 190; + throw iRc; + } + + // truncate the file to the new size + if(( iRc = xbTruncate( uiHeaderLen + uiRecordLen * ulLastPackedRec )) != XB_NO_ERROR ){ + iErrorStop = 200; + throw iRc; + } + } + + if( ulNoOfRecs > 0 ){ + if(( iRc = GetRecord( 1 )) != XB_NO_ERROR ){ + iErrorStop = 210; + throw iRc; + } + } else { + BlankRecord(); + ulCurRec = 0; + } + + #ifdef XB_MEMO_SUPPORT + if( iMemoFieldCnt > 0 ){ + if(( iRc = Memo->PackMemo( 0 )) != XB_NO_ERROR ){ + iErrorStop = 220; + throw iRc; + } + } + #endif // XB_MEMO_SUPPORT + } + 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 )); + } + } + #ifdef XB_LOCKING_SUPPORT + if( bLocked ){ + LockTable( XB_UNLOCK ); + } + #endif + 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 + if( iAutoLock && !bTableLocked ){ + if(( iRc = LockHeader( XB_LOCK )) != XB_NO_ERROR ){ + throw iRc; + } + } + #endif // XB_LOCKING_SUPPORT + + if(( iRc = ReadHeader( 1, 1 )) != XB_NO_ERROR ){ + iErrorStop = 150; + throw iRc; + } + + // verify valid record number request + if( ulRecNo > ulNoOfRecs || ulRecNo == 0L ){ + iErrorStop = 160; + iRc = XB_INVALID_RECORD; + throw iRc; + } + + #ifdef XB_LOCKING_SUPPORT + if( iAutoLock && !bTableLocked ){ + if(( iRc = LockRecord( XB_LOCK, ulRecNo )) != XB_NO_ERROR ){ + iErrorStop = 170; + throw iRc; + } + + #ifdef XB_INDEX_SUPPORT + if(( iRc = LockIndices( XB_LOCK )) != XB_NO_ERROR ){ + iErrorStop = 180; + throw iRc; + } + #endif // XB_INDEX_SUPPORT + } + #endif // XB_LOCKING_SUPPORT + + // 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; + } + throw 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()) != XB_NO_ERROR ){ + iErrorStop = 220; + throw iRc; + } + ixList = ixList->next; + } + #endif // XB_INDEX_SUPPORT + + // update latest header date if changed + xbDate d; + d.Sysdate(); + 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; + } + } + + // 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 + + ulCurRec = ulRecNo; + iDbfStatus = XB_OPEN; + } + + catch (xbInt16 iRc ){ + if( iRc != XB_LOCK_FAILED && iRc != XB_KEY_NOT_UNIQUE ){ + 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 )); + } + } + + #ifdef XB_LOCKING_SUPPORT + if( iAutoLock ){ + LockHeader( XB_UNLOCK ); + LockAppend( XB_UNLOCK ); + LockRecord( XB_UNLOCK, ulRecNo ); + #ifdef XB_INDEX_SUPPORT + LockIndices( XB_UNLOCK ); + #endif // XB_INDEX_SUPPORT + } + #endif // XB_LOCKING_SUPPORT + + 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_INF_SUPPORT +// @brief Update .INF data file. +/* + Protected method. + \returns Return Codes +*/ + +xbInt16 xbDbf::SaveInfData(){ + + xbInt16 iRc = 0; + xbInt16 iErrorStop = 0; + xbFile fMd( xbase ); + + try{ + + xbUInt32 llNodeCnt = llInfData.GetNodeCnt(); + + xbString sInfFileName; + if(( iRc = GetInfFileName( sInfFileName )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + + // open the file + if(( iRc = fMd.xbFopen( "w", sInfFileName, GetShareMode())) != XB_NO_ERROR ){ + iErrorStop = 110; + throw iRc; + } + + xbString s1; + xbString s2; + s2.Sprintf( "[dbase]%c%c", 0x0d, 0x0a ); + if(( iRc = fMd.xbFputs( s2 )) != XB_NO_ERROR ){ + iErrorStop = 120; + throw iRc; + } + + // for each entry in the linked list, write a line + xbLinkListNode * llN = llInfData.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 = 130; + throw iRc; + } + llN = llN->GetNextNode(); + } + + // close the file + if(( iRc = fMd.xbFclose()) != XB_NO_ERROR ){ + iErrorStop = 140; + throw iRc; + } + + } catch( xbInt16 iRc ){ + if( fMd.FileIsOpen()) + fMd.xbFclose(); + xbString sMsg; + sMsg.Sprintf( "xbdbf::SaveInfData() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return iRc; +} +#endif // XB_INF_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 +*/ +#ifdef XB_LOCKING_SUPPORT +void xbDbf::SetAutoLock( xbInt16 iAutoLock ){ + if( iAutoLock == -1 ) + this->iAutoLock = xbase->GetDefaultAutoLock(); + else + this->iAutoLock = iAutoLock; +} +#endif + + +/************************************************************************/ +#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", MDX or TDX", + \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 +*/ +#ifdef XB_LOCKING_SUPPORT +void xbDbf::SetHeaderLocked( xbBool bHeaderLocked ){ + this->bHeaderLocked = bHeaderLocked; +} +#endif + +/************************************************************************/ +//! @brief Set lock flavor. +/*! + This routine is for future expansion. + \param iLockFlavor 1 - Use Dbase (tm) style locking. + \returns void +*/ +#ifdef XB_LOCKING_SUPPORT +void xbDbf::SetLockFlavor( xbInt16 iLockFlavor ){ + this->iLockFlavor = iLockFlavor; +} +#endif + +/************************************************************************/ +//! @brief Set table locked status. +/*! + \param bTableLocked - xbTrue Table locked.
xbFalse Table unlocked. + \returns void +*/ +#ifdef XB_LOCKING_SUPPORT +void xbDbf::SetTableLocked( xbBool bTableLocked ){ + this->bTableLocked = bTableLocked; +} +#endif +/************************************************************************/ +//! @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; +} + +/************************************************************************/ + +//! @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; +} +#endif // XB_INDEX_SUPPORT + +/************************************************************************/ +// @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; + + #ifdef XB_LOCKING_SUPPORT + xbBool bLocked = xbFalse; + #endif // XB_LOCKING_SUPPORT + + try{ + if( iDbfStatus != XB_OPEN ){ + iErrorStop = 100; + iRc = XB_DBF_FILE_NOT_OPEN; + throw iRc; + } + #ifdef XB_LOCKING_SUPPORT + if( iAutoLock ){ + if(( iRc = LockTable( XB_LOCK )) != XB_NO_ERROR ){ + iErrorStop = 110; + throw iRc; + } + } + #endif + + xbDate d; + d.Sysdate(); + 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 = 120; + throw iRc; + } + + // truncate the file to the new size + if(( iRc = xbTruncate( uiHeaderLen )) != XB_NO_ERROR ){ + iErrorStop = 130; + throw iRc; + } + BlankRecord(); + ulCurRec = 0; + + #ifdef XB_MEMO_SUPPORT + if( iMemoFieldCnt ){ + if(( iRc = Memo->Zap()) != XB_NO_ERROR ){ + iErrorStop = 140; + 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 + } + 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 )); + } + } + #ifdef XB_LOCKING_SUPPORT + if( bLocked ){ + LockTable( XB_UNLOCK ); + } + #endif // XB_LOCKING_SUPPORT + return iRc; +} +/************************************************************************/ +#ifdef XB_BLOCKREAD_SUPPORT +// block read processing is designed to provide a way to rapidly retrieve +// all the records from a .DBF file in sequential order. + +// It is not designed for doing any read/write processing or data retrieval based on a tag sort. +// It is designed for doing a fast sequentil block read out of a table + + +xbInt16 xbDbf::DisableBlockReadProcessing(){ + + if( bBlockReadEnabled ){ + bBlockReadEnabled = xbFalse; + delete pRb; + pRb = NULL; + } + return XB_NO_ERROR; +} + +xbBool xbDbf::GetBlockReadStatus() const { + return bBlockReadEnabled; +} + +xbInt16 xbDbf::EnableBlockReadProcessing(){ + + xbInt16 iRc = 0; + xbInt16 iErrorStop = 0; + + try{ + + if( !bBlockReadEnabled ){ + if( iDbfStatus == XB_UPDATED ){ + if( GetAutoCommit() == 1 ){ + if(( iRc = Commit()) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + } else { + if(( iRc = Abort()) != XB_NO_ERROR ){ + iErrorStop = 110; + throw iRc; + } + } + } + pRb = new xbBlockRead( this ); + if( !pRb ){ + iErrorStop = 120; + iRc = XB_NO_MEMORY; + throw iRc; + } + if(( iRc = pRb->Init()) != XB_NO_ERROR ){ + iErrorStop = 130; + throw iRc; + } + + bBlockReadEnabled = xbTrue; + } + + } catch( xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbdbf::EnableBlockReadProcessing() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return iRc; +} + +#endif // XB_BLOCKREAD_SUPPORT +/************************************************************************/ +} /* namespace */ \ No newline at end of file diff --git a/src/core/xbdbf3.cpp b/src/core/xbdbf3.cpp new file mode 100755 index 0000000..926cc0d --- /dev/null +++ b/src/core/xbdbf3.cpp @@ -0,0 +1,768 @@ +/* xbdbf3.cpp + +XBase64 Software Library + +Copyright (c) 1997,2003,2014,2022,2023 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" + + +#ifdef XB_DBF3_SUPPORT + +namespace xb{ + + +/************************************************************************/ +//! @brief Constructor. + +xbDbf3::xbDbf3(xbXBase * x) : xbDbf( x ) { + #ifdef XB_MEMO_SUPPORT + ulCreateMemoBlockSize = 512; + #endif + iFileVersion = 3; +}; + +/************************************************************************/ +//! @brief Destructor. + +xbDbf3::~xbDbf3() {}; + +/************************************************************************/ +//! @brief Create Version 3 table. +/*! + This routine creates a Dbase III Plus (tm) DBF file. + + \param sTableName DBF table name. + \param sAlias Table alias + \param pSchema Pointer to schema structure with field definitions. + \param iOverlay xbTrue - Overlay.
xbFalse - Don't overlay. + \param iShareMode XB_SINGLE_USER
XB_MULTI_USER + \returns Return Codes + +*/ +xbInt16 xbDbf3::CreateTable( const xbString & sTableName, const xbString & sAlias, xbSchema *pSchema, xbInt16 iOverlay, xbInt16 iShareMode ){ + + xbInt16 i, k, k2; + xbInt16 rc = 0; + xbInt16 iErrorStop = 0; + iDbfStatus = XB_CLOSED; + + xbString sNfn; + try{ + sNfn = sTableName; + xbase->GetLogStatus(); + + rc = NameSuffixMissing( sNfn, 1 ); + if( rc > 0 ) + sNfn += ".DBF"; + + SetFileName( sNfn ); + this->sAlias = sAlias; + + /* check if the file already exists */ + if( FileExists( 0 )){ + if( !iOverlay ){ + iErrorStop = 100; + rc = XB_FILE_EXISTS; + throw rc; + } + + // remove other files if they exist + xbString sMname = sNfn; + xbUInt32 iMnameLen = sMname.Len(); + sMname.PutAt( iMnameLen-2, 'I' ); + sMname.PutAt( iMnameLen-1, 'N' ); + sMname.PutAt( iMnameLen, 'F' ); + xbFile fTemp( xbase ); + fTemp.SetFileName( sMname ); + if( fTemp.FileExists() ) + fTemp.xbRemove(); + + sMname.PutAt( iMnameLen-2, 'D' ); + sMname.PutAt( iMnameLen-1, 'B' ); + sMname.PutAt( iMnameLen, 'T' ); + fTemp.SetFileName( sMname ); + if( fTemp.FileExists() ) + fTemp.xbRemove(); + + sMname.PutAt( iMnameLen-2, 'M' ); + sMname.PutAt( iMnameLen-1, 'D' ); + sMname.PutAt( iMnameLen, 'X' ); + fTemp.SetFileName( sMname ); + if( fTemp.FileExists() ) + fTemp.xbRemove(); + } + + /* check if we already have a file with this alias */ + if(( rc = xbase->AddTblToTblList( this, GetFqFileName(), sAlias )) != XB_NO_ERROR ){ + iErrorStop = 110; + throw rc; + } + + rc = ValidateSchema( pSchema ); + if( rc < 0 ){ + iErrorStop = 120; + throw rc; + } else + iNoOfFields = rc; + + #ifdef XB_MEMO_SUPPORT + // if we have memo fields + iMemoFieldCnt = 0; + i = 0; + while( pSchema[i].cType != 0){ + if( pSchema[i].cType == 'M' ) + iMemoFieldCnt++; /* number of memo fields in the incoming definition */ + i++; + } + + if( iMemoFieldCnt > 0 ){ + xbString sMfn = sNfn; /* memo file name, same as filename except ends with a "t", not an "f" */ + xbUInt32 iMfnLen = sMfn.Len(); + sMfn.PutAt( iMfnLen, 'T' ); + + // dont overlay the memo file if it exists, and Overlay switch is off + xbFile fTemp( xbase ); + fTemp.SetFileName( sMfn ); + if( fTemp.FileExists() && !iOverlay ){ + iErrorStop = 130; + rc = XB_FILE_EXISTS; + throw rc; + } + + Memo = new xbMemoDbt3( this, fTemp.GetFqFileName()); + + if(( rc = Memo->CreateMemoFile()) != XB_NO_ERROR ){ + iErrorStop = 140; + throw rc; + } + } + #endif + + /* this is the dBase III version of the class */ + cVersion = 0x03; // 0x03 for Dbase level 5 + #ifdef XB_MEMO_SUPPORT + if( iMemoFieldCnt > 0 ){ +// cVersion = cVersion |= 0x80; // Version III memo, compiler complaints + cVersion |= 0x80; // Version III memo + } + #endif + + if(( rc = xbFopen( "w+b", iShareMode )) != XB_NO_ERROR ){ + iErrorStop = 150; + throw rc; + } + uiRecordLen++; /* add one byte for 0x0D */ + + if(( RecBuf = (char *) malloc( uiRecordLen )) == NULL ){ + iErrorStop = 160; + throw rc; + } + + if(( RecBuf2 = (char *) malloc( uiRecordLen )) == NULL ){ + iErrorStop = 170; + rc = XB_NO_MEMORY; + throw rc; + } + + /* BlankRecord(); */ + memset( RecBuf, 0x20, uiRecordLen ); + memset( RecBuf2, 0x20, uiRecordLen ); + ulCurRec = 0L; + uiHeaderLen = 33 + iNoOfFields * 32; + xbDate d; + d.Sysdate(); + cUpdateYY = (char) (d.YearOf() - 1900); + cUpdateMM = (char) d.MonthOf(); + cUpdateDD = (char) d.DayOf( XB_FMT_MONTH ); + + /* write the header prolog */ + if(( rc = WriteHeader( 0, 0 )) != XB_NO_ERROR ){ + iErrorStop = 180; + rc = XB_WRITE_ERROR; + throw rc; + } + if((SchemaPtr = (xbSchemaRec *) malloc( (size_t) iNoOfFields * sizeof( xbSchemaRec ))) == NULL){ + iErrorStop = 190; + rc = XB_NO_MEMORY; + throw rc; + } + + /* write the field information into the header */ + for( i = 0, k = 1; i < iNoOfFields; i++ ){ + + memset( SchemaPtr[i].cFieldName, 0x00, 11 ); + for( int x = 0; x < 10 && pSchema[i].cFieldName[x]; x++ ) + SchemaPtr[i].cFieldName[x] = pSchema[i].cFieldName[x]; + + SchemaPtr[i].cType = pSchema[i].cType; + SchemaPtr[i].cFieldLen = (unsigned char) pSchema[i].iFieldLen; + SchemaPtr[i].cNoOfDecs = (unsigned char) pSchema[i].iNoOfDecs; + + if( SchemaPtr[i].cNoOfDecs > SchemaPtr[i].cFieldLen ){ + iErrorStop = 200; + rc = XB_WRITE_ERROR; + throw rc; + } + + k2 = k; + k += SchemaPtr[i].cFieldLen; + + if(( xbFwrite( &SchemaPtr[i].cFieldName, 1, 11 )) != XB_NO_ERROR ) { + iErrorStop = 210; + rc = XB_WRITE_ERROR; + throw rc; + } + + if(( xbFwrite( &SchemaPtr[i].cType, 1, 1 )) != XB_NO_ERROR ) { + iErrorStop = 220; + rc = XB_WRITE_ERROR; + throw rc; + } + + for( int j = 0; j < 4; j++ ) + xbFputc( 0x00 ); + + if(( xbFwrite( &SchemaPtr[i].cFieldLen, 1, 1 )) != XB_NO_ERROR ) { + iErrorStop = 230; + rc = XB_WRITE_ERROR; + throw rc; + } + + if(( xbFwrite( &SchemaPtr[i].cNoOfDecs, 1, 1 )) != XB_NO_ERROR ) { + iErrorStop = 240; + rc = XB_WRITE_ERROR; + throw rc; + } + + /* 14 bytes reserved */ + for( int j = 0; j < 14; j++ ) + xbFputc( 0x00 ); + + SchemaPtr[i].pAddress = RecBuf + k2; + SchemaPtr[i].pAddress2 = RecBuf2 + k2; + } + + /* write the header terminator */ + if(( xbFputc( XB_CHARHDR )) != XB_NO_ERROR ){ + iErrorStop = 250; + rc = XB_WRITE_ERROR; + throw rc; + } + } + catch( xbInt16 rc ) + { + xbString sMsg; + sMsg.Sprintf( "xbdbf3::CreateTable() Exception Caught Error Stop = [%d] rc = [%d]", iErrorStop, rc ); + xbase->WriteLogMessage( sMsg ); + xbase->WriteLogMessage( GetErrorMessage( rc )); + + sMsg.Sprintf( "Table Name = [%s]", GetFqFileName().Str()); + xbase->WriteLogMessage( sMsg ); + sMsg.Sprintf( "Alias Name = [%s]", sAlias.Str()); + xbase->WriteLogMessage( sMsg ); + + xbFclose(); + if( RecBuf ){ + free( RecBuf ); + RecBuf = NULL; + } + if( RecBuf2 ){ + free( RecBuf2 ); + RecBuf2 = NULL; + } + if( SchemaPtr ){ + free( SchemaPtr ); + SchemaPtr = NULL; + } + + InitVars(); + if( rc != XB_FILE_EXISTS ) + xbase->RemoveTblFromTblList( sAlias ); + } + + if( rc == XB_NO_ERROR ) + iDbfStatus = XB_OPEN; + + return rc; +} + +/************************************************************************/ +//! @brief Get version. +/*! + The version info can be retrieved to determine + which class is being used for a given dbf instance. + \returns 3 +*/ + +xbInt16 xbDbf3::GetVersion() const { + return 3; +} + + + +/************************************************************************/ +//! @brief Open dbf file/table. +/*! + \param sTableName DBF table name. + \param sAlias Table alias + \param iOpenMode XB_READ
XB_READ_WRITE + \param iShareMode XB_SINGLE_USER
XB_MULTI_USER + \returns Return Codes +*/ + +xbInt16 xbDbf3::Open( const xbString & sTableName, const xbString & sAlias, + xbInt16 iOpenMode, xbInt16 iShareMode ){ + xbInt16 i, j, iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + char buf[33]; + char *p; + + #ifdef XB_MEMO_SUPPORT + iMemoFieldCnt = 0; + #endif + + try{ + + /* verify the file is not already open */ + if( iDbfStatus != XB_CLOSED ){ + iErrorStop = 100; + iRc = XB_ALREADY_OPEN; + throw iRc; + } + /* copy the file name to the class variable */ + SetFileName( sTableName ); + this->sAlias = sAlias; + + if( !FileExists()){ + iErrorStop = 110; + iRc = XB_FILE_NOT_FOUND; + throw iRc; + } + + if(( iRc = xbase->AddTblToTblList( this, GetFqFileName(), sAlias )) != XB_NO_ERROR ){ + iErrorStop = 120; + throw iRc; + } + + /* open the file */ + if(( iRc = xbFopen( iOpenMode, iShareMode )) != XB_NO_ERROR ){ + iErrorStop = 130; + throw iRc; + } + + /* copy the header into memory */ + if(( iRc = ReadHeader( 1, 0 )) != XB_NO_ERROR ){ + iErrorStop = 140; + throw iRc; + } + + /* check the version */ + if(( cVersion & 0x07 ) != 3 ){ + // if( xFileVersion != 3 && xFileVersion != 4 ){ + iErrorStop = 150; + iRc = XB_FILE_TYPE_NOT_SUPPORTED; + throw iRc; + } + iFileVersion = 3; + + /* calculate the number of fields */ + if( cVersion == (char)0x30 ) { + iNoOfFields = ( uiHeaderLen - 296 ) / 32 ; + } else { + iNoOfFields = ( uiHeaderLen - 33 ) / 32; + } + + if(( RecBuf = (char *) malloc( (size_t) uiRecordLen )) == NULL ){ + iErrorStop = 160; + iRc = XB_NO_MEMORY; + throw iRc; + } + + if(( RecBuf2 = (char *) malloc( uiRecordLen )) == NULL ) { + iErrorStop = 170; + iRc = XB_NO_MEMORY; + throw iRc; + } + + if((SchemaPtr=(xbSchemaRec *)malloc( (size_t) iNoOfFields * sizeof( xbSchemaRec ))) == NULL){ + iErrorStop = 180; + iRc = XB_NO_MEMORY; + throw iRc; + } + + memset( SchemaPtr, 0x00, ( (size_t) iNoOfFields * sizeof(xbSchemaRec) )); + /* copy field info into memory */ + for( i = 0, j = 1; i < iNoOfFields; i++ ){ + xbFseek( ((xbInt64)i*32+32), SEEK_SET ); + xbFread( &buf, 1, 32 ); + p = buf; + for( int x = 0; x < 10 && buf[x]; x++ ){ + SchemaPtr[i].cFieldName[x] = buf[x]; + } + p = buf + 11; + SchemaPtr[i].cType = *p++; + SchemaPtr[i].pAddress = RecBuf + j; + SchemaPtr[i].pAddress2 = RecBuf2 + j; + SchemaPtr[i].cFieldLen = (unsigned char) *( p + 4 ); + SchemaPtr[i].cNoOfDecs = (unsigned char) *( p + 5 ); + j += SchemaPtr[i].cFieldLen; + #ifdef XB_MEMO_SUPPORT + if( (SchemaPtr[i].cType == 'M' || SchemaPtr[i].cType == 'B' || SchemaPtr[i].cType == 'O' )) + iMemoFieldCnt++; + #endif + } + ulCurRec = 0L; + iDbfStatus = XB_OPEN; + if(( iRc = BlankRecord()) != XB_NO_ERROR ){ + iErrorStop = 190; + throw iRc; + } + + #ifdef XB_MEMO_SUPPORT + if( iMemoFieldCnt > 0 ){ /* does this table have memo fields ? */ + + // build the file name + xbString sMfn = GetFqFileName(); /* memo file name, same as filename except ends with a "t", not an "f" */ + xbUInt32 ulMfnLen = sMfn.Len(); + if( sMfn[ulMfnLen] == 'F' ) + sMfn.PutAt( ulMfnLen, 'T' ); + else + sMfn.PutAt( ulMfnLen, 't' ); + xbFile fTemp( xbase ); + fTemp.SetFileName( sMfn );; + + Memo = new xbMemoDbt3( this, fTemp.GetFqFileName()); + + if(( iRc = Memo->OpenMemoFile()) != XB_NO_ERROR ){ + iErrorStop = 200; + throw iRc; + } + } + #endif // XB_MEMO_SUPPORT + + } + catch ( xbInt16 iRc ) + { + xbString sMsg; + sMsg.Sprintf( "xbdbf3::Open() Exception Caught Error Stop = [%d] iRc = [%d] ShareMode = [%d] OpenMode = [%d]", iErrorStop, iRc, iShareMode, iOpenMode ); + xbase->WriteLogMessage( sMsg ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + xbFclose(); + if( RecBuf ){ + free( RecBuf ); + RecBuf = NULL; + } + if( RecBuf2 ){ + free( RecBuf2 ); + RecBuf2 = NULL; + } + if( SchemaPtr ){ + free( SchemaPtr ); + SchemaPtr = NULL; + } + InitVars(); + + #ifdef XB_MEMO_SUPPORT + if( Memo ){ + Memo->CloseMemoFile(); + delete Memo; + Memo = NULL; + } + #endif // XB_MEMO_SUPPORT + } + + if( iRc == XB_NO_ERROR ) + iDbfStatus = XB_OPEN; + + return iRc; +} +/************************************************************************/ +//! @brief Rename table. +/*! + This routine renames a give table, associated memo and inf files + \param sNewName - New file name. + \returns Return Codes +*/ +xbInt16 xbDbf3::Rename( const xbString sNewName ){ + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + + xbString sNewDbf; + + xbBool bDbfRenamed = xbFalse; + + #ifdef XB_INF_SUPPORT + xbString sNewInf; + xbString sThisInf; + xbBool bInfRenamed = xbFalse; + #endif + + #ifdef XB_MEMO_SUPPORT + xbString sNewDbt; + xbString sThisDbt; + xbBool bDbtRenamed = xbFalse; + #endif // XB_MEMO_SUPPORT + + #ifdef XB_LOCKING_SUPPORT + xbBool bLocked = xbFalse; + #endif + + try{ + + xbString sDir; + xbString sFile; + xbString sExt; + + xbString sNewNameWoExt; + sNewNameWoExt.Set( sNewName ); + if( sNewName.Pos( ".DBF" ) > 0 ) + sNewNameWoExt.Left( sNewName.Len() - 4 ); + + GetFileDirPart ( sDir ); + GetFileNamePart( sFile ); + GetFileExtPart ( sExt ); + + if( FileExists( sNewDbf )) return XB_FILE_EXISTS; + sNewDbf.Sprintf( "%s%s.DBF", sDir.Str(), sNewNameWoExt.Str()); + + #ifdef XB_MEMO_SUPPORT + sNewDbt.Sprintf( "%s%s.DBT", sDir.Str(), sNewNameWoExt.Str()); + if( FileExists( sNewDbt )) return XB_FILE_EXISTS; + sThisDbt.Sprintf( "%s%s.DBT", sDir.Str(), sFile.Str()); + #endif + + #ifdef XB_INF_SUPPORT + sNewInf.Sprintf( "%s%s.INF", sDir.Str(), sNewNameWoExt.Str()); + if( FileExists( sNewInf )) return XB_FILE_EXISTS; + sThisInf.Sprintf( "%s%s.INF", sDir.Str(), sFile.Str()); + #endif // XB_INF_SUPPORT + + #ifdef XB_LOCKING_SUPPORT + if( GetAutoLock() && GetTableLocked() ){ + if(( iRc = LockTable( XB_LOCK )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + bLocked = xbTrue; + } + #endif + + xbInt16 iOpenMode = GetOpenMode(); + xbInt16 iShareMode = GetShareMode(); + xbBool bWasOpen = xbFalse; + if( FileIsOpen() ){ + bWasOpen = xbTrue; + if(( iRc = xbFclose()) != XB_NO_ERROR ){ + iErrorStop = 110; + throw iRc; + } + } + + if(( iRc = xbRename( GetFqFileName().Str(), sNewDbf.Str())) != XB_NO_ERROR ){ + iErrorStop = 120; + throw iRc; + } else { + bDbfRenamed = xbTrue; + } + xbString sNameWext; + sNameWext.Sprintf( "%s.DBF", sNewNameWoExt.Str()); + SetFileName( sNameWext ); + + if( bWasOpen ){ + if(( iRc = xbFopen( iOpenMode, iShareMode )) != XB_NO_ERROR ){ + iErrorStop = 130; + throw iRc; + } + } + + #ifdef XB_MEMO_SUPPORT + if( FileExists( sThisDbt )){ + if( bWasOpen ){ + if(( iRc = Memo->xbFclose()) != XB_NO_ERROR ){ + iErrorStop = 140; + throw iRc; + } + } + if(( xbRename( sThisDbt.Str(), sNewDbt.Str())) != XB_NO_ERROR ){ + iErrorStop = 150; + throw iRc; + } + Memo->SetFileName( sNewDbt ); + if( bWasOpen ){ + if(( iRc = Memo->xbFopen( iOpenMode, iShareMode )) != XB_NO_ERROR ){ + iErrorStop = 160; + throw iRc; + } + } + bDbtRenamed = xbTrue; + } + #endif // XB_MEMO_SUPPORT + + #ifdef XB_INF_SUPPORT + if( FileExists( sThisInf )){ + if(( iRc = xbRename( sThisInf.Str(), sNewInf.Str())) != XB_NO_ERROR ){ + iErrorStop = 170; + throw iRc; + } else { + bInfRenamed = xbTrue; + } + } + #endif // XB_INF_SUPPORT + + // rname the table in the table list + xbTblList *tle = xbase->GetTblListEntry( this ); + if( tle ){ + // std::cout << "setting [" << GetFqFileName().Str() << "][" << sNewNameWoExt.Str() << "]\n"; + tle->psFqTblName->Set( GetFqFileName().Str()); + tle->psTblAlias->Set( sNewNameWoExt.Str()); + } + } + catch ( xbInt16 iRc ) + { + xbString sMsg; + sMsg.Sprintf( "xbdbf3::Rename() Exception Caught Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + + // attempt to reverse things out if unsuccessful + if( bDbfRenamed ){ + #ifdef XB_MEMO_SUPPORT + if( bDbtRenamed ){ + xbRename( sNewDbt.Str(), sThisDbt.Str()); + } + #endif // XB_MEMO_SUPPORT + #ifdef XB_INF_SUPPORT + if( bInfRenamed ){ + xbRename( sNewInf.Str(), sNewInf.Str()); + } + #endif // XB_INF_SUPPORT + } + + #ifdef XB_LOCKING_SUPPORT + if( bLocked ){ + LockTable( XB_UNLOCK ); + } + #endif + + } + return iRc; +} + + +/************************************************************************/ +#ifdef XB_MEMO_SUPPORT +//! @brief Create memo block size. +/*! + This routine sets the memo file block size. This value is used when + the memo file is created so you if you want to change it, this must be + called before creating the table. + + DBF III Plus uses a block size of 512. + + \param ulBlockSize - Block size, must be evenly divisible by 512. + \returns XB_INVALID_BLOCK_SIZE
XB_NO_ERROR +*/ + +xbInt16 xbDbf3::SetCreateMemoBlockSize( xbUInt32 ulBlockSize ){ + + if( ulBlockSize != 512 ) + return XB_INVALID_BLOCK_SIZE; + else + ulCreateMemoBlockSize = 512; + + return XB_NO_ERROR; +} +#endif + +/************************************************************************/ +//! @brief Set version. +/*! + Sets the version to 3. The version info can be retrieved to determine + which class is being used for a given dbf instance. + \returns 3 +*/ + +xbInt16 xbDbf3::SetVersion() { + iFileVersion = 3; + return iFileVersion; +} + + +/************************************************************************/ +//! @brief Validate schema +/*! + This routine verifies the field types are valid for Dbase III Plus (tm). + + \param s Pointer to schema structure with field definitions. + + \returns Number of fields or XB_INVALID_FIELD_TYPE. +*/ + +xbInt16 xbDbf3::ValidateSchema( xbSchema * s ){ + +// This routine validates an input schema +// Negative return value is an error +// Positive return value is the number of fields +// On success, the class variable uiRecordLen will be updated with the record length of the combined total of the fields + + xbInt16 iFieldCnt = 0; + uiRecordLen = 0; + + /* count the number of fields and check paramaters */ + xbInt16 i = 0; + while( s[i].cType != 0 ){ + iFieldCnt++; + // Version 3 field types + if( s[i].cType != 'C' && + s[i].cType != 'N' && + s[i].cType != 'D' && + #ifdef XB_MEMO_SUPPORT + s[i].cType != 'M' && + #endif // XB_MEMO_SUPPORT + s[i].cType != 'L' ){ + return XB_INVALID_FIELD_TYPE; + } + + if(s[i].cType == 'D'){ + s[i].iFieldLen = 8; + s[i].iNoOfDecs = 0; + } + + else if(s[i].cType == 'C') + s[i].iNoOfDecs = 0; + + // check for numeric fields which are too long + else if( s[i].cType == 'N' && s[i].iFieldLen > 19 ){ + return XB_INVALID_FIELD_LEN; + } + // field len must be >= no of decimals + else if( s[i].cType == 'N' && s[i].iFieldLen < s[i].iNoOfDecs ){ + return XB_INVALID_FIELD_LEN; + } + + #ifdef XB_MEMO_SUPPORT + else if(s[i].cType == 'M'){ + s[i].iFieldLen = 10; + s[i].iNoOfDecs = 0; + } + #endif // XB_MEMO_SUPPORT + + uiRecordLen += s[i].iFieldLen; + i++; + } + return iFieldCnt; +} + +} /* namespace */ +#endif /* XB_DBF3_SUPPORT */ \ No newline at end of file diff --git a/src/core/xbdbf4.cpp b/src/core/xbdbf4.cpp new file mode 100755 index 0000000..a1f770e --- /dev/null +++ b/src/core/xbdbf4.cpp @@ -0,0 +1,885 @@ +/* xbdbf4.cpp + +XBase64 Software Library + +Copyright (c) 1997,2003,2014,2022,2023 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" + + +#ifdef XB_DBF4_SUPPORT + +namespace xb{ + + +/************************************************************************/ +//! @brief Constructor. +xbDbf4::xbDbf4(xbXBase * x) : xbDbf( x ) { + + iFileVersion = 4; + #ifdef XB_MEMO_SUPPORT + ulCreateMemoBlockSize = 1024; + #endif +}; + +/************************************************************************/ +//! @brief Destructor. +xbDbf4::~xbDbf4() {}; + +/************************************************************************/ +//! @brief Create Version 4 table. +/*! + This routine creates a Dbase IV (tm) DBF file. + + \param sTableName DBF table name. + \param sAlias Table alias + \param pSchema Pointer to schema structure with field definitions. + \param iOverlay xbTrue - Overlay.
xbFalse - Don't overlay. + \param iShareMode XB_SINGLE_USER
XB_MULTI_USER + \returns Return Codes +*/ + +xbInt16 xbDbf4::CreateTable( const xbString &sTableName, const xbString &sAlias, xbSchema * pSchema, xbInt16 iOverlay, xbInt16 iShareMode ){ + + xbInt16 i, k, k2; + xbInt16 rc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + iDbfStatus = XB_CLOSED; + + xbString sNfn; + + try{ + sNfn = sTableName; + xbase->GetLogStatus(); + + rc = NameSuffixMissing( sNfn, 1 ); + if( rc > 0 ) + sNfn += ".DBF"; + + SetFileName( sNfn ); + this->sAlias = sAlias; + + /* check if the file already exists */ + if( FileExists( 0 )){ + if( !iOverlay ){ + iErrorStop = 100; + rc = XB_FILE_EXISTS; + throw rc; + } + + // remove other files if they exist + xbString sMname = sNfn; + xbUInt32 iMnameLen = sMname.Len(); + sMname.PutAt( iMnameLen-2, 'I' ); + sMname.PutAt( iMnameLen-1, 'N' ); + sMname.PutAt( iMnameLen, 'F' ); + xbFile fTemp( xbase ); + fTemp.SetFileName( sMname ); + if( fTemp.FileExists() ) + fTemp.xbRemove(); + + sMname.PutAt( iMnameLen-2, 'D' ); + sMname.PutAt( iMnameLen-1, 'B' ); + sMname.PutAt( iMnameLen, 'T' ); + fTemp.SetFileName( sMname ); + if( fTemp.FileExists() ) + fTemp.xbRemove(); + + sMname.PutAt( iMnameLen-2, 'M' ); + sMname.PutAt( iMnameLen-1, 'D' ); + sMname.PutAt( iMnameLen, 'X' ); + fTemp.SetFileName( sMname ); + if( fTemp.FileExists() ) + fTemp.xbRemove(); + } + + /* check if we already have a file with this alias */ + if(( rc = xbase->AddTblToTblList( this, GetFqFileName(), sAlias )) != XB_NO_ERROR ){ + iErrorStop = 110; + throw rc; + } + + rc = ValidateSchema( pSchema ); + if( rc < 0 ){ + iErrorStop = 120; + throw rc; + } else + iNoOfFields = rc; + + #ifdef XB_MEMO_SUPPORT + // if we have memo fields + iMemoFieldCnt = 0; + i = 0; + while( pSchema[i].cType != 0){ + if( pSchema[i].cType == 'M' ) + iMemoFieldCnt++; /* number of memo fields in the incoming definition */ + i++; + } + + if( iMemoFieldCnt > 0 ){ + xbString sMfn = sNfn; /* memo file name, same as filename except ends with a "t", not an "f" */ + xbUInt32 ulMfnLen = sMfn.Len(); + sMfn.PutAt( ulMfnLen, 'T' ); + + // dont overlay the memo file if it exists, and Overlay switch is off + xbFile fTemp( xbase ); + fTemp.SetFileName( sMfn ); + if( fTemp.FileExists() && !iOverlay ){ + iErrorStop = 130; + rc = XB_FILE_EXISTS; + throw rc; + } + + Memo = new xbMemoDbt4( this, fTemp.GetFqFileName()); + + if(( rc = Memo->CreateMemoFile()) != XB_NO_ERROR ){ + iErrorStop = 140; + throw rc; + } + } + #endif + + /* this is the dBase IV version of the class */ + cVersion = 0x03; // 0x03 for Dbase level 5 + #ifdef XB_MEMO_SUPPORT + if( iMemoFieldCnt > 0 ){ +// cVersion = cVersion |= 0x88; // version IV memos, compiler complains about this + cVersion |= 0x88; // version IV memos + } + #endif + + if(( rc = xbFopen( "w+b", iShareMode )) != XB_NO_ERROR ){ + iErrorStop = 150; + throw rc; + } + uiRecordLen++; /* add one byte for 0x0D */ + + if(( RecBuf = (char *) malloc( uiRecordLen )) == NULL ){ + iErrorStop = 160; + throw rc; + } + + if(( RecBuf2 = (char *) malloc( uiRecordLen )) == NULL ){ + iErrorStop = 170; + rc = XB_NO_MEMORY; + throw rc; + } + + /* BlankRecord(); */ + memset( RecBuf, 0x20, uiRecordLen ); + memset( RecBuf2, 0x20, uiRecordLen ); + ulCurRec = 0L; + uiHeaderLen = 33 + iNoOfFields * 32; + xbDate d; + d.Sysdate(); + cUpdateYY = (char) (d.YearOf() - 1900); + cUpdateMM = (char) d.MonthOf(); + cUpdateDD = (char) d.DayOf( XB_FMT_MONTH ); + cIndexFlag = 0; + // Default language driver to 0x1b + cLangDriver = 0x1b; + + /* write the header prolog */ + if(( rc = WriteHeader( 0, 0 )) != XB_NO_ERROR ){ + iErrorStop = 180; + rc = XB_WRITE_ERROR; + throw rc; + } + if((SchemaPtr=(xbSchemaRec *)malloc( (size_t) iNoOfFields * sizeof( xbSchemaRec ))) == NULL){ + iErrorStop = 190; + rc = XB_NO_MEMORY; + throw rc; + } + + /* write the field information into the header */ + for( i = 0, k = 1; i < iNoOfFields; i++ ){ + + memset( SchemaPtr[i].cFieldName, 0x00, 11 ); + for( int x = 0; x < 10 && pSchema[i].cFieldName[x]; x++ ) + SchemaPtr[i].cFieldName[x] = pSchema[i].cFieldName[x]; + + SchemaPtr[i].cType = pSchema[i].cType; + SchemaPtr[i].cFieldLen = (unsigned char) pSchema[i].iFieldLen; + SchemaPtr[i].cNoOfDecs = (unsigned char) pSchema[i].iNoOfDecs; + + if( SchemaPtr[i].cNoOfDecs > SchemaPtr[i].cFieldLen ){ + iErrorStop = 110; + rc = XB_WRITE_ERROR; + throw rc; + } + + k2 = k; + k += SchemaPtr[i].cFieldLen; + + if(( xbFwrite( &SchemaPtr[i].cFieldName, 1, 11 )) != XB_NO_ERROR ) { + iErrorStop = 200; + rc = XB_WRITE_ERROR; + throw rc; + } + + if(( xbFwrite( &SchemaPtr[i].cType, 1, 1 )) != XB_NO_ERROR ) { + iErrorStop = 210; + rc = XB_WRITE_ERROR; + throw rc; + } + + for( int j = 0; j < 4; j++ ) + xbFputc( 0x00 ); + + if(( xbFwrite( &SchemaPtr[i].cFieldLen, 1, 1 )) != XB_NO_ERROR ) { + iErrorStop = 220; + rc = XB_WRITE_ERROR; + throw rc; + } + + if(( xbFwrite( &SchemaPtr[i].cNoOfDecs, 1, 1 )) != XB_NO_ERROR ) { + iErrorStop = 230; + rc = XB_WRITE_ERROR; + throw rc; + } + + /* 14 bytes reserved */ + for( int j = 0; j < 14; j++ ) + xbFputc( 0x00 ); + + SchemaPtr[i].pAddress = RecBuf + k2; + SchemaPtr[i].pAddress2 = RecBuf2 + k2; + } + + /* write the header terminator */ + if(( xbFputc( XB_CHARHDR )) != XB_NO_ERROR ){ + iErrorStop = 240; + rc = XB_WRITE_ERROR; + throw rc; + } + } + catch( xbInt16 rc ) + { + xbString sMsg; + sMsg.Sprintf( "xbdbf4::CreateTable() Exception Caught Error Stop = [%d] rc = [%d]", iErrorStop, rc ); + xbase->WriteLogMessage( sMsg ); + xbase->WriteLogMessage( GetErrorMessage( rc )); + + sMsg.Sprintf( "Table Name = [%s]", GetFqFileName().Str()); + xbase->WriteLogMessage( sMsg ); + sMsg.Sprintf( "Alias Name = [%s]", sAlias.Str()); + xbase->WriteLogMessage( sMsg ); + + xbFclose(); + if( RecBuf ){ + free( RecBuf ); + RecBuf = NULL; + } + if( RecBuf2 ){ + free( RecBuf2 ); + RecBuf2 = NULL; + } + if( SchemaPtr ){ + free( SchemaPtr ); + SchemaPtr = NULL; + } + + InitVars(); + if( rc != XB_FILE_EXISTS ) + xbase->RemoveTblFromTblList( sAlias ); + } + + if( rc == XB_NO_ERROR ) + iDbfStatus = XB_OPEN; + + return rc; +} + +/************************************************************************/ +//! @brief Get version. +/*! + The version info can be retrieved to determine + which class is being used for a given dbf instance. + \returns 4 +*/ + +xbInt16 xbDbf4::GetVersion() const { + return 4; +} + +/************************************************************************/ +//! @brief Open dbf file/table. +/*! + \param sTableName DBF table name. + \param sAlias Table alias + \param iOpenMode XB_READ
XB_READ_WRITE + \param iShareMode XB_SINGLE_USER
XB_MULTI_USER + \returns Return Codes +*/ + +xbInt16 xbDbf4::Open( const xbString & sTableName, const xbString & sAlias, + xbInt16 iOpenMode, xbInt16 iShareMode ){ + xbInt16 i, j, iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + char buf[33]; + char *p; + + #ifdef XB_MEMO_SUPPORT + iMemoFieldCnt = 0; + #endif + + try{ + /* verify the file is not already open */ + if( iDbfStatus != XB_CLOSED ){ + iErrorStop = 100; + iRc = XB_ALREADY_OPEN; + throw iRc; + } + /* copy the file name to the class variable */ + SetFileName( sTableName ); + this->sAlias = sAlias; + + if( !FileExists()){ + iErrorStop = 110; + iRc = XB_FILE_NOT_FOUND; + throw iRc; + } + + if(( iRc = xbase->AddTblToTblList( this, GetFqFileName(), sAlias )) != XB_NO_ERROR ){ + iErrorStop = 120; + throw iRc; + } + + /* open the file */ + if(( iRc = xbFopen( iOpenMode, iShareMode )) != XB_NO_ERROR ){ + iErrorStop = 130; + throw iRc; + } + + /* copy the header into memory */ + if(( iRc = ReadHeader( 1, 0 )) != XB_NO_ERROR ){ + iErrorStop = 140; + throw iRc; + } + + /* check the version */ + //if(( xFileVersion = DetermineXbaseTableVersion( cVersion )) != 4 ){ + if(( cVersion & 0x07 ) != 3 ){ + iErrorStop = 150; + iRc = XB_FILE_TYPE_NOT_SUPPORTED; + throw iRc; + } + iFileVersion = 4; + + /* calculate the number of fields */ + if( cVersion == (char)0x30 ) { + iNoOfFields = ( uiHeaderLen - 296 ) / 32 ; + } else { + iNoOfFields = ( uiHeaderLen - 33 ) / 32; + } + + if(( RecBuf = (char *) malloc( uiRecordLen )) == NULL ){ + iErrorStop = 160; + iRc = XB_NO_MEMORY; + throw iRc; + } + + if(( RecBuf2 = (char *) malloc( uiRecordLen )) == NULL ) { + iErrorStop = 170; + iRc = XB_NO_MEMORY; + throw iRc; + } + + if((SchemaPtr=(xbSchemaRec *)malloc((size_t) iNoOfFields * sizeof( xbSchemaRec ))) == NULL){ + iErrorStop = 180; + iRc = XB_NO_MEMORY; + throw iRc; + } + + memset( SchemaPtr, 0x00, (size_t) iNoOfFields * (size_t) sizeof( xbSchemaRec )); + /* copy field info into memory */ + for( i = 0, j = 1; i < iNoOfFields; i++ ){ + xbFseek( ((xbInt64)i*32+32), SEEK_SET ); + xbFread( &buf, 1, 32 ); + p = buf; + for( int x = 0; x < 10 && buf[x]; x++ ){ + SchemaPtr[i].cFieldName[x] = buf[x]; + } + p = buf + 11; + SchemaPtr[i].cType = *p++; + SchemaPtr[i].pAddress = RecBuf + j; + SchemaPtr[i].pAddress2 = RecBuf2 + j; + SchemaPtr[i].cFieldLen = (unsigned char) *( p + 4 ); + SchemaPtr[i].cNoOfDecs = (unsigned char) *( p + 5 ); + SchemaPtr[i].cIxFlag = (unsigned char) *( p + 19 ); + j += SchemaPtr[i].cFieldLen; + #ifdef XB_MEMO_SUPPORT + if( (SchemaPtr[i].cType == 'M' || SchemaPtr[i].cType == 'B' || SchemaPtr[i].cType == 'O' )) + iMemoFieldCnt++; + #endif + } + ulCurRec = 0L; + iDbfStatus = XB_OPEN; + if(( iRc = BlankRecord()) != XB_NO_ERROR ){ + iErrorStop = 190; + throw iRc; + } + + + #ifdef XB_MEMO_SUPPORT + if( iMemoFieldCnt > 0 ){ /* does this table have memo fields ? */ + + // build the file name + xbString sMfn = GetFqFileName(); /* memo file name, same as filename except ends with a "t", not an "f" */ + xbUInt32 ulMfnLen = sMfn.Len(); + if( sMfn[ulMfnLen] == 'F' ) + sMfn.PutAt( ulMfnLen, 'T' ); + else + sMfn.PutAt( ulMfnLen, 't' ); + xbFile fTemp( xbase ); + fTemp.SetFileName( sMfn ); + + Memo = new xbMemoDbt4( this, fTemp.GetFqFileName()); + + if(( iRc = Memo->OpenMemoFile()) != XB_NO_ERROR ){ + iErrorStop = 200; + throw iRc; + } + } + #endif + + #ifdef XB_MDX_SUPPORT + +// printf( "cIndexFlag [%x]\n", cIndexFlag ); + + if( cIndexFlag ){ + // create the file name + xbString sIxFileName = GetFqFileName(); + sIxFileName.Trim(); + xbUInt32 lLen = sIxFileName.Len(); + sIxFileName.PutAt( lLen-2, 'M' ); + sIxFileName.PutAt( lLen-1, 'D' ); + sIxFileName.PutAt( lLen, 'X' ); + if(( iRc = OpenIndex( "MDX", sIxFileName )) != XB_NO_ERROR ){ + iErrorStop = 210; + throw iRc; + } + } + #endif + + } + catch ( xbInt16 iRc ) + { + xbString sMsg; + sMsg.Sprintf( "xbdbf4::Open() Exception Caught Error Stop = [%d] iRc = [%d] ShareMode = [%d] OpenMode = [%d]", iErrorStop, iRc, iShareMode, iOpenMode ); + xbase->WriteLogMessage( sMsg ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + xbFclose(); + if( RecBuf ){ + free( RecBuf ); + RecBuf = NULL; + } + if( RecBuf2 ){ + free( RecBuf2 ); + RecBuf2 = NULL; + } + if( SchemaPtr ){ + free( SchemaPtr ); + SchemaPtr = NULL; + } + InitVars(); + +#ifdef XB_MEMO_SUPPORT + if( Memo ){ + Memo->CloseMemoFile(); + delete Memo; + Memo = NULL; + } +#endif + } + + if( iRc == XB_NO_ERROR ) + iDbfStatus = XB_OPEN; + + return iRc; +} + +/************************************************************************/ +//! @brief Rename table. +/*! + This routine renames a table, associated memo, mdx and inf files + \param sNewName - New file name. + \returns Return Codes +*/ +xbInt16 xbDbf4::Rename( const xbString sNewName ){ + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + + xbString sNewDbf; + + #ifdef XB_MEMO_SUPPORT + xbString sNewDbt; + xbBool bDbtRenamed = xbFalse; + xbString sThisDbt; + #endif + + #ifdef XB_MDX_SUPPORT + xbString sNewMdx; + xbBool bMdxRenamed = xbFalse; + xbString sThisMdx; + #endif + + #ifdef XB_INF_SUPPORT + xbString sNewInf; + xbString sThisInf; + xbBool bInfRenamed = xbFalse; + #endif // XB_INF_SUPPORT + + xbBool bDbfRenamed = xbFalse; + + #ifdef XB_LOCKING_SUPPORT + xbBool bLocked = xbFalse; + #endif // XB_LOCKIN_SUPPORT + + + try{ + + xbString sDir; + xbString sFile; + xbString sExt; + xbString sNewNameWoExt; + sNewNameWoExt.Set( sNewName ); + if( sNewName.Pos( ".DBF" ) > 0 ) + sNewNameWoExt.Left( sNewName.Len() - 4 ); + +// std::cout << "NewName wo ext = [" << sNewNameWoExt.Str() << "]\n"; + + GetFileDirPart ( sDir ); + GetFileNamePart( sFile ); + GetFileExtPart ( sExt ); + + sNewDbf.Sprintf( "%s%s.DBF", sDir.Str(), sNewNameWoExt.Str()); + + #ifdef XB_MEMO_SUPPORT + sNewDbt.Sprintf( "%s%s.DBT", sDir.Str(), sNewNameWoExt.Str()); + if( FileExists( sNewDbt )) return XB_FILE_EXISTS; + sThisDbt.Sprintf( "%s%s.DBT", sDir.Str(), sFile.Str()); + #endif + + #ifdef XB_MDX_SUPPORT + sNewMdx.Sprintf( "%s%s.MDX", sDir.Str(), sNewNameWoExt.Str()); + if( FileExists( sNewMdx )) return XB_FILE_EXISTS; + sThisMdx.Sprintf( "%s%s.MDX", sDir.Str(), sFile.Str()); + #endif + + #ifdef XB_INF_SUPPORT + sNewInf.Sprintf( "%s%s.INF", sDir.Str(), sNewNameWoExt.Str()); + if( FileExists( sNewInf )) return XB_FILE_EXISTS; + sThisInf.Sprintf( "%s%s.INF", sDir.Str(), sFile.Str()); + #endif // XB_INF_SUPPORT + + +/* + std::cout << "xbDbf3::Rename dir = [" << sDir.Str() << "] file = [" << sFile.Str() << "] ext = [" << sExt.Str() << "]\n"; + std::cout << "xbDbf3::Rename new dbf = [" << sNewDbf.Str() << "]\n"; + std::cout << "xbDbf3::Rename new dbt = [" << sNewDbt.Str() << "]\n"; + std::cout << "xbDbf3::Rename new inf = [" << sNewInf.Str() << "]\n"; + std::cout << "xbDbf3::Rename new mdx = [" << sNewMdx.Str() << "]\n"; +*/ + + if( FileExists( sNewDbf )) return XB_FILE_EXISTS; + + #ifdef XB_LOCKING_SUPPORT + if( GetAutoLock() ){ + if(( iRc = LockTable( XB_LOCK )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + bLocked = xbTrue; + } + #endif + + xbInt16 iOpenMode = GetOpenMode(); + xbInt16 iShareMode = GetShareMode(); + xbBool bWasOpen = xbFalse; + if( FileIsOpen() ){ + bWasOpen = xbTrue; + if(( iRc = xbFclose()) != XB_NO_ERROR ){ + iErrorStop = 110; + throw iRc; + } + } + + if(( iRc = xbRename( GetFqFileName().Str(), sNewDbf.Str())) != XB_NO_ERROR ){ + iErrorStop = 120; + throw iRc; + } else { + bDbfRenamed = xbTrue; + } + xbString sNameWext; + sNameWext.Sprintf( "%s.DBF", sNewNameWoExt.Str()); + SetFileName( sNameWext ); + + + if( bWasOpen ){ + if(( iRc = xbFopen( iOpenMode, iShareMode )) != XB_NO_ERROR ){ + iErrorStop = 130; + throw iRc; + } + } + + #ifdef XB_MEMO_SUPPORT + if( FileExists( sThisDbt )){ + if(( iRc = Memo->xbFseek( 8, SEEK_SET )) != XB_NO_ERROR ){ + iErrorStop = 140; + throw iRc; + } + + sNewNameWoExt.PadRight( ' ', 8 ); + for( int i = 1; i < 9; i++ ) + Memo->xbFputc( sNewNameWoExt[i] ); + + if( bWasOpen ){ + if(( iRc = Memo->xbFclose()) != XB_NO_ERROR ){ + iErrorStop = 150; + throw iRc; + } + } + + Memo->SetFileName( sNewDbt ); + if(( xbRename( sThisDbt.Str(), sNewDbt.Str())) != XB_NO_ERROR ){ + iErrorStop = 160; + throw iRc; + } + + if( bWasOpen ){ + if(( iRc = Memo->xbFopen( iOpenMode, iShareMode )) != XB_NO_ERROR ){ + iErrorStop = 150; + throw iRc; + } + } + + bDbtRenamed = xbTrue; + } + #endif + + #ifdef XB_MDX_SUPPORT + if( FileExists( sThisMdx )){ + xbIxMdx *ixMdx; + xbString s; + xbBool bMdxFound = xbFalse; + xbIxList *ixList = GetIxList(); + while( ixList && !bMdxFound ){ + s = ixList->sFmt->Str(); + if( s == "MDX" ){ + ixMdx = (xbIxMdx *) ixList->ix; + bMdxFound = xbTrue; + } + } + + if( bMdxFound ){ + if(( iRc = ixMdx->xbFseek( 4, SEEK_SET )) != XB_NO_ERROR ){ + iErrorStop = 180; + throw iRc; + } + + sNewNameWoExt.PadRight( ' ', 8 ); + for( int i = 1; i < 9; i++ ) + ixMdx->xbFputc( sNewNameWoExt[i] ); + + if( bWasOpen ){ + if(( iRc = ixMdx->xbFclose()) != XB_NO_ERROR ){ + iErrorStop = 190; + throw iRc; + } + } + + ixMdx->SetFileName( sNewMdx ); + if(( xbRename( sThisMdx.Str(), sNewMdx.Str())) != XB_NO_ERROR ){ + iErrorStop = 200; + throw iRc; + } + + if( bWasOpen ){ + if(( iRc = ixMdx->xbFopen( iOpenMode, iShareMode )) != XB_NO_ERROR ){ + iErrorStop = 210; + throw iRc; + } + } + bMdxRenamed = xbTrue; + } + } + #endif // XB_MDX_SUPPORT + + #ifdef XB_INF_SUPPORT + if( FileExists( sThisInf )){ + if(( xbRename( sThisInf.Str(), sNewInf.Str())) != XB_NO_ERROR ){ + iErrorStop = 180; + throw iRc; + } else { + bInfRenamed = xbTrue; + } + } + #endif // XB_INF_SUPPORT + + // rename the table in the table list + xbTblList *tle = xbase->GetTblListEntry( this ); + if( tle ){ + tle->psFqTblName->Set( GetFqFileName().Str()); + tle->psTblAlias->Set( sNewNameWoExt.Str()); + } + + + } + catch ( xbInt16 iRc ) + { + xbString sMsg; + sMsg.Sprintf( "xbdbf4::Rename() Exception Caught Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + + // attempt to reverse things out if unsuccessful + if( bDbfRenamed ){ + + #ifdef XB_MEMO_SUPPORT + if( bDbtRenamed ){ + xbRename( sNewDbt.Str(), sThisDbt.Str()); + } + #endif + + #ifdef XB_MDX_SUPPORT + if( bMdxRenamed ){ + xbRename( sNewMdx.Str(), sThisMdx.Str()); + } + #endif + + #ifdef XB_INF_SUPPORT + if( bInfRenamed ){ + xbRename( sNewInf.Str(), sNewInf.Str()); + } + #endif // XB_INF_SUPPORT + } + + #ifdef XB_LOCKING_SUPPORT + if( GetAutoLock() ){ + iRc = LockTable( XB_UNLOCK ); + } + #endif + + } + + #ifdef XB_LOCKING_SUPPORT + if( bLocked ){ + LockTable( XB_UNLOCK ); + } + #endif + + return iRc; +} + +/************************************************************************/ +#ifdef XB_MEMO_SUPPORT + +//! @brief Create memo block size. +/*! + This routine sets the memo file block size. This value is used when + the memo file is created so you if you want to change it, this must be + called before creating the table. + + The default size for version 4 is 1024. + + \param ulBlockSize - Block size, must be evenly divisible by 512. + \returns XB_INVALID_BLOCK_SIZE
XB_NO_ERROR +*/ + +xbInt16 xbDbf4::SetCreateMemoBlockSize( xbUInt32 ulBlockSize ){ + + if( ulBlockSize % 512 ) + return XB_INVALID_BLOCK_SIZE; + else + ulCreateMemoBlockSize = ulBlockSize; + + return XB_NO_ERROR; +} +#endif // XB_MEMO_SUPPORT +/************************************************************************/ +//! @brief Set version. +/*! + Sets the version to 4. The version info can be retrieved to determine + which class is being used for a given dbf instance. + \returns 4 +*/ +xbInt16 xbDbf4::SetVersion() { + iFileVersion = 4; + return iFileVersion; +} +/************************************************************************/ +//! @brief Validate schema +/*! + This routine verifies the field types are valid for Dbase IV (tm). + + \param s Pointer to schema structure with field definitions. + + \returns Number of fields or XB_INVALID_FIELD_TYPE. +*/ + + +xbInt16 xbDbf4::ValidateSchema( xbSchema * s ){ + + xbInt16 iFieldCnt = 0; + uiRecordLen = 0; + + // Count the number of fields and check paramaters + xbInt16 i = 0; + while( s[i].cType != 0 ){ + iFieldCnt++; + // Version IV field types + if( s[i].cType != 'C' && + s[i].cType != 'N' && + s[i].cType != 'F' && + s[i].cType != 'D' && + #ifdef XB_MEMO_SUPPORT + s[i].cType != 'M' && + #endif /* XB_MEMO_SUPPORT */ + s[i].cType != 'L' ){ + return XB_INVALID_FIELD_TYPE; + } + + if(s[i].cType == 'D'){ + s[i].iFieldLen = 8; + s[i].iNoOfDecs = 0; + } + + else if(s[i].cType == 'C') + s[i].iNoOfDecs = 0; + + // check for numeric fields which are too long + else if((s[i].cType == 'N' || s[i].cType == 'F') && s[i].iFieldLen > 19 ){ + return XB_INVALID_FIELD_LEN; + } + + // field len must be greater then number of decimals + else if((s[i].cType == 'N' || s[i].cType == 'F') && s[i].iFieldLen < s[i].iNoOfDecs ){ + return XB_INVALID_FIELD_LEN; + } + + #ifdef XB_MEMO_SUPPORT + else if(s[i].cType == 'M'){ + s[i].iFieldLen = 10; + s[i].iNoOfDecs = 0; + } + #endif // XB_MEMO_SUPPORT + + uiRecordLen += s[i].iFieldLen; + i++; + } + return iFieldCnt; +} + +} /* namespace */ +#endif /* XB_DBF4_SUPPORT */ \ No newline at end of file diff --git a/src/core/xbexp.cpp b/src/core/xbexp.cpp new file mode 100755 index 0000000..b2d4db9 --- /dev/null +++ b/src/core/xbexp.cpp @@ -0,0 +1,2721 @@ +/* xbexp.cpp + +XBase64 Software Library + +Copyright (c) 1997,2003,2014,2017,2021,2022,2023 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 + + +This module is part of the expression logic and has the code +for parsing various tokens out of an expression + +*/ + +#include "xbase.h" + +#ifdef XB_EXPRESSION_SUPPORT + +namespace xb{ + +/*************************************************************************/ +//! Constructor +/*! + \param x Pointer to xbXBase instance. +*/ + +xbExp::xbExp( xbXBase *x ){ + xbase = x; + dbf = NULL; + nTree = NULL; +} + +/*************************************************************************/ +//! Constructor +/*! + \param x Pointer to xbXBase instance. + \param d Pointer to xbDbf instance. +*/ +xbExp::xbExp( xbXBase *x, xbDbf *d ){ + xbase = x; + dbf = d; + nTree = NULL; +} + +/*************************************************************************/ +//! Deconstrucor. + +xbExp::~xbExp() { + + if( nTree ) + delete nTree; +} + +/*************************************************************************/ +//! Calulate expression return length +/*! + + This function returns the maximum possible length of an expression + The create index functions use this for determining the fixed length keys + It sets the return length field in the node. + + \param n Start node + \returns Return Codes +*/ + +xbInt16 xbExp::CalcFunctionResultLen( xbExpNode * n ) const{ + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + xbInt16 iReturnLenCalc = 0;; + xbInt32 lReturnLenVal = 0; + xbString sNodeText; + + + try{ + + n->GetNodeText( sNodeText ); + char cReturnType = 0; + if(( iRc = xbase->GetFunctionInfo( sNodeText, cReturnType, iReturnLenCalc, lReturnLenVal )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + + if( iReturnLenCalc == 1 ){ + // use the value from iReturnLenVal + n->SetResultLen( (xbUInt32) lReturnLenVal ); + } + else if( iReturnLenCalc == 2 ){ + // use the length from the child node identified in lReturnLenVal + xbExpNode *nChild = n->GetChild( (xbUInt32) lReturnLenVal - 1 ); + if( !nChild ){ + iErrorStop = 110; + iRc = XB_PARSE_ERROR; + throw iRc; + } + n->SetResultLen( nChild->GetResultLen()); + } + + else if( iReturnLenCalc == 3 ){ + // use the length from the child node identified in lReturnLenVal + xbExpNode *nChild = n->GetChild( (xbUInt32) lReturnLenVal - 1 ); + if( !nChild ){ + iErrorStop = 120; + iRc = XB_PARSE_ERROR; + throw iRc; + } + n->SetResultLen( (xbUInt32) nChild->GetNumericResult()); + } + else if( iReturnLenCalc == 4 ){ + // use the value from the length in parm 1 multiplied by the value in parm 2 (REPLICATE) + xbExpNode *nChild1 = n->GetChild( 0 ); + xbExpNode *nChild2 = n->GetChild( 1 ); + if( !nChild1 || !nChild2 ){ + iErrorStop = 130; + iRc = XB_PARSE_ERROR; + throw iRc; + } + n->SetResultLen( nChild1->GetResultLen() * (xbUInt32) nChild2->GetNumericResult()); + } + else if( iReturnLenCalc == 5 ){ + // use the larger of the length of the value in parm2 or parm 3 (IIF statement) + xbExpNode *nChild2 = n->GetChild( 1 ); + xbExpNode *nChild3 = n->GetChild( 2 ); + if( !nChild2 || !nChild3 ){ + iErrorStop = 140; + iRc = XB_PARSE_ERROR; + throw iRc; + } + if( nChild2->GetResultLen() >= nChild3->GetResultLen()) + n->SetResultLen( nChild2->GetResultLen()); + else + n->SetResultLen( nChild3->GetResultLen()); + } + + else if( iReturnLenCalc == 6 ){ + + if( n->GetChildCnt() >= 2 ){ + xbExpNode *nChild2 = n->GetChild( 1 ); + n->SetResultLen( (xbUInt32) nChild2->GetNumericResult()); + } else { + n->SetResultLen( (xbUInt32) lReturnLenVal ); + } + + } else { + iErrorStop = 150; + iRc = XB_PARSE_ERROR; + throw iRc; + } + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbexp::GetFunctionResultLen() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + } + return iRc; +} + +/*************************************************************************/ +//! Check parens and quotes +/*! + This routine looks for unbalanced parens and quotes + + \param sExpression Expression to examine. + \returns Return Codes +*/ + + +xbInt16 xbExp::CheckParensAndQuotes( const xbString &sExpression ){ + + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + + xbBool bInQuotes = xbFalse; + xbInt16 iLparenCtr = 0; + xbInt16 iRparenCtr = 0; + xbInt16 iQuoteType = 0; + const char *s = sExpression; + + try{ + + while( *s ){ + if( !bInQuotes ){ + if( *s == '(' ){ + iLparenCtr++; + } else if( *s == ')' ){ + iRparenCtr++; + } else if( *s == '\'' ){ + bInQuotes++; + iQuoteType = 0; + } else if( *s == '"' ){ + bInQuotes++; + iQuoteType = 1; + } + } else { + if(( *s == '\'' && iQuoteType == 0 ) || (*s == '"' && iQuoteType == 1 )) + bInQuotes--; + } + s++; + } + if( iLparenCtr != iRparenCtr ){ + iErrorStop = 100; + iRc = XB_UNBALANCED_PARENS; + throw iRc; + } + if( bInQuotes ){ + iErrorStop = 110; + iRc = XB_UNBALANCED_QUOTES; + throw iRc; + } + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbexp::CheckParensAndQuots() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( sExpression ); + } + return iRc; +} +/*************************************************************************/ +//! Clear tree handle. +/*! + This routine clears the expression tree and frees any associated memory. + \returns void. +*/ + +void xbExp::ClearTreeHandle(){ + if( nTree ){ + nTree = NULL; + } +} + +/*************************************************************************/ +#ifdef XB_DEBUG_SUPPORT +//! Dump the tree. +/*! + \param iOption - Output opton. + \returns void. +*/ + +void xbExp::DumpTree( xbInt16 iOption ){ + nTree->DumpNode( iOption ); +} + +//! Dump token +/*! + \param iOption - Output opton. + \returns void. +*/ + + +void xbExp::DumpToken( xbExpToken &t, xbInt16 iOption ){ + + xbString sMsg; + sMsg = "Processing Token"; + xbase->WriteLogMessage( sMsg.Str(), iOption ); + + sMsg.Sprintf( "Expression = [%s]", t.sExpression.Str()); + xbase->WriteLogMessage( sMsg.Str(), iOption ); + + sMsg.Sprintf( "Token = [%s]", t.sToken.Str()); + xbase->WriteLogMessage( sMsg.Str(), iOption ); + + sMsg.Sprintf( "NodeType = [%c]", t.cNodeType ); + xbase->WriteLogMessage( sMsg.Str(), iOption ); + + sMsg.Sprintf( "ReturnType = [%c]", t.cReturnType ); + xbase->WriteLogMessage( sMsg.Str(), iOption ); + + sMsg.Sprintf( "Sts = [%d]", t.iSts ); + xbase->WriteLogMessage( sMsg.Str(), iOption ); + + sMsg.Sprintf( "PrevNodeType = [%c]", t.cPrevNodeType ); + xbase->WriteLogMessage( sMsg.Str(), iOption ); + + sMsg.Sprintf( "PrevReturnType = [%c]", t.cPrevReturnType ); + xbase->WriteLogMessage( sMsg.Str(), iOption ); +} + +#endif + +/*************************************************************************/ +//! Get date result. +/*! + If the expression generates a date return type, this method retrieves the date value. + \param dtResult - Output date value. + \returns Return Codes +*/ + +xbInt16 xbExp::GetDateResult( xbDate &dtResult ){ + if( nTree ){ + dtResult.JulToDate8( (xbInt32) nTree->GetNumericResult() ); + return XB_NO_ERROR; + } + else{ + //dtResult = ?; + return XB_PARSE_ERROR; + } +} +/*************************************************************************/ +//! Get bool result. +/*! + If the expression generates a boolean return type, this method retrieves the boolean value. + \param bResult - Output boolean value. + \returns Return Codes +*/ +xbInt16 xbExp::GetBoolResult( xbBool &bResult){ + if( nTree ){ + bResult = nTree->GetBoolResult(); + return XB_NO_ERROR; + } + else{ + return XB_PARSE_ERROR; + } +} + +/*************************************************************************/ +//! Get the next node in the tree. +/*! + \param n Node to starting point. To get the first node of the entire tree, set n = NULL + \returns Pointer to next node. +*/ + +xbExpNode *xbExp::GetNextNode( xbExpNode * n ) const { + + // to get the first node of the entire tree, set n = NULL + // std::cout << "In GetNextNode\n"; + + if( n == nTree ) + return NULL; + + else if( !n ){ + if( !nTree ) + return NULL; + else + return nTree->GetFirstNode(); + } + return n->GetNextNode(); +} + +/*************************************************************************/ +//! GetNextToken +/*! This method returns the next token in an expression of one or more tokens + \param t Token + \returns Return Codes +*/ + +xbInt16 xbExp::GetNextToken( xbExpToken &t ){ + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + + try{ + t.iSts = XB_NO_ERROR; + t.sExpression.Ltrim(); + + if( t.sExpression.Len() == 0 ){ + t.iSts = XB_END_OF_EXPRESSION; + return XB_NO_ERROR; + } + + // Check for date constant + if((t.sExpression.Len() >= 10 && t.sExpression[1] == '{' && t.sExpression[4] == '/' && t.sExpression[7] == '/') && + (t.sExpression[10] == '}' || (t.sExpression.Len() >= 12 && t.sExpression[12] == '}'))){ + if(( iRc = GetTokenDateConstant( t )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + } + // Check for parens + else if( t.sExpression[1] == '(' || t.sExpression[1] == '{' ){ + if(( iRc = GetTokenParen( t )) != XB_NO_ERROR ){ + iErrorStop = 110; + throw iRc; + } + } + // Check for a char constant + else if( t.sExpression[1] == '"' || t.sExpression[1] == '\'' ){ + if(( iRc = GetTokenCharConstant( t )) != XB_NO_ERROR ){ + iErrorStop = 120; + iRc = XB_PARSE_ERROR; + throw iRc; + } + } + // Check for logical constant + else if( IsLogicalConstant( t.sExpression )){ + if(( iRc = GetTokenLogicalConstant( t )) != XB_NO_ERROR ){ + iErrorStop = 130; + iRc = XB_PARSE_ERROR; + throw iRc; + } + } + // check for numeric constant + else if( IsNumericConstant( t.sExpression, t.cPrevNodeType )){ + if(( iRc = GetTokenNumericConstant( t )) != XB_NO_ERROR ){ + iErrorStop = 140; + iRc = XB_PARSE_ERROR; + throw iRc; + } + } + // check for operator + else if( IsOperator( t.sExpression )){ + if(( iRc = GetTokenOperator( t )) != XB_NO_ERROR ){ + iErrorStop = 150; + iRc = XB_PARSE_ERROR; + throw iRc; + } + } + // check for function + else if( IsFunction( t.sExpression, t.cReturnType )){ + if(( iRc = GetTokenFunction( t )) != XB_NO_ERROR ){ + iErrorStop = 160; + iRc = XB_PARSE_ERROR; + throw iRc; + } + } + else if(( iRc = GetTokenDatabaseField( t )) != XB_NO_ERROR ){ + iErrorStop = 170; + iRc = XB_PARSE_ERROR; + throw iRc; + } + } + + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbexp::GetNextToken() Exception Caught. Error Stop = [%d] iRc = [%d] Expression = [%s]", iErrorStop, iRc, t.sExpression.Str() );sMsg.Sprintf( "xbexp::GetNextToken() Exception Caught. Error Stop = [%d] iRc = [%d] Expression = [%s]", iErrorStop, iRc, t.sExpression.Str() );sMsg.Sprintf( "xbexp::GetNextToken() Exception Caught. Error Stop = [%d] iRc = [%d] Expression = [%s]", iErrorStop, iRc, t.sExpression.Str() );sMsg.Sprintf( "xbexp::GetNextToken() Exception Caught. Error Stop = [%d] iRc = [%d] Expression = [%s]", iErrorStop, iRc, t.sExpression.Str() ); + xbase->WriteLogMessage( sMsg.Str() ); + } + return iRc; +} + +/*************************************************************************/ +//! Get numeric result. +/*! + If the expression generates a numeric return type, this method retrieves the numeric value. + \param dResult - Output numeric value. + \returns Return Codes +*/ + +xbInt16 xbExp::GetNumericResult( xbDouble &dResult){ + if( nTree ){ + dResult = nTree->GetNumericResult(); + return XB_NO_ERROR; + } + else{ + dResult = 0; + return XB_PARSE_ERROR; + } +} +/*************************************************************************/ +//! Get result length. +/*! + This routine returns the result length. + \returns Result length. +*/ + +xbInt16 xbExp::GetResultLen() const{ + if( nTree ) + return nTree->GetResultLen(); + else + return 0; +} + +/*************************************************************************/ +//! Get return type. +/*! + \returns Expression return type. +*/ + +char xbExp::GetReturnType() const{ + if( nTree ) + return nTree->GetReturnType(); + else + return ' '; +} + +/*************************************************************************/ +//! Get string result. +/*! + If the expression generates a string return type, this method retrieves the string value. + \param sResult - Output string value. + \returns Return Codes +*/ + +xbInt16 xbExp::GetStringResult( xbString &sResult){ + if( nTree ){ + sResult = nTree->GetStringResult(); + return XB_NO_ERROR; + } + else{ + sResult = ""; + return XB_PARSE_ERROR; + } +} + +/*************************************************************************/ +//! Get string result. +/*! + If the expression generates a string return type, this method retrieves the string value. + \param vpResult - Pointer to user supplied buffer for result. + \param ulLen - Max size of buffer. + \returns Return Codes +*/ + + +xbInt16 xbExp::GetStringResult( char * vpResult, xbUInt32 ulLen ){ + if( nTree ){ + nTree->GetStringResult().strncpy((char *) vpResult, ulLen ); + return XB_NO_ERROR; + } + else{ + return XB_PARSE_ERROR; + } +} + + + +/*************************************************************************/ +//! GetTokenCharConstant +/*! This method returns the character constant in a pair of quotes + + This routine returns the tokens inside a set of matching quotes in sOutToken + If there is nothing between the quotes then sOutToken is returned empty + sOutRemainder contains whatever remains to the right of the right quote + + \param t Token + \returns Return Codes + +*/ + +xbInt16 xbExp::GetTokenCharConstant( xbExpToken &t ){ + + const char *s = t.sExpression; + const char *sToken; // pointer to beginning of token + xbInt16 iQuoteType; + xbUInt32 ulTokenLen = 0; + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + xbBool bDone = xbFalse; + + try{ + if( *s == '"' ) + iQuoteType = 0; + else + iQuoteType = 1; + s++; + sToken = s; + while( *s && !bDone ){ + if(( *s == '"' && iQuoteType == 0 ) || (*s == '\'' && iQuoteType == 1 )) + bDone = xbTrue; + s++; + ulTokenLen++; + } + if( bDone ){ // found matching paren + t.cNodeType = XB_EXP_CONSTANT; + t.cReturnType = XB_EXP_CHAR; + t.sToken.Set( sToken, ulTokenLen - 1 ); + t.sExpression.Ltrunc( ulTokenLen + 1 ); + } else { + iRc = XB_PARSE_ERROR; + t.iSts = XB_UNBALANCED_QUOTES; + iErrorStop = 100; + throw iRc; + } + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbexp::GetTokenCharConstant() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + } + return iRc; +} + + +/*************************************************************************/ +//! GetTokenDateConstant +/*! This method returns the date constant in a pair of {} + + Date format is one of {mm/dd/yy} or {mm/dd/yyyy} + \param t Token. + \returns Return Codes +*/ + +xbInt16 xbExp::GetTokenDateConstant( xbExpToken &t ){ + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + char wBuf[13]; + xbDate dt; + + try{ + memset( wBuf, 0x00, 13 ); + t.cNodeType = XB_EXP_CONSTANT; + t.cReturnType = XB_EXP_DATE; + + if( t.sExpression[10] == '}' ){ + for( xbInt16 i = 0; i < 8; i++ ) + wBuf[i] = t.sExpression[i+2]; + + if(( iRc = dt.CTOD( wBuf )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + t.sToken.Set( dt.Str() ); + t.sExpression.Ltrunc( 10 ); + + } else if( t.sExpression[12] == '}' ){ + + wBuf[0] = t.sExpression[8]; + wBuf[1] = t.sExpression[9]; + wBuf[2] = t.sExpression[10]; + wBuf[3] = t.sExpression[11]; + wBuf[4] = t.sExpression[2]; + wBuf[5] = t.sExpression[3]; + wBuf[6] = t.sExpression[5]; + wBuf[7] = t.sExpression[6]; + + t.sToken.Set( wBuf ); + t.sExpression.Ltrunc( 12 ); + } else { + iRc = XB_PARSE_ERROR; + t.iSts = XB_INVALID_DATE; + iErrorStop = 110; + throw iRc; + } + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbexp::GetTokenDateConstant() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + } + return iRc; +} +/*************************************************************************/ +//! GetTokenField +/*! This method gets a database field token + + Looks for a xbase field in one of the following formats + + FIELDNAME + or + TABLENAME->FIELDNAME + + \param t Token. + \returns Return Codes +*/ + +xbInt16 xbExp::GetTokenDatabaseField( xbExpToken &t ){ + + const char *s = t.sExpression; + xbUInt32 ulTokenLen = 0; + xbUInt32 ulTokenLen2 = 0; + + while( *s && !IsTokenSeparator( *s ) && !IsWhiteSpace( *s )) { + ulTokenLen++; + s++; + } + + // go past any white space + while( *s && !IsTokenSeparator( *s ) && IsWhiteSpace( *s )) { + ulTokenLen2++; + s++; + } + + // look for -> + // remove the table name from before the -> + if( strncmp( s, "->", 2 ) == 0 ){ + ulTokenLen2+=2; + s+=2; + +/* + if( strncmp( s, "->", 2 ) == 0 || strncmp( s, ".", 1 ) == 0){ + if( *s == '.' ){ + ulTokenLen2+=1; + s+=1; + } else { + ulTokenLen2+=2; + s+=2; + } +*/ + + // go past white space + while( *s && !IsTokenSeparator( *s ) && IsWhiteSpace( *s )) { + ulTokenLen2++; + s++; + } + // go to the end + while( *s && !IsTokenSeparator( *s ) && !IsWhiteSpace( *s )) { + ulTokenLen2++; + s++; + } + ulTokenLen += ulTokenLen2; + } + t.cNodeType = XB_EXP_FIELD; + t.cReturnType = XB_EXP_UNKNOWN; + t.sToken.Set( t.sExpression, ulTokenLen ); + t.sExpression.Ltrunc( ulTokenLen ); + + return XB_NO_ERROR; +} + +/*************************************************************************/ +//! GetTokenFunction +/*! + This method gets a function and everything between the following quotes + \param t Token + \returns Return Codes + +*/ + +xbInt16 xbExp::GetTokenFunction( xbExpToken &t ){ + + xbUInt32 lPos = t.sExpression.Pos( '(' ); + if( lPos == 0 ) + return XB_PARSE_ERROR; + + xbBool bDone = xbFalse; + xbUInt32 lLen = t.sExpression.Len(); + xbInt16 iDepthCtr = 1; + + while( ++lPos <= lLen && !bDone ){ + if( t.sExpression[lPos] == ')' ){ + iDepthCtr--; + if( iDepthCtr == 0 ) + bDone = xbTrue; + } else if( t.sExpression[lPos] == '(' ){ + iDepthCtr++; + } + } + + t.cNodeType = XB_EXP_FUNCTION; + t.sToken.Set( t.sExpression, lPos-1 ); + t.sExpression.Ltrunc( lPos-1 ); + +// std::cout << "lPos = [" << lPos << "] done= [" << bDone << "][" << t.sExpression << "] len=[" << lLen << "] return type = [" << t.cReturnType << "]\n"; + + return XB_NO_ERROR; +} + +/*************************************************************************/ +//! GetTokenCharConstant +/*! This method returns the character constant in a pair of quotes + + This routine returns the tokens inside a set of matching quotes in sOutToken + If there is nothing between the quotes then sOutToken is returned empty + sOutRemainder contains whatever remains to the right of the right quote + + \param t Token + \returns Return Codes +*/ + +xbInt16 xbExp::GetTokenLogicalConstant( xbExpToken &t ){ + + t.cNodeType = XB_EXP_CONSTANT; + t.cReturnType = XB_EXP_LOGICAL; + t.sToken = t.sExpression[2]; + + if( t.sExpression[3] == '.' ) + t.sExpression.Ltrunc( 3 ); + else if( t.sExpression[6] == '.' ) + t.sExpression.Ltrunc( 6 ); + else if( t.sExpression[7] == '.' ) + t.sExpression.Ltrunc( 7 ); + + return XB_NO_ERROR; +} + + +/*************************************************************************/ +//! GetTokenNumericConstant +/*! This method returns a numeric constant in + + This routine returns a numeric constant token + sOutRemainder contains whatever remains to the right of the right quote + + \param t Token + \returns Return Codes +*/ + +xbInt16 xbExp::GetTokenNumericConstant( xbExpToken &t ){ + + const char * s = t.sExpression; + xbUInt32 ulTokenLen = 0; + t.sToken = ""; + + t.cNodeType = XB_EXP_CONSTANT; + t.cReturnType = XB_EXP_NUMERIC; + + // capture the leading sign + if( *s == '-' || *s == '+' || *s == '.' ){ + t.sToken = *s; + ulTokenLen++; + s++; + + // go past any white space between sign and number + while( *s && IsWhiteSpace( *s )){ + s++; + ulTokenLen++; + } + } + + // add the number to the token + while( *s && (isdigit( *s ) || *s == '.' )){ + t.sToken += *s; + s++; + ulTokenLen++; + } + t.sExpression.Ltrunc( ulTokenLen ); + return XB_NO_ERROR; +} + +/*************************************************************************/ +//! GetTokenOperator +/*! This method returns the operator + + \param t Token + \returns Return Codes + +*/ + +xbInt16 xbExp::GetTokenOperator( xbExpToken &t ){ + + const char *s = t.sExpression; + + // Logical operators + if((strncmp( s, "<>", 2 ) == 0 ) || (strncmp( s, "!=", 2 ) == 0 ) || + (strncmp( s, "<=", 2 ) == 0 ) || (strncmp( s, ">=", 2 ) == 0 )){ + t.cReturnType = XB_EXP_LOGICAL; + t.sToken.Assign( s, 1, 2 ); + t.sExpression.Ltrunc( 2 ); + t.cNodeType = XB_EXP_OPERATOR; + return XB_NO_ERROR; + } + + if( *s == '=' || *s == '<' || *s == '>' || *s == '$' || *s == '#' ){ + t.cReturnType = XB_EXP_LOGICAL; + t.sToken.Assign( s, 1, 1 ); + t.sExpression.Ltrunc( 1 ); + t.cNodeType = XB_EXP_OPERATOR; + return XB_NO_ERROR; + } + + if( (strncmp( s, ".NOT.", 5 ) == 0 ) || (strncmp( s, ".AND.", 5 ) == 0 )){ + t.cReturnType = XB_EXP_LOGICAL; + t.sToken.Assign( s, 1, 5 ); + t.sExpression.Ltrunc( 5 ); + t.cNodeType = XB_EXP_OPERATOR; + return XB_NO_ERROR; + } + + if( (strncmp( s, "NOT ", 4 ) == 0 ) || (strncmp( s, "AND ", 4 ) == 0 )){ + t.cReturnType = XB_EXP_LOGICAL; + t.sToken.Assign( s, 1, 3 ); + t.sExpression.Ltrunc( 3 ); + t.cNodeType = XB_EXP_OPERATOR; + return XB_NO_ERROR; + } + + if( strncmp( s, ".OR.", 4 ) == 0 ) { + t.cReturnType = XB_EXP_LOGICAL; + t.sToken.Assign( s, 1, 4 ); + t.sExpression.Ltrunc( 4 ); + t.cNodeType = XB_EXP_OPERATOR; + return XB_NO_ERROR; + } + + if( strncmp( s, "OR ", 3 ) == 0 ) { + t.cReturnType = XB_EXP_LOGICAL; + t.sToken.Assign( s, 1, 2 ); + t.sExpression.Ltrunc( 2 ); + t.cNodeType = XB_EXP_OPERATOR; + return XB_NO_ERROR; + } + + // Numeric operators + if(( strncmp( s, "**", 2 ) == 0 ) || ( strncmp( s, "+=", 2 ) == 0 ) || + ( strncmp( s, "-=", 2 ) == 0 ) || ( strncmp( s, "*=", 2 ) == 0 ) || ( strncmp( s, "/=", 2 ) == 0 )){ + t.cReturnType = XB_EXP_NUMERIC; + t.sToken.Assign( s, 1, 2 ); + t.sExpression.Ltrunc( 2 ); + t.cNodeType = XB_EXP_OPERATOR; + return XB_NO_ERROR; + } + + // Pre/post increment/decrement operators ++ or -- + if(( strncmp( s, "--", 2 ) == 0 ) || ( strncmp( s, "++", 2 ) == 0 )){ + t.cReturnType = XB_EXP_NUMERIC; + t.sToken.Assign( s, 1, 2 ); + t.sExpression.Ltrunc( 2 ); + if( t.sExpression.Len() > 0 && (isdigit( t.sExpression[1] ) || isalpha( t.sExpression[1] ))) + t.cNodeType = XB_EXP_PRE_OPERATOR; + else + t.cNodeType = XB_EXP_POST_OPERATOR; + + return XB_NO_ERROR; + } + + if( *s == '*' || *s == '/' || *s == '%' || *s == '^' ){ + t.cReturnType = XB_EXP_NUMERIC; + t.sToken.Assign( s, 1, 1 ); + t.sExpression.Ltrunc( 1 ); + t.cNodeType = XB_EXP_OPERATOR; + return XB_NO_ERROR; + } + + // multi return type operators + t.cReturnType = XB_EXP_UNKNOWN; + if( *s == '+' || *s == '-' ){ + t.sToken.Assign( s, 1, 1 ); + t.sExpression.Ltrunc( 1 ); + t.cNodeType = XB_EXP_OPERATOR; + return XB_NO_ERROR; + } + return XB_PARSE_ERROR; +} + +/*************************************************************************/ +//! GetTokenParen +/*! This method returns the tokens in a pair of enclosed parens + + This routine returns the tokens inside a set of matching parens in sOutToken + If there is nothing between the parens then sOutToken is returned empty + sOutRemainder contains whatever remains to the right of the right paren + + \param t Token + \returns Return Codes +*/ + + +xbInt16 xbExp::GetTokenParen( xbExpToken &t ){ + + const char * s = t.sExpression; + const char * sToken; // pointer to beginning of token + xbInt16 iParenType = 0; + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + xbInt16 iDepthCtr = 0; // depth inside of nested parens + xbUInt32 ulTokenLen = 0; + xbBool bDone = xbFalse; + + try{ + if( *s == '{' ) + iParenType = 0; + else + iParenType = 1; + iDepthCtr = 1; + s++; + sToken = s; + + while( *s && !bDone ){ + if(( *s == ')' && iParenType == 1 ) || (*s == '}' && iParenType == 0 )){ + iDepthCtr--; + if( iDepthCtr == 0 ) + bDone = xbTrue; + } else if(( *s == '(' && iParenType == 1 ) || (*s == '{' && iParenType == 0 )){ + iDepthCtr++; + } + s++; + ulTokenLen++; + } + + if( bDone ){ // found matching paren + t.cNodeType = XB_EXP_NOTROOT; + t.cReturnType = XB_EXP_UNKNOWN; + t.sToken.Set( sToken, ulTokenLen - 1 ); + t.sExpression.Ltrunc( ulTokenLen + 1 ); + } else { + t.sToken = ""; + t.cNodeType = XB_EXP_NOTROOT; + t.cReturnType = XB_EXP_UNKNOWN; + t.iSts = XB_UNBALANCED_PARENS; + iRc = XB_PARSE_ERROR; + iErrorStop = 100; + throw iRc; + } + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbexp::GetTokenParen() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg ); + } + return iRc; +} + +/*************************************************************************/ +//! Get the expression tree handle. +/*! + \returns Pointer to the top most node in the expression tree. +*/ +xbExpNode *xbExp::GetTreeHandle(){ + return nTree; +} + +/*************************************************************************/ +//! Is Function +/*! This method determines if the next token is a function. + + \param sExpression - String expression to be evaluated. + \param cReturnType Output - Return type. + \returns xbTrue - Is a function.
+ xbFalse - Is not a function. +*/ + +xbBool xbExp::IsFunction( const xbString & sExpression, char &cReturnType ){ + + xbInt16 i = 0; + xbInt32 l = 0; + if( sExpression.Pos( '(' ) > 0 ){ + if( xbase->GetFunctionInfo( sExpression, cReturnType, i, l ) == XB_NO_ERROR ) + return xbTrue; + } + return xbFalse; +} + +/*************************************************************************/ +//! Is Logical constant +/*! This method determines if the next token is a logical constant (T/F, etc). + + \param sExpression - String expression to be evaluated. + \returns xbTrue - Is a logical constant.
+ xbFalse - Is not a logical constant. +*/ + +xbBool xbExp::IsLogicalConstant( const xbString & sExpression ){ + + const char *s = sExpression; + if(( strncmp( s, ".T.", 3 ) == 0 ) || ( strncmp( s, ".F.", 3 ) == 0 )) + return xbTrue; + else if( strncmp( s, ".TRUE.", 6 ) == 0 ) + return xbTrue; + else if( strncmp( s, ".FALSE.", 7 ) == 0 ) + return xbTrue; + + return xbFalse; +} + +/*************************************************************************/ +//! Is Numeric constant +/*! This method determines if the next token is a numeric constant. + + \param sExpression - String expression to be evaluated. + \param cPrevNodeType - Type of previous node. + \returns xbTrue - Is a numeric constant.
+ xbFalse - Is not a numeric constant. +*/ +xbBool xbExp::IsNumericConstant( const xbString & sExpression, char cPrevNodeType ){ + + // check for positive, negative or decimal number constants + + const char *s = sExpression; + if(( *s == '-' && ( cPrevNodeType == 'O' || cPrevNodeType == 0 )) || + ( *s == '+' && ( cPrevNodeType == 'O' || cPrevNodeType == 0 ))){ + s++; + while( *s && IsWhiteSpace( *s )) + s++; + } + if( *s == '.' ) + s++; + + if( isdigit( *s )) + return xbTrue; + else + return xbFalse; +} + +/*************************************************************************/ +//! Is Operator. +/*! This method determines if the next token is an operator. + + \param sExpression - String expression to be evaluated. + \returns xbTrue - Is an operator.
+ xbFalse - Is not an operator. +*/ +xbBool xbExp::IsOperator( const xbString & sExpression ){ + + const char *s = sExpression; + if( *s == '+' || *s == '-' || *s == '/' || *s == '^' || *s == '=' || *s == '$' || + *s == '#' || *s == '*' || *s == '<' || *s == '>' || *s == '%' ) + return xbTrue; + + if( strncmp( s, "!=", 2 ) == 0 ) + return xbTrue; + + if((strncmp( s, ".NOT.", 5 ) == 0 ) || (strncmp( s, ".OR.", 4 ) == 0 ) || (strncmp( s, ".AND.", 5 ) == 0 )) + return xbTrue; + + if((strncmp( s, "NOT ", 4 ) == 0 ) || (strncmp( s, "OR ", 3 ) == 0 ) || (strncmp( s, "AND ", 4 ) == 0 )) + return xbTrue; + + return xbFalse; +} + +/*************************************************************************/ +//! Is Token separator +/*! This method determines if the next token is a separator. + + \param sExpression - String expression to be evaluated. + \returns xbTrue - Is a token separator.
+ xbFalse - Is not a token separator. +*/ +char xbExp::IsTokenSeparator( char c ){ + if( c == '-' || c == '+' || c == '*' || c == '/' || c == '$' || c == '#' || + c == '<' || c == '>' || c == '^' || c == '=' || c == '.' || c == '!' ) + return c; + else + return 0; +} +/*************************************************************************/ +//! Is White space +/*! This method determines if a given character is white space. + + \param c - Character to be evaluated. + \returns xbTrue - Is white space.
+ xbFalse - Is not white space. +*/ +xbBool xbExp::IsWhiteSpace( char c ){ + return(( c == 0x20 )? 1 : 0 ); +} + +/*************************************************************************/ +//! Get operator weight. +/*! + This method determines the priority of an operator + + Operator precendence + 10 .AND. .OR. .NOT. (not really an operator) + 9 > or < (includes <= or >=) + 6 unary plus or minus (+,-) -- not passed this routine + 5 prefix increment and/or decrement (++,--) + 4 exponentiation ** or ^ + 3 multiplication,division or modulus (*,/,%) + 2 Addition, subtraction (+,-) + 1 Postfix increment and/or decrement (++,--) + + \param sOper - Operator. + \returns Operator weight + +*/ + +xbInt16 xbExp::OperatorWeight( const xbString &sOper ){ + + if( sOper == "" || sOper.Len() > 5 ) + return 0; + + else if( sOper == "--0" || sOper == "++0" ) // 0 is prefix + return 9; + else if( sOper == "**" || sOper == "^" ) + return 8; + else if( sOper == "*" || sOper == "/" || sOper == "%" || sOper == "*=" || sOper == "/=" ) + return 7; + else if( sOper == "+" || sOper == "-" || sOper == "+=" || sOper == "-=" ) + return 6; + else if( sOper == "--1" || sOper == "++1" ) // 1 is post fix + return 5; + else if( sOper == ">" || sOper == ">=" || sOper == "<" || sOper == "<=" || + sOper == "<>" || sOper == "!=" || sOper == "#" || sOper == "$" || sOper == "=" ) + return 4; + else if( sOper == ".NOT." || sOper == "NOT" ) + return 3; + else if( sOper == ".AND." || sOper == "AND" ) + return 2; + else if( sOper == ".OR." || sOper == "OR" ) + return 1; + + return 0; +} + + +/*************************************************************************/ +//! Parse expression. +/*! + \param sExpression - Expression to parse. + \returns Return Codes +*/ + +xbInt16 xbExp::ParseExpression( const xbString &sExpression ){ + return ParseExpression( sExpression, (xbInt16) 0 ); +} + +/*************************************************************************/ +//! Parse expression. +/*! + \param dbf - Pointer to xbDbf instance. + \param sExpression - Expression to parse. + \returns Return Codes +*/ + +xbInt16 xbExp::ParseExpression( xbDbf *dbf, const xbString &sExpression ){ + this->dbf = dbf; + return ParseExpression( sExpression, (xbInt16) 0 ); +} + +/*************************************************************************/ +//! Parse expression. +/*! + \param sExpression - Expression to parse. + \param iWeight. + \returns Return Codes +*/ +xbInt16 xbExp::ParseExpression( const xbString &sExpression, xbInt16 iWeight ){ + + xbExpNode *n = NULL; + xbExpNode *nLastNode = NULL; // pointer to the last node processed + xbExpToken t; + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + xbString s; + xbBool bNewNode = xbFalse; + + try { + + if( nTree ) + delete nTree; + + if(( iRc = CheckParensAndQuotes( sExpression )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + + t.sExpression = sExpression; + + xbString sOriginalExp; + while( t.iSts == XB_NO_ERROR && iRc == XB_NO_ERROR ){ + + sOriginalExp = t.sExpression; // test code + iRc = GetNextToken( t ); + if( !iRc && !t.iSts ){ + + // comment / uncomment debug / live + // DumpToken( t, 0 ); + + if( t.cNodeType == XB_EXP_NOTROOT ){ + xbExp enr( xbase, dbf ); + if(( iRc = enr.ParseExpression( t.sToken, iWeight + 10 )) != XB_NO_ERROR ){ + iErrorStop = 110; + throw iRc; + } + n = enr.GetTreeHandle(); + enr.ClearTreeHandle(); + + } else { + + switch( t.cNodeType ) { + + case XB_EXP_CONSTANT: + n = new xbExpNode( t.sToken, t.cNodeType ); + bNewNode = xbTrue; + if(( iRc = ParseExpressionConstant( t, n )) != XB_NO_ERROR ){ + iErrorStop = 120; + throw iRc; + } + break; + + case XB_EXP_FUNCTION: + n = new xbExpNode( t.cNodeType ); + bNewNode = xbTrue; + if(( iRc = ParseExpressionFunction( t, n, iWeight )) != XB_NO_ERROR ){ + iErrorStop = 130; + throw iRc; + } + break; + + case XB_EXP_FIELD: + n = new xbExpNode( t.cNodeType ); + bNewNode = xbTrue; + if(( iRc = ParseExpressionField( t, n )) != XB_NO_ERROR ){ + iErrorStop = 140; + throw iRc; + } + break; + + case XB_EXP_OPERATOR: + case XB_EXP_PRE_OPERATOR: + case XB_EXP_POST_OPERATOR: + n = new xbExpNode( t.sToken, t.cNodeType ); + bNewNode = xbTrue; + if(( iRc = ParseExpressionOperator( t, n, iWeight )) != XB_NO_ERROR ){ + iErrorStop = 150; + throw iRc; + } + break; + + default: + iErrorStop = 160; + iRc = XB_PARSE_ERROR; + throw iRc; + // break; + } + } + t.cPrevNodeType = t.cNodeType; + t.cPrevReturnType = t.cReturnType; + + // determine where in the expression tree to insert the latest node "n" + // Is this the first node to be added to the tree? + if( !nTree ){ + nTree = n; + } + + // else if last node was XB_EXB_PRE_OPERATOR then append this as child to last node + else if( nLastNode && nLastNode->GetNodeType() == XB_EXP_PRE_OPERATOR ){ + n->SetParent( nLastNode ); + nLastNode->AddChild( n ); + } + + // else if last node was XB_EXB_POST_OPERATOR then append this as child to last node + else if( nLastNode && n->GetNodeType() == XB_EXP_POST_OPERATOR ){ + n->AddChild( nLastNode ); + nLastNode->SetParent( n ); + if( nLastNode == nTree ){ + nTree = n; + } else { + nLastNode->GetParent()->RemoveLastChild(); + nLastNode->GetParent()->AddChild( n ); + n->SetParent( nLastNode->GetParent() ); + } + } + + else if( n->GetNodeType() == XB_EXP_OPERATOR ){ + xbExpNode * nWorkNode = nLastNode; + while( nWorkNode && ( nWorkNode->GetNodeType() != XB_EXP_OPERATOR || n->GetWeight() <= nWorkNode->GetWeight())){ + nWorkNode = nWorkNode->GetParent(); + } + + if( !nWorkNode ){ // we are at the top + nTree->SetParent( n ); + n->AddChild( nTree ); + nTree = n; + + } else if( nWorkNode->GetChildCnt() == 1 ){ + nWorkNode->AddChild( n ); + n->SetParent( nWorkNode ); + + } else if( nWorkNode->GetChildCnt() == 2 ){ + xbExpNode * nChild2 = nWorkNode->GetChild(1); + n->AddChild( nChild2 ); + nWorkNode->RemoveLastChild(); + nWorkNode->AddChild( n ); + n->SetParent( nWorkNode ); + + } else{ + // should not be stopping on anything but an operator node with one or two children + iErrorStop = 170; + iRc = XB_PARSE_ERROR; + throw iRc; + } + } else { + n->SetParent( nLastNode ); + nLastNode->AddChild( n ); + } + nLastNode = n; + n = NULL; + } + } + + // for each node in the tree, calculate the length if it's not already set + xbExpNode * nWork = GetNextNode( NULL ); + xbExpNode * nChild1; + xbExpNode * nChild2; + + while( nWork ){ + if( nWork->GetReturnType() == XB_EXP_UNKNOWN ){ + nWork->GetNodeText( s ); + + // std::cout << "XB_EXP_UNKNOWN logic [" << s << "][" << nWork->GetChildCnt() << "]\n"; + // if this is "-" and child 1 and child 2 are both dates, set this result type to numeric + if( s == "-" && nWork->GetChildCnt() == 2 && + nWork->GetChild(0)->GetReturnType() == XB_EXP_DATE && nWork->GetChild(1)->GetReturnType() == XB_EXP_DATE ) + nWork->SetReturnType( XB_EXP_NUMERIC ); + else if( nWork->GetChildCnt() > 0 ) + nWork->SetReturnType( nWork->GetChild(0)->GetReturnType()); + else{ + iErrorStop = 180; + iRc = XB_PARSE_ERROR; + throw iRc; + } + } + if( nWork->GetResultLen() == 0 ){ + + switch( nWork->GetReturnType() ){ + + case XB_EXP_NUMERIC: + nWork->SetResultLen( 4 ); + break; + + case XB_EXP_CHAR: + if( nWork->GetNodeType() != XB_EXP_OPERATOR ){ + iErrorStop = 190; + iRc = XB_PARSE_ERROR; + throw iRc; + } + if( nWork->GetChildCnt() < 2 ){ + iErrorStop = 200; + iRc = XB_PARSE_ERROR; + throw iRc; + } + nChild1 = nWork->GetChild( 0 ); + nChild2 = nWork->GetChild( 1 ); + nWork->SetResultLen( nChild1->GetResultLen() + nChild2->GetResultLen()); + break; + + case XB_EXP_DATE: + nWork->SetResultLen( 8 ); + break; + + case XB_EXP_LOGICAL: + nWork->SetResultLen( 1 ); + break; + + default: + iErrorStop = 210; + iRc = XB_PARSE_ERROR; + throw iRc; + // break; + } + } + if( nWork->IsUnaryOperator() ){ + if( nWork->GetChildCnt() != 1 ){ + iErrorStop = 220; + iRc = XB_PARSE_ERROR; + throw iRc; + } + } else if( nWork->IsOperator() && nWork->GetChildCnt() != 2 ){ + iErrorStop = 230; + iRc = XB_PARSE_ERROR; + throw iRc; + } + nWork = GetNextNode( nWork ); + } + } + catch (xbInt16 iRc ){ + if( bNewNode && n ) + delete n; + xbString sMsg; + sMsg.Sprintf( "xbexp::ParseExpression() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + } + return iRc; +} +/*************************************************************************/ +//! Parse expression constant. +/*! + \param t - Token. + \param n - Node. + \returns Return Codes +*/ +xbInt16 xbExp::ParseExpressionConstant( xbExpToken &t, xbExpNode *n ){ + + xbDate dtWork; + n->SetReturnType( t.cReturnType ); + + // std::cout << "parse expression constant[" << t.sToken << "]\n"; + + switch( t.cReturnType ){ + case XB_EXP_CHAR: + n->SetResultLen( t.sToken.Len() ); + n->SetResult( t.sToken ); + break; + + case XB_EXP_DATE: + n->SetResultLen( 8 ); + dtWork.Set( t.sToken ); + n->SetResult( dtWork ); + break; + + case XB_EXP_LOGICAL: + n->SetResultLen( 1 ); + if( strncmp( t.sToken, "T", 1 ) == 0 ) + n->SetResult( (xbBool) xbTrue ); + else + n->SetResult( (xbBool) xbFalse ); + break; + + case XB_EXP_NUMERIC: + n->SetResultLen( 4 ); + n->SetResult( strtod( t.sToken, 0 )); + n->SetResult( t.sToken ); + break; + + default: + return XB_PARSE_ERROR; + // break; + } + return XB_NO_ERROR; +} + +/*************************************************************************/ +//! Parse expression field. +/*! + \param t - Token. + \param n - Node. + \returns Return Codes +*/ +xbInt16 xbExp::ParseExpressionField( xbExpToken &t, xbExpNode *n ){ + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + xbDbf * pDbf; + xbString sFieldName; + + // do the db lookup and set the field number for the field + + try{ + + xbUInt32 lPos; + + if(( lPos = t.sToken.Pos( "->" )) > 0 ){ + // table name is part of the token + xbString sTableName = t.sToken; + sTableName.Left( lPos-1 ); + sFieldName = t.sToken; + sFieldName.Mid( lPos + 2, t.sToken.Len() - lPos - 1 ); + pDbf = (xbDbf *) xbase->GetDbfPtr( sTableName ); + + +/* + // updated 1/2/23 to support either table.field or table->field + if((( lPos = t.sToken.Pos( "->" )) > 0) || (( lPos = t.sToken.Pos( "." )) > 0) ){ + // table name is part of the token + xbString sTableName = t.sToken; + sTableName.Left( lPos-1 ); + sFieldName = t.sToken; + if( t.sToken[lPos] == '.' ) + sFieldName.Mid( lPos + 1, t.sToken.Len() - lPos ); + else // -> + sFieldName.Mid( lPos + 2, t.sToken.Len() - lPos - 1 ); + pDbf = (xbDbf *) xbase->GetDbfPtr( sTableName ); +*/ + + } else { + // table name is not part of the token + pDbf = dbf; + sFieldName = t.sToken; + } + if( !pDbf ){ + iErrorStop = 100; + iRc = XB_INVALID_FIELD; + throw iRc; + } + xbInt16 iFieldNo = 0; + + if(( iRc = pDbf->GetFieldNo( sFieldName, iFieldNo )) != XB_NO_ERROR ){ + iErrorStop = 110; + throw iRc; + } + char cFieldType; + if(( iRc = pDbf->GetFieldType( iFieldNo, cFieldType )) != XB_NO_ERROR ){ + iErrorStop = 120; + throw iRc; + } + n->SetDbfInfo( pDbf, iFieldNo ); + switch( cFieldType ){ + case XB_CHAR_FLD: + n->SetReturnType( XB_EXP_CHAR ); + break; + + case XB_LOGICAL_FLD: + n->SetReturnType( XB_EXP_LOGICAL ); + break; + + case XB_NUMERIC_FLD: + case XB_FLOAT_FLD: + n->SetReturnType( XB_EXP_NUMERIC ); + break; + + case XB_DATE_FLD: + n->SetReturnType( XB_EXP_DATE ); + break; + + case XB_MEMO_FLD: + default: + iErrorStop = 130; + iRc = XB_PARSE_ERROR; + throw iRc; + // break; + } + n->SetNodeText( sFieldName ); + xbInt16 iResultLen = 0; + if(( iRc = pDbf->GetFieldLen( iFieldNo, iResultLen )) != XB_NO_ERROR ){ + iErrorStop = 140; + throw iRc; + } + n->SetResultLen( (xbUInt32) iResultLen ); + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbexp::ParseExpressionField() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + } + return iRc; +} + +/*************************************************************************/ +//! Parse expression function. +/*! + \param t - Token. + \param n - Node. + \param iWeight + \returns Return Codes +*/ +xbInt16 xbExp::ParseExpressionFunction( xbExpToken &t, xbExpNode *n, xbInt16 iWeight ){ + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + + try{ + // find the first "(" + xbUInt32 lPos = t.sToken.Pos( '(' ); + if( lPos == 0 ){ + iErrorStop = 100; + iRc = XB_INVALID_FUNCTION; + throw iRc; + } + // Get the function name and look it up in the table + + + xbString sFunc = t.sToken; + sFunc.Left( lPos - 1 ).Trim(); + char cReturnType; + xbInt16 i = 0; + xbInt32 l = 0; + if(( iRc = xbase->GetFunctionInfo( sFunc, cReturnType, i, l )) != XB_NO_ERROR ){ + iErrorStop = 110; + throw iRc; + } + n->SetNodeText( sFunc ); + + + // Get the function parms + xbString sParms = t.sToken; + sParms.Mid( lPos+1, t.sToken.Len() - lPos ); + lPos = sParms.GetLastPos( ')' ); + if( lPos == 0 ){ + iErrorStop = 120; + iRc = XB_INVALID_FUNCTION; + throw iRc; + } + + // remove the trailing ")" paren + sParms.Left( lPos - 1 ).Trim(); + + // if this function has parms, put them in the tree + if( sParms.Len() > 0 ){ + xbExp enr( xbase, dbf ); + + // create a linked list of parms + xbLinkList llParms; + if(( iRc = ParseExpressionFunctionParms( sParms, llParms )) != XB_NO_ERROR ){ + iErrorStop = 130; + throw iRc; + } + + // for each function parm, recursively process it + xbLinkListNode * llN = llParms.GetHeadNode(); + xbString sParm; + while( llN ){ + sParm = llN->GetKey(); + if(( iRc = enr.ParseExpression( sParm, iWeight + 10 )) != XB_NO_ERROR ){ + iErrorStop = 140; + throw iRc; + } + n->AddChild( enr.GetTreeHandle()); + enr.ClearTreeHandle(); + llN = llN->GetNextNode(); + } + llParms.Clear(); + } + + if( cReturnType == '1' ){ + if( n->GetChildCnt() > 0 ){ + xbExpNode *n1 = n->GetChild( 0 ); + n->SetReturnType( n1->GetReturnType()); + } + + } else { + n->SetReturnType( cReturnType ); + } + + if(( iRc = CalcFunctionResultLen( n )) != XB_NO_ERROR ){ + iErrorStop = 150; + throw iRc; + } + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbexp::ParseExpressionFunction() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + } + return iRc; +} + +/*************************************************************************/ +//! Parse expression function. +/*! + + Creates a linked list of function parms as xbStrings + This function pulls out the parms and addresses embedded parens and quotes within the parms + + \param sParms + \param lParms + \returns Return Codes +*/ + +xbInt16 xbExp::ParseExpressionFunctionParms( const xbString &sParms, xbLinkList & llParms ){ + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + xbInt16 iParenCtr = 0; + xbInt16 iInQuotes = 0; + xbInt16 iDoubleQuotes = 0; + xbInt16 iSingleQuotes = 0; + xbInt32 lStartPos = 0; + xbInt32 lParmLen = 0; + + xbString sParm; + + try{ + const char *sp = sParms; + + while( *sp ){ + if( *sp == '(') + iParenCtr++; + else if( *sp == ')' ) + iParenCtr--; + else if( !iInQuotes && *sp == '"' ){ + iInQuotes++; + iDoubleQuotes++; + } else if( iInQuotes && *sp == '"' ){ + iInQuotes--; + iDoubleQuotes--; + } + else if( !iInQuotes && *sp == '\'' ){ + iInQuotes++; + iSingleQuotes++; + } else if( iInQuotes && *sp == '\'' ){ + iInQuotes--; + iSingleQuotes--; + + } else if( !iInQuotes && !iParenCtr && *sp == ',' ){ + // found a valid comma - at the end of a parm + // add it to the end of the linked list + sParm = sParms; + sParm.Mid( (xbUInt32) lStartPos + 1, (xbUInt32) lParmLen ).Trim(); // mid is one based + llParms.InsertAtEnd( sParm ); + + // set the start pos for the next one on the list + lStartPos += lParmLen + 1; + lParmLen = -1; + // lParmLen = 0; + } + lParmLen++; + sp++; + } + if( lParmLen > 0 ){ + // get the last parm, it didn't end with a comma + sParm = sParms; + sParm.Mid( (xbUInt32) lStartPos + 1, (xbUInt32) lParmLen ).Trim(); + llParms.InsertAtEnd( sParm ); + } + + } + // try / catch not used in this method, structure added for potential future use + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbexp::ParseExpressionFunctionParms() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + } + return iRc; +} +/*************************************************************************/ +//! Parse expression operator. +/*! + \param t - Token. + \param n - Node. + \param iWeight + \returns Return Codes +*/ + +xbInt16 xbExp::ParseExpressionOperator( xbExpToken &t, xbExpNode *n, xbInt16 iWeight ){ + + n->SetResult( t.sToken ); + n->SetWeight( iWeight + OperatorWeight( t.sToken) ); + +// std::cout << "ParseExpressionOperator [" << t.cPrevNodeType << "][" << t.sToken << "] Weight = [" << iWeight; +// std::cout << "] PrevReturnType [" << t.cPrevReturnType; +// std::cout << "] Operator weight [" << OperatorWeight( t.sToken ) << "] getweight [" << n->GetWeight() << "]\n"; + + if( t.sToken == "**" || t.sToken == "^" || + t.sToken == "*" || t.sToken == "/" || t.sToken == "%" || t.sToken == "*=" || t.sToken == "/=" ) + n->SetReturnType( XB_EXP_NUMERIC ); + + else if( t.sToken == "--" || t.sToken == "++" || t.sToken == "+=" || t.sToken == "-=" ) // could be date or numeric + n->SetReturnType( XB_EXP_UNKNOWN ); + + else if( t.cPrevReturnType == XB_EXP_CHAR && ( t.sToken == "+" || t.sToken == "-" )) + n->SetReturnType( XB_EXP_CHAR ); + + else if( t.sToken == ".AND." || t.sToken == ".OR." || t.sToken == ".NOT." || + t.sToken == "AND" || t.sToken == "OR" || t.sToken == "NOT" || + t.sToken == ">" || t.sToken == ">=" || t.sToken == "<" || + t.sToken == "<=" || t.sToken == "<>" || t.sToken == "!=" || + t.sToken == "$" || t.sToken == "#" || t.sToken == "=" ) + n->SetReturnType( XB_EXP_LOGICAL ); + + + else if( t.cPrevReturnType == XB_EXP_UNKNOWN ) + n->SetReturnType( XB_EXP_UNKNOWN ); + + // added for date constant logic 10/28/17 + else if(( t.sToken == "+" || t.sToken == "-" ) && t.cPrevReturnType == XB_EXP_DATE ) + n->SetReturnType( XB_EXP_DATE ); + + else if( t.sToken == "+" || t.sToken == "-" ) + n->SetReturnType( XB_EXP_NUMERIC ); + + return XB_NO_ERROR; +} + + +/************************************************************************/ +//! ProcessExpression +/*! This method processes an expression tree leaving the result in the + root node of the tree +*/ +xbInt16 xbExp::ProcessExpression(){ + return ProcessExpression( 0 ); +} +/************************************************************************/ +//! ProcessExpression +/*! This method processes a parsed expression tree leaving the result in the + root node of the tree + \param iRecBufSw Record buffer to use when evaluating expression.
+ 0 - Current record buffer.
+ 1 - Original record buffer. +*/ + +xbInt16 xbExp::ProcessExpression( xbInt16 iRecBufSw ){ + +// iRecBufSw 0 - Record Buffer +// 1 - Original Record Buffer + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + + try{ + + xbExpNode * nWork = GetNextNode( NULL ); + xbExpNode * nChild1; + xbDbf * dbf; + xbString sWork1; + xbString sWork2; + xbString sOperator; + xbDate dtWork1; + + xbBool bWork; + xbDouble dWork; + + while( nWork ){ + switch( nWork->GetNodeType() ){ + + case XB_EXP_CONSTANT: + break; + + case XB_EXP_PRE_OPERATOR: // increment value before setting in head node + if( nWork->GetChildCnt() != 1 ){ + iErrorStop = 100; + iRc = XB_PARSE_ERROR; + throw iRc; + } + nChild1 = nWork->GetChild( 0 ); + //if( nChild1->GetReturnType() == XB_EXP_DATE ) + // nChild1->SetResult( (xbDouble) nChild1->GetDateResult().JulianDays()); + + nWork->GetNodeText( sWork1 ); + if( sWork1 == "++" ) + nChild1->SetResult( nChild1->GetNumericResult() + 1 ); + else + nChild1->SetResult( nChild1->GetNumericResult() - 1 ); + + nWork->SetResult( nChild1->GetNumericResult()); + + //if( nChild1->GetReturnType() == XB_EXP_DATE ){ + // dtWork1.JulToDate8( (xbInt32) nChild1->GetNumericResult()); + // nChild1->SetResult( dtWork1 ); + // nWork->SetResult( dtWork1 ); + // } + break; + + case XB_EXP_POST_OPERATOR: // increment value after setting in head node + if( nWork->GetChildCnt() != 1 ){ + iErrorStop = 110; + iRc = XB_PARSE_ERROR; + throw iRc; + } + nChild1 = nWork->GetChild( 0 ); + //if( nChild1->GetReturnType() == XB_EXP_DATE ){ + // nWork->SetResult( nChild1->GetDateResult()); + // nChild1->SetResult( (xbDouble) nChild1->GetDateResult().JulianDays()); + //} + //else + nWork->SetResult( nChild1->GetNumericResult()); + + nWork->GetNodeText( sWork1 ); + if( sWork1 == "++" ) + nChild1->SetResult( nChild1->GetNumericResult() + 1 ); + else + nChild1->SetResult( nChild1->GetNumericResult() - 1 ); + + //if( nChild1->GetReturnType() == XB_EXP_DATE ){ + // dtWork1.JulToDate8( (xbInt32) nChild1->GetNumericResult()); + // nChild1->SetResult( dtWork1 ); + // } + break; + + case XB_EXP_FIELD: + + if(( dbf = nWork->GetDbf()) == NULL ){ + iErrorStop = 120; + iRc = XB_PARSE_ERROR; + throw iRc; + } + switch( nWork->GetReturnType()){ + case XB_EXP_CHAR: + if(( iRc = dbf->GetField( nWork->GetFieldNo(), sWork1, iRecBufSw )) < XB_NO_ERROR ){ + iErrorStop = 130; + throw iRc; + } + nWork->SetResult( sWork1 ); + break; + + case XB_EXP_DATE: + + if(( iRc = dbf->GetField( nWork->GetFieldNo(), sWork1, iRecBufSw )) < XB_NO_ERROR ){ + iErrorStop = 140; + throw iRc; + } + if( sWork1 == " " ){ + // std::cout << "xbExp::ProcessExpression() line 1938 sWork is spaces\n"; + //nWork->SetResult( (xbDouble) 21474835648 ); // dbase sets a date value in both ndx and mdx index files to this if spaces on dbf record + nWork->SetResult( (xbDouble) XB_NULL_DATE ); + } else { + dtWork1.Set( sWork1 ); + nWork->SetResult( (xbDouble) dtWork1.JulianDays() ); + } + break; + + case XB_EXP_LOGICAL: + if(( iRc = dbf->GetLogicalField( nWork->GetFieldNo(), bWork, iRecBufSw )) != XB_NO_ERROR ){ + iErrorStop = 150; + throw iRc; + } + nWork->SetResult( bWork ); + break; + + case XB_EXP_NUMERIC: + if(( iRc = dbf->GetDoubleField( nWork->GetFieldNo(), dWork, iRecBufSw )) != XB_NO_ERROR ){ + iErrorStop = 160; + throw iRc; + } + nWork->SetResult( dWork ); + break; + + default: + iErrorStop = 170; + iRc = XB_PARSE_ERROR; + throw iRc; + // break; + } + break; + + + case XB_EXP_OPERATOR: + if(( iRc = ProcessExpressionOperator( nWork )) != XB_NO_ERROR ){ + iErrorStop = 180; + throw iRc; + } + + break; + + case XB_EXP_FUNCTION: + if(( iRc = ProcessExpressionFunction( nWork, iRecBufSw )) != XB_NO_ERROR ){ + iErrorStop = 190; + throw iRc; + } + break; + + default: + iErrorStop = 200; + iRc = XB_PARSE_ERROR; + throw iRc; + // break; + } + nWork = GetNextNode( nWork ); + } + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbexp::ProcessExpression() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + } + return iRc; +} +/*************************************************************************/ +//! ProcessExpression +/*! This method processes an expression tree for a given node. +*/ + +xbInt16 xbExp::ProcessExpressionFunction( xbExpNode * n, xbInt16 iRecBufSw ){ + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + + try{ + + xbString sFunction; + xbString sResult; + xbDouble dResult; + xbDate dtResult; + xbBool bResult; + + n->GetNodeText( sFunction ); + + // process functions with no children first + xbExpNode * nChild1; + if( n->GetChildCnt() == 0 ){ + if( sFunction == "DATE" ){ + if(( iRc = xbase->DATE( dtResult )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + n->SetResult( dtResult ); + } else if( sFunction == "DEL" ){ + if(( iRc = xbase->DEL( dbf, sResult, iRecBufSw )) != XB_NO_ERROR ){ + iErrorStop = 110; + throw iRc; + } + n->SetResult( sResult ); + } else if( sFunction == "DELETED" ){ + if(( iRc = xbase->DELETED( dbf, bResult, iRecBufSw )) != XB_NO_ERROR ){ + iErrorStop = 120; + throw iRc; + } + n->SetResult( bResult ); + } else if( sFunction == "RECCOUNT" ){ + if(( iRc = xbase->RECCOUNT( dbf, dResult )) != XB_NO_ERROR ){ + iErrorStop = 130; + throw iRc; + } + n->SetResult( dResult ); + } else if( sFunction == "RECNO" ){ + if(( iRc = xbase->RECNO( dbf, dResult )) != XB_NO_ERROR ){ + iErrorStop = 140; + throw iRc; + } + n->SetResult( dResult ); + } + // process functions with one child + } else if( n->GetChildCnt() == 1 ){ + + nChild1 = n->GetChild( 0 ); + + if( sFunction == "ABS" ){ + if(( iRc = xbase->ABS( nChild1->GetNumericResult(), dResult )) != XB_NO_ERROR ){ + iErrorStop = 200; + throw iRc; + } + n->SetResult( dResult ); + } else if( sFunction == "ALLTRIM" ){ + if(( iRc = xbase->ALLTRIM( nChild1->GetStringResult(), sResult )) != XB_NO_ERROR ){ + iErrorStop = 210; + throw iRc; + } + n->SetResult( sResult ); + } else if( sFunction == "ASC" ){ + if(( iRc = xbase->ASC( nChild1->GetStringResult(), dResult )) != XB_NO_ERROR ){ + iErrorStop = 220; + throw iRc; + } + n->SetResult( dResult ); + } else if( sFunction == "CDOW" ){ + xbDate d( (xbInt32) nChild1->GetNumericResult()); + if(( iRc = xbase->CDOW( d, sResult )) != XB_NO_ERROR ){ + iErrorStop = 230; + throw iRc; + } + n->SetResult( sResult ); + } else if( sFunction == "CHR" ){ + if(( iRc = xbase->CHR( nChild1->GetNumericResult(), sResult )) != XB_NO_ERROR ){ + iErrorStop = 240; + throw iRc; + } + n->SetResult( sResult ); + } else if( sFunction == "CMONTH" ){ + xbDate d( (xbInt32) nChild1->GetNumericResult()); + if(( iRc = xbase->CMONTH( d, sResult )) != XB_NO_ERROR ){ + iErrorStop = 250; + throw iRc; + } + n->SetResult( sResult ); + } else if( sFunction == "CTOD" ){ + if(( iRc = xbase->CTOD( nChild1->GetStringResult(), dtResult )) != XB_NO_ERROR ){ + iErrorStop = 260; + throw iRc; + } + n->SetResult( dtResult ); + } else if( sFunction == "DAY" ){ + xbDate d( (xbInt32) nChild1->GetNumericResult()); + if(( iRc = xbase->DAY( d, dResult )) != XB_NO_ERROR ){ + iErrorStop = 270; + throw iRc; + } + n->SetResult( dResult ); + } else if( sFunction == "DESCEND" ){ + + if( n->GetReturnType() == XB_EXP_CHAR ){ + if(( iRc = xbase->DESCEND( nChild1->GetStringResult(), sResult )) != XB_NO_ERROR ){ + iErrorStop = 280; + throw iRc; + } + n->SetResult( sResult ); + + } else if( n->GetReturnType() == XB_EXP_DATE ){ + xbDate d( (xbInt32) nChild1->GetNumericResult()); + if(( iRc = xbase->DESCEND( d, dtResult )) != XB_NO_ERROR ){ + iErrorStop = 290; + throw iRc; + } + n->SetResult( dtResult ); + + } else if( n->GetReturnType() == XB_EXP_NUMERIC ){ + if(( iRc = xbase->DESCEND( nChild1->GetNumericResult(), dResult )) != XB_NO_ERROR ){ + iErrorStop = 300; + throw iRc; + } + n->SetResult( dResult ); + + } else { + iErrorStop = 310; + iRc = XB_PARSE_ERROR; + throw iRc; + } + + } else if( sFunction == "DOW" ){ + xbDate d( (xbInt32) nChild1->GetNumericResult()); + if(( iRc = xbase->DOW( d, dResult )) != XB_NO_ERROR ){ + iErrorStop = 320; + throw iRc; + } + n->SetResult( dResult ); + } else if( sFunction == "DTOC" ){ + xbDate d( (xbInt32) nChild1->GetNumericResult()); + if(( iRc = xbase->DTOC( d, sResult )) != XB_NO_ERROR ){ + iErrorStop = 330; + throw iRc; + } + n->SetResult( sResult ); + } else if( sFunction == "DTOS" ){ + xbDate d( (xbInt32) nChild1->GetNumericResult()); + if(( iRc = xbase->DTOS( d, sResult )) != XB_NO_ERROR ){ + iErrorStop = 340; + throw iRc; + } + n->SetResult( sResult ); + } else if( sFunction == "EXP" ){ + if(( iRc = xbase->EXP( nChild1->GetNumericResult(), dResult )) != XB_NO_ERROR ){ + iErrorStop = 350; + throw iRc; + } + n->SetResult( dResult ); + } else if( sFunction == "INT" ){ + if(( iRc = xbase->INT( nChild1->GetNumericResult(), dResult )) != XB_NO_ERROR ){ + iErrorStop = 360; + throw iRc; + } + n->SetResult( dResult ); + } else if( sFunction == "ISALPHA" ){ + if(( iRc = xbase->ISALPHA( nChild1->GetStringResult(), bResult )) != XB_NO_ERROR ){ + iErrorStop = 370; + throw iRc; + } + n->SetResult( bResult ); + } else if( sFunction == "ISLOWER" ){ + if(( iRc = xbase->ISLOWER( nChild1->GetStringResult(), bResult )) != XB_NO_ERROR ){ + iErrorStop = 380; + throw iRc; + } + n->SetResult( bResult ); + } else if( sFunction == "ISUPPER" ){ + if(( iRc = xbase->ISUPPER( nChild1->GetStringResult(), bResult )) != XB_NO_ERROR ){ + iErrorStop = 390; + throw iRc; + } + n->SetResult( bResult ); + } else if( sFunction == "LEN" ){ + if(( iRc = xbase->LEN( nChild1->GetStringResult(), dResult )) != XB_NO_ERROR ){ + iErrorStop = 400; + throw iRc; + } + n->SetResult( dResult ); + } else if( sFunction == "LOG" ){ + if(( iRc = xbase->LOG( nChild1->GetNumericResult(), dResult )) != XB_NO_ERROR ){ + iErrorStop = 410; + throw iRc; + } + n->SetResult( dResult ); + } else if( sFunction == "LTRIM" ){ + if(( iRc = xbase->LTRIM( nChild1->GetStringResult(), sResult )) != XB_NO_ERROR ){ + iErrorStop = 420; + throw iRc; + } + n->SetResult( sResult ); + } else if( sFunction == "LOWER" ){ + if(( iRc = xbase->LOWER( nChild1->GetStringResult(), sResult )) != XB_NO_ERROR ){ + iErrorStop = 430; + throw iRc; + } + n->SetResult( sResult ); + } else if( sFunction == "MONTH" ){ + xbDate d( (xbInt32) nChild1->GetNumericResult()); + if(( iRc = xbase->MONTH( d, dResult )) != XB_NO_ERROR ){ + iErrorStop = 440; + throw iRc; + } + n->SetResult( dResult ); + } else if( sFunction == "RTRIM" ){ + if(( iRc = xbase->RTRIM( nChild1->GetStringResult(), sResult )) != XB_NO_ERROR ){ + iErrorStop = 450; + throw iRc; + } + n->SetResult( sResult ); + } else if( sFunction == "STOD" ){ + if(( iRc = xbase->STOD( nChild1->GetStringResult(), dtResult )) != XB_NO_ERROR ){ + iErrorStop = 460; + throw iRc; + } + n->SetResult( dtResult ); + } else if( sFunction == "SPACE" ){ + if(( iRc = xbase->SPACE( (xbInt32) nChild1->GetNumericResult(), sResult )) != XB_NO_ERROR ){ + iErrorStop = 470; + throw iRc; + } + n->SetResult( sResult ); + } else if( sFunction == "SQRT" ){ + if(( iRc = xbase->SQRT( nChild1->GetNumericResult(), dResult )) != XB_NO_ERROR ){ + iErrorStop = 480; + throw iRc; + } + n->SetResult( dResult ); + } else if( sFunction == "STR" ){ + if(( iRc = xbase->STR( nChild1->GetNumericResult(), sResult )) != XB_NO_ERROR ){ + iErrorStop = 490; + throw iRc; + } + n->SetResult( sResult ); + } else if( sFunction == "TRIM" ){ + if(( iRc = xbase->TRIM( nChild1->GetStringResult(), sResult )) != XB_NO_ERROR ){ + iErrorStop = 500; + throw iRc; + } + n->SetResult( sResult ); + } else if( sFunction == "UPPER" ){ + if(( iRc = xbase->UPPER( nChild1->GetStringResult(), sResult )) != XB_NO_ERROR ){ + iErrorStop = 510; + throw iRc; + } + n->SetResult( sResult ); + } else if( sFunction == "VAL" ){ + if(( iRc = xbase->VAL( nChild1->GetStringResult(), dResult )) != XB_NO_ERROR ){ + iErrorStop = 520; + throw iRc; + } + n->SetResult( dResult ); + } else if( sFunction == "YEAR" ){ + xbDate d( (xbInt32) nChild1->GetNumericResult()); + if(( iRc = xbase->YEAR( d, dResult )) != XB_NO_ERROR ){ + iErrorStop = 530; + throw iRc; + } + n->SetResult( dResult ); + } else { + iErrorStop = 540; + iRc = XB_PARSE_ERROR; + throw iRc; + } + } else if( n->GetChildCnt() == 2 ){ + xbExpNode * nChild2; + nChild1 = n->GetChild( 0 ); + nChild2 = n->GetChild( 1 ); + + if( sFunction == "AT" ){ + if(( iRc = xbase->AT( nChild1->GetStringResult(), nChild2->GetStringResult(), dResult )) != XB_NO_ERROR ){ + iErrorStop = 700; + throw iRc; + } + n->SetResult( dResult ); + } else if( sFunction == "LEFT" ){ + if(( iRc = xbase->LEFT( nChild1->GetStringResult(), (xbUInt32) nChild2->GetNumericResult(), sResult )) != XB_NO_ERROR ){ + iErrorStop = 710; + throw iRc; + } + n->SetResult( sResult ); + } else if( sFunction == "MAX" ){ + if(( iRc = xbase->MAX( nChild1->GetNumericResult(), nChild2->GetNumericResult(), dResult )) != XB_NO_ERROR ){ + iErrorStop = 720; + throw iRc; + } + n->SetResult( dResult ); + } else if( sFunction == "MIN" ){ + if(( iRc = xbase->MIN( nChild1->GetNumericResult(), nChild2->GetNumericResult(), dResult )) != XB_NO_ERROR ){ + iErrorStop = 730; + throw iRc; + } + n->SetResult( dResult ); + } + else if( sFunction == "REPLICATE" ){ + if(( iRc = xbase->REPLICATE( nChild1->GetStringResult(), (xbUInt32) nChild2->GetNumericResult(), sResult )) != XB_NO_ERROR ){ + iErrorStop = 800; + throw iRc; + } + n->SetResult( sResult ); + } else if( sFunction == "RIGHT" ){ + if(( iRc = xbase->RIGHT( nChild1->GetStringResult(), (xbUInt32) nChild2->GetNumericResult(), sResult )) != XB_NO_ERROR ){ + iErrorStop = 810; + throw iRc; + } + n->SetResult( sResult ); + } else if( sFunction == "STR" ){ + if(( iRc = xbase->STR( nChild1->GetNumericResult(), (xbUInt32) nChild2->GetNumericResult(), sResult )) != XB_NO_ERROR ){ + iErrorStop = 820; + throw iRc; + } + n->SetResult( sResult ); + } else { + iErrorStop = 830; + iRc = XB_PARSE_ERROR; + throw iRc; + } + } else if( n->GetChildCnt() == 3 ){ + xbExpNode * nChild2; + xbExpNode * nChild3; + nChild1 = n->GetChild( 0 ); + nChild2 = n->GetChild( 1 ); + nChild3 = n->GetChild( 2 ); + + if( sFunction == "IIF" ){ + if(( iRc = xbase->IIF( nChild1->GetBoolResult(), nChild2->GetStringResult(), nChild3->GetStringResult(), sResult )) != XB_NO_ERROR ){ + iErrorStop = 900; + throw iRc; + } + n->SetResult( sResult ); + } else if( sFunction == "STR" ){ + if(( iRc = xbase->STR( nChild1->GetNumericResult(), (xbUInt32) nChild2->GetNumericResult(), (xbUInt32) nChild3->GetNumericResult(), sResult )) != XB_NO_ERROR ){ + iErrorStop = 910; + throw iRc; + } + n->SetResult( sResult ); + } else if( sFunction == "STRZERO" ){ + if(( iRc = xbase->STRZERO( nChild1->GetNumericResult(), (xbUInt32) nChild2->GetNumericResult(), (xbUInt32) nChild3->GetNumericResult(), sResult )) != XB_NO_ERROR ){ + iErrorStop = 920; + throw iRc; + } + n->SetResult( sResult ); + } else if( sFunction == "SUBSTR" ){ + if(( iRc = xbase->SUBSTR( nChild1->GetStringResult(), (xbUInt32) nChild2->GetNumericResult(), (xbUInt32) nChild3->GetNumericResult(), sResult )) != XB_NO_ERROR ){ + iErrorStop = 930; + throw iRc; + } + n->SetResult( sResult ); + } else { + iErrorStop = 950; + iRc = XB_PARSE_ERROR; + throw iRc; + } + } else if( n->GetChildCnt() == 4 ){ + xbExpNode * nChild2; + xbExpNode * nChild3; + xbExpNode * nChild4; + nChild1 = n->GetChild( 0 ); + nChild2 = n->GetChild( 1 ); + nChild3 = n->GetChild( 2 ); + nChild4 = n->GetChild( 3 ); + + if( sFunction == "STR" ){ + if(( iRc = xbase->STR( nChild1->GetNumericResult(), (xbUInt32) nChild2->GetNumericResult(), + (xbUInt32) nChild3->GetNumericResult(), nChild4->GetStringResult(), sResult )) != XB_NO_ERROR ){ + iErrorStop = 1000; + throw iRc; + } + n->SetResult( sResult ); + } else { + iErrorStop = 1010; + iRc = XB_PARSE_ERROR; + throw iRc; + } + + } else { + iErrorStop = 2000; + iRc = XB_PARSE_ERROR; + throw iRc; + } + + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbexp::ProcessExpressionFunction() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + } + return iRc; +} +/*************************************************************************/ +//! Process Expression Operator +/*! This method processes an expression operator for a given node. +*/ + +xbInt16 xbExp::ProcessExpressionOperator( xbExpNode * n ){ + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + + xbExpNode * nChild1 = NULL; + xbExpNode * nChild2 = NULL; + xbString sOperator; + xbString sWork1; + xbString sWork2; + xbDate dtWork1; + + xbString sMsg; + + try{ + n->GetNodeText( sOperator ); + nChild1 = n->GetChild( 0 ); + if( !n->IsUnaryOperator()) + nChild2 = n->GetChild( 1 ); + + switch( n->GetReturnType()){ + case XB_EXP_CHAR: + if( sOperator == "+" ){ + sWork1 = nChild1->GetStringResult(); + sWork1 += nChild2->GetStringResult(); + n->SetResult( sWork1 ); + } else if( sOperator == "-" ){ + sWork1 = nChild1->GetStringResult(); + sWork1.Rtrim(); + sWork1 += nChild2->GetStringResult(); + sWork1.PadRight( ' ', n->GetResultLen()); + n->SetResult( sWork1 ); + } else { + iErrorStop = 100; + iRc = XB_PARSE_ERROR; + throw iRc; + } + break; + + case XB_EXP_NUMERIC: + if( sOperator == "+" ) + n->SetResult( nChild1->GetNumericResult() + nChild2->GetNumericResult()); + else if( sOperator == "-" ){ + n->SetResult( nChild1->GetNumericResult() - nChild2->GetNumericResult()); + + } + else if( sOperator == "*" ) + n->SetResult( nChild1->GetNumericResult() * nChild2->GetNumericResult()); + else if( sOperator == "/" ) + n->SetResult( nChild1->GetNumericResult() / nChild2->GetNumericResult()); + else if( sOperator == "^" || sOperator == "**" ) + n->SetResult( pow( nChild1->GetNumericResult(), nChild2->GetNumericResult())); + else if( sOperator == "+=" ){ + n->SetResult( nChild1->GetNumericResult() + nChild2->GetNumericResult()); + nChild1->SetResult( n->GetNumericResult() ); + } + else if( sOperator == "-=" ){ + n->SetResult( nChild1->GetNumericResult() - nChild2->GetNumericResult()); + nChild1->SetResult( n->GetNumericResult() ); + } + else if( sOperator == "*=" ){ + n->SetResult( nChild1->GetNumericResult() * nChild2->GetNumericResult()); + nChild1->SetResult( n->GetNumericResult() ); + } + else if( sOperator == "/=" ){ + n->SetResult( nChild1->GetNumericResult() / nChild2->GetNumericResult()); + nChild1->SetResult( n->GetNumericResult() ); + } else { + iErrorStop = 200; + iRc = XB_PARSE_ERROR; + throw iRc; + } + break; + + + case XB_EXP_DATE: + // if date values in the leaf nodes, convert to numeric for operator logic + + if( sOperator == "+" ) + n->SetResult( nChild1->GetNumericResult() + nChild2->GetNumericResult()); + else if( sOperator == "-" ){ + n->SetResult( nChild1->GetNumericResult() - nChild2->GetNumericResult()); + xbDate d( (xbInt32) n->GetNumericResult()); + } + else if( sOperator == "+=" ){ + n->SetResult( nChild1->GetNumericResult() + nChild2->GetNumericResult()); + nChild1->SetResult( n->GetNumericResult() ); + } + else if( sOperator == "-=" ){ + n->SetResult( nChild1->GetNumericResult() - nChild2->GetNumericResult()); + nChild1->SetResult( n->GetNumericResult() ); + } else { + iErrorStop = 300; + iRc = XB_PARSE_ERROR; + throw iRc; + } + break; + + case XB_EXP_LOGICAL: + + if( !n->IsUnaryOperator() && (nChild1->GetReturnType() != nChild2->GetReturnType())){ + iErrorStop = 400; + iRc = XB_INCOMPATIBLE_OPERANDS; + throw iRc; + } + + if( sOperator == ".AND." || sOperator == "AND" ) + n->SetResult((xbBool) (nChild1->GetBoolResult() && nChild2->GetBoolResult()) ); + + else if( sOperator == ".OR." || sOperator == "OR" ) + n->SetResult((xbBool) (nChild1->GetBoolResult() || nChild2->GetBoolResult()) ); + + else if( sOperator == ".NOT." || sOperator == "NOT" ){ + if( nChild1->GetBoolResult()) + n->SetResult((xbBool) xbFalse ); + else + n->SetResult((xbBool) xbTrue ); + } + + else if( sOperator == ">" ){ + + if( nChild1->GetReturnType() == XB_EXP_CHAR ) + n->SetResult((xbBool)(nChild1->GetStringResult() > nChild2->GetStringResult())); + + else if( nChild1->GetReturnType() == XB_EXP_NUMERIC ) + n->SetResult((xbBool)(nChild1->GetNumericResult() > nChild2->GetNumericResult())); + + else if( nChild1->GetReturnType() == XB_EXP_DATE ){ + xbDouble d1 = nChild1->GetNumericResult(); + xbDouble d2 = nChild2->GetNumericResult(); + if( d1 == XB_NULL_DATE ) d1 = 0; + if( d2 == XB_NULL_DATE ) d2 = 0; + n->SetResult((xbBool)( d1 > d2)); + // n->SetResult((xbBool)(nChild1->GetNumericResult() > nChild2->GetNumericResult())); + + } else { + iErrorStop = 410; + iRc = XB_PARSE_ERROR; + throw iRc; + } + } + + else if( sOperator == ">=" ){ + if( nChild1->GetReturnType() == XB_EXP_CHAR ) + n->SetResult((xbBool)(nChild1->GetStringResult() >= nChild2->GetStringResult())); + + else if( nChild1->GetReturnType() == XB_EXP_NUMERIC ) + n->SetResult((xbBool)(nChild1->GetNumericResult() >= nChild2->GetNumericResult())); + + else if( nChild1->GetReturnType() == XB_EXP_DATE ){ + xbDouble d1 = nChild1->GetNumericResult(); + xbDouble d2 = nChild2->GetNumericResult(); + if( d1 == XB_NULL_DATE ) d1 = 0; + if( d2 == XB_NULL_DATE ) d2 = 0; + n->SetResult((xbBool)( d1 >= d2)); + //n->SetResult((xbBool)(nChild1->GetNumericResult() >= nChild2->GetNumericResult())); + + } else { + iErrorStop = 420; + iRc = XB_PARSE_ERROR; + throw iRc; + } + } + + else if( sOperator == "<" ){ + + if( nChild1->GetReturnType() == XB_EXP_CHAR ){ + n->SetResult((xbBool)( nChild1->GetStringResult() < nChild2->GetStringResult())); + + } else if( nChild1->GetReturnType() == XB_EXP_NUMERIC ){ + n->SetResult((xbBool)( nChild1->GetNumericResult() < nChild2->GetNumericResult())); + + } else if( nChild1->GetReturnType() == XB_EXP_DATE ){ + xbDouble d1 = nChild1->GetNumericResult(); + xbDouble d2 = nChild2->GetNumericResult(); + if( d1 == XB_NULL_DATE ) d1 = 0; + if( d2 == XB_NULL_DATE ) d2 = 0; + + n->SetResult((xbBool)( d1 < d2)); + + // std::cout << "xbexp() line 2567 [" << nChild1->GetNumericResult() << "][" << nChild2->GetNumericResult() << "]\n"; + + } else { + iErrorStop = 430; + iRc = XB_PARSE_ERROR; + throw iRc; + } + } + + else if( sOperator == "<=" ){ + + if( nChild1->GetReturnType() == XB_EXP_CHAR ) + n->SetResult((xbBool)( nChild1->GetStringResult() <= nChild2->GetStringResult())); + + else if( nChild1->GetReturnType() == XB_EXP_NUMERIC ) + n->SetResult((xbBool)( nChild1->GetNumericResult() <= nChild2->GetNumericResult())); + + else if( nChild1->GetReturnType() == XB_EXP_DATE ){ + xbDouble d1 = nChild1->GetNumericResult(); + xbDouble d2 = nChild2->GetNumericResult(); + if( d1 == XB_NULL_DATE ) d1 = 0; + if( d2 == XB_NULL_DATE ) d2 = 0; + n->SetResult((xbBool)( d1 <= d2)); + // n->SetResult((xbBool)( nChild1->GetNumericResult() <= nChild2->GetNumericResult())); + + } else { + iErrorStop = 440; + iRc = XB_PARSE_ERROR; + throw iRc; + } + } + + else if( sOperator == "<>" || sOperator == "#" || sOperator == "!=" ){ + + if( nChild1->GetReturnType() == XB_EXP_CHAR ) + n->SetResult((xbBool)( nChild1->GetStringResult() != nChild2->GetStringResult())); + + else if( nChild1->GetReturnType() == XB_EXP_NUMERIC ) + n->SetResult((xbBool)( nChild1->GetNumericResult() != nChild2->GetNumericResult())); + + else if( nChild1->GetReturnType() == XB_EXP_DATE ){ + xbDouble d1 = nChild1->GetNumericResult(); + xbDouble d2 = nChild2->GetNumericResult(); + if( d1 == XB_NULL_DATE ) d1 = 0; + if( d2 == XB_NULL_DATE ) d2 = 0; + n->SetResult((xbBool)( d1 != d2)); + // n->SetResult((xbBool)( nChild1->GetNumericResult() != nChild2->GetNumericResult())); + + } else { + iErrorStop = 450; + iRc = XB_PARSE_ERROR; + throw iRc; + } + } + + else if( sOperator == "$" ){ + if( nChild1->GetReturnType() == XB_EXP_CHAR ) + if( nChild2->GetStringResult().Pos( nChild1->GetStringResult()) > 0 ) + n->SetResult((xbBool) xbTrue ); + else + n->SetResult((xbBool) xbFalse ); + else { + iErrorStop = 460; + iRc = XB_INCOMPATIBLE_OPERANDS; + throw iRc; + } + } + + else if( sOperator == "=" ){ + + if( nChild1->GetReturnType() == XB_EXP_CHAR ){ + xbString sChld1 = nChild1->GetStringResult(); + xbString sChld2 = nChild2->GetStringResult(); + sChld1.Rtrim(); + sChld2.Rtrim(); + n->SetResult((xbBool)( sChld1 == sChld2 )); + + } else if( nChild1->GetReturnType() == XB_EXP_NUMERIC ){ + n->SetResult((xbBool)( nChild1->GetNumericResult() == nChild2->GetNumericResult())); + + } else if( nChild1->GetReturnType() == XB_EXP_DATE ){ + xbDouble d1 = nChild1->GetNumericResult(); + xbDouble d2 = nChild2->GetNumericResult(); + if( d1 == XB_NULL_DATE ) d1 = 0; + if( d2 == XB_NULL_DATE ) d2 = 0; + n->SetResult((xbBool)( d1 == d2)); + // n->SetResult((xbBool)( nChild1->GetNumericResult() == nChild2->GetNumericResult())); + + } else { + iErrorStop = 470; + iRc = XB_PARSE_ERROR; + throw iRc; + } + + + } else { + iErrorStop = 500; + iRc = XB_PARSE_ERROR; + throw iRc; + } + + break; + + default: + iErrorStop = 600; + iRc = XB_PARSE_ERROR; + throw iRc; + // break; + } + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbexp::ProcessExpressionOperator() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + } + return iRc; +} + +/*************************************************************************/ +}; // namespace +#endif // XB_EXPRESSION_SUPPORT +/*************************************************************************/ diff --git a/src/core/xbexpnode.cpp b/src/core/xbexpnode.cpp new file mode 100755 index 0000000..d11e8cc --- /dev/null +++ b/src/core/xbexpnode.cpp @@ -0,0 +1,562 @@ +/* xbexpnode.cpp + +XBase64 Software Library + +Copyright (c) 1997,2003,2014,2017,2022,2023 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" + +#ifdef XB_EXPRESSION_SUPPORT + +namespace xb{ +/************************************************************************/ +//! @brief Constructor +xbExpNode::xbExpNode(){ + sNodeText = ""; + cReturnType = 0; + cNodeType = 0; + dResult = 0; + iFieldNo = 0; + ulResultLen = 0; + iWeight = 0; + nParent = NULL; + dbf = NULL; +} + +/************************************************************************/ +//! @brief Constructor +/*! + \param sNodeText Node text. + \param cReturnType Return type. + \param cNodeType Node type. +*/ + +xbExpNode::xbExpNode( xbString &sNodeText, char cReturnType, char cNodeType ){ + this->sNodeText = sNodeText; + this->cReturnType = cReturnType; + this->cNodeType = cNodeType; + dResult = 0; + iFieldNo = 0; + ulResultLen = 0; + iWeight = 0; + nParent = NULL; + dbf = NULL; +} + +/************************************************************************/ +//! @brief Constructor +/*! + \param sNodeText Node text. + \param cNodeType Node type. +*/ +xbExpNode::xbExpNode( xbString &sNodeText, char cNodeType ){ + this->sNodeText = sNodeText; + this->cReturnType = 0x00; + this->cNodeType = cNodeType; + dResult = 0; + iFieldNo = 0; + ulResultLen = 0; + iWeight = 0; + nParent = NULL; + dbf = NULL; +} + +/************************************************************************/ +//! @brief Constructor +/*! + \param cNodeType Node type. +*/ +xbExpNode::xbExpNode( char cNodeType ){ + this->cReturnType = 0x00; + this->cNodeType = cNodeType; + dResult = 0; + iFieldNo = 0; + ulResultLen = 0; + iWeight = 0; + nParent = NULL; + dbf = NULL; +} + +/************************************************************************/ +//! @brief Deconstructor +xbExpNode::~xbExpNode(){ + + xbExpNode * n; + while( llChildren.GetNodeCnt() > 0 ){ + llChildren.RemoveFromFront( n ); + delete n; + } +} + +/************************************************************************/ +//! @brief Add child node to linked list. +/*! + \param n Pointer to node to add to linked list. + \returns Return Codes +*/ +xbInt16 xbExpNode::AddChild( xbExpNode *n ){ + n->SetParent( this ); + return llChildren.InsertAtEnd( n ); +} + +/************************************************************************/ +#ifdef XB_DEBUG_SUPPORT + +//! @brief Dump Node. +/*! + \param iOption xbTrue - Print child info if they exist. + xbFalse - Don't print child info. + \returns void. +*/ +void xbExpNode::DumpNode( xbInt16 iOption ) const { + xbString sMsg; + std::cout << "Me=[" << this << "] "; + if( nParent ) + std::cout << "Par=[" << nParent << "] "; + + std::cout << "nTyp=[" << cNodeType << "] "; + std::cout << "dTyp=[" << cReturnType << "] "; + if( iWeight != 0 ) + std::cout << "W=[" << iWeight << "] "; + + if( cNodeType == XB_EXP_FIELD ) + std::cout << "FieldNo=[" << iFieldNo << "] "; + + std::cout << "Txt=[" << sNodeText << "] "; + if( sResult != "" ) + std::cout << "sVal=[" << sResult << "] "; + + if( ulResultLen > 0 ) + std::cout << "Len=[" << ulResultLen << "] "; + if( cReturnType == XB_EXP_DATE ){ + xbDate d( (xbInt32) dResult ); + std::cout << "dtVal=[" << d.Str() << "] "; + } + + if( cReturnType == XB_EXP_DATE || cReturnType == XB_EXP_NUMERIC ){ + sMsg.Sprintf( "dVal=[%f]\n", dResult ); + std::cout << sMsg.Str(); + } + + if( cReturnType == XB_EXP_LOGICAL ){ + sMsg.Sprintf( "lVal=[%d]\n", (xbInt32) dResult ); + std::cout << sMsg.Str(); + } + + if( iOption ){ + xbLinkListNode *lln = llChildren.GetHeadNode(); + xbExpNode *n; + if( lln ){ + std::cout << " Children: "; + while( lln ){ + n = lln->GetKey(); + std::cout << " [" << n << "]"; + lln = lln->GetNextNode(); + } + std::cout << std::endl; + + lln = llChildren.GetHeadNode(); + while( lln ){ + n = lln->GetKey(); + n->DumpNode( iOption ); + lln = lln->GetNextNode(); + } + } + } + + std::cout << std::endl; +} +#endif + +/************************************************************************/ +//! @brief Get boolean result. +/*! + \returns Boolean result. +*/ + +xbBool xbExpNode::GetBoolResult() const { + return (xbBool) dResult; +} + +/************************************************************************/ +//! @brief Get pointer to child. +/*! + \param ulChildNo - Which child? 1,2 or3 + \returns Pointer to child node or null if none exists. +*/ +xbExpNode *xbExpNode::GetChild( xbUInt32 ulChildNo ) const { + + xbLinkListNode *lln = llChildren.GetNodeForNo( ulChildNo ); + if( lln ) + return lln->GetKey(); + else + return 0x00; +} +/************************************************************************/ +//! @brief Get child count. +/*! + \returns Child count. +*/ +xbUInt32 xbExpNode::GetChildCnt() const{ + return llChildren.GetNodeCnt(); +} + +/************************************************************************/ +//! @brief Get the current child number for this node. +/*! + \returns Child number: 1, 2 or 3. +*/ + +xbUInt32 xbExpNode::GetChildNo() const { + + if( !nParent ) + return 0; + + for( xbUInt32 i = 0; i < nParent->GetChildCnt(); i++ ){ + if( this == nParent->GetChild( i )){ + // std::cout << "get child no [" << this << "][" << nParent->GetChild(i) << "]\n"; + return i; + } + } + return 0; +} + +/************************************************************************/ +//! @brief Get the dbf pointer. +/*! + \returns Pointer to dbf. +*/ +xbDbf *xbExpNode::GetDbf() const { + return dbf; +} +/************************************************************************/ +//! @brief Get the field number. +/*! + \returns Field number for field node. +*/ + +xbInt16 xbExpNode::GetFieldNo() const { + return iFieldNo; +} + +/*************************************************************************/ +//! @brief Get the first node. +/*! + \returns Pointer to left most child node or *this if childless. +*/ + +xbExpNode *xbExpNode::GetFirstNode() { + xbExpNode *n = this; + while( n && n->GetChildCnt() > 0 ) + n = n->GetChild(0); + return n; +} + +/*************************************************************************/ +//! @brief Get the next node. +/*! + \returns Pointer to right node or parent if right node does not exist. +*/ + +xbExpNode *xbExpNode::GetNextNode() const { + + if( HasRightSibling()) + return GetRightSibling()->GetFirstNode(); + else + return nParent; +} + +/************************************************************************/ +//! @brief Get the node text. +/*! + \param sOutText Output string containing node text. + \returns void +*/ + +void xbExpNode::GetNodeText( xbString &sOutText ) const{ + sOutText = sNodeText; +} + +/************************************************************************/ +//! @brief Get the node type. +/*! + \returns Node type. +*/ + +char xbExpNode::GetNodeType() const{ + return cNodeType; +} + +/************************************************************************/ +//! @brief Get numeric result. +/*! + \returns Numeric result. +*/ + +xbDouble xbExpNode::GetNumericResult() const { + return dResult; +} + +/************************************************************************/ +//! @brief Get parent. +/*! + \returns Pointer to parent node. +*/ + +xbExpNode *xbExpNode::GetParent() const{ + return nParent; +} + +/************************************************************************/ +//! @brief Get result length. +/*! + \returns Result length. +*/ +xbUInt32 xbExpNode::GetResultLen() const{ + return ulResultLen; +} +/************************************************************************/ +//! @brief Get result type. +/*! + \returns Result type. +*/ +char xbExpNode::GetReturnType() const{ + return cReturnType; +} + +/*************************************************************************/ +//! @brief Get right sibling. +/*! + \returns Pointer to right sibling. +*/ + +xbExpNode *xbExpNode::GetRightSibling() const { + + xbExpNode * nParent; + if(( nParent = GetParent()) == NULL ) + return NULL; + + if( nParent->GetChildCnt() <= 0 ) + return NULL; + + xbUInt32 ulChildNo = GetChildNo(); + + if( ulChildNo < (nParent->GetChildCnt() - 1)) + return nParent->GetChild( ulChildNo + 1 ); + else + return NULL; +} +/************************************************************************/ +//! @brief Get string result. +/*! + \returns String result. +*/ +xbString &xbExpNode::GetStringResult(){ + return sResult; +} +/************************************************************************/ +//! @brief Get node weight. +/*! + Each node is assigned a weight used internally to detmerine processing sequence. + \returns Node weight. +*/ + +xbInt16 xbExpNode::GetWeight() const { + return iWeight; +} + +/*************************************************************************/ +//! @brief Determine if node has a right sibling. +/*! + \returns xbTrue - Node has right sibling.
+ xbFalse - Node has no right sibling. +*/ + +xbBool xbExpNode::HasRightSibling() const { + + // std::cout << "in HasRightSibling [" << sNodeText << "]\n"; + + if( nParent == NULL ) + return xbFalse; + + xbUInt32 ulChildNo = GetChildNo(); + + if( ulChildNo < (nParent->GetChildCnt() - 1)){ + // std::cout << "Has Right Sibling = " << iChildNo << "] of [" << nParent->GetChildCnt() << "]\n"; + return xbTrue; +} + else + return xbFalse; +} + + +/*************************************************************************/ +//! @brief Determine if node is an operator. +/*! + \returns xbTrue - Node is an operator.
+ xbFalse - Node is not an operator. +*/ + +xbBool xbExpNode::IsOperator() const { + + if( cNodeType == XB_EXP_OPERATOR ) + return xbTrue; + else + return xbFalse; +} + +/*************************************************************************/ +//! @brief Determine if node is a unary operator. +/*! + \returns xbTrue - Node is a unary operator.
+ xbFalse - Node is not a unary operator. +*/ +xbBool xbExpNode::IsUnaryOperator() const { + + if( cNodeType == XB_EXP_PRE_OPERATOR || cNodeType == XB_EXP_POST_OPERATOR ) + return xbTrue; + else if( cNodeType == XB_EXP_OPERATOR && (sNodeText == ".NOT." || sNodeText == "NOT" )) + return xbTrue; + else + return xbFalse; +} +/************************************************************************/ +//! @brief Remove last child from node. +/*! + \returns void. +*/ + +void xbExpNode::RemoveLastChild(){ + xbExpNode *n; + llChildren.RemoveFromEnd( n ); +} + +/************************************************************************/ +//! @brief Set dbf info on node. +/*! + \param dbf Pointer to dbf. + \param iFieldNo Field number of field. + \returns void. +*/ +void xbExpNode::SetDbfInfo( xbDbf *dbf, xbInt16 iFieldNo ){ + this->dbf = dbf; + this->iFieldNo = iFieldNo; +} + +/************************************************************************/ +//! @brief Set dbf info on node. +/*! + \param dbf Pointer to dbf. + \returns void. +*/ +void xbExpNode::SetDbfInfo( xbDbf *dbf ){ + this->dbf = dbf; +} +/************************************************************************/ +//! @brief Set node type. +/*! + \param cNodeType Node type. + \returns void. +*/ +void xbExpNode::SetNodeType( char cNodeType ){ + this->cNodeType = cNodeType; +} + +/************************************************************************/ +//! @brief Set node text. +/*! + \param sNodeText Node text. + \returns void. +*/ +void xbExpNode::SetNodeText( xbString & sNodeText ){ + this->sNodeText = sNodeText; +} + +/************************************************************************/ +//! @brief Set parent. +/*! + \param n Pointer to parent. + \returns void. +*/ +void xbExpNode::SetParent( xbExpNode *n ){ + this->nParent = n; +} + +/************************************************************************/ +//! @brief Set date result. +/*! + \param dtResult Date result. + \returns void. +*/ +void xbExpNode::SetResult( xbDate &dtResult ){ + this->dResult = dtResult.JulianDays(); +} + +/************************************************************************/ +//! @brief Set boolean result. +/*! + \param bResult Boolean result. + \returns void. +*/ +void xbExpNode::SetResult( xbBool bResult ){ + this->dResult = bResult; +} +/************************************************************************/ +//! @brief Set numeric result. +/*! + \param dResult Double numeric result. + \returns void. +*/ +void xbExpNode::SetResult( xbDouble dResult ){ + this->dResult = dResult; +} +/************************************************************************/ +//! @brief Set string result. +/*! + \param sResult String result. + \returns void. +*/ +void xbExpNode::SetResult( xbString &sResult ){ + this->sResult = sResult; +} +/************************************************************************/ +//! @brief Set result length. +/*! + \param ulResultLen Set result length. + \returns void. +*/ +void xbExpNode::SetResultLen( xbUInt32 ulResultLen ){ + this->ulResultLen = ulResultLen; +} +/************************************************************************/ +//! @brief Set return type. +/*! + \param cReturnType Return Type. + \returns void. +*/ +void xbExpNode::SetReturnType( char cReturnType ){ + this->cReturnType = cReturnType; +} +/************************************************************************/ +//! @brief Set weight. +/*! + \param iWeight Weight to set this node at. + \returns void. +*/ +void xbExpNode::SetWeight( xbInt16 iWeight ){ + this->iWeight = iWeight; +} +/*************************************************************************/ +}; // namespace +#endif // XB_EXPRESSION_SUPPORT +/*************************************************************************/ diff --git a/src/core/xbfields.cpp b/src/core/xbfields.cpp new file mode 100755 index 0000000..85ac145 --- /dev/null +++ b/src/core/xbfields.cpp @@ -0,0 +1,1189 @@ +/* xbfields.cpp + +XBase64 Software Library + +Copyright (c) 1997,2003,2014,2022,2023 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 Get xbDouble field for field name. +/*! + \param sFieldName Name of field to retrieve. + \param dFieldValue Output field value. + \returns The field routines return one of:
+ XB_NO_ERROR
XB_INVALID_DATA
XB_INVALID_FIELD_NO
XB_INVALID_FIELD_NAME
+ XB_INVALID_FIELD_TYPE
XB_INVALID_DATA +*/ + +xbInt16 xbDbf::GetDoubleField( const xbString &sFieldName, xbDouble &dFieldValue ) const { + return GetDoubleField( GetFieldNo( sFieldName ), dFieldValue ); +} + +/************************************************************************/ +//! @brief Get xbDouble field for field number. +/*! + \param iFieldNo Number of field to retrieve. + \param dFieldValue Output field value. + \returns The field routines return one of:
+ XB_NO_ERROR
XB_INVALID_DATA
XB_INVALID_FIELD_NO
XB_INVALID_FIELD_NAME
+ XB_INVALID_FIELD_TYPE
XB_INVALID_DATA +*/ +xbInt16 xbDbf::GetDoubleField( xbInt16 iFieldNo, xbDouble &dFieldValue ) const { + xbInt16 iRc = XB_NO_ERROR; + char buf[21]; + memset( buf, 0x00, 21 ); + if(( iRc = GetRawField( iFieldNo, buf, 21, 0 )) >= XB_NO_ERROR ){ + dFieldValue = strtod( buf, NULL ); + return XB_NO_ERROR; + } else + return iRc; +} +/************************************************************************/ +//! @brief Get xbDouble field for field number. +/*! + \param iFieldNo Number of field to retrieve. + \param dFieldValue Output field value. + \param iRecBufSw 0 - Record buffer with any updates.
1 - Record buffer with original data. + \returns The field routines return one of:
+ XB_NO_ERROR
XB_INVALID_DATA
XB_INVALID_FIELD_NO
XB_INVALID_FIELD_NAME
+ XB_INVALID_FIELD_TYPE
XB_INVALID_DATA +*/ + +xbInt16 xbDbf::GetDoubleField( xbInt16 iFieldNo, xbDouble &dFieldValue, xbInt16 iRecBufSw ) const { + xbInt16 iRc = XB_NO_ERROR; + char buf[21]; + memset( buf, 0x00, 21 ); + if(( iRc = GetRawField( iFieldNo, buf, 21, iRecBufSw )) >= XB_NO_ERROR ){ + dFieldValue = strtod( buf, NULL ); + return XB_NO_ERROR; + } else + return iRc; +} + +/************************************************************************/ +//! @brief Get xbDate field for field number. +/*! + \param iFieldNo Number of field to retrieve. + \param dtFieldValue Output field value. + \returns The field routines return one of:
+ XB_NO_ERROR
XB_INVALID_DATA
XB_INVALID_FIELD_NO
XB_INVALID_FIELD_NAME
+ XB_INVALID_FIELD_TYPE
XB_INVALID_DATA +*/ + +xbInt16 xbDbf::GetDateField( xbInt16 iFieldNo, xbDate &dtFieldValue ) const{ + xbString s; + xbInt16 iRc; + if(( iRc = GetField( iFieldNo, s )) != XB_NO_ERROR ) + return iRc; + return dtFieldValue.Set( s ); + // return XB_NO_ERROR; +} + +/************************************************************************/ +//! @brief Get xbDate field for field name. +/*! + \param sFieldName Name of field to retrieve. + \param dtFieldValue Output field value. + \returns The field routines return one of:
+ XB_NO_ERROR
XB_INVALID_DATA
XB_INVALID_FIELD_NO
XB_INVALID_FIELD_NAME
+ XB_INVALID_FIELD_TYPE
XB_INVALID_DATA +*/ + +xbInt16 xbDbf::GetDateField( const xbString &sFieldName, xbDate &dtFieldValue ) const{ + xbString s; + xbInt16 iRc; + if(( iRc = GetField( sFieldName, s )) != XB_NO_ERROR ) + return iRc; + return dtFieldValue.Set( s ); + // return XB_NO_ERROR; +} + +/************************************************************************/ +//! @brief Get xbString field for field name. +/*! + \param sFieldName Name of field to retrieve + \param sFieldValue Output field value. + \returns The field routines return one of:
+ XB_NO_ERROR
XB_INVALID_DATA
XB_INVALID_FIELD_NO
XB_INVALID_FIELD_NAME
+ XB_INVALID_FIELD_TYPE
XB_INVALID_DATA +*/ + +xbInt16 xbDbf::GetField( const xbString &sFieldName, xbString &sFieldValue ) const{ + return GetField( GetFieldNo( sFieldName ), sFieldValue, 0 ); +} +/************************************************************************/ +//! @brief Get field data for field number. +/*! + \param iFieldNo Number of field to retrieve. + \param sFieldValue Output field value. + \returns The field routines return one of:
+ XB_NO_ERROR
XB_INVALID_DATA
XB_INVALID_FIELD_NO
XB_INVALID_FIELD_NAME
+ XB_INVALID_FIELD_TYPE
XB_INVALID_DATA +*/ + +xbInt16 xbDbf::GetField( xbInt16 iFieldNo, xbString &sFieldValue ) const{ + return GetField( iFieldNo, sFieldValue, 0 ); +} +/************************************************************************/ +//! @brief Get field data for field number. +/*! + \param iFieldNo Number of field to retrieve. + \param sFieldValue Output field value. + \param iRecBufSw 0 - Record buffer with any updates.
1 - Record buffer with original data. + \returns The field routines return one of:
+ XB_NO_ERROR
XB_INVALID_DATA
XB_INVALID_FIELD_NO
XB_INVALID_FIELD_NAME
+ XB_INVALID_FIELD_TYPE
XB_INVALID_DATA +*/ + +xbInt16 xbDbf::GetField( xbInt16 iFieldNo, xbString &sFieldValue, xbInt16 iRecBufSw) const +{ + xbUInt32 iLen; + if( iFieldNo < 0 || iFieldNo >= iNoOfFields ) { + sFieldValue = ""; + return XB_INVALID_FIELD_NO; + } + iLen = SchemaPtr[iFieldNo].cFieldLen; + sFieldValue = ""; + if( iRecBufSw ) + sFieldValue.Append( (char *) SchemaPtr[iFieldNo].pAddress2, iLen ); // original record buffer + else + sFieldValue.Append( (char *) SchemaPtr[iFieldNo].pAddress, iLen ); // current record buffer + return XB_NO_ERROR; +} + + +/************************************************************************/ +//! @brief Get decimal for field name. +/*! + This routine retreives a field's decimal length. + \param sFieldName Name of field to retrieve + \param iFieldDecimal Output field value. + \returns The field routines return one of:
+ XB_NO_ERROR
XB_INVALID_DATA
XB_INVALID_FIELD_NO
XB_INVALID_FIELD_NAME
+ XB_INVALID_FIELD_TYPE
XB_INVALID_DATA +*/ + +xbInt16 xbDbf::GetFieldDecimal( const xbString &sFieldName, xbInt16 & iFieldDecimal ) const { + return GetFieldDecimal( GetFieldNo( sFieldName ), iFieldDecimal ); +} + + +/************************************************************************/ +//! @brief Get decimal for field number. +/*! + This routine retreives a field's decimal length. + \param iFieldNo Number of field to retrieve + \param iFieldDecimal Output field value. + \returns The field routines return one of:
+ XB_NO_ERROR
XB_INVALID_DATA
XB_INVALID_FIELD_NO
XB_INVALID_FIELD_NAME
+ XB_INVALID_FIELD_TYPE
XB_INVALID_DATA +*/ + +xbInt16 xbDbf::GetFieldDecimal( xbInt16 iFieldNo, xbInt16 & iFieldDecimal ) const { + + if( iFieldNo < 0 || iFieldNo >= iNoOfFields ) { + return XB_INVALID_FIELD_NO; + } + iFieldDecimal = SchemaPtr[iFieldNo].cNoOfDecs; + return XB_NO_ERROR; +} + +/************************************************************************/ +//! @brief Get field length for field name. +/*! + + This function retrieves a field's length. + + \param sFieldName Name of field to retrieve + \param iFieldLen Output field length value. + \returns The field routines return one of:
+ XB_NO_ERROR
XB_INVALID_DATA
XB_INVALID_FIELD_NO
XB_INVALID_FIELD_NAME
+ XB_INVALID_FIELD_TYPE
XB_INVALID_DATA +*/ + +xbInt16 xbDbf::GetFieldLen( const xbString &sFieldName, xbInt16 &iFieldLen ) const { + return GetFieldLen( GetFieldNo( sFieldName ), iFieldLen ); +} + + +/************************************************************************/ +//! @brief Get field length for field number. +/*! + This function retrieves a field's length. + + \param iFieldNo Name of field to retrieve + \param iFieldLen Output field length value. + \returns The field routines return one of:
+ XB_NO_ERROR
XB_INVALID_DATA
XB_INVALID_FIELD_NO
XB_INVALID_FIELD_NAME
+ XB_INVALID_FIELD_TYPE
XB_INVALID_DATA +*/ + +xbInt16 xbDbf::GetFieldLen( xbInt16 iFieldNo, xbInt16 &iFieldLen ) const { + if( iFieldNo >= 0 && iFieldNo < iNoOfFields ){ + iFieldLen = SchemaPtr[iFieldNo].cFieldLen; + return XB_NO_ERROR; + } else + return XB_INVALID_FIELD_NO; +} + +/************************************************************************/ +//! @brief Get the field number for name. +/*! Returns the field number for the named field. + + All field get/put methods require either a field number or field name as + one of the parameters. Using the methods that take the field numbers will + yield slightly better performance because the methods that take a name, have + to look up the number. + + \param sFieldName Name of field. + \param iFieldNo Output field number for the given name. + \returns Number of field named fldName. +*/ + +xbInt16 xbDbf::GetFieldNo( const xbString & sFieldName, xbInt16 &iFieldNo ) const +{ + int i; + + if( sFieldName.Len() > 10 ) + return XB_INVALID_FIELD_NAME; + + for( i = 0; i < iNoOfFields; i++ ){ + if( sFieldName == SchemaPtr[i].cFieldName ){ + iFieldNo = i; + return XB_NO_ERROR; + } + } + return XB_INVALID_FIELD_NAME; +} + +/************************************************************************/ +//! Get field ID number for a given field name. +/*! Returns the field number for the named field. + + \param sFieldName Name of field. + \returns Number of field or XB_INVALID_FIELD_NAME. +*/ + +xbInt16 xbDbf::GetFieldNo( const xbString &sFieldName ) const { + int i; + + if( sFieldName.Len() > 10 ) + return XB_INVALID_FIELD_NAME; + + for( i = 0; i < iNoOfFields; i++ ){ + if( sFieldName == SchemaPtr[i].cFieldName ) + return i; + } + return XB_INVALID_FIELD_NAME; +} + +/************************************************************************/ +//! Get field type for field number. +/*! + \param iFieldNo Field number. + \param cFieldType Output field type. + \returns The field routines return one of:
+ XB_NO_ERROR
XB_INVALID_DATA
XB_INVALID_FIELD_NO
XB_INVALID_FIELD_NAME
+ XB_INVALID_FIELD_TYPE
XB_INVALID_DATA +*/ +xbInt16 xbDbf::GetFieldType( xbInt16 iFieldNo, char & cFieldType ) const{ + + if( iFieldNo >= 0 && iFieldNo < iNoOfFields ){ + cFieldType = SchemaPtr[iFieldNo].cType; + return XB_NO_ERROR; + } + else + return XB_INVALID_FIELD_NO; +} + +/************************************************************************/ +//! Get field type for field name. +/*! + \param sFieldName Field name. + \param cFieldType Output field type. + \returns The field routines return one of:
+ XB_NO_ERROR
XB_INVALID_DATA
XB_INVALID_FIELD_NO
XB_INVALID_FIELD_NAME
+ XB_INVALID_FIELD_TYPE
XB_INVALID_DATA +*/ + +xbInt16 xbDbf::GetFieldType( const xbString &sFieldName, char & cFieldType ) const{ + return( GetFieldType( GetFieldNo( sFieldName ), cFieldType)); +} + +/************************************************************************/ +//! @brief Returns the name of the specified field. +/*! Returns a pointer to the name for the field specified by iFieldNo. + + \param iFieldNo Number of field. + \param sFieldName Output variable containing the field name. + \returns The field routines return one of:
+ XB_NO_ERROR
XB_INVALID_DATA
XB_INVALID_FIELD_NO
XB_INVALID_FIELD_NAME
+ XB_INVALID_FIELD_TYPE
XB_INVALID_DATA +*/ +xbInt16 xbDbf::GetFieldName( xbInt16 iFieldNo, xbString &sFieldName ) const{ + if( iFieldNo >= 0 && iFieldNo < iNoOfFields ){ + sFieldName = SchemaPtr[iFieldNo].cFieldName; + return XB_NO_ERROR; + } + else + return XB_INVALID_FIELD_NO; +} + +/************************************************************************/ +//! @brief Get xbFloat field for field number. +/*! + \param iFieldNo Number of field to retrieve. + \param fFieldValue Output field value. + \returns The field routines return one of:
+ XB_NO_ERROR
XB_INVALID_DATA
XB_INVALID_FIELD_NO
XB_INVALID_FIELD_NAME
+ XB_INVALID_FIELD_TYPE
XB_INVALID_DATA +*/ +xbInt16 xbDbf::GetFloatField( xbInt16 iFieldNo, xbFloat & fFieldValue ) const { + + char cFieldType; + xbInt16 rc = GetFieldType( iFieldNo, cFieldType ); + if( rc < 0 ) + return rc; + + if( cFieldType != 'N' && cFieldType != 'F' ) + return XB_INVALID_FIELD_TYPE; + + xbString sTemp; + rc = GetField( iFieldNo, sTemp, 0 ); + if( rc < 0 ) + return rc; + + fFieldValue = (xbFloat) atof( sTemp.Str()); + return XB_NO_ERROR; +} + + +/************************************************************************/ +//! @brief Get xbFloat field for field name. +/*! + \param sFieldName Number of field to retrieve. + \param fFieldValue Output field value. + \returns The field routines return one of:
+ XB_NO_ERROR
XB_INVALID_DATA
XB_INVALID_FIELD_NO
XB_INVALID_FIELD_NAME
+ XB_INVALID_FIELD_TYPE
XB_INVALID_DATA +*/ + +xbInt16 xbDbf::GetFloatField( const xbString & sFieldName, xbFloat & fFieldValue ) const { + return GetFloatField( GetFieldNo(sFieldName ), fFieldValue ); +} + +/************************************************************************/ +//! @brief Get logical field for field number. +/*! + \param iFieldNo Number of field to retrieve. + \param sFieldValue Output field value. + \returns The field routines return one of:
+ XB_NO_ERROR
XB_INVALID_DATA
XB_INVALID_FIELD_NO
XB_INVALID_FIELD_NAME
+ XB_INVALID_FIELD_TYPE
XB_INVALID_DATA +*/ +xbInt16 xbDbf::GetLogicalField( xbInt16 iFieldNo, xbString &sFieldValue ) const { + + char cFieldType; + xbInt16 iRc = GetFieldType( iFieldNo, cFieldType ); + if( iRc < 0 ) + return iRc; + else if( cFieldType != 'L' ) + return XB_INVALID_FIELD_TYPE; + + if(( iRc = GetField( iFieldNo, sFieldValue )) < XB_NO_ERROR ) + return iRc; + else + return XB_NO_ERROR; +} + +/************************************************************************/ +//! @brief Get logical field for field name. +/*! + \param sFieldName Name of field to retrieve. + \param sFieldValue Output field value. + \returns The field routines return one of:
+ XB_NO_ERROR
XB_INVALID_DATA
XB_INVALID_FIELD_NO
XB_INVALID_FIELD_NAME
+ XB_INVALID_FIELD_TYPE
XB_INVALID_DATA +*/ + +xbInt16 xbDbf::GetLogicalField( const xbString &sFieldName, xbString &sFieldValue ) const { + return GetLogicalField( GetFieldNo( sFieldName ), sFieldValue ); +} + + +/************************************************************************/ +//! @brief Get logical field for field number. +/*! + \param iFieldNo Number of field to retrieve. + \param bFieldValue Output field value. + \returns The field routines return one of:
+ XB_NO_ERROR
XB_INVALID_DATA
XB_INVALID_FIELD_NO
XB_INVALID_FIELD_NAME
+ XB_INVALID_FIELD_TYPE
XB_INVALID_DATA +*/ + +xbInt16 xbDbf::GetLogicalField( xbInt16 iFieldNo, xbBool &bFieldValue ) const { + return GetLogicalField( iFieldNo, bFieldValue, 0 ); +} + +/************************************************************************/ +//! @brief Get logical field for field number. +/*! + \param iFieldNo Number of field to retrieve. + \param bFieldValue Output field value. + \param iRecBufSw 0 - Record buffer with any updates.
1 - Record buffer with original data. + \returns The field routines return one of:
+ XB_NO_ERROR
XB_INVALID_DATA
XB_INVALID_FIELD_NO
XB_INVALID_FIELD_NAME
+ XB_INVALID_FIELD_TYPE
XB_INVALID_DATA +*/ + +xbInt16 xbDbf::GetLogicalField( xbInt16 iFieldNo, xbBool &bFieldValue, xbInt16 iRecBufSw ) const { + + char cFieldType; + xbInt16 iRc = GetFieldType( iFieldNo, cFieldType ); + if((iRc = GetFieldType( iFieldNo, cFieldType )) != XB_NO_ERROR ) + return iRc; + + if( cFieldType != 'L' ) + return XB_INVALID_FIELD_TYPE; + + xbString sFieldValue; + if(( iRc = GetField( iFieldNo, sFieldValue, iRecBufSw )) < XB_NO_ERROR ) + return iRc; + + if( sFieldValue == 'T' || sFieldValue == 't' || sFieldValue == 'Y' || sFieldValue == 'y' ) + bFieldValue = xbTrue; + else + bFieldValue = xbFalse; + + return XB_NO_ERROR; +} + +/************************************************************************/ +//! @brief Get logical field for field name. +/*! + \param sFieldName Name of field to retrieve. + \param bFieldValue Output field value. + \returns The field routines return one of:
+ XB_NO_ERROR
XB_INVALID_DATA
XB_INVALID_FIELD_NO
XB_INVALID_FIELD_NAME
+ XB_INVALID_FIELD_TYPE
XB_INVALID_DATA +*/ +xbInt16 xbDbf::GetLogicalField( const xbString &sFieldName, xbBool &bFieldValue ) const { + return GetLogicalField( GetFieldNo( sFieldName ), bFieldValue ); +} +/************************************************************************/ +//! @brief Get long field for field number. +/*! + \param iFieldNo Number of field to retrieve. + \param lFieldValue Output field value. + \returns The field routines return one of:
+ XB_NO_ERROR
XB_INVALID_DATA
XB_INVALID_FIELD_NO
XB_INVALID_FIELD_NAME
+ XB_INVALID_FIELD_TYPE
XB_INVALID_DATA +*/ +xbInt16 xbDbf::GetLongField( xbInt16 iFieldNo, xbInt32 & lFieldValue ) const { + + xbInt16 iRc = 0; + xbInt16 iErrorStop = 0; + char cFieldType; + xbString sTemp; + + try{ + + if(( iRc = GetFieldType( iFieldNo, cFieldType )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + + if( cFieldType != 'N' && cFieldType != 'F' && cFieldType != 'M' ){ + iErrorStop = 110; + iRc = XB_INVALID_FIELD_TYPE; + throw iRc; + } + + if(( iRc = GetField( iFieldNo, sTemp, 0 )) < 0 ){ + iErrorStop = 120; + throw iRc; + } + sTemp.Trim(); + + if( !sTemp.ValidNumericValue() ){ + iErrorStop = 130; + iRc = XB_INVALID_DATA; + throw iRc; + } + + if( sTemp.Pos( '.' ) > 0){ + iErrorStop = 140; + iRc = XB_INVALID_DATA; + throw iRc; + } + + lFieldValue = atol( sTemp.Str()); + } + + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbDbf::GetLongField() Exception Caught. Error Stop = [%d] rc = [%d] [%s]", iErrorStop, iRc, sTemp.Str() ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + + return XB_NO_ERROR; +} + +/************************************************************************/ +#ifdef XB_MEMO_SUPPORT +//! @brief Get memo field for field number. +/*! + \param iFieldNo Number of field to retrieve. + \param sMemoValue Output field value. + \returns The field routines return one of:
+ XB_NO_ERROR
XB_INVALID_DATA
XB_INVALID_FIELD_NO
XB_INVALID_FIELD_NAME
+ XB_INVALID_FIELD_TYPE
XB_INVALID_DATA +*/ +xbInt16 xbDbf::GetMemoField( xbInt16 iFieldNo, xbString &sMemoValue ){ + return Memo->GetMemoField( iFieldNo, sMemoValue ); +} +/************************************************************************/ +//! @brief Get memo field for field name. +/*! + + \param sFieldName Name of field to retrieve. + \param sMemoValue Output field value. + \returns The field routines return one of:
+ XB_NO_ERROR
XB_INVALID_DATA
XB_INVALID_FIELD_NO
XB_INVALID_FIELD_NAME
+ XB_INVALID_FIELD_TYPE
XB_INVALID_DATA +*/ +xbInt16 xbDbf::GetMemoField( const xbString & sFieldName, xbString & sMemoValue ){ + return Memo->GetMemoField( GetFieldNo( sFieldName ), sMemoValue ); +} + +/************************************************************************/ +//! @brief Get the memo field count for this table. +/*! + \returns Returns the number of memo fields in the table, +*/ +xbInt16 xbDbf::GetMemoFieldCnt() const { + return iMemoFieldCnt; +} +/************************************************************************/ +//! @brief Get memo field length for field number. +/*! + \param iFieldNo Number of field to retrieve. + \param ulMemoFieldLen Output memo field value length. + \returns The field routines return one of:
+ XB_NO_ERROR
XB_INVALID_DATA
XB_INVALID_FIELD_NO
XB_INVALID_FIELD_NAME
+ XB_INVALID_FIELD_TYPE
XB_INVALID_DATA +*/ + +xbInt16 xbDbf::GetMemoFieldLen( xbInt16 iFieldNo, xbUInt32 & ulMemoFieldLen ){ + return Memo->GetMemoFieldLen( iFieldNo, ulMemoFieldLen ); +} +/************************************************************************/ +//! @brief Get memo field length for field name. +/*! + \param sFieldName Name of field to retrieve. + \param ulMemoFieldLen Output memo field value length. + \returns The field routines return one of:
+ XB_NO_ERROR
XB_INVALID_DATA
XB_INVALID_FIELD_NO
XB_INVALID_FIELD_NAME
+ XB_INVALID_FIELD_TYPE
XB_INVALID_DATA +*/ +xbInt16 xbDbf::GetMemoFieldLen( const xbString &sFieldName, xbUInt32 &ulMemoFieldLen ){ + return Memo->GetMemoFieldLen( GetFieldNo( sFieldName ), ulMemoFieldLen ); +} + +#endif // XB_MEMO_SUPPORT + +/************************************************************************/ +//! @brief Get field null status +/*! + \param iFieldNo Number of field to retrieve. + \param bIsNull Output field value. If field is all spaces on record buffer, returns true. + \returns The field routines return one of:
+ XB_NO_ERROR
XB_INVALID_DATA
XB_INVALID_FIELD_NO
XB_INVALID_FIELD_NAME
+ XB_INVALID_FIELD_TYPE
XB_INVALID_DATA +*/ +xbInt16 xbDbf::GetNullSts( xbInt16 iFieldNo, xbBool &bIsNull ) const { + return GetNullSts( iFieldNo, bIsNull, 0 ); +} + +/************************************************************************/ +//! @brief Get field null status +/*! + \param iFieldName Field Name of field to retrieve. + \param bIsNull Output field value. If field is all spaces on record buffer, returns true. + \returns The field routines return one of:
+ XB_NO_ERROR
XB_INVALID_DATA
XB_INVALID_FIELD_NO
XB_INVALID_FIELD_NAME
+ XB_INVALID_FIELD_TYPE
XB_INVALID_DATA +*/ + +xbInt16 xbDbf::GetNullSts( const xbString &sFieldName, xbBool &bIsNull ) const { + return GetNullSts( GetFieldNo( sFieldName ), bIsNull, 0 ); +} + +/************************************************************************/ +//! @brief Get field null status +/*! + \param iFieldNo Number of field to retrieve. + \param bIsNull Output field value. If field is all spaces on record buffer, returns true. + \param iRecBufSw 0 - Record buffer with any updates.
1 - Record buffer with original data. + \returns The field routines return one of:
+ XB_NO_ERROR
XB_INVALID_DATA
XB_INVALID_FIELD_NO
XB_INVALID_FIELD_NAME
+ XB_INVALID_FIELD_TYPE
XB_INVALID_DATA +*/ + +xbInt16 xbDbf::GetNullSts( xbInt16 iFieldNo, xbBool &bIsNull, xbInt16 iRecBufSw ) const +{ + if( iFieldNo < 0 || iFieldNo >= iNoOfFields ) { + return XB_INVALID_FIELD_NO; + } + bIsNull = xbTrue; + char *p; + if( iRecBufSw ) + p = (char *) SchemaPtr[iFieldNo].pAddress2; + else + p = (char *) SchemaPtr[iFieldNo].pAddress; + + xbUInt32 ulLen = SchemaPtr[iFieldNo].cFieldLen; + xbUInt32 ul = 0; + + while( ul < ulLen && bIsNull ){ + if( *p++ != ' ' ) + bIsNull = xbFalse; + else + ul++; + } + return XB_NO_ERROR; +} + +/************************************************************************/ +//! @brief Get unsigned long field for field number. +/*! + \param iFieldNo Number of field to retrieve. + \param ulFieldValue Output field value. + \returns The field routines return one of:
+ XB_NO_ERROR
XB_INVALID_DATA
XB_INVALID_FIELD_NO
XB_INVALID_FIELD_NAME
+ XB_INVALID_FIELD_TYPE
XB_INVALID_DATA +*/ +xbInt16 xbDbf::GetULongField( xbInt16 iFieldNo, xbUInt32 & ulFieldValue ) const { + + xbInt16 rc = 0; + xbInt16 iErrorStop = 0; + char cFieldType; + + try{ + + if(( rc = GetFieldType( iFieldNo, cFieldType )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw rc; + } + if( cFieldType != 'N' && cFieldType != 'F' && cFieldType != 'M' ){ + iErrorStop = 110; + rc = XB_INVALID_FIELD_TYPE; + throw rc; + } + xbString sTemp; + if(( rc = GetField( iFieldNo, sTemp, 0 )) != XB_NO_ERROR ){ + iErrorStop = 120; + throw rc; + } + sTemp.Trim(); + if( !sTemp.ValidNumericValue() || ((int) sTemp.Pos( '.' ) > 0)){ + iErrorStop = 130; + rc = XB_INVALID_DATA; + throw rc; + } + ulFieldValue = strtoul( sTemp.Str(), NULL, 10 ); + } + catch (xbInt16 rc ){ + xbString sMsg; + sMsg.Sprintf( "xbDbf::GetULongField() Exception Caught. Error Stop = [%d] rc = [%d]", iErrorStop, rc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( rc )); + } + return XB_NO_ERROR; +} + +/************************************************************************/ +//! @brief Get long field for field name. +/*! + \param sFieldName Number of field to retrieve. + \param lFieldValue Output field value. + \returns The field routines return one of:
+ XB_NO_ERROR
XB_INVALID_DATA
XB_INVALID_FIELD_NO
XB_INVALID_FIELD_NAME
+ XB_INVALID_FIELD_TYPE
XB_INVALID_DATA +*/ + +xbInt16 xbDbf::GetLongField( const xbString &sFieldName, xbInt32 &lFieldValue ) const { + return GetLongField( GetFieldNo( sFieldName ), lFieldValue ); +} + +/************************************************************************/ +//! @brief Get unsigned long field for field name. +/*! + \param sFieldName Number of field to retrieve. + \param ulFieldValue Output field value. + \returns The field routines return one of:
+ XB_NO_ERROR
XB_INVALID_DATA
XB_INVALID_FIELD_NO
XB_INVALID_FIELD_NAME
+ XB_INVALID_FIELD_TYPE
XB_INVALID_DATA +*/ + +xbInt16 xbDbf::GetULongField( const xbString &sFieldName, xbUInt32 &ulFieldValue ) const { + return GetULongField( GetFieldNo( sFieldName ), ulFieldValue ); +} + +/************************************************************************/ +//! @brief Get raw field data for field number. +/*! + + This is a protected method, used by other methods. This method would be + subject to buffer overflows if made public. + + \param iFieldNo Number of field to retrieve. + \param cBuf Pointer to buffer area provided by calling application program. + \param ulBufSize Size of data to copy + \param iRecBufSw 0 - Record buffer with any updates.
1 - Record buffer with original data. + \returns The field routines return one of:
+ XB_NO_ERROR
XB_INVALID_DATA
XB_INVALID_FIELD_NO
XB_INVALID_FIELD_NAME
+ XB_INVALID_FIELD_TYPE
XB_INVALID_DATA +*/ + + +xbInt16 xbDbf::GetRawField( xbInt16 iFieldNo, char *cBuf, xbUInt32 ulBufSize, xbInt16 iRecBufSw ) const +{ + if( iFieldNo < 0 || iFieldNo >= iNoOfFields ) { + return XB_INVALID_FIELD_NO; + } + + size_t stCopySize; + if( ulBufSize > (size_t) (SchemaPtr[iFieldNo].cFieldLen )) + stCopySize = (size_t) (SchemaPtr[iFieldNo].cFieldLen ); + else + stCopySize = ulBufSize - 1; + + if( iRecBufSw ) + memcpy( cBuf, SchemaPtr[iFieldNo].pAddress2, stCopySize ); + else + memcpy( cBuf, SchemaPtr[iFieldNo].pAddress, stCopySize ); + + cBuf[stCopySize] = 0x00; + return XB_NO_ERROR; +} + + +/************************************************************************/ +//! @brief Put double field for field name. +/*! + \param sFieldName Name of field to update. + \param dFieldValue Field value. + \returns The field routines return one of:
+ XB_NO_ERROR
XB_INVALID_DATA
XB_INVALID_FIELD_NO
XB_INVALID_FIELD_NAME
+ XB_INVALID_FIELD_TYPE
XB_INVALID_DATA +*/ +xbInt16 xbDbf::PutDoubleField( const xbString &sFieldName, xbDouble dFieldValue ){ + return PutDoubleField( GetFieldNo( sFieldName ), dFieldValue ); +} + +/************************************************************************/ +//! @brief Put double field for field number. +/*! + \param iFieldNo Number of field to update. + \param dFieldValue Field value. + \returns The field routines return one of:
+ XB_NO_ERROR
XB_INVALID_DATA
XB_INVALID_FIELD_NO
XB_INVALID_FIELD_NAME
+ XB_INVALID_FIELD_TYPE
XB_INVALID_DATA +*/ + +xbInt16 xbDbf::PutDoubleField( xbInt16 iFieldNo, xbDouble dFieldValue ){ + + xbInt16 rc; + xbString sDoubleFmt; + xbString sDoubleFmt2; + xbString sDoubleVal; + xbInt16 iFieldLen; + xbInt16 iNoOfDecs; + + if(( rc = GetFieldDecimal( iFieldNo, iNoOfDecs )) != XB_NO_ERROR ) + return rc; + + if(( rc = GetFieldLen( iFieldNo, iFieldLen )) != XB_NO_ERROR ) + return rc; + + sDoubleFmt.Sprintf( "%d.%df", iFieldLen, iNoOfDecs ); + sDoubleFmt2 = "%-"; + sDoubleFmt2 += sDoubleFmt; + sDoubleVal.Sprintf( sDoubleFmt2.Str(), dFieldValue ); + sDoubleVal.Rtrim(); + return PutField( iFieldNo, sDoubleVal ); +} + +/************************************************************************/ +//! @brief Put date field for field name. +/*! + \param sFieldName Name of field to update. + \param dtFieldValue Field value. + \returns The field routines return one of:
+ XB_NO_ERROR
XB_INVALID_DATA
XB_INVALID_FIELD_NO
XB_INVALID_FIELD_NAME
+ XB_INVALID_FIELD_TYPE
XB_INVALID_DATA +*/ + +xbInt16 xbDbf::PutDateField(const xbString &sFieldName, const xbDate &dtFieldValue ){ + return PutField( GetFieldNo( sFieldName ), dtFieldValue.Str() ); +} + +/************************************************************************/ +//! @brief Put date field for field number. +/*! + \param iFieldNo Number of field to update. + \param dtFieldValue Field value. + \returns The field routines return one of:
+ XB_NO_ERROR
XB_INVALID_DATA
XB_INVALID_FIELD_NO
XB_INVALID_FIELD_NAME
+ XB_INVALID_FIELD_TYPE
XB_INVALID_DATA +*/ +xbInt16 xbDbf::PutDateField( xbInt16 iFieldNo, const xbDate &dtFieldValue ){ + return PutField( iFieldNo, dtFieldValue.Str() ); +} + +/************************************************************************/ +//! @brief Put field for field name. +/*! + \param sFieldName Name of field to update. + \param sFieldValue Field value. + \returns The field routines return one of:
+ XB_NO_ERROR
XB_INVALID_DATA
XB_INVALID_FIELD_NO
XB_INVALID_FIELD_NAME
+ XB_INVALID_FIELD_TYPE
XB_INVALID_DATA +*/ +xbInt16 xbDbf::PutField(const xbString &sFieldName, const xbString &sFieldValue ) { + return PutField( GetFieldNo( sFieldName ), sFieldValue ); +} +/************************************************************************/ +//! @brief Put field for field number. +/*! + \param iFieldNo Number of field to update. + \param sFieldValue Field value. + \returns The field routines return one of:
+ XB_NO_ERROR
XB_INVALID_DATA
XB_INVALID_FIELD_NO
XB_INVALID_FIELD_NAME
+ XB_INVALID_FIELD_TYPE
XB_INVALID_DATA +*/ + +xbInt16 xbDbf::PutField( xbInt16 iFieldNo, const xbString &sFieldValue ) { + xbInt16 iLen; + xbInt16 iDecPos; /* Decimal Position */ + char * startpos; + char * tp; /* Target Pointer */ + + if( iFieldNo < 0 || iFieldNo >= iNoOfFields ) + return XB_INVALID_FIELD_NO; + + if( SchemaPtr[iFieldNo].cType == 'L' && !sFieldValue.ValidLogicalValue()) + return XB_INVALID_DATA; + + else if( SchemaPtr[iFieldNo].cType == 'D' ){ + xbDate d; + if( !d.DateIsValid( sFieldValue )) + return XB_INVALID_DATA; + iLen = 8; + } + else + { + iLen = (xbInt16) sFieldValue.Len(); + if( SchemaPtr[iFieldNo].cType == 'F' || SchemaPtr[iFieldNo].cType == 'N' || SchemaPtr[iFieldNo].cType == 'M' ){ + if( !sFieldValue.ValidNumericValue()) { + return XB_INVALID_DATA; + } + else { + iDecPos = (xbInt16) sFieldValue.Pos( "." ); // 0 is no decimal + int mlod; // max no of digits left of decimal point digit count + + + if( SchemaPtr[iFieldNo].cNoOfDecs > 0 ) + mlod = SchemaPtr[iFieldNo].cFieldLen - SchemaPtr[iFieldNo].cNoOfDecs - 1; + else + mlod = iLen; + + if( iDecPos == 0 ){ // no decimal in incoming data + + // check digits to the left of the decimal + if( SchemaPtr[iFieldNo].cNoOfDecs > 0 && iLen > mlod ) /* no decimal in incoming data */ + return XB_INVALID_DATA; + + else if( SchemaPtr[iFieldNo].cNoOfDecs == 0 && iLen > SchemaPtr[iFieldNo].cFieldLen ) + return XB_INVALID_DATA; + + } + else // decimal in incoming data + { + if( (iDecPos-1) > mlod ) // too many digits to the left of dec in incoming data + return XB_INVALID_DATA; + + // check digits to the right of the decimal + else if((iLen - iDecPos) > SchemaPtr[iFieldNo].cNoOfDecs ) + return XB_INVALID_DATA; + } + } + } + } + + // do all field edits before this point + if( iDbfStatus != XB_UPDATED ){ + iDbfStatus = XB_UPDATED; + memcpy( RecBuf2, RecBuf, uiRecordLen ); // save the original record bufer before making updates + } + + memset( SchemaPtr[iFieldNo].pAddress, 0x20, SchemaPtr[iFieldNo].cFieldLen ); + + if( iLen > SchemaPtr[iFieldNo].cFieldLen ) + iLen = SchemaPtr[iFieldNo].cFieldLen; + + if( SchemaPtr[iFieldNo].cType == 'F' || SchemaPtr[iFieldNo].cType == 'N' + || SchemaPtr[iFieldNo].cType == 'M') { + + xbInt16 iDecPos = (xbInt16) sFieldValue.Pos( "." ); + if( iDecPos == 0 ){ + iLen = (xbInt16) sFieldValue.Len(); + iDecPos = 0; + } + else{ + iLen = iDecPos - 1; + } + + if( SchemaPtr[iFieldNo].cNoOfDecs > 0 ){ + tp = SchemaPtr[iFieldNo].pAddress; + tp += SchemaPtr[iFieldNo].cFieldLen - SchemaPtr[iFieldNo].cNoOfDecs - 1; + *tp++ = '.'; + + if( iDecPos == 0 ){ + for( xbInt32 i = 0; i < SchemaPtr[iFieldNo].cNoOfDecs; i++ ) + *tp++ = '0'; + } else { + xbInt32 j = iDecPos + 1; + for( xbInt32 i = 0; i < SchemaPtr[iFieldNo].cNoOfDecs; i++, j++ ){ + if( j <= (xbInt32) sFieldValue.Len()) + *tp++ = sFieldValue[j]; + else + *tp++ = '0'; + } + } + startpos= SchemaPtr[iFieldNo].pAddress + + SchemaPtr[iFieldNo].cFieldLen - + SchemaPtr[iFieldNo].cNoOfDecs - iLen - 1; + + } + else + startpos=SchemaPtr[iFieldNo].pAddress+SchemaPtr[iFieldNo].cFieldLen-iLen; + } + else + startpos = SchemaPtr[iFieldNo].pAddress; + + memcpy( startpos, sFieldValue.Str(), (size_t) iLen ); + return 0; +} + +/************************************************************************/ +//! @brief Put float field for field number. +/*! + \param iFieldNo Number of field to update. + \param fFieldValue Field value. + \returns The field routines return one of:
+ XB_NO_ERROR
XB_INVALID_DATA
XB_INVALID_FIELD_NO
XB_INVALID_FIELD_NAME
+ XB_INVALID_FIELD_TYPE
XB_INVALID_DATA +*/ +xbInt16 xbDbf::PutFloatField( xbInt16 iFieldNo, xbFloat fFieldValue ){ + + xbInt16 rc; + xbString sFloatFmt; + xbString sFloatFmt2; + xbString sFloatVal; + xbInt16 iFieldLen; + xbInt16 iNoOfDecs; + + if(( rc = GetFieldDecimal( iFieldNo, iNoOfDecs )) != XB_NO_ERROR ) + return rc; + + if(( rc = GetFieldLen( iFieldNo, iFieldLen )) != XB_NO_ERROR ) + return rc; + + sFloatFmt.Sprintf( "%d.%df", iFieldLen, iNoOfDecs ); + sFloatFmt2 = "%-"; + sFloatFmt2 += sFloatFmt; + sFloatVal.Sprintf( sFloatFmt2.Str(), fFieldValue ); + sFloatVal.Rtrim(); + return PutField( iFieldNo, sFloatVal ); +} + +/************************************************************************/ +//! @brief Put float field for field name. +/*! + \param sFieldName Name of field to update. + \param fFieldValue Field value. + \returns The field routines return one of:
+ XB_NO_ERROR
XB_INVALID_DATA
XB_INVALID_FIELD_NO
XB_INVALID_FIELD_NAME
+ XB_INVALID_FIELD_TYPE
XB_INVALID_DATA +*/ + +xbInt16 xbDbf::PutFloatField( const xbString &sFieldName, xbFloat fFieldValue ){ + return PutFloatField( GetFieldNo( sFieldName ), fFieldValue ); +} + +/************************************************************************/ +//! @brief Put logical field for field number. +/*! + \param iFieldNo Number of field to update. + \param sFieldValue Field value. + \returns The field routines return one of:
+ XB_NO_ERROR
XB_INVALID_DATA
XB_INVALID_FIELD_NO
XB_INVALID_FIELD_NAME
+ XB_INVALID_FIELD_TYPE
XB_INVALID_DATA +*/ + +xbInt16 xbDbf::PutLogicalField( xbInt16 iFieldNo, const xbString &sFieldValue ) { + return PutField( iFieldNo, sFieldValue ); +} + +/************************************************************************/ +//! @brief Put logical field for field name. +/*! + \param sFieldName Name of field to update. + \param sFieldValue Field value. + \returns The field routines return one of:
+ XB_NO_ERROR
XB_INVALID_DATA
XB_INVALID_FIELD_NO
XB_INVALID_FIELD_NAME
+ XB_INVALID_FIELD_TYPE
XB_INVALID_DATA +*/ +xbInt16 xbDbf::PutLogicalField( const xbString &sFieldName, const xbString &sFieldValue ) { + return PutField( GetFieldNo( sFieldName ), sFieldValue ); +} + +/************************************************************************/ +//! @brief Put logical field for field number. +/*! + \param iFieldNo Number of field to update. + \param bFieldValue Field value. + \returns The field routines return one of:
+ XB_NO_ERROR
XB_INVALID_DATA
XB_INVALID_FIELD_NO
XB_INVALID_FIELD_NAME
+ XB_INVALID_FIELD_TYPE
XB_INVALID_DATA +*/ + +xbInt16 xbDbf::PutLogicalField( xbInt16 iFieldNo, xbBool bFieldValue ) { + + if( bFieldValue ) + return PutField( iFieldNo, "T" ); + else + return PutField( iFieldNo, "F" ); +} + +/************************************************************************/ +//! @brief Put logical field for field name. +/*! + \param sFieldName Name of field to update. + \param bFieldValue Field value. + \returns The field routines return one of:
+ XB_NO_ERROR
XB_INVALID_DATA
XB_INVALID_FIELD_NO
XB_INVALID_FIELD_NAME
+ XB_INVALID_FIELD_TYPE
XB_INVALID_DATA +*/ +xbInt16 xbDbf::PutLogicalField( const xbString &sFieldName, xbBool bFieldValue ) { + return PutLogicalField( GetFieldNo( sFieldName ), bFieldValue ); +} + +/************************************************************************/ +//! @brief Put long field for field number. +/*! + \param iFieldNo Number of field to update. + \param lFieldValue Field value. + \returns The field routines return one of:
+ XB_NO_ERROR
XB_INVALID_DATA
XB_INVALID_FIELD_NO
XB_INVALID_FIELD_NAME
+ XB_INVALID_FIELD_TYPE
XB_INVALID_DATA +*/ + +xbInt16 xbDbf::PutLongField( xbInt16 iFieldNo, xbInt32 lFieldValue ) { + xbString sLong; + sLong.Sprintf( "%ld", (xbInt32) lFieldValue ); + return PutField( iFieldNo, sLong.Str() ); +} + +/************************************************************************/ +//! @brief Put long field for field name. +/*! + \param sFieldName Name of field to update. + \param lFieldValue Field value. + \returns The field routines return one of:
+ XB_NO_ERROR
XB_INVALID_DATA
XB_INVALID_FIELD_NO
XB_INVALID_FIELD_NAME
+ XB_INVALID_FIELD_TYPE
XB_INVALID_DATA +*/ + +xbInt16 xbDbf::PutLongField( const xbString &sFieldName, xbInt32 lFieldValue ) { + return PutLongField( GetFieldNo( sFieldName ), lFieldValue ); +} + +/************************************************************************/ +//! @brief Put unsigned long field for field number. +/*! + \param iFieldNo Number of field to update. + \param ulFieldValue Field value. + \returns The field routines return one of:
+ XB_NO_ERROR
XB_INVALID_DATA
XB_INVALID_FIELD_NO
XB_INVALID_FIELD_NAME
+ XB_INVALID_FIELD_TYPE
XB_INVALID_DATA +*/ + +xbInt16 xbDbf::PutULongField( xbInt16 iFieldNo, xbUInt32 ulFieldValue ) { + xbString sLong; + sLong.Sprintf( "%lu", (xbInt32) ulFieldValue ); + return PutField( iFieldNo, sLong.Str() ); +} + +/************************************************************************/ +//! @brief Put unsigned long field for field name. +/*! + \param sFieldName Name of field to update. + \param ulFieldValue Field value. + \returns The field routines return one of:
+ XB_NO_ERROR
XB_INVALID_DATA
XB_INVALID_FIELD_NO
XB_INVALID_FIELD_NAME
+ XB_INVALID_FIELD_TYPE
XB_INVALID_DATA +*/ + +xbInt16 xbDbf::PutULongField( const xbString &sFieldName, xbUInt32 ulFieldValue ) { + return PutLongField( GetFieldNo( sFieldName ), (xbInt32) ulFieldValue ); +} + +/************************************************************************/ +#ifdef XB_MEMO_SUPPORT +//! @brief Check if memo field exists for field name. +/*! + \param sFieldName Name of field to check. + \returns xbTrue Field exists.
xbFale Field does not exist. +*/ + +xbBool xbDbf::MemoFieldExists( const xbString &sFieldName ) const{ + return MemoFieldExists( GetFieldNo( sFieldName )); +} + +/************************************************************************/ +//! @brief Check if memo field exists for field number. +/*! + \param iFieldNo Number of field to check. + \returns xbTrue Field exists.
xbFale Field does not exist. +*/ + +xbBool xbDbf::MemoFieldExists( xbInt16 iFieldNo ) const{ + + xbInt32 lFld = 0L; + GetLongField( iFieldNo, lFld ); + if( lFld == 0L ) + return xbFalse; + else + return xbTrue; +} + +#endif + +} /* namespace */ + diff --git a/src/core/xbfile.cpp b/src/core/xbfile.cpp new file mode 100755 index 0000000..6376e9a --- /dev/null +++ b/src/core/xbfile.cpp @@ -0,0 +1,2217 @@ +/* xbfile.cpp + +XBase64 Software Library + +Copyright (c) 1997,2003,2014,2022,2023 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 + +This module handles all the low level file I/O and is the base class +for the table, memo and index classes + +*/ + +#include "xbase.h" + +namespace xb{ + +/************************************************************************/ +//! @brief Class Constructor. +xbFile::xbFile( xbXBase * x ){ + fp = NULL; + bFileOpen = xbFalse; + ulBlockSize = 0; + iFileNo = 0; + xbase = x; + if( GetMultiUser() == xbTrue ) + iShareMode = XB_MULTI_USER; + else + iShareMode = XB_SINGLE_USER; + iOpenMode = 0; + #ifdef XB_LOCKING_SUPPORT + iLockRetries = -1; + #endif + #ifdef HAVE_SETENDOFFILE_F + fHandle = NULL; + #endif +} + +/************************************************************************/ +//! @brief Class Destructor. +xbFile::~xbFile(){ + if( bFileOpen ) + xbFclose(); +} + +/************************************************************************/ +//! @brief Create a unique file name. +/*! + Given a directory and file extension as inputs, create a unique file name. + + \param sDirIn Directory + \param sExtIn File Extension + \param sFqnOut A fully qualifed unique file name as output + \returns Return Codes +*/ +/* +xbInt16 xbFile::CreateUniqueFileName( const xbString & sDirIn, const xbString & sExtIn, xbString &sFqnOut ){ + return CreateUniqueFileName( sDirIn, sExtIn, sFqnOut, 0 ); +} +*/ +/************************************************************************/ +//! @brief Create a unique file name. +/*! + Given a directory and file extension as inputs, create a unique file name. + + \param sDirIn Directory + \param sExtIn File Extension + \param iOption 0 - look only for one file for a given directory and extension
+ 1 - if file name extension is "dbf" or "DBF", verify unique dbt or DBT (memo) file is also available
+ \param sFqnOut A fully qualifed unique file name as output + \returns Return Codes +*/ + +xbInt16 xbFile::CreateUniqueFileName( const xbString & sDirIn, const xbString & sExtIn, xbString &sFqnOut, xbInt16 iOption ){ + + xbBool bUniqueFileNameFound = xbFalse; + xbFile f( xbase); + xbInt32 l = 1; + xbString sMemoFileName; + + xbString sDir = sDirIn; + char c = GetPathSeparator(); + if( sDirIn.Len() > 0 && sDirIn[sDirIn.Len()] != c ) + sDir += c; + + while( !bUniqueFileNameFound ){ + sFqnOut.Sprintf( "%sxbTmp%03d.%s", sDir.Str(), l, sExtIn.Str()); + if( iOption == 1 && sExtIn == "DBF" ){ + sMemoFileName.Sprintf( "%sxbTmp%03d.DBT", sDirIn.Str(), l ); + } + else if( iOption == 1 && sExtIn == "dbf" ){ + sMemoFileName.Sprintf( "%sxbTmp%03d.dbt", sDirIn.Str(), l ); + } + if( f.FileExists( sFqnOut ) || ( sMemoFileName.Len() > 0 && f.FileExists( sMemoFileName ))){ + l++; + } + else + { + bUniqueFileNameFound = xbTrue; + } + if( l > 999 ) + return XB_FILE_EXISTS; + } + return XB_NO_ERROR; +} + +/*************************************************************************/ +//! @brief Determine which version the memo (dbt) file is. +/*! + + This routine uses the first byte in the dbf file to determine which memo + file version is in use. The main difference between version 3 and 4 is that + version 4 will reuse blocks if they become available. Version 3 does not. + + \param cFileTypeByte is an output field and is one of:
+
+ 0 - none
+ 3 - Dbase III+
+ 4 - Dbase IV
+ + \returns Return Codes +*/ + +xbInt16 xbFile::DetermineXbaseMemoVersion( unsigned char cFileTypeByte ) const { + + + if( BitSet( cFileTypeByte, 3 ) && BitSet( cFileTypeByte, 7 )) + return 4; + else if( BitSet( cFileTypeByte, 7 )) + return 3; + + return 0; +} +/*************************************************************************/ +//! @brief Determine xbase dbf version. +/*! + + This routine is used to determine which version of the Xbase classes can + be used for a given DBF file.
+ + It attempts to use the highest version compiled into the library.
+ + References:
+ This routine uses the first byte from the dbf file.
+ Per DBase documentation:
+ Valid dBASE for Windows table file, bits 0-2 indicate version number: 3 for dBASE Level 5, 4 for dBASE Level 7.
+ Bit 3 and bit 7 indicate presence of a dBASE IV or dBASE for Windows memo file;
+ Bits 4-6 indicate the presence of a dBASE IV SQL table;
+ Bit 7 indicates the presence of any .DBT memo file (either a dBASE III PLUS type or a dBASE IV or dBASE for Windows memo file).
+
+ Bachmann spec (used extensively in library build), page 7 - does not match DBase documentation
+
+ returns
+ 0 - unknown
+ 3 - Dbase level 3
+ 4 - Dbase level 4
+ 5 - Dbase Level 5 (future)
+ 7 - Dbase Level 7 (future)
+
+ 1x - Clipper files (future)
+ 2x - Foxbase files (future)
+
+*/ + +xbInt16 xbFile::DetermineXbaseTableVersion( unsigned char cFileTypeByte ) const { + + xbInt16 iMemoVersion = DetermineXbaseMemoVersion(cFileTypeByte); + char cDbfLevel = cFileTypeByte & 0x07; + + #ifdef XB_DBF4_SUPPORT + if( cDbfLevel == 3 && iMemoVersion != 3 ) + return 4; + #endif + + #ifdef XB_DBF3_SUPPORT + if( cDbfLevel == 3 && iMemoVersion != 4 ) + return 3; + #endif + + return 0; +} + +/*************************************************************************/ +//! @brief Get a portable double value. +/*! + + This routine returns a double value from an 8 byte character stream, + accounting for endian type. + + Converts a double (64 bit floating point) value stored at p from a portable + format to the machine format. + + \param p pointer to memory containing the portable double value + + \returns the double value. +*/ + +xbDouble xbFile::eGetDouble( const char *p ) const { + xbDouble d; + const char *sp; + char *tp; + xbInt16 i; + + tp = (char *) &d; + sp = p; + if( iEndianType == 'L' ) + for( i = 0; i < 8; i++ ) *tp++ = *sp++; + else + { + sp+=7; + for( i = 0; i < 8; i++ ) *tp++ = *sp--; + } + + return d; +} + +/*************************************************************************/ +//! @brief Get a portable long value. + +/*! + This routine returns a long int value from a 4 byte character stream, + accounting for endian type. + + \param p pointer to memory containing the portable long value + + \returns the long value. +*/ + +xbInt32 xbFile::eGetInt32( const char *p ) const { + xbInt32 l; + char *tp; + xbInt16 i; + + tp = (char *) &l; + if( iEndianType == 'L' ) + for( i = 0; i < 4; i++ ) *tp++ = *p++; + else { + p+=3; + for( i = 0; i < 4; i++ ) *tp++ = *p--; + } + return l; + +} +/*************************************************************************/ +//! @brief Get a portable unsigned long value. + +/*! + This routine returns an unsigned long int value from a 4 byte character stream, + accounting for endian type. + + \param p pointer to memory containing the portable long value + + \returns the unsigned long value. +*/ + +xbUInt32 xbFile::eGetUInt32( const char *p ) const { + xbUInt32 ul; + xbInt16 i; + char *tp; + + tp = (char *) &ul; + if( iEndianType == 'L' ) + for( i = 0; i < 4; i++ ) *tp++ = *p++; + else{ + p+=3; + for( i = 0; i < 4; i++ ) *tp++ = *p--; + } + return ul; +} + +/*************************************************************************/ +//! @brief Get a portable short value. + +/*! + This routine returns a short int value from a 2 byte character stream, + accounting for endian type. + + \param p pointer to memory containing the portable long value + + \returns the short value. +*/ + +xbInt16 xbFile::eGetInt16(const char *p) const { + xbInt16 s, i; + char *tp; + tp = (char *) &s; + if( iEndianType == 'L' ) + for( i = 0; i < 2; i++ ) *tp++ = *p++; + else{ + p++; + for( i = 0; i < 2; i++ ) *tp++ = *p--; + } + return s; +} +/*************************************************************************/ +//! @brief Get a portable unsigned short value. + +/*! + This routine returns a short unsigned int value from a 2 byte character stream, + accounting for endian type. + + \param p pointer to memory containing the portable long value + + \returns the short value. +*/ + +xbUInt16 xbFile::eGetUInt16(const char *p) const { + xbInt16 i; + xbUInt16 uI; + char *tp; + + tp = (char *) &uI; + if( iEndianType == 'L' ) + for( i = 0; i < 2; i++ ) *tp++ = *p++; + else{ + p++; + for( i = 0; i < 2; i++ ) *tp++ = *p--; + } + return uI; +} + + +/*************************************************************************/ +//! @brief Write a portable double value to memory location. +/*! + Converts a double (64 bit floating point) value from machine format to a + portable format and stores the converted value in the memory referenced + by c. + + This routine puts a double value to an 8 byte character stream + + \param c Pointer to memory to hold converted value + \param d Input value to be converted +*/ + +void xbFile::ePutDouble( char *c, xbDouble d ){ + const char *sp; + char *tp; + xbInt16 i; + + tp = c; + sp = (const char *) &d; + if( iEndianType == 'L' ) + for( i = 0; i < 8; i++ ) *tp++ = *sp++; + else + { + sp+=7; + for( i = 0; i < 8; i++ ) *tp++ = *sp--; + } + return; +} + +/*************************************************************************/ +//! @brief Write a portable short value to memory location. +/*! + Converts a short (16 bit integer) value from machine format to a + portable format and stores the converted value in the memory referenced + by c. + + This routine puts a short value to a 2 byte character stream + + \param c Pointer to memory to hold converted value + \param s Input value to be converted +*/ + +void xbFile::ePutInt16( char * c, xbInt16 s ){ + const char *sp; + char *tp; + xbInt16 i; + + tp = c; + sp = (const char *) &s; + + if( iEndianType == 'L' ) + for( i = 0; i < 2; i++ ) *tp++ = *sp++; + else{ /* big endian */ + sp++; + for( i = 0; i < 2; i++ ) *tp++ = *sp--; + } + return; +} +/*************************************************************************/ +//! @brief Write a portable unsigned short value to memory location. +/*! + Converts an unsigned short (16 bit integer) value from machine format to a + portable format and stores the converted value in the memory referenced + by c. + + This routine puts an unsigned short value to a 2 byte character stream + + \param c Pointer to memory to hold converted value + \param s Input value to be converted +*/ + +void xbFile::ePutUInt16( char * c, xbUInt16 s ){ + const char *sp; + char *tp; + xbInt16 i; + + tp = c; + sp = (const char *) &s; + + if( iEndianType == 'L' ) + for( i = 0; i < 2; i++ ) *tp++ = *sp++; + else{ /* big endian */ + sp++; + for( i = 0; i < 2; i++ ) *tp++ = *sp--; + } + return; +} + +/*************************************************************************/ +//! @brief Write a portable long value to memory location. +/*! + Converts a long (32 bit integer) value from machine format to a + portable format and stores the converted value in the memory referenced + by c. + + This routine puts a long value to a 4 byte character stream. + + \param c Pointer to memory to hold converted value + \param l Input value to be converted +*/ + +void xbFile::ePutInt32( char * c, xbInt32 l ) +{ + const char *sp; + char *tp; + xbInt16 i; + + tp = c; + sp = (const char *) &l; + if( iEndianType == 'L' ) + for( i = 0; i < 4; i++ ) *tp++ = *sp++; + else { + sp+=3; + for( i = 0; i < 4; i++ ) *tp++ = *sp--; + } + return; +} + + +/*************************************************************************/ +//! @brief Write a portable unsigned long value to memory location. +/*! + Converts an unsigned long (32 bit integer) value from machine format to a + portable format and stores the converted value in the memory referenced + by c. + + This routine puts an unsigned long value to a 4 byte character stream. + + \param c Pointer to memory to hold converted value + \param ul Input value to be converted +*/ + +void xbFile::ePutUInt32( char * c, xbUInt32 ul ) +{ + const char *sp; + char *tp; + xbInt16 i; + + tp = c; + sp = (const char *) &ul; + if( iEndianType == 'L' ) + for( i = 0; i < 4; i++ ) *tp++ = *sp++; + else + { + sp+=3; + for( i = 0; i < 4; i++ ) *tp++ = *sp--; + } + return; +} + +/************************************************************************/ +//! @brief Determines if a file exists. +/*! + \returns xbTrue if file exists
+ xbFalse if file does not exist + +*/ +xbBool xbFile::FileExists() const { + return FileExists( sFqFileName, 0 ); +} +/************************************************************************/ +//! @brief Determines if a file exists. +/*! + \param iOption if 1, assume this is a request for a dbf file and + check for the a dbt memo file also, returns true if both files are found + + \returns xbTrue if both files exist
+ xbFalse if file does not exist + +*/ +xbBool xbFile::FileExists( xbInt16 iOption ) const { + return FileExists( sFqFileName, iOption ); +} + +/************************************************************************/ +//! @brief Determines if a file exists. +/*! + \param sFileName - file name to check for + + \returns xbTrue if file exists
+ xbFalse if file does not exist +*/ + +xbBool xbFile::FileExists(const xbString &sFileName ) const { + return FileExists( sFileName, 0 ); +} + +/************************************************************************/ +//! @brief Determines if a file exists. +/*! + \param sFileName - file name to check for + \param iOption if 1, assume this is a request for a dbf file and + check for the a dbt memo file also, returns true if both files are found + + \returns xbTrue if both dbf and dbt files exist
+ xbFalse if file does not exist +*/ + +xbBool xbFile::FileExists( const xbString & sFileName, xbInt16 iOption ) const { + + struct stat buffer; + if(( stat( sFileName.Str(), &buffer ) != 0 )){ + return xbFalse; + } + + #ifdef XB_MEMO_SUPPORT + if( iOption == 1 ){ + xbString sFileName2 = sFileName; + + if( sFileName2[sFileName2.Len()] == 'F' ) + sFileName2.PutAt( sFileName2.Len(), 'T' ); + else + sFileName2.PutAt( sFileName2.Len(), 't' ); + + if(( stat( sFileName2.Str(), &buffer) != 0 )) + return xbFalse; + } + #endif + return xbTrue; +} + +/************************************************************************/ +//! @brief Determines if file is open. +/*! + + \returns xbTrue if file is open
+ xbFalse if file is not open +*/ + +xbBool xbFile::FileIsOpen() const { + return bFileOpen; +} +/************************************************************************/ +//! @brief Get the block size. +/*! + \returns Block Size +*/ + +xbUInt32 xbFile::GetBlockSize() const { + return ulBlockSize; +} + +/************************************************************************/ +//! @brief Get the directory name. +/*! + \returns the directory name of the file +*/ + +const xbString & xbFile::GetDirectory() const { + return sDirectory; +} + +/************************************************************************/ +//! @brief Get the directory part of the file name. +/*! + \param sFileDirPartOut - the returned directory name + \returns Return Codes +*/ + +xbInt16 xbFile::GetFileDirPart( xbString & sFileDirPartOut ) const { + return GetFileDirPart( sFqFileName, sFileDirPartOut ); +} + +/************************************************************************/ +//! @brief Get the directory part of the file name. +/*! + \param sCompleteFileNameIn - a fully qualfied input file name + \param sFileDirPartOut - the returned directory name part out of sCompleteFileNameIn + \returns Return Codes +*/ + + +xbInt16 xbFile::GetFileDirPart( const xbString & sCompleteFileNameIn, xbString & sFileDirPartOut ) const { + + sFileDirPartOut = sCompleteFileNameIn; + sFileDirPartOut.SwapChars( '\\', '/' ); + xbUInt32 iPos = sFileDirPartOut.GetLastPos( '/' ); + + if( iPos > 0 ){ + xbString sTemp = sFileDirPartOut; + sFileDirPartOut.Assign( sTemp, 1, iPos ); + return XB_NO_ERROR; + } + return XB_INVALID_DATA; +} + +/************************************************************************/ +//! @brief Get the extension part of the file name. +/*! + \param sFileNameExtOut - the returned extension part out of sCompleteFileNameIn + \returns Return Codes +*/ +xbInt16 xbFile::GetFileExtPart( xbString & sFileNameExtOut ) const { + return GetFileExtPart( sFqFileName, sFileNameExtOut ); +} + +/************************************************************************/ +//! @brief Get the extension part of the file name. +/*! + \param sCompleteFileNameIn - a fully qualfied input file name + + \param sFileExtPartOut - the returned directory name part out of sCompleteFileNameIn + \returns Return Codes +*/ + + +xbInt16 xbFile::GetFileExtPart( const xbString & sCompleteFileNameIn, xbString & sFileExtPartOut ) const { + + + sFileExtPartOut = sCompleteFileNameIn; + xbUInt32 iPos = sFileExtPartOut.GetLastPos( '.' ); + if( iPos > 0 ){ /* get rid of the directory part of the name */ + sFileExtPartOut.Ltrunc( iPos ); + return XB_NO_ERROR; + } + return XB_INVALID_DATA; +} +/************************************************************************/ +//! @brief Get the time of last file modification timestamp as reported by the OS. +/*! + \param mtime - returned time of last file modification + \returns Return Codes +*/ + +xbInt16 xbFile::GetFileMtime( time_t &mtime ){ + + struct stat buffer; + if( stat( sFqFileName.Str(), &buffer )) + return XB_FILE_NOT_FOUND; + else{ + mtime = buffer.st_mtime; + return XB_NO_ERROR; + } +} +/************************************************************************/ +//! @brief Get the file name. +/*! + \returns the file name portion of the file +*/ + +const xbString & xbFile::GetFileName() const { + return sFileName; +} + + +/************************************************************************/ +//! @brief Get the name part of the file name. +/*! + \param sFileNamePartOut - the returned file name part out of sCompleteFileNameIn + \returns Return Codes +*/ + + +xbInt16 xbFile::GetFileNamePart( xbString & sFileNamePartOut ) const { + return GetFileNamePart( sFqFileName, sFileNamePartOut ); +} + +/************************************************************************/ +//! @brief Get the name part of the file name. +/*! + \param sCompleteFileNameIn - a fully qualified input file name + \param sFileNamePartOut - the returned file name part out of sCompleteFileNameIn + \returns Return Codes +*/ + +//*********** fixme should this be static????? + +xbInt16 xbFile::GetFileNamePart( const xbString & sCompleteFileNameIn, xbString & sFileNamePartOut ) const { + /* extract the file name part out of the string */ + + sFileNamePartOut = sCompleteFileNameIn; + sFileNamePartOut.SwapChars( '\\', '/' ); + xbUInt32 iPos = sFileNamePartOut.GetLastPos( '/' ); + if( iPos > 0 ) /* get rid of the directory part of the name */ + sFileNamePartOut.Ltrunc( iPos ); + iPos = sFileNamePartOut.Pos( '.' ); + if( iPos > 0 ){ /* get rid of the extension part of the name */ + xbString sTemp = sFileNamePartOut; + sFileNamePartOut.Assign( sTemp, 1, iPos-1 ); + } + return XB_NO_ERROR; +} + + +/************************************************************************/ +//! @brief Get the size of the file as reported by the OS. +/*! + \param ullFileSize - unsigned long long field as output + \returns Return Codes +*/ + +xbInt16 xbFile::GetFileSize( xbUInt64 &ullFileSize ){ + + xbInt16 iRc = 0; + xbInt16 iErrorStop = 0; + + try{ + + if(( iRc = xbFseek( 0, SEEK_END )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + ullFileSize = xbFtell(); + } + + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbFile::GetFileSize() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return iRc; +} + +/************************************************************************/ +//! @brief Get the file type aka Capitalized file extension +/*! + \param sFileTypeOut - the returned extension part out of sCompleteFileNameIn + \returns Return Codes +*/ +xbInt16 xbFile::GetFileType( xbString & sFileTypeOut ) const { + + xbInt16 iRc = GetFileExtPart( sFqFileName, sFileTypeOut ); + sFileTypeOut.ToUpperCase(); + return iRc; +} +/************************************************************************/ +//! @brief Get the fully qualified file name. +/*! + \returns the fully qualfied name of the file +*/ + +const xbString & xbFile::GetFqFileName() const { + return sFqFileName; +} + +/************************************************************************/ +//! @brief Get the open mode of the file. +/*! + \returns XB_READ
+ XB_READ_WRITE
+ XB_WRITE
+*/ + +xbInt16 xbFile::GetOpenMode() const { + return iOpenMode; +} + +/************************************************************************/ +//! @brief Get the share mode of the file. +/*! + \returns XB_SINGLE_USER - (file buffering on>
+ XB_MULTI_USER - (file buffering off)
+*/ + +xbInt16 xbFile::GetShareMode() const { + return iShareMode; +} + +/************************************************************************/ +//! @brief Get the file type byte and version of the dbf file. +/*! + + Pull the first bye off the DBF file for further inspection. + First byte has various bits set to determine what the file format is. + + \param sFileName - Name of file to examine + \param iVersion - Returned file version + \returns Return Codes +*/ + +xbInt16 xbFile::GetXbaseFileTypeByte( const xbString &sFileName, xbInt16 &iVersion ) +{ + unsigned char cFileTypeByte; + return GetXbaseFileTypeByte( sFileName, cFileTypeByte, iVersion ); +} + +/************************************************************************/ +//! @brief Get the file type byte and version of the dbf file. +/*! + Pull the first bye off the DBF file for further inspection. + First byte has various bits set to determine what the file format is. + + \param sFileName - Name of file to examine + \param cFileTypeByte - Retruned first byte of dbf file + \returns Return Codes +*/ + + +xbInt16 xbFile::GetXbaseFileTypeByte( const xbString &sFileName, unsigned char &cFileTypeByte ) +{ + xbInt16 iVersion; + return GetXbaseFileTypeByte( sFileName, cFileTypeByte, iVersion ); +} + +/************************************************************************/ +//! @brief Get the file type byte and version of the dbf file. +/*! + Pull the first bye off the DBF file for further inspection. + First byte has various bits set to determine what the file format is. + + + \param sFileName - Name of file to examine + \param cFileTypeByte - Returned first byte of dbf file + \param iVersion - Returned file version + \returns Return Codes +*/ + +xbInt16 xbFile::GetXbaseFileTypeByte( const xbString &sFileName, unsigned char &cFileTypeByte, xbInt16 &iVersion ){ + xbInt16 iErrorStop = 0; + xbInt16 iRc = XB_NO_ERROR; + size_t stRc; + FILE *tfp; + + try{ + + iVersion = 0; + cFileTypeByte = 0x00; + #ifdef HAVE__FSOPEN_F + // 0x40 is SH_DENYNO or _SH_DENYNO + if(( tfp = _fsopen( sFileName.Str(), "r", 0x40 )) == NULL ){ + iErrorStop = 100; + iRc = XB_OPEN_ERROR; + throw iRc; + } + #else + if(( tfp = fopen( sFileName.Str(), "r" )) == NULL ){ + iErrorStop = 110; + iRc = XB_OPEN_ERROR; + throw iRc; + } + #endif + + #ifdef HAVE_FSEEKO_F + iRc = fseeko( tfp, 0, SEEK_SET ); + #else + iRc = fseek( tfp, 0, SEEK_SET ); + #endif + + if( iRc != 0 ){ + iErrorStop = 120; + iRc = XB_SEEK_ERROR; + throw iRc; + } + stRc = fread( &cFileTypeByte, (size_t) 1, (size_t) 1, tfp ); + if( stRc != (size_t) 1 ){ + iErrorStop = 130; + iRc = XB_READ_ERROR; + throw iRc; + } + iRc = XB_NO_ERROR; + fclose( tfp ); + iVersion = DetermineXbaseTableVersion( cFileTypeByte ); + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbFile::GetXbaseFileType() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return iRc; +} + +/************************************************************************/ +//! @brief Determines status of file extension. +/*! + + \param sFileName - Name of file to examine + \param iOption - Inspection type
+ 1 check for DBF
+ 2 check for NDX
+ 3 check for MDX
+ 4 check for NTX
+ + \returns 0 if suffix found
+ 1 if suffix not found, lower case
+ 2 is suffix not found, upper case
+ +*/ + +xbInt16 xbFile::NameSuffixMissing( const xbString & sFileName, xbInt16 iOption ) const { + + xbUInt32 ulLen = sFileName.Len(); + if( ulLen <= 4 ){ + if( sFileName[ulLen] >= 'A' && sFileName[ulLen] <= 'Z' ) + return 2; + else + return 1; + } + if( iOption == 1 && sFileName[ulLen-3] == '.' && + ( sFileName[ulLen-2] == 'd' || sFileName[ulLen-2] == 'D' ) && + ( sFileName[ulLen-1] == 'b' || sFileName[ulLen-1] == 'B' ) && + ( sFileName[ulLen] == 'f' || sFileName[ulLen] == 'F' ) + ) + return 0; + if( iOption == 2 && sFileName[ulLen-3] == '.' && + ( sFileName[ulLen-2] == 'n' || sFileName[ulLen-2] == 'N' ) && + ( sFileName[ulLen-1] == 'd' || sFileName[ulLen-1] == 'D' ) && + ( sFileName[ulLen] == 'x' || sFileName[ulLen] == 'X' ) + ) + return 0; + if( iOption == 3 && sFileName[ulLen-3] == '.' && + ( sFileName[ulLen-2] == 'm' || sFileName[ulLen-2] == 'M' ) && + ( sFileName[ulLen-1] == 'd' || sFileName[ulLen-1] == 'D' ) && + ( sFileName[ulLen] == 'x' || sFileName[ulLen] == 'X' ) + ) + return 0; + if( iOption == 4 && sFileName[ulLen-3] == '.' && + ( sFileName[ulLen-2] == 'n' || sFileName[ulLen-2] == 'N' ) && + ( sFileName[ulLen-1] == 't' || sFileName[ulLen-1] == 'T' ) && + ( sFileName[ulLen] == 'x' || sFileName[ulLen] == 'X' ) + ) + return 0; + // next line might be problematic if file naem has mixed case and extension is missing + if( sFileName[ulLen-4] >= 'A' && sFileName[ulLen-4] <= 'Z' ) + return 2; + else + return 1; +} + +/***********************************************************************/ +//! @brief Read a block of data from file. +/*! + + \param ulBlockNo - block number to read + \param lReadSize - size of data to read at block location, set to 0 to read blocksize + \param *buf - pointer to buffer to write output data, assumed to be previosuly allocated + and large enough to contain data + \returns Return Codes + +*/ + +xbInt16 xbFile::ReadBlock( xbUInt32 ulBlockNo, size_t lReadSize, void * buf ){ + return ReadBlock( ulBlockNo, ulBlockSize, lReadSize, buf ); +} + +/***********************************************************************/ +//! @brief Read a block of data from file. +/*! + + \param ulBlockNo - block number to read + \param ulBlockSize - block size + \param lReadSize - size of data to read at block location, set to 0 to read blocksize + \param buf - pointer to buffer to write output data, assumed to be previosuly allocated + and large enough to contain data + \returns Return Codes + +*/ +xbInt16 xbFile::ReadBlock( xbUInt32 ulBlockNo, xbUInt32 ulBlockSize, size_t lReadSize, void * buf ){ + + xbInt16 iErrorStop = 0; + xbInt16 iRc = XB_NO_ERROR; + + try{ + if( ulBlockSize <= 0 ){ + iErrorStop = 100; + iRc = XB_INVALID_BLOCK_SIZE; + throw iRc; + } + + if(( iRc = xbFseek(((xbInt64) ulBlockNo*ulBlockSize ), SEEK_SET )) != XB_NO_ERROR ){ + iErrorStop = 110; + iRc = XB_SEEK_ERROR; + throw iRc; + } + + if( lReadSize <= 0 ) + lReadSize = ulBlockSize; + + if(( iRc = xbFread( buf, lReadSize, 1 )) != XB_NO_ERROR ){ + iErrorStop = 120; + iRc = XB_READ_ERROR; + throw iRc; + } + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbFile::ReadBlock() Exception Caught. Error Stop = [%d] iRc = [%d] BlkNo=[%ld] BlkSize=[%ld] ReadSize=[%ld]", iErrorStop, iRc, ulBlockNo, ulBlockSize, lReadSize ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return iRc; +} +/************************************************************************/ +//! @brief Set the block size. +/*! + + \param ulBlockSize - unsigned long block size, divisible by 512 + \returns Return Codes +*/ + +xbInt16 xbFile::SetBlockSize( xbUInt32 ulBlockSize ){ + if( ulBlockSize %512 != 0 ) + return XB_INVALID_BLOCK_SIZE; + + this->ulBlockSize = ulBlockSize; + return XB_NO_ERROR; +} +/************************************************************************/ +//! @brief Set the directory. +/*! + \param sDirectory - Valid directory name +*/ + +void xbFile::SetDirectory( const xbString & sDirectory ){ + + this->sDirectory = sDirectory; + char cLastChar = sDirectory[sDirectory.Len()]; + if( cLastChar != '/' && cLastChar != '\\' ) + sFqFileName.Sprintf( "%s/%s", sDirectory.Str(), sFileName.Str()); + else + sFqFileName.Sprintf( "%s%s", sDirectory.Str(), sFileName.Str()); + + #ifdef WIN32 + sFqFileName.SwapChars( '/', '\\' ); + #else + sFqFileName.SwapChars( '\\', '/' ); + #endif +} + +/************************************************************************/ +//! @brief Set the filename. +/*! + This routine builds out two internal variables from the input file name
+ sFileName - the file name part
+ sFqFileName - the fully qualified file name
+ + + \param sFileName - Input file name +*/ +void xbFile::SetFileName( const xbString & sFileName ){ + + if( sFileName == "" ){ + sFqFileName = ""; + return; + } + + char cPathSep = sFileName.GetPathSeparator(); + + if( cPathSep ){ + + xbString sName; + xbString sExt; + // GetFileDirPart( this->sDirectory ); + GetFileNamePart( sFileName, sName ); + GetFileExtPart( sFileName, sExt ); + this->sFileName.Sprintf( "%s.%s", sName.Str(), sExt.Str()); + sFqFileName = sFileName; + + } else { + + this->sFileName = sFileName; + if( sDirectory.Len() == 0 ){ + sDirectory = GetDataDirectory(); + char cLastChar = sDirectory[sDirectory.Len()]; + if( cLastChar != '/' && cLastChar != '\\' ) + sFqFileName.Sprintf( "%s/%s", sDirectory.Str(), sFileName.Str() ); + else + sFqFileName = sDirectory + sFileName; + } + else{ + char cLastChar = sDirectory[sDirectory.Len()]; + if( cLastChar != '/' && cLastChar != '\\' ) + sFqFileName.Sprintf( "%s/%s", sDirectory.Str(), sFileName.Str() ); + else + sFqFileName = sDirectory + sFileName; + } + } + + #ifdef WIN32 + sFqFileName.SwapChars( '/', '\\' ); + #else + sFqFileName.SwapChars( '\\', '/' ); + #endif +} + +/************************************************************************/ +//! @brief Set the fully qualifed filename. +/*! + \param sFqFileName - Fully qualifed input file name +*/ + +void xbFile::SetFqFileName( const xbString & sFqFileName ){ + this->sFqFileName = sFqFileName; + + xbString sDir; + xbString sName; + xbString sExt; + + GetFileDirPart ( sFqFileName, sDir ); + GetFileNamePart( sFqFileName, sName ); + GetFileExtPart ( sFqFileName, sExt ); + + sDirectory = sDir; + sFileName.Sprintf( "%s.%s", sName.Str(), sExt.Str() ); + + #ifdef WIN32 + this->sDirectory.SwapChars ( '/', '\\' ); + this->sFqFileName.SwapChars( '/', '\\' ); + #else + this->sDirectory.SwapChars ( '\\', '/' ); + this->sFqFileName.SwapChars( '\\', '/' ); + #endif +} + +/************************************************************************/ +//! @brief Set Home Folders. +/*! + Create xbase64 log, data and temp folders in the home directory for current usre. + + \returns Return Codes +*/ + +xbInt16 xbFile::SetHomeFolders(){ + + xbInt16 iErrorStop = 0; + xbInt16 iRc = XB_NO_ERROR; + xbString sHomeDir; + char cPathSeparator; + xbString sDflt; + + try{ + + GetHomeDir( sHomeDir ); + //std::cout << "CreateHomeFolders() home dir = [" << sHomeDir.Str() << "]\n"; + + if( FileExists( sHomeDir ) == xbFalse ){ + iErrorStop = 100; + iRc = XB_DIRECTORY_ERROR; + throw iRc; + } + + #ifdef WIN32 + cPathSeparator = '\\'; + #else + cPathSeparator = '/'; + #endif + sDflt.Sprintf( ".%c", cPathSeparator ); + // set the default folders just in case later steps fail + xbase->SetDataDirectory( sDflt ); + #ifdef XB_LOGGING_SUPPORT + xbase->SetLogDirectory( sDflt ); + #endif + + if( sHomeDir[sHomeDir.Len()] != cPathSeparator ) + sHomeDir += cPathSeparator; + + xbString sWork( sHomeDir ); + sWork += "xbase64"; + + if( FileExists( sWork ) == xbFalse ){ + #ifdef WIN32 + if( CreateDirectory( sWork.Str(), NULL ) == 0 ){ + iErrorStop = 130; + iRc = XB_DIRECTORY_ERROR; + throw iRc; + } + #else + // 0777 mode is correct, the mode will be modified by the user's umask + if( mkdir( sWork.Str(), 0777 ) == -1 ){ + iErrorStop = 140; + iRc = XB_DIRECTORY_ERROR; + throw iRc; + } + #endif + } + + #ifdef XB_LOGGING_SUPPORT + sWork.Sprintf( "%sxbase64%clogs", sHomeDir.Str(), cPathSeparator ); + // std::cout << "logdir = " << sWork.Str() << "\n"; + + if( FileExists( sWork ) == xbFalse ){ + #ifdef WIN32 + if( CreateDirectory( sWork.Str(), NULL ) == 0 ){ + iErrorStop = 110; + iRc = XB_DIRECTORY_ERROR; + throw iRc; + } + #else + if( mkdir( sWork.Str(), 0777 ) == -1 ){ + iErrorStop = 120; + iRc = XB_DIRECTORY_ERROR; + throw iRc; + } + #endif + } + xbase->SetLogDirectory( sWork ); + #endif // XB_LOGGING_SUPPORT + + sWork.Sprintf( "%sxbase64%cdata", sHomeDir.Str(), cPathSeparator ); + // std::cout << "datadir = " << sWork.Str() << "\n"; + if( FileExists( sWork ) == xbFalse ){ + #ifdef WIN32 + if( CreateDirectory( sWork.Str(), NULL ) == 0 ){ + iErrorStop = 130; + iRc = XB_DIRECTORY_ERROR; + throw iRc; + } + #else + if( mkdir( sWork.Str(), 0777 ) == -1 ){ + iErrorStop = 140; + iRc = XB_DIRECTORY_ERROR; + throw iRc; + } + #endif + } + + sWork.Sprintf( "%sxbase64%ctemp%c", sHomeDir.Str(), cPathSeparator, cPathSeparator ); + // std::cout << "tempdir = " << sWork.Str() << "\n"; + if( FileExists( sWork ) == xbFalse ){ + #ifdef WIN32 + if( CreateDirectory( sWork.Str(), NULL ) == 0 ){ + iErrorStop = 150; + iRc = XB_DIRECTORY_ERROR; + throw iRc; + } + #else + if( mkdir( sWork.Str(), 0777 ) == -1 ){ + iErrorStop = 160; + iRc = XB_DIRECTORY_ERROR; + throw iRc; + } + #endif + } + xbase->SetTempDirectory( sWork ); + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbFile::CreateHomeFolders() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return iRc; +} + +/************************************************************************/ +//! @brief Write a block of data to file. +/*! + + \param ulBlockNo - block number to write + \param lWriteSize - size of data to write, set to 0 to write blocksize + \param *buf - pointer to buffer of data to be written + \returns Return Codes + +*/ +xbInt16 xbFile::WriteBlock( xbUInt32 ulBlockNo, size_t lWriteSize, void * buf ){ + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + + try{ + + if( ulBlockSize == 0 ){ + iErrorStop = 100; + iRc = XB_INVALID_BLOCK_SIZE; + throw iRc; + } + if( lWriteSize <= 0 ) + lWriteSize = ulBlockSize; + if(( iRc = xbFseek(( (xbInt64) ulBlockNo*ulBlockSize), SEEK_SET )) != XB_NO_ERROR ){ + iErrorStop = 110; + throw iRc; + } + if(( iRc = xbFwrite( buf, lWriteSize, 1 )) != XB_NO_ERROR ){ + iErrorStop = 120; + throw iRc; + } + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbFile::WriteBlock() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return iRc; +} + +/************************************************************************/ +//! @brief Xbase wrapper for standard libary fclose. +/*! + \returns Return Codes +*/ + +xbInt16 xbFile::xbFclose(){ + + int iRc = 0; + if( bFileOpen ){ + iRc = fclose( fp ); + if( iRc != 0 ){ + return XB_CLOSE_ERROR; + } + else{ + bFileOpen = xbFalse; + } + iFileNo = 0; + } + return XB_NO_ERROR; +} + + + +/************************************************************************/ +//! @brief Xbase wrapper for standard libary feof. +/*! + \returns non zero if end-of-file is set for the stream. +*/ +xbInt16 xbFile::xbFeof(){ + return feof( fp ); +} + +/************************************************************************/ +//! @brief Xbase wrapper for standard libary fflush. +/*! + \returns Return Codes +*/ + +xbInt16 xbFile::xbFflush() { + + if( fflush( fp ) ) + return XB_WRITE_ERROR; + else + return XB_NO_ERROR; +} + +/************************************************************************/ +//! @brief Xbase wrapper for standard libary fgetc. +/*! + \param c - output integer returned by fgetc + \returns Return Codes +*/ + +xbInt16 xbFile::xbFgetc( xbInt32 &c ) { + + int i; + + i = fgetc( fp ); + if( i == EOF ) + return XB_EOF; + + c = i; + return XB_NO_ERROR; +} + +/************************************************************************/ +//! @brief Xbase wrapper for standard libary fgetc. +/*! + \param c - output character returned by fgetc + \returns Return Codes +*/ + +xbInt16 xbFile::xbFgetc( char &c ) { + + int i; + i = fgetc( fp ); + if( i == EOF ) + return XB_EOF; + + c = (char) i; + return XB_NO_ERROR; +} +/************************************************************************/ +//! @brief Xbase wrapper for standard libary fgets. +/*! + \param lSize - reads in at most, one character less than lSize + \param s - an xbString containing data returned by fseek + \returns Return Codes +*/ + +xbInt16 xbFile::xbFgets( size_t lSize, xbString &s ) { + + s = ""; + if( feof( fp )) + return XB_EOF; + + char *sBuf = (char *) malloc( lSize + 1 ); + + if( fgets( sBuf, (xbInt32) lSize, fp ) == NULL ){ + free( sBuf ); + return XB_EOF; + } + s.Set( sBuf ); + free( sBuf ); + return XB_NO_ERROR; +} +/************************************************************************/ +//! @brief Xbase wrapper for standard libary fopen. +/*! + + This routine supports all the standard C library open modes. The Xbase routines only + use "r" and "r+b". + + \param sOpenMode + +
OpenModeDescription +
rReading +
r+Reading and Writing +
wOpen for writing. Truncate to zero bytes if it exists +
w+Open for reading and writing, truncate to zero bytes if it exists +
aOpen for append +
a+Open for reading and writing (at end). +
+ The mode can also include the letter "b" for binary ie; "r+b". The "b" is ignored on + POSIX compliant systems, but is included for cross platform compatibility. + \param sFileName File name to open + \param iShareMode + XB_SINGLE_USER
+ XB_MULTI_USER
+ \returns Return Codes +*/ + +xbInt16 xbFile::xbFopen( const xbString &sOpenMode, const xbString &sFileName, xbInt16 iShareMode ) { + if( sFileName == "" || sFqFileName == "" ) + SetFileName( sFileName ); + return xbFopen( sOpenMode, iShareMode ); +} + +/************************************************************************/ +//! @brief Xbase wrapper for standard libary fopen. +/*! + + This routine supports all the standard C library open modes. The Xbase routines only + use "r" and "r+". + + + \param sOpenMode + +
OpenModeDescription +
rReading +
r+Reading and Writing +
wOpen for writing. Truncate to zero bytes if it exists +
w+Open for reading and writing, truncate to zero bytes if it exists +
aOpen for append +
a+Open for reading and writing (at end). +
+ The mode can also include the letter "b" for binary ie; "r+b". The "b" is ignored on + POSIX compliant systems, but is included for cross platform compatibility. + \param iShareMode + XB_SINGLE_USER
+ XB_MULTI_USER
+ \returns Return Codes +*/ + +xbInt16 xbFile::xbFopen( const xbString & sOpenMode, xbInt16 iShareMode ) { + + #ifdef HAVE__FSOPEN_F + if(( fp = _fsopen( sFqFileName.Str(), sOpenMode.Str(), 0x40 )) != NULL ){ + #else + if(( fp = fopen( sFqFileName.Str(), sOpenMode.Str())) != NULL ){ + #endif + + if( sOpenMode == "r" ) + iOpenMode = XB_READ; + else if( sOpenMode == "w" ) + iOpenMode = XB_WRITE; + else + iOpenMode = XB_READ_WRITE; + + bFileOpen = xbTrue; + this->iShareMode = iShareMode; + + #ifdef HAVE__FILENO_F + iFileNo = _fileno( fp ); + #else + iFileNo = fileno( fp ); + #endif + + #ifdef HAVE_SETENDOFFILE_F + //used by visual studio, 32 bit + fHandle = (HANDLE) _get_osfhandle( iFileNo ); + #endif + + #ifdef XB_LOCKING_SUPPORT + if( iShareMode ) + xbFTurnOffFileBuffering(); + #endif + + return XB_NO_ERROR; + } + else + return XB_OPEN_ERROR; +} +/************************************************************************/ +//! @brief Xbase wrapper for standard libary fopen. +/*! + \param iOpenMode + XB_READ
+ XB_READ_WRITE
+ \param iShareMode + XB_SINGLE_USER
+ XB_MULTI_USER
+ \returns Return Codes +*/ + +xbInt16 xbFile::xbFopen( xbInt16 iOpenMode, xbInt16 iShareMode ) { + this->iOpenMode = iOpenMode; + if( iOpenMode == XB_READ_WRITE ) + return xbFopen( "r+b", iShareMode ); + else if( iOpenMode == XB_READ ) + return xbFopen( "r", iShareMode ); + else + return XB_INVALID_OPTION; +} + +/************************************************************************/ +//! @brief Xbase wrapper for standard libary fputc. +/*! + \param c Character to write + \returns Return Codes +*/ + + +xbInt16 xbFile::xbFputc( xbInt32 c ) { + return xbFputc( c, 1 ); +} + +/************************************************************************/ +//! @brief Xbase wrapper for standard libary fputc. +/*! + \param c Character to write + \param iNoOfTimes Number of times to write the character + \returns Return Codes +*/ + + +xbInt16 xbFile::xbFputc( xbInt32 c, xbInt32 iNoOfTimes ) { + + for( xbInt32 l = 0; l < iNoOfTimes; l++ ) + if( fputc( c, fp ) != (int) c ) + return XB_WRITE_ERROR; + + return XB_NO_ERROR; +} + +/************************************************************************/ +//! @brief Xbase wrapper for standard libary fputs. +/*! + \param s xbString to write to file + \returns Return Codes +*/ + +xbInt16 xbFile::xbFputs( const xbString & s ){ + if( fputs( s.Str(), fp ) < 0 ) + return XB_WRITE_ERROR; + else + return XB_NO_ERROR; +} + +/************************************************************************/ +//! @brief Xbase wrapper for standard libary fread. +/*! + \param p Pointer to data to write + \param size size of write + \param nmemb Number of times to read it + \returns Return Codes +*/ + +xbInt16 xbFile::xbFread( void *p, size_t size, size_t nmemb ) { + + size_t iRc; + iRc = fread( p, size, nmemb, fp ); + if( iRc == nmemb ) + return XB_NO_ERROR; + else + return XB_READ_ERROR; +} + +/************************************************************************/ +//! @brief Xbase wrapper for standard libary fseek. +/*! + \param lOffset Position in file to seek to + \param iWhence SEEK_SET - from beginning of file
+ SEEK_CUR - from current position
+ SEEK_END - from end of file
+ \returns Return Codes +*/ + +xbInt16 xbFile::xbFseek( xbInt64 lOffset, xbInt32 iWhence ) { + + xbInt32 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + try { + + #if defined(HAVE_FSEEKO_F) + iRc = fseeko( fp, lOffset, iWhence ); + if( iRc != 0 ){ + iErrorStop = 100; + throw iRc; + } + + #elif defined(HAVE__FSEEKI64_F) + iRc = _fseeki64( fp, lOffset, iWhence ); + if( iRc != 0 ){ + iErrorStop = 110; + throw iRc; + } + #else + #ifdef XB_PLATFORM_32 + /* if request is larger than 2 gig,this is a part of a locking request, + assuming offset is less than 4 gig, split the request into 2 fseek calls */ + if( lOffset > 2147483647 && iWhence == SEEK_SET ){ + /* move forward max amt - 2G */ + if(( iRc = fseek( fp, 2147483647, SEEK_SET )) != 0 ){ + iErrorStop = 120; + throw iRc; + } + lOffset -= 2147483647; + iWhence = SEEK_CUR; + } + #endif + iRc = fseek( fp, (long) lOffset, iWhence ); + if( iRc != 0 ){ + iErrorStop = 310; + throw iRc; + } + #endif + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbFile::xbFseek() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + iRc = XB_SEEK_ERROR; + } + return iRc; +} + +/************************************************************************/ +//! @brief Xbase wrapper for standard libary ftell. +/*! + Returns the current file position. + \returns Current file position. +*/ + +size_t xbFile::xbFtell() { + return (size_t) ftell( fp ); +} + +/************************************************************************/ +//! @brief Turn off file buffering. +/*! + Turns off file buffering. File buffering can't be used while in multi user mode. + +*/ + +void xbFile::xbFTurnOffFileBuffering() { + setvbuf( fp, NULL, _IONBF, 0 ); +} + +/************************************************************************/ +//! @brief Xbase wrapper for standard libary fwrite. +/*! + \param p Pointer to data buffer to write + \param size Size of data to write + \param nmemb Number of times to write data buffer + \returns Return Codes +*/ +xbInt16 xbFile::xbFwrite( const void *p, size_t size, size_t nmemb ) { + + size_t iRc; + iRc = fwrite( p, size, nmemb, fp ); + if( iRc == nmemb ) + return XB_NO_ERROR; + else + return XB_READ_ERROR; +} + +/************************************************************************/ +//! @brief Read file until a particular character is encountered on input stream. +/*! + This routine will read until cDelim is encountered or eof, which ever occurs first. + + \param cDelim Delimiter to stop writing at. + \param sOut Output xbString containing data read + \returns Return Codes +*/ + +xbInt16 xbFile::xbReadUntil( const char cDelim, xbString &sOut ){ + + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + char c; + + try{ + sOut = ""; + if(( iRc = xbFgetc( c )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + sOut = c; + while( iRc == XB_NO_ERROR && c != cDelim ){ + if(( iRc = xbFgetc( c )) != XB_NO_ERROR ){ + iErrorStop = 110; + throw iRc; + } + sOut += c; + } + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbFile::xbReadUntil() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return iRc; +} + +/************************************************************************/ +//! @brief Delete file. +/*! + \returns Return Codes +*/ +xbInt16 xbFile::xbRemove() { + return xbRemove( sFqFileName.Str(), 0 ); +} + +/************************************************************************/ +//! @brief Delete file. +/*! + \param sFileNameIn Name of file to delete + \returns Return Codes +*/ + +xbInt16 xbFile::xbRemove( const xbString & sFileNameIn ) { + return xbRemove( sFileNameIn, 0 ); +} + + +/************************************************************************/ +//! @brief Delete file. +/*! + \param sFileNameIn Name of file to delete + \param iOption If Set to 1, assume this is a delete request for a dbf file, and should rename the dbt file also + \returns Return Codes +*/ +xbInt16 xbFile::xbRemove( const xbString & sFileNameIn, xbInt16 iOption ) { + + xbInt32 iRc = remove( sFileNameIn.Str()); + + if( iRc != 0 ) + return XB_DELETE_FAILED; + + if( iOption == 1 ){ + xbString sFileName2 = sFileNameIn; + + if( sFileName2[sFileName2.Len()] == 'F' ) + sFileName2.PutAt( sFileName2.Len(), 'T' ); + else + sFileName2.PutAt( sFileName2.Len(), 't' ); + + iRc = remove( sFileName2.Str()); + if( iRc != 0 ) + return XB_DELETE_FAILED; + } + + return XB_NO_ERROR; +} + +/************************************************************************/ +//! @brief Rename file. +/*! + \param sOldName Original file name + \param sNewName New file name + \returns Return Codes +*/ + +xbInt16 xbFile::xbRename( const xbString & sOldName, const xbString & sNewName ){ + if( rename( sOldName.Str(), sNewName.Str())) + return XB_RENAME_ERROR; + else + return XB_NO_ERROR; +} + +/************************************************************************/ +//! @brief Xbase wrapper for rewind. +/*! + Set file pointer at beginning of file. +*/ + +void xbFile::xbRewind() { + rewind( fp ); +} + +/************************************************************************/ +//! @brief Xbase wrapper for ftruncate. +/*! + Set file size to llSize + \param llSize New file size. + \returns Return Codes +*/ +xbInt16 xbFile::xbTruncate( xbInt64 llSize ) { + + xbInt16 iRc = 0; + xbInt16 iErrorStop = 0; + try{ + #ifdef HAVE_FTRUNCATE_F + if(( iRc = ftruncate( iFileNo, llSize )) != 0 ){ + iErrorStop = 100; + iRc = XB_WRITE_ERROR; + throw iRc; + } + #elif defined(HAVE_SETENDOFFILE_F) + if(( iRc = xbFseek( llSize, SEEK_SET )) != XB_NO_ERROR ){ + iErrorStop = 110; + throw iRc; + } + if(( iRc = SetEndOfFile( fHandle )) == 0 ){ + iErrorStop = 120; + iRc = XB_WRITE_ERROR; + throw iRc; + } else { + iRc = XB_NO_ERROR; + } + #else + + // check that cmake can find function SetEndOfFile - + // cmake could not find for Borland 5.5 + FATAL_COMPILE_ERROR + CANT_LOCATE_FUNCTION_ftruncate_or_SetEndOfFile + + #endif + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbFile::xbTruncate() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return iRc; +} + +/***********************************************************************/ + +#ifdef XB_LOCKING_SUPPORT + +//! @brief Lock / unlock file. +/*! + \param iFunction XB_LOCK
+ XB_UNLOCK
+ \param lOffset Position in file to lock + \param stLen Length to lock + \returns Return Codes +*/ +xbInt16 xbFile::xbLock( xbInt16 iFunction, xbInt64 lOffset, size_t stLen ){ + + xbInt16 iRc = 0; + xbInt16 iErrorStop = 0; + xbInt16 iTries = 0; + + try{ + #ifdef HAVE_FCNTL_F + /* Unix lock function */ + + if(( iRc = xbFseek( lOffset, SEEK_SET )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + + struct flock fl; + switch( iFunction ){ + case( XB_LOCK ): + fl.l_type = F_WRLCK; + break; + case( XB_UNLOCK ): + fl.l_type = F_UNLCK; + break; + default: + iErrorStop = 110; + iRc = XB_INVALID_LOCK_OPTION; + throw iRc; + break; + } + fl.l_whence = SEEK_CUR; + fl.l_start = 0; + fl.l_len = (xbInt32) stLen; + do{ + iRc = fcntl( iFileNo, F_SETLK, &fl ); + if( iRc && (errno == EACCES || errno == EAGAIN )){ + iTries++; + xbase->xbSleep( GetDefaultLockWait() ); + } else if( iRc ){ + iErrorStop = 120; + iRc = XB_LOCK_FAILED; + throw iRc; + } + } while( iRc && iTries < GetLockRetryCount()); + if( iRc ) + iRc = XB_LOCK_FAILED; // lock failed, don't log an exception + + #elif defined(HAVE_LOCKFILE_F) + /* Windows 64 byte lock functions */ + /* split a quad word into two double words */ + typedef union{ + size_t Qword; + xbUInt32 Dword[2]; + } Qsplit; + + Qsplit lPos; + Qsplit lLen; + lPos.Qword = (size_t) lOffset; + lLen.Qword = stLen; + + do{ + if( iFunction == XB_LOCK ){ + if(( iRc = LockFile( fHandle, lPos.Dword[0], lPos.Dword[1], lLen.Dword[0], lLen.Dword[1] )) == 0 ){ + iTries++; + xbase->xbSleep( GetDefaultLockWait() ); + } + } + else if( iFunction == XB_UNLOCK ){ + if(( iRc = UnlockFile( fHandle, lPos.Dword[0], lPos.Dword[1], lLen.Dword[0], lLen.Dword[1] )) == 0 ){ + iTries++; + xbase->xbSleep( GetDefaultLockWait() ); + } + } + else + { + iErrorStop = 130; + iRc = XB_INVALID_LOCK_OPTION; + throw iRc; + } + } while( iRc == 0 && iTries < GetLockRetryCount()); + if( iRc == 0 ) + iRc = XB_LOCK_FAILED; // lock failed, don't log an exception + else + iRc = XB_NO_ERROR; + + #elif defined(HAVE_LOCKING_F) || defined(HAVE__LOCKING_F) + + /* older 32 bit locking functions */ + xbInt32 iLockType; + if( iFunction == XB_LOCK ){ + iLockType = 2; + } else if( iFunction == XB_UNLOCK ){ + iLockType = 0; + } else { + iErrorStop = 140; + iRc = XB_INVALID_LOCK_OPTION; + throw iRc; + } + + if(( iRc = xbFseek( lOffset, SEEK_SET )) != XB_NO_ERROR ){ + iErrorStop = 150; + iRc = XB_SEEK_ERROR; + throw iRc; + } + + do{ + #ifdef HAVE__LOCKING_F + if(( iRc = _locking( iFileNo, iLockType, stLen )) != 0 ){ + #else + if(( iRc = locking( iFileNo, iLockType, stLen )) != 0 ){ + #endif + iTries++; + xbase->xbSleep( GetDefaultLockWait() ); + } + } while( iRc != 0 && iTries < GetLockRetryCount()); + + if( iRc != 0 ) + iRc = XB_LOCK_FAILED; // lock failed, don't log an exception + else + iRc = XB_NO_ERROR; + + #else + + FATAL ERROR - CANT BUILD LIBRARY IN CURRENT CONFIG - MISSING - no file locking function defined in xbfile.cpp + + #endif + + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbFile::xbLock() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + + return iRc; + + +} + +//! @brief Return the locking retry setting. +/*! + + \returns The lock retry setting for this file or ths system default setting if the lock retry for the file + has not been set. +*/ + +xbInt16 xbFile::GetLockRetryCount() const { + + if( iLockRetries == -1 ) + return xbase->GetDefaultLockRetries(); + else + return iLockRetries; +} + +//! @brief Set the lock retry countr for this specific file. +/*! + \param iLockRetries The number of retries to attempt before returning failure for this file +*/ + +void xbFile::SetLockRetryCount( xbInt16 iLockRetries ) { + this->iLockRetries = iLockRetries; +} + +#endif + + +/***********************************************************************/ +#ifdef XB_DEBUG_SUPPORT +//! @brief Debugging routine - dump mem to the log file. +/*! + This routine dumps data from meemory to the log file. This is + primarily used for debugging and analysis purposes. + + \param p Pointer to data to write + \param lBlxkSize Size of block + \returns Return Codes +*/ +xbInt16 xbFile::DumpMemToDisk( char *p, size_t lSize ){ + + + xbInt16 iRc = 0; + xbInt16 iErrorStop = 0; + xbString sDir; + xbString sFn; + + FILE *fpd = NULL; + + try{ + + // sDir = GetLogDirectory(); + sDir = xbase->GetLogDirectory(); + + char cLastChar = sDir[sDir.Len()]; + + // build logfile name + if( cLastChar != '/' && cLastChar != '\\' ) + sFn.Sprintf( "%s/MemDump.txt", sDir.Str()); + else + sFn.Sprintf( "%sMemDump.txt", sDir.Str()); + + // open the dump file for append + #ifdef HAVE__FSOPEN_F + if(( fpd = _fsopen( sFn.Str(), "w+b", 0x40 )) == NULL ){ + #else + if(( fpd = fopen( sFn.Str(), "w+b")) == NULL ){ + #endif + iErrorStop = 100; + iRc = XB_OPEN_ERROR; + throw iRc; + } + + int i; + // dump the block to the file + for( size_t l = 0; l < lSize; l++ ){ + i = *p; + if( fputc( i, fpd ) == EOF ){ + iErrorStop = 110; + iRc = XB_WRITE_ERROR; + throw iRc; + } + p++; + } + // close the dump file + fclose( fpd ); + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbFile::DumpBlockToDisk() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + if( fpd ) + fclose( fpd ); + } + return iRc; +} + +/***********************************************************************/ +//! @brief Debugging routine - dump a block to the log file. +/*! + This routine dumps a block to the log file. This is + primarily used for debugging and analysis purposes. + + \param ulBlockNo Block number to write + \param lBlxkSize Size of block + \returns Return Codes +*/ +xbInt16 xbFile::DumpBlockToDisk( xbUInt32 ulBlockNo, size_t lBlkSize ){ + + xbInt16 iRc = 0; + xbInt16 iErrorStop = 0; + + xbUInt32 ulStartBlock; + xbUInt32 ulEndBlock; + + char *p = 0x00; + + xbString sDir; + xbString sFn; + char *buf = NULL; + FILE *fpd = NULL; + try{ + + if( ulBlockNo == 0 ){ + ulStartBlock = 0; + xbUInt64 ullFileSizeulBlockNo; + if(( iRc = GetFileSize( ullFileSizeulBlockNo )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + ulEndBlock = (xbUInt32) (ullFileSizeulBlockNo / lBlkSize); + } else { + ulStartBlock = ulBlockNo; + ulEndBlock = ulBlockNo; + } + + if(( buf = (char *) malloc( lBlkSize )) == NULL ){ + iErrorStop = 110; + iRc = XB_NO_MEMORY; + throw iRc; + } + +// sDir = GetLogDirectory(); + sDir = xbase->GetLogDirectory(); + char cLastChar = sDir[sDir.Len()]; + + for( xbUInt32 l = ulStartBlock; l < ulEndBlock; l++ ){ + + if(( iRc = ReadBlock( l, lBlkSize, buf )) != XB_NO_ERROR ){ + iErrorStop = 120; + throw iRc; + } + + // build logfile name + if( cLastChar != '/' && cLastChar != '\\' ) + sFn.Sprintf( "%s/BlockDump.B%ld", sDir.Str(), l); + else + sFn.Sprintf( "%sBlockDump.%ld", sDir.Str(), l); + + // open the dump file for append + #ifdef HAVE__FSOPEN_F + if(( fpd = _fsopen( sFn.Str(), "w+b", 0x40 )) == NULL ){ + #else + if(( fpd = fopen( sFn.Str(), "w+b")) == NULL ){ + #endif + iErrorStop = 130; + iRc = XB_OPEN_ERROR; + throw iRc; + } + + // dump the block to the file + p = buf; + for( size_t l = 0; l < lBlkSize; l++ ){ + //if( fputc( *p, fpd ) != *p ){ + if( fputc( *p, fpd ) == EOF ){ + iErrorStop = 140; + iRc = XB_WRITE_ERROR; + throw iRc; + } + p++; + } + // close the dump file + fclose( fpd ); + } + + // free the buffer + if( buf ) + free( buf ); + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbFile::DumpBlockToDisk() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + if( buf ) + free( buf ); + if( fpd ) + fclose( fpd ); + } + return iRc; +} +#endif +/***********************************************************************/ +} /* namespace xb */ + + + diff --git a/src/core/xbfilter.cpp b/src/core/xbfilter.cpp new file mode 100755 index 0000000..5c5f276 --- /dev/null +++ b/src/core/xbfilter.cpp @@ -0,0 +1,544 @@ +/* xbfilter.cpp + +XBase64 Software Library + +Copyright (c) 1997,2003,2014,2022,2023 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 + + +This module handles uda (user data area) methods + +*/ + +#include "xbase.h" + + +// might need to change thisto XB_EXPRESSION_SUPPORT +#ifdef XB_FILTER_SUPPORT + + +namespace xb{ + +/************************************************************************/ +xbFilter::xbFilter( xbDbf *dbf ) { + this->dbf = dbf; + this->exp = NULL; + lLimit = 0; // max number of responses + lCurQryCnt = 0; // current number, this query + = moving fwd + // - = moving backwards + + #ifdef XB_INDEX_SUPPORT + pIx = NULL; // if index is set, the class uses the index tag, otherwise table + vpTag = NULL; + #endif // XB_INDEX_SUPPORT + +} +/************************************************************************/ +xbFilter::~xbFilter() { + if( exp ) + delete exp; +} +/************************************************************************/ +xbInt32 xbFilter::GetLimit() const { + return lLimit; +} +/************************************************************************/ +xbInt32 xbFilter::GetQryCnt() const { + return lCurQryCnt; +} +/************************************************************************/ +void xbFilter::SetLimit( xbInt32 lLimit ){ + this->lLimit = lLimit; +} +/************************************************************************/ +void xbFilter::ResetQryCnt(){ + this->lCurQryCnt = 0; +} + +/************************************************************************/ +xbInt16 xbFilter::Set( const char *sFilter ) { + xbString sFilt( sFilter ); + return Set( sFilt ); +} + +/************************************************************************/ +xbInt16 xbFilter::Set( xbString &sFilter ) { + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + try{ + + if( exp ) + delete exp; + + exp = new xbExp( dbf->GetXbasePtr(), dbf ); + if(( iRc = exp->ParseExpression( sFilter.Str() )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + if( exp->GetReturnType() != XB_EXP_LOGICAL ){ + iErrorStop = 110; + iRc = XB_INVALID_EXPRESSION; + delete exp; + exp = NULL; + throw iRc; + } + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbFilter::SetExpression() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + dbf->GetXbasePtr()->WriteLogMessage( sMsg.Str() ); + dbf->GetXbasePtr()->WriteLogMessage( dbf->GetXbasePtr()->GetErrorMessage( iRc )); + } + return iRc; +} +/************************************************************************/ +xbInt16 xbFilter::GetFirstRecord( xbInt16 iOption ) { + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + try{ + + if( !exp ){ + iErrorStop = 100; + throw iRc; + } + + lCurQryCnt = 0; + if(( iRc = dbf->GetFirstRecord( iOption )) != XB_NO_ERROR ){ + if( iRc == XB_EMPTY || iRc == XB_EOF ) + return iRc; + else{ + iErrorStop = 110; + throw iRc; + } + } + + xbBool bFound = xbFalse; + while( !bFound && iRc == XB_NO_ERROR ){ + if(( iRc = exp->ProcessExpression()) != XB_NO_ERROR ){ + iErrorStop = 120; + throw iRc; + } + if(( iRc = exp->GetBoolResult( bFound )) != XB_NO_ERROR ){ + iErrorStop = 130; + throw iRc; + } + if( !bFound ){ + if(( iRc = dbf->GetNextRecord( iOption )) != XB_NO_ERROR ){ + if( iRc == XB_EOF ){ + return iRc; + } else { + iErrorStop = 140; + throw iRc; + } + } + } + } + lCurQryCnt++; + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbFilter::GetFirstRecord() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + dbf->GetXbasePtr()->WriteLogMessage( sMsg.Str() ); + dbf->GetXbasePtr()->WriteLogMessage( dbf->GetXbasePtr()->GetErrorMessage( iRc )); + } + return iRc; +} +/************************************************************************/ +xbInt16 xbFilter::GetNextRecord( xbInt16 iOption ){ + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + try{ + + if( !exp ){ + iErrorStop = 100; + throw iRc; + } + + if( lLimit != 0 && abs( lCurQryCnt ) >= lLimit ) + return XB_LIMIT_REACHED; + + if(( iRc = dbf->GetNextRecord( iOption )) != XB_NO_ERROR ){ + if( iRc == XB_EOF ) + return iRc; + else{ + iErrorStop = 110; + throw iRc; + } + } + + xbBool bFound = xbFalse; + while( !bFound && iRc == XB_NO_ERROR ){ + if(( iRc = exp->ProcessExpression()) != XB_NO_ERROR ){ + iErrorStop = 120; + throw iRc; + } + if(( iRc = exp->GetBoolResult( bFound )) != XB_NO_ERROR ){ + iErrorStop = 130; + throw iRc; + } + if( !bFound ){ + if(( iRc = dbf->GetNextRecord( iOption )) != XB_NO_ERROR ){ + if( iRc == XB_EOF ){ + return iRc; + } else { + iErrorStop = 140; + throw iRc; + } + } + } + } + lCurQryCnt++; + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbFilter::GetNextRecord() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + dbf->GetXbasePtr()->WriteLogMessage( sMsg.Str() ); + dbf->GetXbasePtr()->WriteLogMessage( dbf->GetXbasePtr()->GetErrorMessage( iRc )); + } + return iRc; +} +/************************************************************************/ +xbInt16 xbFilter::GetPrevRecord( xbInt16 iOption ){ + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + try{ + + if( !exp ){ + iErrorStop = 100; + throw iRc; + } + + if( lLimit != 0 && abs( lCurQryCnt ) >= lLimit ) + return XB_LIMIT_REACHED; + + if(( iRc = dbf->GetPrevRecord( iOption )) != XB_NO_ERROR ){ + if( iRc == XB_BOF ) + return iRc; + else{ + iErrorStop = 110; + throw iRc; + } + } + + xbBool bFound = xbFalse; + while( !bFound && iRc == XB_NO_ERROR ){ + if(( iRc = exp->ProcessExpression()) != XB_NO_ERROR ){ + iErrorStop = 120; + throw iRc; + } + if(( iRc = exp->GetBoolResult( bFound )) != XB_NO_ERROR ){ + iErrorStop = 130; + throw iRc; + } + if( !bFound ){ + if(( iRc = dbf->GetPrevRecord( iOption )) != XB_NO_ERROR ){ + if( iRc == XB_BOF ){ + return iRc; + } else { + iErrorStop = 140; + throw iRc; + } + } + } + } + lCurQryCnt--; + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbFilter::GetPrevRecord() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + dbf->GetXbasePtr()->WriteLogMessage( sMsg.Str() ); + dbf->GetXbasePtr()->WriteLogMessage( dbf->GetXbasePtr()->GetErrorMessage( iRc )); + } + return iRc; +} +/************************************************************************/ +xbInt16 xbFilter::GetLastRecord( xbInt16 iOption ){ + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + try{ + + if( !exp ){ + iErrorStop = 100; + throw iRc; + } + + lCurQryCnt = 0; + if(( iRc = dbf->GetLastRecord( iOption )) != XB_NO_ERROR ){ + if( iRc == XB_EOF ) + return iRc; + else{ + iErrorStop = 110; + throw iRc; + } + } + + xbBool bFound = xbFalse; + while( !bFound && iRc == XB_NO_ERROR ){ + if(( iRc = exp->ProcessExpression()) != XB_NO_ERROR ){ + iErrorStop = 120; + throw iRc; + } + if(( iRc = exp->GetBoolResult( bFound )) != XB_NO_ERROR ){ + iErrorStop = 130; + throw iRc; + } + if( !bFound ){ + if(( iRc = dbf->GetPrevRecord( iOption )) != XB_NO_ERROR ){ + if( iRc == XB_BOF ){ + return iRc; + } else { + iErrorStop = 140; + throw iRc; + } + } + } + } + lCurQryCnt--; + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbFilter::GetLastRecord() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + dbf->GetXbasePtr()->WriteLogMessage( sMsg.Str() ); + dbf->GetXbasePtr()->WriteLogMessage( dbf->GetXbasePtr()->GetErrorMessage( iRc )); + } + return iRc; +} + +/************************************************************************/ + +#ifdef XB_INDEX_SUPPORT + +/************************************************************************/ +xbInt16 xbFilter::GetFirstRecordIx( xbInt16 iOption ) { + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + try{ + + if( !exp ){ + iErrorStop = 100; + throw iRc; + } + + lCurQryCnt = 0; + if(( iRc = dbf->GetCurIx()->GetFirstKey( dbf->GetCurTag(), iOption )) != XB_NO_ERROR ){ + if( iRc == XB_EMPTY || iRc == XB_EOF ) + return iRc; + else{ + iErrorStop = 110; + throw iRc; + } + } + + xbBool bFound = xbFalse; + while( !bFound && iRc == XB_NO_ERROR ){ + if(( iRc = exp->ProcessExpression()) != XB_NO_ERROR ){ + iErrorStop = 120; + throw iRc; + } + if(( iRc = exp->GetBoolResult( bFound )) != XB_NO_ERROR ){ + iErrorStop = 130; + throw iRc; + } + if( !bFound ){ + // if(( iRc = pIx->GetNextKey( vpTag, iOption )) != XB_NO_ERROR ){ + if(( iRc = dbf->GetCurIx()->GetNextKey( dbf->GetCurTag(), iOption )) != XB_NO_ERROR ){ + if( iRc == XB_EOF ){ + return iRc; + } else { + iErrorStop = 140; + throw iRc; + } + } + } + } + lCurQryCnt++; + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbFilter::GetFirstRecordIx() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + dbf->GetXbasePtr()->WriteLogMessage( sMsg.Str() ); + dbf->GetXbasePtr()->WriteLogMessage( dbf->GetXbasePtr()->GetErrorMessage( iRc )); + } + return iRc; +} +/************************************************************************/ +xbInt16 xbFilter::GetNextRecordIx( xbInt16 iOption ){ + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + try{ + + if( !exp ){ + iErrorStop = 100; + throw iRc; + } + + if( lLimit != 0 && abs( lCurQryCnt ) >= lLimit ) + return XB_LIMIT_REACHED; + + if(( iRc = dbf->GetCurIx()->GetNextKey( dbf->GetCurTag(), iOption )) != XB_NO_ERROR ){ + if( iRc == XB_EOF ) + return iRc; + else{ + iErrorStop = 110; + throw iRc; + } + } + + xbBool bFound = xbFalse; + while( !bFound && iRc == XB_NO_ERROR ){ + if(( iRc = exp->ProcessExpression()) != XB_NO_ERROR ){ + iErrorStop = 120; + throw iRc; + } + if(( iRc = exp->GetBoolResult( bFound )) != XB_NO_ERROR ){ + iErrorStop = 130; + throw iRc; + } + if( !bFound ){ + // if(( iRc = pIx->GetNextKey( vpTag, iOption )) != XB_NO_ERROR ){ + if(( iRc = dbf->GetCurIx()->GetNextKey( dbf->GetCurTag(), iOption )) != XB_NO_ERROR ){ + if( iRc == XB_EOF ){ + return iRc; + } else { + iErrorStop = 140; + throw iRc; + } + } + } + } + lCurQryCnt++; + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbFilter::GetNextRecordIx() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + dbf->GetXbasePtr()->WriteLogMessage( sMsg.Str() ); + dbf->GetXbasePtr()->WriteLogMessage( dbf->GetXbasePtr()->GetErrorMessage( iRc )); + } + return iRc; +} +/************************************************************************/ +xbInt16 xbFilter::GetPrevRecordIx( xbInt16 iOption ){ + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + try{ + + if( !exp ){ + iErrorStop = 100; + throw iRc; + } + + if( lLimit != 0 && abs( lCurQryCnt ) >= lLimit ) + return XB_LIMIT_REACHED; + + if(( iRc = dbf->GetCurIx()->GetPrevKey( dbf->GetCurTag(), iOption )) != XB_NO_ERROR ){ + if( iRc == XB_BOF ) + return iRc; + else{ + iErrorStop = 110; + throw iRc; + } + } + + xbBool bFound = xbFalse; + while( !bFound && iRc == XB_NO_ERROR ){ + if(( iRc = exp->ProcessExpression()) != XB_NO_ERROR ){ + iErrorStop = 120; + throw iRc; + } + if(( iRc = exp->GetBoolResult( bFound )) != XB_NO_ERROR ){ + iErrorStop = 130; + throw iRc; + } + if( !bFound ){ + //if(( iRc = pIx->GetPrevKey( vpTag, iOption )) != XB_NO_ERROR ){ + if(( iRc = dbf->GetCurIx()->GetPrevKey( dbf->GetCurTag(), iOption )) != XB_NO_ERROR ){ + if( iRc == XB_BOF ){ + return iRc; + } else { + iErrorStop = 140; + throw iRc; + } + } + } + } + lCurQryCnt--; + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbFilter::GetPrevRecordIx() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + dbf->GetXbasePtr()->WriteLogMessage( sMsg.Str() ); + dbf->GetXbasePtr()->WriteLogMessage( dbf->GetXbasePtr()->GetErrorMessage( iRc )); + } + return iRc; +} +/************************************************************************/ +xbInt16 xbFilter::GetLastRecordIx( xbInt16 iOption ){ + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + try{ + + if( !exp ){ + iErrorStop = 100; + throw iRc; + } + lCurQryCnt = 0; + if(( iRc = dbf->GetCurIx()->GetLastKey( dbf->GetCurTag(), iOption )) != XB_NO_ERROR ){ + if( iRc == XB_EOF ) + return iRc; + else{ + iErrorStop = 110; + throw iRc; + } + } + xbBool bFound = xbFalse; + while( !bFound && iRc == XB_NO_ERROR ){ + if(( iRc = exp->ProcessExpression()) != XB_NO_ERROR ){ + iErrorStop = 120; + throw iRc; + } + if(( iRc = exp->GetBoolResult( bFound )) != XB_NO_ERROR ){ + iErrorStop = 130; + throw iRc; + } + if( !bFound ){ + if(( iRc = dbf->GetCurIx()->GetPrevKey( dbf->GetCurTag(), iOption )) != XB_NO_ERROR ){ + if( iRc == XB_BOF ){ + return iRc; + } else { + iErrorStop = 140; + throw iRc; + } + } + } + } + lCurQryCnt--; + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbFilter::GetLastRecordIx() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + dbf->GetXbasePtr()->WriteLogMessage( sMsg.Str() ); + dbf->GetXbasePtr()->WriteLogMessage( dbf->GetXbasePtr()->GetErrorMessage( iRc )); + } + return iRc; +} +#endif // XB_INDEX_SUPPORT + + +/************************************************************************/ +} /* namespace */ +#endif /* XB_FILTER_SUPPORT */ \ No newline at end of file diff --git a/src/core/xbfuncs.cpp b/src/core/xbfuncs.cpp new file mode 100755 index 0000000..f127211 --- /dev/null +++ b/src/core/xbfuncs.cpp @@ -0,0 +1,851 @@ +/* xbfuncs.cpp + +XBase64 Software Library + +Copyright (c) 1997,2003,2014,2017,2022,2023 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" + +#ifdef XB_FUNCTION_SUPPORT + +namespace xb{ + + +// All funtions have a similar structure, return an xbInt16 return code +// Have a variable number of input operands and one output operand + +/************************************************************************/ +//! @brief Calculate absolute value of a numeric expression. +/*! + Expression function ABS(). + \param dIn Input - Numeric expression. + \param dOut Output - Absolute value. + \returns Return Codes +*/ + +xbInt16 xbXBase::ABS( xbDouble dIn, xbDouble &dOut ){ + if( dIn < 0 ) + dOut = dIn * -1; + else + dOut = dIn; + return XB_NO_ERROR; +} + +/*************************************************************************/ +//! @brief Trim leading and trailing white space from a string. +/*! + Expression function ALLTRIM(). + \param sIn Input - Input string to trim. + \param sOut Output - Trimmed string. + \returns Return Codes +*/ + +xbInt16 xbXBase::ALLTRIM( const xbString &sIn, xbString &sOut ){ + sOut = sIn; + sOut.Trim(); + return XB_NO_ERROR; +} + +/*************************************************************************/ +//! @brief Return ASCII code for the first character in a string. +/*! + Expression function ASC(). + \param sIn Input - Input character string. + \param dAscOut Output - Ascii code of first character in string. + \returns Return Codes +*/ +xbInt16 xbXBase::ASC( const xbString &sIn, xbDouble &dAscOut ){ + if( sIn == "" ) + return XB_PARSE_ERROR; + + dAscOut = sIn[1]; + return XB_NO_ERROR; +} +/*************************************************************************/ +//! @brief Return number indicating starting position of string within a string. +/*! + Expression function AT(). + \param s1 Input - Input string to search for. + \param s2 Input - Input string to search. + \param dPos Output - Position of string s1 within s2. + \returns Return Codes +*/ +xbInt16 xbXBase::AT( const xbString &s1, const xbString &s2, xbDouble &dPos ){ + /* looks for s1 in s2 */ + xbInt32 lCnt = 0; + const char *p; + const char *p2 = s2; + if( strlen( s1 ) > strlen( s2 )) return 0; + if(( p = strstr( s2, s1 )) == NULL ) + return XB_NO_ERROR; + while( p2++ != p ) lCnt++; + dPos = lCnt + 1; + return XB_NO_ERROR; +} + +/*************************************************************************/ +//! @brief Return character weekday name for date. +/*! + Expression function CDOW(). + \param dInDate Input - Input date. + \param sOutDow Output - Character day of week. + \returns Return Codes +*/ +xbInt16 xbXBase::CDOW( xbDate &dInDate, xbString &sOutDow ){ + return dInDate.CharDayOf( sOutDow ); +} + +/*************************************************************************/ +//! @brief Convert numeric expression to a character. +/*! + Expression function CHR(). + \param dAsciCd Input - Numeric expression. + \param sOut Output - Character result. + \returns Return Codes +*/ +xbInt16 xbXBase::CHR( xbDouble dAsciCd, xbString &sOut ){ + static char buf[2]; + buf[0] = (char) dAsciCd; + buf[1] = 0x00; + sOut = buf; + return XB_NO_ERROR; +} + +/*************************************************************************/ +//! @brief Return character month name for date. +/*! + Expression function CMONTH(). + \param dInDate Input - Input date. + \param sOutMonth Output - Character month. + \returns Return Codes +*/ +xbInt16 xbXBase::CMONTH( xbDate &dInDate, xbString &sOutMonth ){ + return dInDate.CharMonthOf( sOutMonth ); +} +/*************************************************************************/ +//! @brief Return date from character input date. +/*! + Expression function CTOD(). + \param sInDate Input - Input date in MM/DD/YY format. + \param dOutDate Output - Output date. + \returns Return Codes +*/ +xbInt16 xbXBase::CTOD( const xbString &sInDate, xbDate &dOutDate ){ + return dOutDate.CTOD( sInDate ); +} +/*************************************************************************/ +//! @brief Return system date. +/*! + Expression function DATE(). + \param dOutDate Output - Output date. + \returns Return Codes +*/ +xbInt16 xbXBase::DATE( xbDate &dOutDate ){ + return dOutDate.Sysdate(); +} +/*************************************************************************/ +//! @brief Return the day of the month from a date. +/*! + Expression function DAY(). + \param dInDate Input - Input date. + \param dOutDay Output - Output day of month. + \returns Return Codes +*/ +xbInt16 xbXBase::DAY( const xbDate &dInDate, xbDouble &dOutDay ){ + xbInt16 iOutDay;; + iOutDay = dInDate.DayOf( XB_FMT_MONTH ); + if( iOutDay < 0 ){ + return iOutDay; + }else{ + dOutDay = iOutDay; + return XB_NO_ERROR; + } +} +/*************************************************************************/ +//! @brief Return record deletion status for current record. +/*! + Expression function DEL(). + \param dbf Input - Table to check record deletion status. + \param iRecBufSw Input - Which buffer. 0 = Current record buffer, 1 = Original record buffer + \param sOut Output - "*" if record is deleted, otherise space. + \returns XB_NO_ERROR
XB_PARSE_ERROR. + +*/ +xbInt16 xbXBase::DEL( xbDbf *dbf , xbString &sOut, xbInt16 iRecBufSw ) { + + if( dbf ){ + if( dbf->RecordDeleted( iRecBufSw )) + sOut = "*"; + else + sOut = " "; + return XB_NO_ERROR; + } else { + return XB_PARSE_ERROR; + } +} + +/*************************************************************************/ +//! @brief Return record deletion status for current record. +/*! + Expression function DELETED(). + \param dbf Input - Table to check record deletion status for. + \param iRecBufSw Input - Which buffer. 0 = Current record buffer, 1 = Original record buffer + \param bOut Output - xbTrue if record is deleted.
xbFalse if record is not deleted. + \returns XB_NO_ERROR
XB_PARSE_ERROR. +*/ +xbInt16 xbXBase::DELETED( xbDbf *dbf , xbBool &bOut, xbInt16 iRecBufSw ) { + + if( dbf ){ + bOut = dbf->RecordDeleted( iRecBufSw ); + return XB_NO_ERROR; + } else { + return XB_PARSE_ERROR; + } +} + +/*************************************************************************/ +//! @brief Clipper DESCEND function. +/*! + Expression function DESCEND(). + \param dtInDate Input - Input date. + \param dtOutDate Output - Output date. + \returns XB_NO_ERROR. +*/ +xbInt16 xbXBase::DESCEND( const xbDate &dtInDate, xbDate &dtOutDate ){ + xbDate d( "29991231" ); + dtOutDate.JulToDate8( 2415021 + d.JulianDays() - dtInDate.JulianDays()); + return XB_NO_ERROR; +} + +/*************************************************************************/ +//! @brief Clipper DESCEND function. +/*! + Expression function DESEND(). + \param dIn Input - Input number. + \param dOut Output - Output number. + \returns XB_NO_ERROR. +*/ +xbInt16 xbXBase::DESCEND( xbDouble dIn, xbDouble &dOut ){ + dOut = dIn * -1; + return XB_NO_ERROR; +} + +/*************************************************************************/ +//! @brief Clipper DESCEND function. +/*! + Expression function DESEND(). + \param sIn Input - Input string. + \param sOut Output - Output string. + \returns XB_NO_ERROR. +*/ +xbInt16 xbXBase::DESCEND( const xbString &sIn, xbString &sOut ){ + + sOut = sIn; + for( xbUInt32 l = 0; l < sIn.Len(); l++ ) + sOut.PutAt( l+1, (char) (255 - sOut[l+1])); + return XB_NO_ERROR; +} + +/*************************************************************************/ +//! @brief Return number of day of week. +/*! + Expression function DOW(). + \param dtInDate Input - Input date. + \param dDowOut Output - Output day of week. + \returns Return Codes +*/ +xbInt16 xbXBase::DOW( const xbDate &dtInDate, xbDouble &dDowOut ){ + xbInt16 iDow; + iDow = dtInDate.DayOf( XB_FMT_WEEK ); + if( iDow < 0 ){ + return XB_PARSE_ERROR; + }else{ + dDowOut = iDow; + return XB_NO_ERROR; + } +} + +/*************************************************************************/ +//! @brief Return character date from input date. +/*! + Expression function DTOC(). + \param dInDate Input - Input date. + \param sOutFmtDate Output - Output date. + \returns Return Codes +*/ +xbInt16 xbXBase::DTOC( xbDate &dInDate, xbString &sOutFmtDate ){ + return dInDate.FormatDate( "MM/DD/YY", sOutFmtDate ); +} +/*************************************************************************/ +//! @brief Return char CCYYMMDD date from input date. +/*! + Expression function DTOS(). + \param dtInDate Input - Input date. + \param sOutFmtDate Output - Output date. + \returns Return Codes +*/ +xbInt16 xbXBase::DTOS( xbDate &dtInDate, xbString &sOutFmtDate ){ + return dtInDate.FormatDate( "YYYYMMDD", sOutFmtDate ); +} +/*************************************************************************/ +//! @brief Return exponent value. +/*! + Expression function EXP(). + This function returns e**x where e is approximately 2.71828 and x is dIn. + + \param dIn Input - exp value. + \param dOut Output - value. + \returns XB_NO_ERROR +*/ +xbInt16 xbXBase::EXP( xbDouble dIn, xbDouble &dOut ) +{ + dOut = exp( dIn ); + return XB_NO_ERROR; +} +/*************************************************************************/ +//! @brief Immediate if. +/*! + Expression function IIF(). + \param bResult Input - boolean expression. + \param sTrueResult Input - value if boolean expression is true. + \param sFalseResult Input - value if boolean expression is false. + \param sResult Output - sTrueResult or sFalseResult depending on bResultvalue. + \returns XB_NO_ERROR +*/ +xbInt16 xbXBase::IIF( xbBool bResult, const xbString &sTrueResult, const xbString &sFalseResult, xbString &sResult ) +{ + if( sFalseResult.Len() != sTrueResult.Len()) + return XB_INCONSISTENT_PARM_LENS; + if( bResult ) + sResult = sTrueResult; + else + sResult = sFalseResult; + return XB_NO_ERROR; +} +/*************************************************************************/ +//! @brief Convert number to integer, truncate any decimals. +/*! + Expression function INT(). + \param dIn Input - Input number. + \param dOut Output - integer. + \returns XB_NO_ERROR +*/ + +xbInt16 xbXBase::INT( xbDouble dIn, xbDouble &dOut ) +{ + xbInt64 ll = (xbInt64) dIn; + dOut = (xbDouble) ll; + return XB_NO_ERROR; +} + +/*************************************************************************/ +//! @brief Check if string begins with alpha character. +/*! + Expression function ISALPHA(). + \param sIn Input - Input string. + \param bResult Output - xbTrue if string begins with alpha character.
xbFalse if string does not begin with alpha character. + \returns XB_NO_ERROR +*/ +xbInt16 xbXBase::ISALPHA( const xbString &sIn, xbBool &bResult ){ + if( isalpha(sIn[1])) + bResult = 1; + else + bResult = 0; + return XB_NO_ERROR; +} + +/*************************************************************************/ +//! @brief Check if string begins with lower case alpha character. +/*! + Expression function ISLOWER(). + \param sIn Input - Input string. + \param bResult Output - xbTrue if string begins with lower case alpha character.
+ xbFalse if string does not begin with lower case alpha character. + \returns XB_NO_ERROR +*/ + +xbInt16 xbXBase::ISLOWER( const xbString &sIn, xbBool &bResult ){ + if( islower(sIn[1])) + bResult = 1; + else + bResult = 0; + return XB_NO_ERROR; +} +/*************************************************************************/ +//! @brief Check if string begins with upper case alpha character. +/*! + Expression function ISUPPER(). + \param sIn Input - Input string. + \param bResult Output - xbTrue if string begins with upper case alpha character.
+ xbFalse if string does not begin with upper case alpha character. + \returns XB_NO_ERROR +*/ + +xbInt16 xbXBase::ISUPPER( const xbString &sIn, xbBool &bResult ){ + if( isupper(sIn[1])) + bResult = 1; + else + bResult = 0; + return XB_NO_ERROR; +} + +/*************************************************************************/ +//! @brief Return left characters from string. +/*! + Expression function LEFT(). + \param sIn Input - string. + \param ulCharCnt Input - number of characters to extract from string. + \param sOut Output - resultant string. + \returns XB_NO_ERROR +*/ +xbInt16 xbXBase::LEFT( const xbString &sIn, xbUInt32 ulCharCnt, xbString &sOut ){ + sOut.Assign( sIn, 1, ulCharCnt ); + return XB_NO_ERROR; +} + +/*************************************************************************/ +//! @brief Return length of string. +/*! + Expression function LEN(). + \param sIn Input - Input string. + \param dOut Output - string length. + \returns XB_NO_ERROR +*/ + +xbInt16 xbXBase::LEN( const xbString &sIn, xbDouble &dOut ){ + dOut = sIn.Len(); + return XB_NO_ERROR; +} + +/*************************************************************************/ +//! @brief Calculate logarithm. +/*! + Expression function LOG(). + \param dIn Input - numeric expression. + \param dOut Output - numeric log value. + \returns XB_NO_ERROR +*/ +xbInt16 xbXBase::LOG( xbDouble dIn, xbDouble &dOut ){ + dOut = log( dIn ); + return XB_NO_ERROR; +} +/*************************************************************************/ +//! @brief Trim left side of string. +/*! + Expression function LTRIM(). + \param sIn Input - string. + \param sOut Output - string result. + \returns XB_NO_ERROR +*/ +xbInt16 xbXBase::LTRIM( const xbString &sIn, xbString &sOut ){ + sOut = sIn; + sOut.Ltrim(); + return XB_NO_ERROR; +} +/*************************************************************************/ +//! @brief Convert upper case to lower case. +/*! + Expression function LOWER(). + \param sIn Input - string. + \param sOut Output - string result. + \returns XB_NO_ERROR +*/ + +xbInt16 xbXBase::LOWER( const xbString &sIn, xbString &sOut ){ + sOut = sIn; + sOut.ToLowerCase(); + return XB_NO_ERROR; +} + +/*************************************************************************/ +//! @brief Return higher of two values. +/*! + Expression function MAX(). + \param d1 Input - Numeric value 1. + \param d2 Input - Numeric value 2. + \param dOut Output - Higher of d1 or d2. + \returns XB_NO_ERROR +*/ +xbInt16 xbXBase::MAX( xbDouble d1, xbDouble d2, xbDouble &dOut ) +{ + if( d1 > d2 ) + dOut = d1; + else + dOut = d2; + return XB_NO_ERROR; +} + +/*************************************************************************/ +//! @brief Return lessor of two values. +/*! + Expression function MIN(). + \param d1 Input - Numeric value 1. + \param d2 Input - Numeric value 2. + \param dOut Output - Lessor of d1 or d2. + \returns XB_NO_ERROR +*/ +xbInt16 xbXBase::MIN( xbDouble d1, xbDouble d2, xbDouble &dOut ) +{ + if( d1 < d2 ) + dOut = d1; + else + dOut = d2; + return XB_NO_ERROR; +} + +/*************************************************************************/ +//! @brief Return number of month for a given date. +/*! + Expression function MONTH(). + \param dInDate Input date. + \param dOutMonth - Month number. + \returns XB_NO_ERROR.
XB_PARSE_ERROR. +*/ +xbInt16 xbXBase::MONTH( xbDate &dInDate, xbDouble &dOutMonth ){ + + xbInt16 iRc = dInDate.MonthOf(); + if( iRc < 0 ) + return XB_PARSE_ERROR; + else{ + dOutMonth = iRc; + return XB_NO_ERROR; + } +} + +/*************************************************************************/ +//! @brief Return number of records in a given table. +/*! + Expression function RECCOUNT(). + \param dbf - Table. + \param dOut - Number of records. + \returns Return Codes +*/ +xbInt16 xbXBase::RECCOUNT( xbDbf *dbf , xbDouble &dOut ) { + + xbUInt32 ulRecCnt; + xbInt16 iRc = dbf->GetRecordCnt( ulRecCnt ); + dOut = (xbDouble) ulRecCnt; + return iRc; + +} + +/*************************************************************************/ +//! @brief Return current record number for a given table. +/*! + Expression function RECNO(). + \param dbf - Table. + \param dOut - Record number. + \returns XB_NO_ERROR.
XB_PARSE_ERROR. +*/ + +xbInt16 xbXBase::RECNO( xbDbf *dbf , xbDouble &dOut ) { + if( dbf ){ + dOut = (xbDouble) dbf->GetCurRecNo(); + return XB_NO_ERROR; + } else { + dOut = -1; + return XB_PARSE_ERROR; + } +} + +/*************************************************************************/ +//! @brief Repeat character expression N times. +/*! + Expression function REPLICATE(). + \param sIn Inout - String to replicate. + \param ulRepCnt Input - Number of times to repeat. + \param sOut Output - String result. + \returns XB_NO_ERROR.
XB_PARSE_ERROR. +*/ +xbInt16 xbXBase::REPLICATE( const xbString &sIn, xbUInt32 ulRepCnt, xbString &sOut ){ + sOut = ""; + for( xbUInt32 i = 0; i < ulRepCnt; i++ ) + sOut += sIn; + return XB_NO_ERROR; +} + +/*************************************************************************/ +//! @brief Return right characters from string. +/*! + Expression function RIGHT(). + \param sIn Input - string. + \param ulCharCnt Input - number of characters to extract from string. + \param sOut Output - resultant string. + \returns XB_NO_ERROR +*/ + +xbInt16 xbXBase::RIGHT( const xbString &sIn, xbUInt32 ulCharCnt, xbString &sOut ){ + if( sIn.Len() < ulCharCnt ) + sOut = sIn; + else + sOut.Assign( sIn, sIn.Len() - ulCharCnt + 1, ulCharCnt ); + return XB_NO_ERROR; +} + +/*************************************************************************/ +//! @brief Trim right side of string. +/*! + Expression function RTRIM(). + \param sIn Input - string. + \param sOut Output - string result. + \returns XB_NO_ERROR +*/ + +xbInt16 xbXBase::RTRIM( const xbString &sIn, xbString &sOut ){ + sOut = sIn; + sOut.Rtrim(); + return XB_NO_ERROR; +} + +/*************************************************************************/ +//! @brief Generate a string of N spaces. +/*! + Expression function SPACE(). + \param lCnt Input - Number of spaces. + \param sOut Output - Output string consisting of specified number of spaces. + \returns XB_NO_ERROR. +*/ +xbInt16 xbXBase::SPACE( xbInt32 lCnt, xbString &sOut ){ + sOut = ""; + for( xbInt32 i = 0; i < lCnt; i++ ) + sOut += ' '; + return XB_NO_ERROR; +} + +/*************************************************************************/ +//! @brief Calculate a square root. +/*! + Expression function SQRT(). + \param dBase Input - Base number. + \param dSqrRoot Output - Square root. + \returns XB_NO_ERROR. +*/ + +xbInt16 xbXBase::SQRT( xbDouble dBase, xbDouble &dSqrRoot ) +{ + dSqrRoot = sqrt( dBase ); + return XB_NO_ERROR; +} + +/*************************************************************************/ +//! @brief Converts a valid 8 byte (CCYYMMDD) input date into a date class. +/*! + Expression function STOD(). + \param sInDate Input - Input date. + \param dtOutDate Output - Output date. + \returns XB_NO_ERROR.
XB_INVALID_DATE. +*/ + +xbInt16 xbXBase::STOD( const xbString &sInDate, xbDate &dtOutDate ){ + + if( dtOutDate.DateIsValid( sInDate )){ + dtOutDate = sInDate; + return XB_NO_ERROR; + } else { + return XB_INVALID_DATE; + } +} + +/*************************************************************************/ +//! @brief Convert number to a character string. +/*! + Expression function STR(). + \param dIn Input - Number. + \param sOut Output - String. + \returns XB_NO_ERROR. +*/ + +xbInt16 xbXBase::STR( xbDouble dIn, xbString &sOut) { + xbString sPadChar = " "; + return STR( dIn, 10, 0, sPadChar, sOut ); +} + +/*************************************************************************/ +//! @brief Convert number to a character string. +/*! + Expression function STR(). + \param dIn Input - Number. + \param ulLen Input - Length. + \param sOut Output - String. + \returns XB_NO_ERROR. +*/ +xbInt16 xbXBase::STR( xbDouble dIn, xbUInt32 ulLen, xbString &sOut) { + xbString sPadChar = " "; + return STR( dIn, ulLen, 0, sPadChar, sOut ); +} + +/*************************************************************************/ +//! @brief Convert number to a character string. +/*! + Expression function STR(). + \param dIn Input - Number. + \param ulLen Input - Length. + \param ulDec Input - Number of decimals. + \param sOut Output - String. + \returns XB_NO_ERROR. +*/ + +xbInt16 xbXBase::STR( xbDouble dIn, xbUInt32 ulLen, xbUInt32 ulDec, xbString &sOut) { + xbString sPadChar = " "; + return STR( dIn, ulLen, ulDec, sPadChar, sOut ); +} + +/*************************************************************************/ +//! @brief Convert number to a character string. +/*! + Expression function STR(). + \param dIn Input - Number. + \param ulLen Input - Length. + \param ulDec Input - Number of decimals. + \param sPadChar Input - Left pad character, typically zero or space. + \param sOut Output - String. + \returns XB_NO_ERROR. +*/ + +xbInt16 xbXBase::STR( xbDouble dIn, xbUInt32 ulLen, xbUInt32 ulDec, xbString &sPadChar, xbString &sOut) { + + xbString sFmt; + sFmt.Sprintf( "%c%d.%df", '%', ulLen, ulDec ); + sOut.Sprintf( sFmt.Str(), dIn, 0 ); + + // convert to all "*" if result is too long + if( sOut.Len() > ulLen ){ + sOut = "*"; + do{ + sOut += "*"; + } while( ulLen > sOut.Len()); + } else if( sPadChar.Len() > 0 && sPadChar != " " ){ + // this logic doesn't make sense when processing negative numbers, + // but it does behave like the original dbase + // you could end up with something like 0000-12.17 when you really want -000012.17 + // that is probably why the STRZERO function came into being + xbUInt32 l = 1; + while( sOut[l] == ' ' ){ + sOut.PutAt( l, sPadChar[1]); + l++; + } + } + return XB_NO_ERROR; +} + +/*************************************************************************/ +//! @brief Convert number to a character string with leading zeroes. +/*! + Expression function STRZERO(). + \param dIn Input - Number. + \param ulLen Input - Length. + \param ulDec Input - Number of decimals. + \param sOut Output - String. + \returns XB_NO_ERROR. +*/ +xbInt16 xbXBase::STRZERO( xbDouble dIn, xbUInt32 ulLen, xbUInt32 ulDec, xbString &sOut){ + + xbString sFmt; + if( dIn < 0 ) + sFmt.Sprintf( "%c+0%d.%df", '%', ulLen, ulDec ); + else + sFmt.Sprintf( "%c0%d.%df", '%', ulLen, ulDec ); + sOut.Sprintf( sFmt.Str(), dIn ); + + // convert to all "*" if result is too long + if( sOut.Len() > ulLen ){ + sOut = "*"; + do{ + sOut += "*"; + } while( ulLen > sOut.Len()); + } + return XB_NO_ERROR; +} + +/*************************************************************************/ +//! @brief Extract a portion of a string from another string. +/*! + Expression function SUBSTR(). + \param sIn Input - Source string. + \param ulStartPos Input - Starting position for string extraction. + \param ulLen Input - Number of characters to extract. + \param sOut Output - String. + \returns XB_NO_ERROR. +*/ +xbInt16 xbXBase::SUBSTR( const xbString &sIn, xbUInt32 ulStartPos, xbUInt32 ulLen, xbString &sOut ){ + sOut = sIn; + sOut.Mid( ulStartPos, ulLen ); + return XB_NO_ERROR; +} + +/*************************************************************************/ +//! @brief Trim left and right sides of string. +/*! + Expression function TRIM(). + \param sIn Input - string. + \param sOut Output string result. + \returns XB_NO_ERROR +*/ +xbInt16 xbXBase::TRIM( const xbString &sIn, xbString &sOut ){ + return RTRIM( sIn, sOut ); +} + +/*************************************************************************/ +//! @brief Convert lower case to upper case. +/*! + Expression function UPPER(). + \param sIn Input - string. + \param sOut Output - string result. + \returns XB_NO_ERROR +*/ +xbInt16 xbXBase::UPPER( const xbString &sIn, xbString &sOut ){ + sOut = sIn; + sOut.ToUpperCase(); + return XB_NO_ERROR; +} + +/*************************************************************************/ +//! @brief Convert numeric characters to number. +/*! + Expression function VAL(). + \param sIn Input - string. + \param dOut Output - numeric result. + \returns XB_NO_ERROR +*/ + +xbInt16 xbXBase::VAL( const xbString &sIn, xbDouble &dOut ) +{ + if( sIn ) + // strtod(nptr,NULL); + dOut = atof( sIn ); + else + dOut = 0; + return XB_NO_ERROR; +} +/*************************************************************************/ +//! @brief Return year for a given date. +/*! + Expression function YEAR(). + \param dInDate Input date. + \param dOutYear - Year. + \returns XB_NO_ERROR.
XB_PARSE_ERROR. +*/ + +xbInt16 xbXBase::YEAR( xbDate &dInDate, xbDouble &dOutYear ){ + + xbInt16 iRc = dInDate.YearOf(); + if( iRc < 0 ) + return XB_PARSE_ERROR; + else{ + dOutYear = iRc; + return XB_NO_ERROR; + } +} +/*************************************************************************/ +}; // namespace +#endif // XB_FUNCTION_SUPPORT +/*************************************************************************/ diff --git a/src/core/xbixbase.cpp b/src/core/xbixbase.cpp new file mode 100755 index 0000000..e2f929c --- /dev/null +++ b/src/core/xbixbase.cpp @@ -0,0 +1,789 @@ +/* xbixbase.cpp + +XBase64 Software Library + +Copyright (c) 1997,2003,2014,2022,2023 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 + + Base index class + +*/ + +#include "xbase.h" +#ifdef XB_INDEX_SUPPORT + +namespace xb{ + +/***********************************************************************/ +//! @brief Class constructor. +/*! + /param dbf Pointer to dbf instance. +*/ +xbIx::xbIx( xbDbf *dbf ) : xbFile( dbf->GetXbasePtr()) { + this->dbf = dbf; + vpCurTag = NULL; + cNodeBuf = NULL; + bLocked = xbFalse; +} +/***********************************************************************/ +//! @brief Class Destructor. +xbIx::~xbIx(){} + + +/***********************************************************************/ +//! @brief Add Keys for record number +/*! + For a given a record number, add keys to each tag in the index file + if it was updated + + \param ulRecNo Record number to add keys for + \returns Return Codes +*/ + +xbInt16 xbIx::AddKeys( xbUInt32 ulRecNo ){ + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + xbInt16 i = 0; + xbInt16 iKeySts; + + try{ + void * vpTag; + xbInt16 iTagCount = GetTagCount(); + + for( i = 0; i < iTagCount; i++ ){ + vpTag = GetTag( i ); + iKeySts = GetKeySts( vpTag ); + + if( iKeySts == 1 || iKeySts == 2 ){ + if(( iRc = UpdateTagKey( 'A', vpTag, ulRecNo )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + } + } + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbIx::AddKeys() Exception Caught. Error Stop = [%d] iRc = [%d] Tag=[%d]", iErrorStop, iRc, i ); + xbase->WriteLogMessage( sMsg ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return iRc; +} + +/***********************************************************************/ +//! @brief Allocate memory for index node. +/*! + Allocate an index node. + + \param ulBufSize Size of buffer to allocate + \returns null on error
Pointer to newly allocated xbIxNode on success +*/ +xbIxNode * xbIx::AllocateIxNode( xbUInt32 ulBufSize, xbInt16 ) +{ + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + + try{ + xbIxNode * pNode = (xbIxNode *) calloc( 1, sizeof( xbIxNode )); + if( pNode == NULL ){ + iErrorStop = 100; + iRc = XB_NO_MEMORY; + throw iRc; + } + if( ulBufSize == 0 ) + ulBufSize = GetBlockSize(); + + pNode->ulBufSize = ulBufSize; + pNode->cpBlockData = (char *) calloc( 1, ulBufSize ); + if( pNode->cpBlockData == NULL ){ + free( pNode ); + iErrorStop = 110; + iRc = XB_NO_MEMORY; + throw iRc; + } + return pNode; + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbIx::AllocateIxNode() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return NULL; +} + + +/***********************************************************************/ +//! @brief Binary search for given value on an index node. +/*! + + Binary search for key lookups + \param cKeyType Key type + \param npNode Pointer to index node for search + \param lKeyItemLen Lenth of key plus pointer values + \param vpKey Pointer to key value + \param lSearchKeyLen length of key to search + \param iCompRc output return code from the CompareKey routine. CompareKey returns an + integer value less than, equal to or greater than zero in when comparing values + + \param bDescending xbTrue for descending index key lookup.
+ xbFalse for ascending index key lookup. + \return The position in the node the key was found, if multiples it returns the first occurrence. + If the key is not found, it returns the slot it should be in. +*/ + +xbInt16 xbIx::BSearchBlock( char cKeyType, xbIxNode *npNode, xbInt32 lKeyItemLen, const void *vpKey, + xbInt32 lSearchKeyLen, xbInt16 &iCompRc, xbBool bDescending ) const { + xbInt32 lLo = 0; + xbInt32 lHi = 0; + xbInt32 lMid = 0; + xbInt32 lKeyCnt = GetKeyCount( npNode ); + + + if( !bDescending ){ + lHi = lKeyCnt - 1; + + while( lLo <= lHi ){ + lMid = (lLo + lHi) / 2; + iCompRc = CompareKey( cKeyType, GetKeyData( npNode, lMid, lKeyItemLen ), vpKey, (size_t) lSearchKeyLen ); + if( iCompRc > 0 ) + lHi = lMid - 1; + else if( iCompRc < 0 ) + lLo = lMid + 1; + else{ // found match, look for leftmost occurrence + + xbInt32 lFoundPos = lMid; + lMid--; + while( lMid >= 0 && CompareKey( cKeyType, GetKeyData( npNode, lMid, lKeyItemLen ), vpKey, (size_t) lSearchKeyLen ) == 0 ){ + lFoundPos = lMid; + lMid--; + } + iCompRc = 0; + lLo = lFoundPos; + lHi = -1; + } + } + // update the compare key results + if( lMid != lLo ){ + if( lLo >= lKeyCnt ) + iCompRc = 1; + else + iCompRc = CompareKey( cKeyType, GetKeyData( npNode, lLo, lKeyItemLen ), vpKey, (size_t) lSearchKeyLen ); + } + return (xbInt16) lLo; + + } else { // descending key + + lLo = lKeyCnt - 1; + while( lLo >= lHi && lHi != -1 ){ + lMid = (lLo + lHi) / 2; + iCompRc = CompareKey( cKeyType, GetKeyData( npNode, lMid, lKeyItemLen ), vpKey, (size_t) lSearchKeyLen ); + + if( iCompRc > 0 ) { + lHi = lMid + 1; + } + else if( iCompRc < 0) { + lLo = lMid - 1; + } + else{ // found match, look for leftmost occurrence + + xbInt32 lFoundPos = lMid; + lMid--; + while( lMid >= 0 && CompareKey( cKeyType, GetKeyData( npNode, lMid, lKeyItemLen ), vpKey, (size_t) lSearchKeyLen ) == 0 ){ + lFoundPos = lMid; + lMid--; + } + iCompRc = 0; + lHi = lFoundPos; + lLo = -1; + } + } + + // std::cout << "BSB1 lo = " << lLo << " mid = " << lMid << " hi = " << lHi << " keycnt = " << lKeyCnt << " iCompRc = " << iCompRc << "\n"; // key=" << (char *) vpKey << "\n"; + if( lLo < 0 && iCompRc < 0 ) + iCompRc = 1; + else if( iCompRc != 0 ) { + iCompRc = CompareKey( cKeyType, GetKeyData( npNode, (lLo < 0 ? 0 : lLo), lKeyItemLen ), vpKey, (size_t) lSearchKeyLen ); + } + // std::cout << "BSB2 lo = " << lLo << " mid = " << lMid << " hi = " << lHi << " keycnt = " << lKeyCnt << " iCompRc = " << iCompRc << "\n"; // key=" << (char *) vpKey << "\n"; + return (xbInt16) lHi; + } + + // should never get here + // return (xbInt16) 0; +} + +/***********************************************************************/ +//! @brief Check for duplicate keys. +/*! + \returns Return Codes +*/ +xbInt16 xbIx::CheckForDupKeys(){ + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + xbInt16 i = 0; + + try{ + void * vpTag; + xbInt16 iTagCount = GetTagCount(); + for( i = 0; i < iTagCount; i++ ){ + vpTag = GetTag( i ); + if(( iRc = CheckForDupKey( vpTag )) < XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + } + } + catch (xbInt16 iRc ){ + if( iRc != XB_KEY_NOT_UNIQUE ){ + xbString sMsg; + sMsg.Sprintf( "xbIxBase::CheckForDupKeys() Exception Caught. Error Stop = [%d] iRc = [%d] Tag=[%d]", iErrorStop, iRc, i ); + xbase->WriteLogMessage( sMsg ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + } + return iRc; +} +/***********************************************************************/ +//! @brief Close index file. +/*! + \returns Return Codes +*/ + +xbInt16 xbIx::Close(){ + + xbInt16 iRc = 0; + xbInt16 iErrorStop = 0; + + try{ + if(( iRc = xbFclose()) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbIx::Close() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return iRc; +} + +/***********************************************************************/ +//! @brief Compare keys. +/*! + \param cKeyType C - Character compare.
+ N - Numeric BCD compare.
+ D - Numeric compare.
+ F - Numeric compare.
+ \param v1 Left compare.
v2 - Right Compare. + \param iSearchKeyLen Length of key compare. + \returns 1 - Left operand is greater then right operand.
+ 0 - Left operand is equal to right operand.
+ -1 - Left operand is less than right operand. +*/ +inline xbInt16 xbIx::CompareKey( char cKeyType, const void *v1, const void *v2, size_t iSearchKeyLen ) const{ + if( cKeyType == 'C' ){ // character compare + return memcmp( v1, v2, iSearchKeyLen ); + } else if( cKeyType == 'N' ){ // numeric bcd compare, mdx bcd numeric indices + xbBcd bcdk1( v1 ); + return bcdk1.Compare( v2 ); + } else if( cKeyType == 'D' || cKeyType == 'F' ){ // julian date compare, ndx float numeric indices + xbDouble *d1 = (xbDouble *) v1; + xbDouble *d2 = (xbDouble *) v2; + if( *d1 < *d2 ) + return -1; + else if( *d1 > *d2 ) + return 1; + else + return 0; + } else { +// std::cout << "Unhandled key type [" << cKeyType << "]\n"; + } + return XB_NO_ERROR; +} +/***********************************************************************/ +//! @brief Create Keys for record number +/*! + \param iOpt 0 Build a key for FindKey usage, only rec buf 0.
+ 1 Append Mode, Create key for an append, only use rec buf 0, set updated switch.
+ 2 Update Mode, Create old version and new version keys, check if different, set update switch appropriately. + \returns Return Codes +*/ +xbInt16 xbIx::CreateKeys( xbInt16 iOpt ) { + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + xbInt16 i = 0; + + try{ + void * vpTag; + xbInt16 iTagCount = GetTagCount(); + + for( i = 0; i < iTagCount; i++ ){ + vpTag = GetTag( i ); + if(( iRc = CreateKey( vpTag, iOpt )) < XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + } + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbIx::CreateKeys() Exception Caught. Error Stop = [%d] iRc = [%d] Tag=[%d]", iErrorStop, iRc, i ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return XB_NO_ERROR; +} +/***********************************************************************/ +//! @brief Delete keys for record number +/*! + Delete keys to each tag in the index file if it was updated as determined + by CreateKeys function + + \returns Return Codes +*/ +//xbInt16 xbIx::DeleteKeys( xbUInt32 ulRecNo ){ + +xbInt16 xbIx::DeleteKeys(){ + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + xbInt16 i = 0; + + try{ + void * vpTag; + xbInt16 iTagCount = GetTagCount(); + + for( i = 0; i < iTagCount; i++ ){ + vpTag = GetTag( i ); + if( GetKeySts( vpTag ) > 1 ){ // 0 = no update 1 = add 2 = update, 3 = delete + if(( iRc = UpdateTagKey( 'D', vpTag )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + } + } + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbIx::DeleteKeys() Exception Caught. Error Stop = [%d] iRc = [%d] Tag=[%d]", iErrorStop, iRc, i ); + xbase->WriteLogMessage( sMsg ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return iRc; +} + +/***********************************************************************/ +#ifdef XB_DEBUG_SUPPORT +//! @brief Dump anode for debug purposes. +/*! + \param pNode Pointer to node to dump. + \param iOption 0 = stdout
+ 1 = Syslog
+ 2 = Both
+ \returns Return Codes +*/ + +xbInt16 xbIx::DumpNode( void *, xbIxNode *pNode, xbInt16 iOption ) const +{ + xbString s; + s.Sprintf( "Dump Node Block=[%d] CurKey=[%d]", pNode->ulBlockNo, pNode->iCurKeyNo ); + xbase->WriteLogMessage( s, iOption ); + + return XB_NO_ERROR; +} +#endif // XB_DEBUG_SUPPORT + +/***********************************************************************/ +//! @brief Find double key +/*! + \param vpTag Pointer to tag to search. + \param dKey Double value to search for. + \param iRetrieveSw xbTrue - Retrieve the record if key found.
+ xbFalse - Don't retrieve record, check for key existence only. + \returns XB_NO_ERROR - Key found.
+ XB_NOT_FOUND - Key not found.
+ Return Codes +*/ +xbInt16 xbIx::FindKey( void *vpTag, xbDouble dKey, xbInt16 iRetrieveSw ){ + return FindKey( vpTag, &dKey, 8, iRetrieveSw ); +} +/***********************************************************************/ +//! @brief Find string key +/*! + \param vpTag Pointer to tag to search. + \param sKey String data to search for. + \param iRetrieveSw xbTrue - Retrieve the record if key found.
+ xbFalse - Don't retrieve record, check for key existence only. + \returns XB_NO_ERROR - Key found.
+ XB_NOT_FOUND - Key not found.
+ Return Codes +*/ +xbInt16 xbIx::FindKey( void *vpTag, const xbString &sKey, xbInt16 iRetrieveSw ){ + return FindKey( vpTag, sKey.Str(), (xbInt32) sKey.Len(), iRetrieveSw ); +} +/***********************************************************************/ +//! @brief Find character key +/*! + \param vpTag Pointer to tag to search. + \param cKey String data to search for. + \param lKeyLen Length of key to search for. + \param iRetrieveSw xbTrue - Retrieve the record if key found.
+ xbFalse - Don't retrieve record, check for key existence only. + \returns XB_NO_ERROR - Key found.
+ XB_NOT_FOUND - Key not found.
+ Return Codes +*/ +xbInt16 xbIx::FindKey( void *vpTag, const char *cKey, xbInt32 lKeyLen, xbInt16 iRetrieveSw ){ + return FindKey( vpTag, (void *) cKey, lKeyLen, iRetrieveSw ); +} +/***********************************************************************/ +//! @brief Find bcd key +/*! + \param vpTag Pointer to tag to search. + \param bcd BCD data to search for. + \param iRetrieveSw xbTrue - Retrieve the record if key found.
+ xbFalse - Don't retrieve record, check for key existence only. + + \returns XB_NO_ERROR - Key found.
+ XB_NOT_FOUND - Key not found.
+ Return Codes +*/ +xbInt16 xbIx::FindKey( void *vpTag, const xbBcd &bcd, xbInt16 iRetrieveSw ){ + return FindKey( vpTag, bcd.GetBcd(), 12, iRetrieveSw ); +} +/***********************************************************************/ +//! @brief Find date key +/*! + \param vpTag Pointer to tag to search. + \param dtKey Date data to search for. + \param iRetrieveSw xbTrue - Retrieve the record if key found.
+ xbFalse - Don't retrieve record, check for key existence only. + \returns XB_NO_ERROR - Key found.
+ XB_NOT_FOUND - Key not found.
+ Return Codes +*/ +xbInt16 xbIx::FindKey( void *vpTag, const xbDate &dtKey, xbInt16 iRetrieveSw ){ + xbDouble d = (xbDouble) dtKey.JulianDays(); + return FindKey( vpTag, &d, 8, iRetrieveSw ); +} +/***********************************************************************/ +//! @brief Free all nodes in a linked list. +/*! + \param np Pointer to first node in linked list to free. + \returns NULL. +*/ +xbIxNode *xbIx::FreeNodeChain( xbIxNode *np ){ + + // routine returns NULL + if( np ){ + // free memory for a given chain of nodes + xbIxNode * np2; + + // Clear the previous node's next pointer + if( np->npPrev ) + np->npPrev->npNext = NULL; + + // Clear out the tree + while( np ){ + np2 = np->npNext; + NodeFree( np ); + np = NULL; + np = np2; + } + } + return NULL; +} +/***********************************************************************/ +//! @brief Read block for block number. +/*! + Routine to read a node/block out of an index file and store in xbIxNode structure + \param vpTag Pointer to tag. + \param ulBlockNo Block number to read off disk. + \param iOpt + 0 = Node is read into block buffer, not added to the node chain
+ 1 = Node is read into new xbIxNode, then added to the node chain, and sets CurNode with new node
+ 2 = Node is read into new xbIxNode, not added to the node chain
+ CurNode points to new node
+ \param ulAddlBuf Additional buffer size added to memory + \returns Return Codes + +*/ + +xbInt16 xbIx::GetBlock( void *vpTag, xbUInt32 ulBlockNo, xbInt16 iOpt, xbUInt32 ulAddlBuf ){ + xbInt16 iRc = 0; + xbInt16 iErrorStop = 0; + xbIxNode *np = NULL; + try{ + + if( !vpTag && iOpt == 1 ){ + iErrorStop = 100; + throw iRc; + } + + // set target location of block read to read + char *cp; + if( iOpt == 0 ) + cp = cNodeBuf; + else{ + if(( np = AllocateIxNode(GetBlockSize() + ulAddlBuf )) == NULL ){ + iErrorStop = 110; + iRc = XB_NO_MEMORY; + throw( iRc ); + } + cp = np->cpBlockData; + } + if(( iRc = ReadBlock( ulBlockNo, GetBlockSize(), cp )) != XB_NO_ERROR ){ + iErrorStop = 120; + throw iRc; + } + if( iOpt == 0 ) + return iRc; + np->ulBlockNo = ulBlockNo; + np->iCurKeyNo = 0; + if( iOpt == 1 ) + AppendNodeChain( vpTag, np ); + else if( iOpt == 2 ){ + std::cout << "Future use stub. xbIxbase::GetBlock() option 2 not coded.\n"; + iErrorStop = 130; + iRc = XB_INVALID_OPTION; + throw iRc; + // SetCurNode( vpTag, np ); + } + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbIx::GetBlock() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + if( np ) NodeFree( np ); + } + return iRc; +} + +/***********************************************************************/ +//! @brief Get pointer to current tag. +/*! + \returns Pointer to current tag. +*/ + +void *xbIx::GetCurTag() const { + return vpCurTag; +} +/***********************************************************************/ +//! @brief Get pointer to dbf. +/*! + \returns Pointer to dbf. +*/ +xbDbf *xbIx::GetDbf() const { + return this->dbf; +} +/***********************************************************************/ +//! @brief Get the first key for the current tag. +/*! + \returns Return Codes +*/ +xbInt16 xbIx::GetFirstKey(){ + return GetFirstKey( vpCurTag, 0 ); +} + +/***********************************************************************/ +//! @brief Get the first key for a given tag. +/*! + \param vpTag Tag for get first key operation. + \returns Return Codes +*/ +xbInt16 xbIx::GetFirstKey( void *vpTag ){ + return GetFirstKey( vpTag, 0 ); +} +/***********************************************************************/ +//! @brief Get the last key for the current tag. +/*! + \returns Return Codes +*/ +xbInt16 xbIx::GetLastKey(){ + return GetLastKey( 0, vpCurTag, 0 ); +} +/***********************************************************************/ +//! @brief Get the last key for a given tag. +/*! + \param vpTag Tag for get last key operation. + \returns Return Codes +*/ +xbInt16 xbIx::GetLastKey( void *vpTag ){ + return GetLastKey( 0, vpTag, 0 ); +} + +/***********************************************************************/ +//! @brief Get the file lock status. +/*! + \returns xbTrue - Index file is locked.
xbFalse - Index file is not locked. +*/ +xbBool xbIx::GetLocked() const { + return bLocked; +} + +/***********************************************************************/ +//! @brief Get the key count for number of keys on a node. +/*! + \param np Given node for key count. + \returns Number of keys on the node. +*/ +xbInt32 xbIx::GetKeyCount( xbIxNode *np ) const { + // assumes the first four bytes of the block is a four byte number + // representing the number of keys contained on the block + return eGetInt32( np->cpBlockData ); +} +/***********************************************************************/ +//! @brief Get key data for a given key number. +/*! + \param np Given node for key retrieval. + \param iKeyNo Which key to pull. + \param iKeyItemLen Length of key plus pointers. + \returns Pointer to a given key. +*/ +char * xbIx::GetKeyData( xbIxNode *np, xbInt16 iKeyNo, xbInt16 iKeyItemLen ) const { + if( !np ) return NULL; + char *p = np->cpBlockData; + xbUInt32 ulKeyCnt = eGetUInt32( p ); + if( iKeyNo < 0 || iKeyNo > (xbInt16) ulKeyCnt ) return NULL; + xbInt16 iOffset = 12 + (iKeyNo * iKeyItemLen); + p+=iOffset; + return p; +} +/***********************************************************************/ +//! @brief Get the next key for the current tag. +/*! + \returns Return Codes +*/ +xbInt16 xbIx::GetNextKey(){ + return GetNextKey( vpCurTag, 0 ); +} +/***********************************************************************/ +//! @brief Get the next key for the given tag. +/*! + \param vpTag Tag for next key operation. + \returns Return Codes +*/ +xbInt16 xbIx::GetNextKey( void *vpTag ){ + return GetNextKey( vpTag, 0 ); +} +/***********************************************************************/ +//! @brief Get the prev key for the current tag. +/*! + \returns Return Codes +*/ +xbInt16 xbIx::GetPrevKey(){ + return GetPrevKey( vpCurTag, 0 ); +} +/***********************************************************************/ +//! @brief Get the previous key for the given tag. +/*! + \param vpTag Tag for previous key operation. + \returns Return Codes +*/ +xbInt16 xbIx::GetPrevKey( void *vpTag ){ + return GetPrevKey( vpTag, 0 ); +} +/***********************************************************************/ +//! @brief Free an index node +/*! + \param ixNode Pointer to index node to free. + \returns void +*/ +void xbIx::NodeFree( xbIxNode *ixNode ){ + if( ixNode ){ + if( ixNode->cpBlockData ){ + free( ixNode->cpBlockData ); + ixNode->cpBlockData = NULL; + } + free( ixNode ); + ixNode = NULL; + } +} +/***********************************************************************/ +//! @brief Open an index file. +/*! + MDX files are opened automatically and don't need opened. + NDX files that are associated with the DBF file are opened automatically. + + Non production indexes that haven't been opened will need to be opened to be used. + \param sFileName Index file name to open. + \returns Return Codes +*/ +xbInt16 xbIx::Open( const xbString & sFileName ){ + + // There are no locking requirements when opening an NDX index + xbInt16 iRc = 0; + xbInt16 iErrorStop = 0; + + try{ + /* copy the file name to the class variable */ + this->SetFileName( sFileName ); + + if( !FileExists()){ + iErrorStop = 100; + iRc = XB_FILE_NOT_FOUND; + throw iRc; + } + /* open the file */ + if(( iRc = xbFopen( dbf->GetOpenMode(), dbf->GetShareMode())) != XB_NO_ERROR ){ + iErrorStop = 110; + throw iRc; + } + if(( iRc = ReadHeadBlock()) != XB_NO_ERROR ){ + iErrorStop = 120; + throw iRc; + } + SetCurTag( (xbInt16) 0 ); // default the first tag as the current tag + } + + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbIx::Open( %s ) Exception Caught. Error Stop = [%d] iRc = [%d]", sFileName.Str(), iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return iRc; +} +/***********************************************************************/ +//! @brief Set the current tag. +/*! + \param vpCurTag Pointer to tag to set as current. + \returns void +*/ +void xbIx::SetCurTag( void *vpCurTag ){ + this->vpCurTag = vpCurTag; +} +/***********************************************************************/ +//! @brief Set the dbf pointer. +/*! + \param dbf Dbf pointer to set. + \returns void +*/ +void xbIx::SetDbf( xbDbf *dbf ){ + this->dbf = dbf; +} +/***********************************************************************/ +//! @brief Set the file lock status. +/*! + \param bLocked xbTrue - Set to locked.
xbFalse - Set to unlocked. + \returns void +*/ +void xbIx::SetLocked( xbBool bLocked ){ + this->bLocked = bLocked; +} +/***********************************************************************/ +} /* namespace */ +#endif /* XB_INDEX_SUPPORT */ diff --git a/src/core/xbixmdx.cpp b/src/core/xbixmdx.cpp new file mode 100755 index 0000000..0eef64b --- /dev/null +++ b/src/core/xbixmdx.cpp @@ -0,0 +1,4844 @@ +/* xbixmdx.cpp + +XBase64 Software Library + +Copyright (c) 1997,2003,2014,2022,2023 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 + + + MDX indices are comprised of blocks and pages. + A page is 512 bytes. + A Block is one or more pages. + The default block size is two 512 byte pages per block. + Nodes are used for storing block images in memory + +*/ + +#include "xbase.h" + +#ifdef XB_MDX_SUPPORT + +namespace xb{ + +/***********************************************************************/ +xbIxMdx::xbIxMdx( xbDbf *dbf ) : xbIx( dbf ){ + Init(); +} +/***********************************************************************/ +//void xbIxMdx::Init( xbInt16 iOpt ){ +void xbIxMdx::Init( xbInt16 ){ + + cVersion = 0; + cCreateYY = 0; + cCreateMM = 0; + cCreateDD = 0; + sFileName = ""; + iBlockFactor = 0; + cProdIxFlag = 0; + cTagEntryCnt = 0; + iTagLen = 0; + iTagUseCnt = 0; + cNextTag = 0; + c1B = 0x0b; + ulPageCnt = 0; + ulFirstFreePage = 0; + ulNoOfBlockAvail = 0; + cUpdateYY = 0; + cUpdateMM = 0; + cUpdateDD = 0; + mdxTagTbl = NULL; + cNodeBuf = NULL; + bReuseEmptyNodes = xbTrue; +} +/***********************************************************************/ +xbIxMdx::~xbIxMdx(){ + if( cNodeBuf ) + free( cNodeBuf ); + + if( FileIsOpen()) + Close(); +} +/***********************************************************************/ +//! @brief Add key. +/*! + Add key. If this is a unique index, this logic assumes the duplicate + check logic was already done. + + \param vpTag Tag to update. + \param ulRecNo Record number to add key for. + \returns Return Codes +*/ + +xbInt16 xbIxMdx::AddKey( void * vpTag, xbUInt32 ulRecNo ){ + + xbMdxTag * npTag = (xbMdxTag *) vpTag; + if( GetUniqueKeyOpt() == XB_EMULATE_DBASE && npTag->bFoundSts ) + return XB_NO_ERROR; + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + xbInt16 iHeadNodeUpdateOpt = 2; + xbIxNode * npRightNode = NULL; + xbUInt32 ulNewRightChild = 0; + + try{ + if(( iRc = xbIxMdx::KeySetPosAdd( npTag, ulRecNo )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + xbInt32 lKeyCnt = GetKeyCount( npTag->npCurNode ); + if( lKeyCnt < npTag->iKeysPerBlock ){ + // Section A - add key to appropriate position if space available + // std::cout << "AddKey Section A begin\n"; + if(( iRc = InsertNodeL( npTag, npTag->npCurNode, npTag->npCurNode->iCurKeyNo, npTag->cpKeyBuf, ulRecNo )) != XB_NO_ERROR ){ + iErrorStop = 110; + throw iRc; + } + } else { + // land here with a full leaf node + iHeadNodeUpdateOpt = 1; + npRightNode = AllocateIxNode( npTag, GetBlockSize() + (xbUInt32) npTag->iKeyItemLen, npTag->npCurNode->ulBlockNo * (xbUInt32) iBlockFactor ); + if( !npRightNode ){ + iErrorStop = 120; + iRc = XB_NO_MEMORY; + throw iRc; + } + + if(( npTag->npCurNode->ulBlockNo * (xbUInt32) iBlockFactor ) == npTag->ulRightChild ){ + ulNewRightChild = npRightNode->ulBlockNo * (xbUInt32) iBlockFactor; + } + + if(( iRc = xbIxMdx::SplitNodeL( npTag, npTag->npCurNode, npRightNode, npTag->npCurNode->iCurKeyNo, npTag->cpKeyBuf, ulRecNo )) != XB_NO_ERROR ){ + iErrorStop = 130; + throw iRc; + } + xbUInt32 ulTempBlockNo = npRightNode->ulBlockNo; + + // section C - go up the tree, splitting nodes as necessary + xbIxNode * npParent = npTag->npCurNode->npPrev; + while( npParent && GetKeyCount( npParent ) >= npTag->iKeysPerBlock ){ + //std::cout << "Section C begin interior node is full\n"; + npRightNode = FreeNodeChain( npRightNode ); + npRightNode = AllocateIxNode( npTag, GetBlockSize() + (xbUInt32) npTag->iKeyItemLen, npParent->ulBlockNo * (xbUInt32) iBlockFactor ); + //std::cout << "Section C - B new right node block number for interior node split= " << npRightNode->ulBlockNo << "\n"; + + if( !npRightNode ){ + iErrorStop = 140; + iRc = XB_NO_MEMORY; + throw iRc; + } + //std::cout << "Section C - going to split interior node C\n"; + + if(( iRc = SplitNodeI( npTag, npParent, npRightNode, npParent->iCurKeyNo, BlockToPage( ulTempBlockNo ))) != XB_NO_ERROR ){ + iErrorStop = 150; + throw iRc; + } + // std::cout << "Section C - interior node split \n"; + ulTempBlockNo = npRightNode->ulBlockNo; + npTag->npCurNode = npParent; + npParent = npParent->npPrev; + } + + // section D - if cur node is split root, create new root + if(( npTag->npCurNode->ulBlockNo * (xbUInt32) iBlockFactor ) == npTag->ulRootPage ){ + if(( iRc = AddKeyNewRoot( npTag, npTag->npCurNode, npRightNode )) != XB_NO_ERROR ){ + iErrorStop = 160; + throw iRc; + } + if( npRightNode ) + npRightNode = FreeNodeChain( npRightNode ); + + } else { + + // std::cout << "Section E, put key in parent\n"; + if(( iRc = InsertNodeI( (void *) vpTag, (xbIxNode *) npParent, (xbInt16) npParent->iCurKeyNo, BlockToPage( npRightNode->ulBlockNo ))) != XB_NO_ERROR ){ + iErrorStop = 170; + throw iRc; + } + } + } + + // update the header + if(( iRc = WriteHeadBlock( iHeadNodeUpdateOpt )) != XB_NO_ERROR ){ + iErrorStop = 180; + throw iRc; + } + + // if adding the first key, set the cHasKeys field + if( !npTag->cHasKeys ){ + npTag->cHasKeys = 0x01; + if(( iRc = xbFseek( ((npTag->ulTagHdrPageNo * 512) + 246), SEEK_SET )) != XB_NO_ERROR ){ + iErrorStop = 190; + throw iRc; + } + if(( iRc = xbFwrite( &npTag->cHasKeys, 1, 1 )) != XB_NO_ERROR ){ + iErrorStop = 200; + throw iRc; + } + } + + if( ulNewRightChild > 0 ){ + + char cBuf[4]; + ePutUInt32( cBuf, ulNewRightChild ); + if(( iRc = xbFseek( ((npTag->ulTagHdrPageNo * 512) + 252), SEEK_SET )) != XB_NO_ERROR ){ + iErrorStop = 210; + throw iRc; + } + if(( iRc = xbFwrite( &cBuf, 4, 1 )) != XB_NO_ERROR ){ + iErrorStop = 220; + throw iRc; + } + npTag->ulRightChild = ulNewRightChild; + } + + if( npRightNode ) + npRightNode = FreeNodeChain( npRightNode ); + npTag->npNodeChain = FreeNodeChain( npTag->npNodeChain ); + npTag->npCurNode = NULL; + + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbIxMdx::AddKey() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return iRc; +} +/***********************************************************************/ +void xbIxMdx::AppendNodeChain( void *vpTag, xbIxNode * npNode ){ + xbMdxTag * mdxTag = (xbMdxTag *) vpTag; + if( mdxTag->npNodeChain == NULL ){ + mdxTag->npNodeChain = npNode; + mdxTag->npCurNode = npNode; + } else { + npNode->npPrev = mdxTag->npCurNode; + mdxTag->npCurNode->npNext = npNode; + mdxTag->npCurNode = npNode; + } + // time stamp the node chain + GetFileMtime( mdxTag->tNodeChainTs ); +} + +/***********************************************************************/ +//! @brief Add new root node. +/*! + \param mpTag Tag to update. + \param npLeft Left node. + \param npRight Right node. + \returns Return Codes +*/ +xbInt16 xbIxMdx::AddKeyNewRoot( xbMdxTag *npTag, xbIxNode *npLeft, xbIxNode *npRight ){ + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + char *pLastKey = NULL; + + try{ + xbIxNode *npRoot = AllocateIxNode( npTag, GetBlockSize() + (xbUInt32) npTag->iKeyItemLen, npRight->ulBlockNo * (xbUInt32) iBlockFactor ); + if( !npRoot ){ + iErrorStop = 100; + iRc = XB_NO_MEMORY; + throw iRc; + } + npTag->ulRootPage = npRoot->ulBlockNo; + + pLastKey = (char *) malloc( (size_t) npTag->iKeyLen ); + if(( iRc = GetLastKeyForBlockNo( npTag, npLeft->ulBlockNo, pLastKey )) != XB_NO_ERROR ){ + iErrorStop = 110; + throw iRc; + } + char * pTrg = npRoot->cpBlockData; + + // set no of keys to 1 + ePutUInt32( pTrg, 1 ); + + // set the left node number + pTrg += 8; + ePutUInt32( pTrg, npLeft->ulBlockNo * (xbUInt32) iBlockFactor ); + + // set the key + pTrg+= 4; + memcpy( pTrg, pLastKey, (size_t) npTag->iKeyLen ); + pTrg+= npTag->iKeyItemLen - 4; + ePutUInt32( pTrg, npRight->ulBlockNo * (xbUInt32) iBlockFactor ); + + // write out the new block + if(( iRc = WriteBlock( npRoot->ulBlockNo, GetBlockSize(), npRoot->cpBlockData )) != XB_NO_ERROR ){ + iErrorStop = 120; + throw iRc; + } + + // write out the new root node number in the tag header + // position the file + xbUInt32 ulPagePos = npTag->ulTagHdrPageNo * 512; + + // save the number to a buffer + char cBuf[4]; + ePutUInt32( cBuf, npRoot->ulBlockNo * ((xbUInt32) iBlockFactor )); + + if(( iRc = xbFseek( ulPagePos, SEEK_SET )) != XB_NO_ERROR ){ + iErrorStop = 130; + throw iRc; + } + if(( iRc = xbFwrite( &cBuf, 4, 1 )) != XB_NO_ERROR ){ + iErrorStop = 140; + throw iRc; + } + + if( pLastKey ) + free( pLastKey ); + + npRoot = FreeNodeChain( npRoot ); + + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbIxMdx::AddKeyNewRoot() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + if( pLastKey ) + free( pLastKey ); + } + return iRc; +} + + + +/***********************************************************************/ +//! @brief Allocate a node. +/*! + \param mpTag Pointer to mdx tag + \param ulBufSize Buffer size. + \param ulBlock2 Value to load in ulBlock2 field, bytes 4-7 in the first page of the block + \returns Pointer to new node. +*/ +xbIxNode * xbIxMdx::AllocateIxNode( xbMdxTag * mpTag, xbUInt32 ulBufSize, xbUInt32 ulBlock2 ){ + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + xbIxNode *n = NULL; + + try{ + if(( n = xbIx::AllocateIxNode( ulBufSize )) == NULL ){ + iRc = XB_NO_MEMORY; + iErrorStop = 100; + throw iRc; + } + char *p = n->cpBlockData; + p += 4; + + if( ulFirstFreePage > 0 && bReuseEmptyNodes ){ + // have an empty node we can reuse + n->ulBlockNo = PageToBlock( ulFirstFreePage ); + if(( iRc = ReadBlock( n->ulBlockNo, GetBlockSize(), n->cpBlockData )) != XB_NO_ERROR ){ + iRc = 110; + throw iRc; + } + // update ulFirstFreePage + ulFirstFreePage = eGetUInt32( p ); + if(( iRc = xbFseek( 36, SEEK_SET )) != XB_NO_ERROR ){ + iErrorStop = 120; + throw iRc; + } + if(( iRc = xbFwrite( p, 4, 1 )) != XB_NO_ERROR ){ + iErrorStop = 130; + throw iRc; + } + // memset cpBlockData to zeroes + memset( n->cpBlockData, 0x00, GetBlockSize()); + + } else { + n->ulBlockNo = ulPageCnt / (xbUInt32) iBlockFactor; + ulPageCnt += (xbUInt32) iBlockFactor; + } + + mpTag->ulTagSize += (xbUInt32) iBlockFactor; + if( ulBlock2 > 0 ){ + ePutUInt32( p, ulBlock2 ); + } + } + catch( xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbIxMdx::AllocateIxNode() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + if( n ) + n = FreeNodeChain( n ); + } + return n; +} +/***********************************************************************/ +//! @brief Calculate B-tree pointers. +/*! + Set binary tree pointer value. The MDX tags are stored with binary + tree positions. This routine calculates the value in memory. + \returns void +*/ + +void xbIxMdx::CalcBtreePointers(){ + + xbInt16 iaLeftChild[48]; + xbInt16 iaRightChild[48]; + xbInt16 iaParent[48]; + + for( xbInt16 i = 0; i < 48; i++ ){ + iaLeftChild[i] = 0; + iaRightChild[i] = 0; + iaParent[i] = 0; + } + + // anything to do? + if( iTagUseCnt > 1 ){ + xbString sBaseTag; + xbString sThisTag; + xbString sWorkTag; + xbInt16 iWorkTagNo; + xbBool bDone; + sBaseTag = GetTagName( GetTag( 0 )); + + for( xbInt16 iThisTagNo = 1; iThisTagNo < iTagUseCnt; iThisTagNo++ ){ + iWorkTagNo = 0; + sWorkTag.Set( sBaseTag ); + sThisTag = GetTagName( GetTag( iThisTagNo )); + bDone = xbFalse; + while( !bDone ){ + if( sThisTag < sWorkTag ){ + if( iaLeftChild[iWorkTagNo] == 0 ) { + iaLeftChild[iWorkTagNo] = iThisTagNo + 1; + iaParent[iThisTagNo] = iWorkTagNo + 1; + bDone = xbTrue; + } else { + iWorkTagNo = iaLeftChild[iWorkTagNo]-1; + sWorkTag = GetTagName( GetTag( iWorkTagNo)); + } + } else { + if( iaRightChild[iWorkTagNo] == 0 ) { + iaRightChild[iWorkTagNo] = iThisTagNo + 1; + iaParent[iThisTagNo] = iWorkTagNo + 1; + bDone = xbTrue; + } else { + iWorkTagNo = iaRightChild[iWorkTagNo]-1; + sWorkTag = GetTagName( GetTag( iWorkTagNo )); + } + } + } + } + } + + xbString s; + xbMdxTag *mpTag = mdxTagTbl; + for( xbInt16 i = 0; i < iTagUseCnt; i++ ){ + mpTag->cLeftChild = (char ) iaLeftChild[i]; + mpTag->cRightChild = (char ) iaRightChild[i]; + mpTag->cParent = (char ) iaParent[i]; + mpTag = mpTag->next; + } +} + +/**************************************************************************************************************/ +//! @brief Calculate the page number for a given block +/*! + This routine is called by any function needing to calculate the page number for a given block. + Page numbers are stored internally in the physical file, and the library reads and writes in + blocks of one or more pages. + + Assumes valid data input + + \param ulBlockNo Block Number + \returns Calculated page number. +*/ + +inline xbUInt32 xbIxMdx::BlockToPage( xbUInt32 ulBlockNo ){ + return ulBlockNo * (xbUInt32) iBlockFactor; +} +/***********************************************************************/ +char xbIxMdx::CalcTagKeyFmt( xbExp &exp ){ + + xbExpNode *n = exp.GetTreeHandle(); + if( n->GetChildCnt() == 0 && n->GetNodeType() == XB_EXP_FIELD ) + return 0x01; + else + return 0; +} +/***********************************************************************/ +//! @brief Check for duplicate key. +/*! + \param vpTag Tag to check. + \returns XB_KEY_NOT_UNIQUE
XB_NO_ERROR +*/ + +xbInt16 xbIxMdx::CheckForDupKey( void *vpTag ) +{ + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + xbMdxTag *mpTag = (xbMdxTag *) vpTag; + mpTag->bFoundSts = xbFalse; + try{ + if( GetUnique( mpTag )){ + if( mpTag->iKeySts == XB_ADD_KEY || mpTag->iKeySts == XB_UPD_KEY ) + if( KeyExists( mpTag )){ + if( GetUniqueKeyOpt() == XB_EMULATE_DBASE ){ + mpTag->bFoundSts = xbTrue; + return 0; + } else { + return XB_KEY_NOT_UNIQUE; + } + } + } + return 0; + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbIxMdx::CheckForDupKey() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return iRc; +} +/***********************************************************************/ +//! @brief Check tag integrity. +/*! + Check a tag for accuracy. + + \param vpTag Tag to create key for. + \param iOpt Output message destination
+ 0 = stdout
+ 1 = Syslog
+ 2 = Both
+ \returns Return Codes +*/ +xbInt16 xbIxMdx::CheckTagIntegrity( void *vpTag, xbInt16 iOpt ){ + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iRc2; + xbInt16 iRc3; + xbInt16 iErrorStop = 0; + xbUInt32 ulIxCnt = 0; + xbUInt32 ulThisRecNo = 0; + xbUInt32 ulPrevRecNo = 0; + xbBool bDone = false; + xbString sMsg; + char cKeyType; + char *pPrevKeyBuf = NULL; + xbMdxTag *npTag = (xbMdxTag *) vpTag; + xbBool bDescending = npTag->cKeyFmt2 & 0x08; + + #ifdef XB_LOCKING_SUPPORT + xbBool bLocked = xbFalse; + #endif + + try{ + + #ifdef XB_LOCKING_SUPPORT + if( dbf->GetAutoLock() && !dbf->GetTableLocked() ){ + if(( iRc = dbf->LockTable( XB_LOCK )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + bLocked = xbTrue; + } + #endif + + memset( npTag->cpKeyBuf2, 0x00, (size_t) npTag->iKeyLen ); + cKeyType = GetKeyType( vpTag ); + + pPrevKeyBuf = (char *) calloc( 1, (size_t) npTag->iKeyLen ); + iRc = GetFirstKey( vpTag, 0 ); + + memcpy( pPrevKeyBuf, GetKeyData( npTag->npCurNode, npTag->npCurNode->iCurKeyNo, npTag->iKeyItemLen ), (size_t) npTag->iKeyLen ); + + // for each key in the index, make sure it is trending in the right direction + while( iRc == XB_NO_ERROR && !bDone ){ + ulIxCnt++; + + iRc = GetNextKey( vpTag, 0 ); + if( iRc == XB_NO_ERROR ){ + + // compare this key to prev key + iRc2 = CompareKey( cKeyType, GetKeyData( npTag->npCurNode, npTag->npCurNode->iCurKeyNo, npTag->iKeyItemLen ), + pPrevKeyBuf, (size_t) npTag->iKeyLen ); + + if(( iRc2 < 0 && !bDescending ) || ( iRc2 > 0 && bDescending )){ + sMsg.Sprintf( "Key sequence error at key number [%ld].", ulIxCnt ); + xbase->WriteLogMessage( sMsg, iOpt ); + iErrorStop = 110; + iRc = XB_INVALID_INDEX; + throw iRc; + } + + ulThisRecNo = 0; + if(( iRc3 = GetDbfPtr( vpTag, npTag->npCurNode->iCurKeyNo, npTag->npCurNode, ulThisRecNo )) != XB_NO_ERROR ){ + iErrorStop = 120; + throw iRc3; + } + + if( iRc2 == 0 && (ulThisRecNo <= ulPrevRecNo )){ + sMsg.Sprintf( "Dbf record number sequence error at key number [%ld].", iOpt ); + xbase->WriteLogMessage( sMsg, iOpt ); + iErrorStop = 130; + iRc = XB_INVALID_INDEX; + throw iRc; + } + // save this key info to prev key + memcpy( pPrevKeyBuf, GetKeyData( npTag->npCurNode, npTag->npCurNode->iCurKeyNo, npTag->iKeyItemLen ), (size_t) npTag->iKeyLen ); + ulPrevRecNo = ulThisRecNo; + } + } + + xbUInt32 ulDbfCnt = 0; + if(( iRc = dbf->GetRecordCnt( ulDbfCnt )) != XB_NO_ERROR ){ + iErrorStop = 140; + throw iRc; + } + + xbUInt32 ulFiltCnt = 0; + xbBool bFiltered = xbTrue; + // verify each record in the dbf file has a corresponding index entry + xbUInt32 j = 0; + while( j < ulDbfCnt ){ + + if(( iRc = dbf->GetRecord( ++j )) != XB_NO_ERROR ){ + iErrorStop = 150; + throw iRc; + } + if( npTag->cHasFilter ){ + if(( iRc = npTag->filter->ProcessExpression( 0 )) != XB_NO_ERROR ){ + iErrorStop = 160; + throw iRc; + } + if(( iRc = npTag->filter->GetBoolResult( bFiltered )) != XB_NO_ERROR ){ + iErrorStop = 170; + throw iRc; + } + } + + if( bFiltered ){ + if(( iRc = FindKeyForCurRec( vpTag )) != XB_NO_ERROR ){ + ulThisRecNo = j; + iErrorStop = 180; + throw iRc; + } + ulFiltCnt++; + } + } + + if((GetUniqueKeyOpt() == XB_EMULATE_DBASE) && (GetUnique( vpTag ))){ + // can't compare counts if using XB_EMULATE_DBASE and it's a unique index + } else { + if( ulIxCnt != ulFiltCnt && GetUniqueKeyOpt() == XB_HALT_ON_DUPKEY ){ + if( npTag->cHasFilter ) + sMsg.Sprintf( "xbIxMdx::CheckTagIntegrity() Filtered index entry count [%ld] does not match dbf record count [%ld] for tag [%s]", ulIxCnt, ulFiltCnt, npTag->cTagName ); + else + sMsg.Sprintf( "xbIxMdx::CheckTagIntegrity() Index entry count [%ld] does not match dbf record count [%ld] for tag [%s]", ulIxCnt, ulFiltCnt, npTag->cTagName ); + + xbase->WriteLogMessage( sMsg, iOpt ); + iErrorStop = 190; + iRc = XB_INVALID_INDEX; + throw iRc; + } + if( npTag->cHasFilter ) + sMsg.Sprintf( "xbIxMdx::CheckTagIntegrity() Filtered index entry count [%ld] matches dbf record count [%ld] for tag [%s]", ulIxCnt, ulFiltCnt, npTag->cTagName ); + else + sMsg.Sprintf( "xbIxMdx::CheckTagIntegrity() Index entry count [%ld] matches dbf record count [%ld] for tag [%s]", ulIxCnt, ulFiltCnt, npTag->cTagName ); + xbase->WriteLogMessage( sMsg, iOpt ); + } + + if( pPrevKeyBuf ) + free( pPrevKeyBuf ); + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbIxMdx::CheckTagIntegrity() Exception Caught. Error Stop = [%d] iRc = [%d] Tag = [%s]", iErrorStop, iRc, npTag->cTagName ); + xbase->WriteLogMessage( sMsg, iOpt ); + xbase->WriteLogMessage( GetErrorMessage( iRc ), iOpt ); + if( pPrevKeyBuf ) + free( pPrevKeyBuf ); + + if( iErrorStop == 160 ){ + sMsg.Sprintf( "xbIxMdx::CheckTagIntegrity() Missing index entry for record [%d]", ulThisRecNo ); + xbase->WriteLogMessage( sMsg, iOpt ); + } + } + #ifdef XB_LOCKING_SUPPORT + if( bLocked ){ + dbf->LockTable( XB_UNLOCK ); + } + #endif + + return iRc; +} + +/***********************************************************************/ +xbMdxTag *xbIxMdx::ClearTagTable(){ + + // clear the list of tags + xbMdxTag *tt = mdxTagTbl; + xbMdxTag *tt2; + while( tt ){ + tt2 = tt; + tt = tt->next; + tt2->npNodeChain = FreeNodeChain( tt2->npNodeChain ); + tt2->npCurNode = NULL; + if( tt2->cpKeyBuf ) + free( tt2->cpKeyBuf ); + if( tt2->cpKeyBuf2 ) + free( tt2->cpKeyBuf2 ); + if( tt2->exp ) + delete tt2->exp; + if( tt2->filter ) + delete tt2->filter; + if( tt2->sKeyExp ) + delete tt2->sKeyExp; + if( tt2->sTagName ) + delete tt2->sTagName; + if( tt2->sFiltExp ) + delete tt2->sFiltExp; + free( tt2 ); + } + return NULL; +} +/***********************************************************************/ +xbInt16 xbIxMdx::Close(){ + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + + try{ + mdxTagTbl = ClearTagTable(); + if(( iRc = xbIx::Close()) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbIxMdx::Close() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return iRc; +} + +/***********************************************************************/ +//! @brief Create key. +/*! + + \param vpTag Tag + \param iOpt 1 = Append, 2 = Update + \returns XB_KEY_NOT_UNIQUE
XB_NO_ERROR + + iKeySts 0 - No Updates + 1 - Add Key XB_ADD_KEY + 2 - Update Key XB_UPD_KEY + 3 - Delete Key XB_DEL_KEY + + bKeyFiltered xbFalse - Key filtered out + xbTrue - Key filtered in + + cpKeyBuf - Key buffer for add + cpKeyBuf2 - Key buffer for delete + +*/ + +xbInt16 xbIxMdx::CreateKey( void *vpTag, xbInt16 iOpt ){ + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + + xbBool bFilter0 = xbFalse; // filter against RecBuf, updated record buffer + xbBool bFilter1 = xbFalse; // filter against recBuf2, original record buffer + + try{ + + xbMdxTag *npTag = (xbMdxTag *) vpTag; + npTag->iKeySts = 0; + + // do tag filter logic + if( npTag->cHasFilter ){ + if(( iRc = npTag->filter->ProcessExpression( 0 )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + if(( iRc = npTag->filter->GetBoolResult( bFilter0 )) != XB_NO_ERROR ){ + iErrorStop = 110; + throw iRc; + } + } else { + bFilter0 = xbTrue; + } + + // if add request and filtered out, we're done + if( iOpt == 1 && !bFilter0 ) + return XB_NO_ERROR; + + if(( iRc = npTag->exp->ProcessExpression( 0 )) != XB_NO_ERROR ){ + iErrorStop = 120; + throw iRc; + } + if( npTag->exp->GetReturnType() == XB_EXP_CHAR ) + npTag->exp->GetStringResult( npTag->cpKeyBuf, (xbUInt32) npTag->iKeyLen ); + else if( npTag->exp->GetReturnType() == XB_EXP_NUMERIC ){ + xbDouble d; + npTag->exp->GetNumericResult( d ); + xbBcd bcd( d ); + bcd.ToChar( npTag->cpKeyBuf ); + } + else if( npTag->exp->GetReturnType() == XB_EXP_DATE ){ + xbDouble d; + npTag->exp->GetNumericResult( d ); + memcpy( npTag->cpKeyBuf, &d, 8 ); + } + + if( iOpt == 1 ) // Append + npTag->iKeySts = XB_ADD_KEY; + + else if( iOpt == 2 ){ // Update + if( npTag->cHasFilter ){ + if(( iRc = npTag->filter->ProcessExpression( 1 )) != XB_NO_ERROR ){ + iErrorStop = 130; + throw iRc; + } + if(( iRc = npTag->filter->GetBoolResult( bFilter1 )) != XB_NO_ERROR ){ + iErrorStop = 140; + throw iRc; + } + } else { + bFilter1 = xbTrue; + } + + if(( iRc = npTag->exp->ProcessExpression( 1 )) != XB_NO_ERROR ){ + iErrorStop = 150; + throw iRc; + } + + if( npTag->exp->GetReturnType() == XB_EXP_CHAR ){ + npTag->exp->GetStringResult( npTag->cpKeyBuf2, (xbUInt32) npTag->iKeyLen ); + + } else if( npTag->exp->GetReturnType() == XB_EXP_NUMERIC ){ + xbDouble d; + npTag->exp->GetNumericResult( d ); + xbBcd bcd( d ); + bcd.ToChar( npTag->cpKeyBuf2 ); + + } else if( npTag->exp->GetReturnType() == XB_EXP_DATE ){ + xbDouble d; + npTag->exp->GetNumericResult( d ); + memcpy( npTag->cpKeyBuf2, &d, 8 ); + + } + + if( bFilter1 ){ // original key was indexed + if( bFilter0 ){ // new key s/b indexed, update it if changed + if( memcmp( npTag->cpKeyBuf, npTag->cpKeyBuf2, (size_t) npTag->iKeyLen )){ + npTag->iKeySts = XB_UPD_KEY; + } + } else { // original key indexed, new key not indexed, delete it + npTag->iKeySts = XB_DEL_KEY; + } + } else { // original key not indexed + if( bFilter0 ) + npTag->iKeySts = XB_ADD_KEY; + } + } + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbIxMdx::CreateKey() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return iRc; +} +/***********************************************************************/ +//! @brief Create new tag. +/*! + This routine creates a new tag. When complete, sets the cur tag pointer to + the newly created tag. + + + \param sName Tag Name, including .MDX suffix + \param sKey Key Expression + \param sFilter Filter expression. + \param iDescending + \param iUnique xbtrue - Unique.
xbFalse - Not unique. + \param iOverLay xbTrue - Overlay if file already exists.
xbFalse - Don't overlay. + \param vpTag Output from method Pointer to vptag pointer. + \returns Return Codes +*/ + + +xbInt16 xbIxMdx::CreateTag( const xbString &sName, const xbString &sKey, const xbString &sFilter, xbInt16 iDescending, xbInt16 iUnique, xbInt16 iOverlay, void **vpTag ){ + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + xbMdxTag *tte = NULL; + + try{ + // verify room for new tag + if( !( iTagUseCnt < 47 )){ + iErrorStop = 100; + iRc = XB_LIMIT_REACHED; + throw iRc; + } + + // verify valid tag name + xbString sWorker = sName; + sWorker.Trim(); + if( sWorker.Len() > 10 ){ + iErrorStop = 110; + iRc = XB_INVALID_TAG; + throw iRc; + } + + // verify tag not already defined + if( iTagUseCnt > 0 ){ + if( GetTag( sWorker )){ + iErrorStop = 120; + iRc = XB_INVALID_TAG; + throw iRc; + } + } + + // allocate a tag structure here + if(( tte = (xbMdxTag *) calloc( 1, (size_t) sizeof( xbMdxTag ))) == NULL ){ + iErrorStop = 130; + iRc = XB_NO_MEMORY; + throw iRc; + } + *vpTag = tte; + tte->sTagName = new xbString( sWorker ); + + //set up the key expression + sWorker = sFilter; + sWorker.Trim(); + if( sWorker.Len() > 0 ){ + if( sWorker.Len() == 0 || sWorker.Len() > 220 ){ + iRc = XB_INVALID_TAG; + iErrorStop = 140; + throw iRc; + } + tte->sFiltExp = new xbString( sWorker ); + tte->filter = new xbExp( dbf->GetXbasePtr()); + if(( iRc = tte->filter->ParseExpression( dbf, sWorker )) != XB_NO_ERROR ){ + iErrorStop = 150; + throw iRc; + } + + if((tte->filter->GetReturnType()) != 'L' ){ + iRc = XB_INVALID_TAG; + iErrorStop = 160; + throw iRc; + } + tte->cHasFilter = 0x01; + } + + //set up the key expression + sWorker = sKey; + sWorker.Trim(); + if( sWorker.Len() == 0 || sWorker.Len() > 100 ){ + iRc = XB_INVALID_TAG; + iErrorStop = 170; + throw iRc; + } + tte->sKeyExp = new xbString( sWorker ); + tte->exp = new xbExp( dbf->GetXbasePtr()); + if(( iRc = tte->exp->ParseExpression( dbf, sWorker )) != XB_NO_ERROR ){ + iErrorStop = 180; + throw iRc; + } + + xbDate d; + d.Sysdate(); + if( iTagUseCnt == 0 ){ + // first tag, new mdx file + // create the file name + xbString sIxFileName = dbf->GetFqFileName(); + sIxFileName.Trim(); + xbUInt32 lLen = sIxFileName.Len(); + sIxFileName.PutAt( lLen-2, 'M' ); + sIxFileName.PutAt( lLen-1, 'D' ); + sIxFileName.PutAt( lLen, 'X' ); + + // copy the file name to the class variable + this->SetFileName( sIxFileName ); + if( FileExists() && !iOverlay ){ + iErrorStop = 190; + iRc = XB_FILE_EXISTS; + throw iRc; + } + + // first tag, need to create the file + if(( iRc = xbFopen( "w+b", dbf->GetShareMode())) != XB_NO_ERROR ){ + iErrorStop = 200; + throw iRc; + } + cVersion = 2; + cCreateYY = (char) d.YearOf() - 1900; + cCreateMM = (char) d.MonthOf(); + cCreateDD = (char) d.DayOf( XB_FMT_MONTH ); + + GetFileNamePart( sFileName ); + sFileName.ToUpperCase(); + + SetBlockSize( (xbUInt32) dbf->GetCreateMdxBlockSize()); + iBlockFactor = GetBlockSize() / 512; + + cProdIxFlag = 1; + cTagEntryCnt = 48; + iTagLen = 32; + ulPageCnt = 4; + ulFirstFreePage = 0; + ulNoOfBlockAvail = 0; + cNextTag = 1; + c1B = 0x1B; + cUpdateYY = cCreateYY; + cUpdateMM = cCreateMM; + cUpdateDD = cCreateDD; + + if(( iRc = WriteHeadBlock( 0 )) != XB_NO_ERROR ){ + iErrorStop = 210; + throw iRc; + } + } + + // populate the tag table entry structure + tte->ulTagHdrPageNo = ulPageCnt; + ulPageCnt += (xbUInt32) iBlockFactor; + tte->sTagName->strncpy( tte->cTagName, 10 ); + + // cKeyFmt is always 0x10; + // tested 2+ZIPCD CITY+STATE or just standalone field - always 0x10 + tte->cKeyFmt = 0x10; // = CalcTagKeyFmt( *tte->exp ); + + switch( tte->exp->GetReturnType()){ + case XB_EXP_CHAR: + tte->cKeyType = 'C'; + tte->iKeyLen = tte->exp->GetResultLen(); + tte->iSecKeyType = 0; + break; + + case XB_EXP_NUMERIC: + tte->cKeyType = 'N'; + tte->iKeyLen = 12; + tte->iSecKeyType = 0; + break; + + case XB_EXP_DATE: + tte->cKeyType = 'D'; + tte->iKeyLen = 8; + tte->iSecKeyType = 1; + break; + + default: + iErrorStop = 200; + iRc = XB_INVALID_INDEX; + throw iRc; + } + + tte->cpKeyBuf = (char *) malloc( (size_t) tte->iKeyLen + 1 ); + tte->cpKeyBuf2 = (char *) malloc( (size_t) tte->iKeyLen + 1 ); + + // write the new tte entry here + char tteBuf[21]; + memset( tteBuf, 0x00, 21 ); + + ePutUInt32( &tteBuf[0], tte->ulTagHdrPageNo ); + for( xbUInt32 l = 0; l < tte->sTagName->Len() && l < 10; l++ ){ + tteBuf[l+4] = tte->sTagName->GetCharacter(l+1); + } + tteBuf[15] = tte->cKeyFmt; + tteBuf[19] = 0x02; // appears to always be a 0x02 + tteBuf[20] = tte->cKeyType; + + if(( iRc = xbFseek( (iTagUseCnt * 32) + 544, SEEK_SET )) != XB_NO_ERROR ){ + iErrorStop = 210; + throw iRc; + } + if(( iRc = xbFwrite( tteBuf, 21, 1 )) != XB_NO_ERROR ){ + iErrorStop = 220; + throw iRc; + } + + // Begin Tag Header + tte->ulRootPage = ulPageCnt; + tte->ulTagSize = (xbUInt32) iBlockFactor; + ulPageCnt += 2; + tte->cKeyFmt2 = 0x10; + if( iDescending ) + tte->cKeyFmt2 += 0x08; + if( iUnique ){ + tte->cKeyFmt2 += 0x40; + tte->cUnique = 0x01; + } + + tte->cTag11 = 0x1B; // always 0x1b ? + tte->cSerialNo = 0x01; // version incremented with each tag update + tte->ulLeftChild = tte->ulRootPage; + tte->ulRightChild = tte->ulRootPage; + + tte->cTagYY = (char) d.YearOf() - 1900; + tte->cTagMM = (char) d.MonthOf(); + tte->cTagDD = (char) d.DayOf( XB_FMT_MONTH ); + + tte->cKeyType2 = tte->cKeyType; + tte->iKeyItemLen = tte->iKeyLen + 4; + while(( tte->iKeyItemLen % 4 ) != 0 ) tte->iKeyItemLen++; + + tte->iKeysPerBlock = (xbInt16) (GetBlockSize() - 12) / tte->iKeyItemLen; + tte->cKeyFmt3 = CalcTagKeyFmt( *tte->exp ); + + char *pBuf; + if(( pBuf = (char *) calloc( 1, (size_t) GetBlockSize())) == NULL ){ + iErrorStop = 230; + iRc = XB_NO_MEMORY; + throw iRc; + } + char *wPtr; + wPtr = pBuf; + ePutUInt32( wPtr, tte->ulRootPage ); + + wPtr += 4; + ePutUInt32( wPtr, tte->ulTagSize ); + + wPtr += 4; + *wPtr = tte->cKeyFmt2; + + wPtr++; + *wPtr = tte->cKeyType2; + + wPtr += 2; + *wPtr = tte->cTag11; + + wPtr += 1; + ePutInt16( wPtr, tte->iKeyLen ); + + wPtr += 2; + ePutInt16( wPtr, tte->iKeysPerBlock ); + + wPtr += 2; + ePutInt16( wPtr, tte->iSecKeyType ); + + wPtr += 2; + ePutInt16( wPtr, tte->iKeyItemLen ); + + wPtr += 2; + *wPtr = tte->cSerialNo; + + wPtr += 3; + *wPtr = tte->cUnique; + + wPtr++; + for( xbUInt32 l = 0; l < tte->sKeyExp->Len(); l++ ) + *wPtr++ = tte->sKeyExp->GetCharacter(l+1); + + wPtr = pBuf; + + tte->cHasKeys = 0x00; + pBuf[246] = tte->cHasKeys; + + wPtr += 248; + ePutUInt32( wPtr, tte->ulLeftChild ); + wPtr += 4; + ePutUInt32( wPtr, tte->ulRightChild ); + + pBuf[257] = tte->cTagYY; + pBuf[258] = tte->cTagMM; + pBuf[259] = tte->cTagDD; + pBuf[480] = tte->cKeyFmt3; + + if( sFilter.Len() > 0 ){ + pBuf[245] = tte->cHasFilter; + wPtr = pBuf; + wPtr += 762; + for( xbUInt32 l = 0; l < sFilter.Len(); l++ ) + *wPtr++ = sFilter.GetCharacter(l+1); + } + + if(( iRc = xbFseek( tte->ulTagHdrPageNo * 512, SEEK_SET )) != XB_NO_ERROR ){ + iErrorStop = 240; + throw iRc; + } + + if(( iRc = xbFwrite( pBuf, GetBlockSize(), 1 )) != XB_NO_ERROR ){ + iErrorStop = 250; + throw iRc; + } + + memset( pBuf, 0x00, GetBlockSize() ); + if(( iRc = xbFwrite( pBuf, GetBlockSize(), 1 )) != XB_NO_ERROR ){ + iErrorStop = 260; + throw iRc; + } + + iTagUseCnt++; + cNextTag++; + + if(( iRc = WriteHeadBlock( 1 )) != XB_NO_ERROR ){ + iErrorStop = 270; + throw iRc; + } + + // update the dbf file if needed - discreet field, has no filter + // 10/15/22 - dbase 7 does not update this field on index creation + if( tte->cKeyFmt3 == 0x01 && !tte->cHasFilter ){ + // printf( "cKeyFmt3 = [%x]\n", tte->cKeyFmt3 ); + xbInt16 iFldNo; + if(( iRc = dbf->GetFieldNo( sKey, iFldNo )) != XB_NO_ERROR ){ + iErrorStop = 280; + throw iRc; + } + xbInt64 lOffset = 31 + ((iFldNo + 1) * 32 ); + + if(( iRc = dbf->xbFseek( lOffset, SEEK_SET )) != XB_NO_ERROR ){ + iErrorStop = 290; + iRc = XB_SEEK_ERROR; + throw iRc; + } + char cBuf[2]; + cBuf[0] = 0x01; + cBuf[1] = 0x00; + + if(( iRc = dbf->xbFwrite( cBuf, 1, 1 )) != XB_NO_ERROR ){ + iErrorStop = 300; + throw iRc; + } + dbf->UpdateSchemaIxFlag( iFldNo, 0x01 ); + } + + // add the new entry to the end of the list of tags + if( mdxTagTbl == NULL ){ + mdxTagTbl = tte; + } else { + xbMdxTag *tteL = mdxTagTbl; + while( tteL->next ) + tteL = tteL->next; + tteL->next = tte; + } + + /* update the btree pointers */ + CalcBtreePointers(); + char bBuf[3]; + xbMdxTag *tteWork = mdxTagTbl; + + if(( iRc = xbFseek( 560, SEEK_SET )) != XB_NO_ERROR ){ + iErrorStop = 310; + throw iRc; + } + while( tteWork ){ + bBuf[0] = tteWork->cLeftChild; + bBuf[1] = tteWork->cRightChild; + bBuf[2] = tteWork->cParent; + + if(( iRc = xbFwrite( bBuf, 3, 1 )) != XB_NO_ERROR ){ + iErrorStop = 320; + throw iRc; + } + if( tteWork->next ){ + if(( iRc = xbFseek( 29, SEEK_CUR )) != XB_NO_ERROR ){ + iErrorStop = 330; + throw iRc; + } + } + tteWork = tteWork->next; + } + free( pBuf ); + } + + catch (xbInt16 iRc ){ + if( tte ){ + if( tte->cpKeyBuf ) + free( tte->cpKeyBuf ); + if( tte->cpKeyBuf2 ) + free( tte->cpKeyBuf2 ); + if( tte->exp ) + delete tte->exp; + if( tte->filter ) + delete tte->filter; + if( tte->sKeyExp ) + delete tte->sKeyExp; + if( tte->sFiltExp ) + delete tte->sFiltExp; + if( tte->sTagName ) + delete tte->sTagName; + free( tte ); + } + xbString sMsg; + sMsg.Sprintf( "xbIxMdx::CreateTag() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + + return iRc; +}; + +/***********************************************************************/ +//! @brief Delete a key from a node. +/*! + This routine deletes a key from a supplied node. + \param vpTag Tag to delete key on. + \param npNode Node to delete key on. + \param iSlotNo Slot number of key to delete. + \returns Return Codes +*/ +xbInt16 xbIxMdx::DeleteFromNode( void *vpTag, xbIxNode * npNode, xbInt16 iSlotNo ) +{ + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + xbMdxTag * npTag = (xbMdxTag *) vpTag; + + try{ + xbInt32 lKeyCnt = GetKeyCount( npNode ); + xbInt16 iLen = (lKeyCnt - iSlotNo - 1) * npTag->iKeyItemLen; + xbBool bLeaf = IsLeaf( vpTag, npNode ); + if( !bLeaf ) + iLen += 4; + + char *pTrg = npNode->cpBlockData; + if( iLen > 0 ){ + pTrg += (8 + (npTag->iKeyItemLen * (iSlotNo)) ); //lTrgPos; + // std::cout << "TrgSpot = " << (8 + (npTag->iKeyItemLen * (iSlotNo)) ) << "\n"; + char *pSrc = pTrg; + pSrc += npTag->iKeyItemLen; + memmove( pTrg, pSrc, (size_t) iLen ); + } + + // zap out the right most key + pTrg = npNode->cpBlockData; + if( bLeaf ){ + pTrg += (8 + (npTag->iKeyItemLen * ( lKeyCnt-1 ))); + + } else { + pTrg += (12 + (npTag->iKeyItemLen * ( lKeyCnt-1 ))); + + } + + for( xbInt16 i = 0; i < npTag->iKeyItemLen; i++ ) + *pTrg++ = 0x00; + + // set the new number of keys + ePutUInt32( npNode->cpBlockData, (xbUInt32) lKeyCnt - 1 ); + + // if node empty, add it to the free node chain + if( lKeyCnt < 2 ){ + if( bReuseEmptyNodes ){ + if( bLeaf && lKeyCnt == 1 ){ + if(( iRc = HarvestEmptyNode( npTag, npNode, 0 )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + } + } + } + + // write out the block + if(( iRc = WriteBlock( npNode->ulBlockNo, GetBlockSize(), npNode->cpBlockData )) != XB_NO_ERROR ){ + iErrorStop = 110; + throw iRc; + } + + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbIxMdx::DeleteFromNode() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return XB_NO_ERROR; +} + +/***********************************************************************/ +//! @brief Delete a key. +/*! + This routine deletes a key. It assumes the key to delete + is the current key in the node chain. + + \param vpTag Tag to delete key on. + + \returns Return Codes +*/ + +xbInt16 xbIxMdx::DeleteKey( void *vpTag ){ + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + xbMdxTag * npTag = (xbMdxTag *) vpTag; + + // save copy of node chain to reset to after delete completed + xbIxNode *npSaveNodeChain = npTag->npNodeChain; + npTag->npNodeChain = NULL; + xbIxNode * npSaveCurNode = npTag->npCurNode; + + try{ + xbString sMsg; + + if(( iRc = xbIxMdx::KeySetPosDel( npTag )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + // Delete key needs to handle two scenarios + // 1 - if delete is on the only key of leaf, + // traverse up tree, trim as needed + // 2 - if last key on node is deleted, and key value is not the same + // as prev key, ascend tree looking for an interior node needing + // updated key value + + xbInt32 lOrigKeyCnt = GetKeyCount( npTag->npCurNode ); + if(( iRc = DeleteFromNode( npTag, npTag->npCurNode, npTag->npCurNode->iCurKeyNo )) != XB_NO_ERROR ){ + iErrorStop = 110; + throw iRc; + } + + if( lOrigKeyCnt == 1 ){ + + // scenario 1 + xbBool bDone = xbFalse; + xbBool bIsLeaf = xbFalse; + xbInt32 lKeyCnt; + npTag->npCurNode = npTag->npCurNode->npPrev; + + while( npTag->npCurNode && !bDone ){ + lKeyCnt = GetKeyCount( npTag->npCurNode ); + bIsLeaf = IsLeaf( npTag, npTag->npCurNode ); + if( lKeyCnt > 0 ){ + if(( iRc = DeleteFromNode( npTag, npTag->npCurNode, npTag->npCurNode->iCurKeyNo )) != XB_NO_ERROR ){ + iErrorStop = 120; + throw iRc; + } + } else if(( iRc = HarvestEmptyNode( npTag, npTag->npCurNode, 1 )) != XB_NO_ERROR ){ + iErrorStop = 130; + throw iRc; + } + + if( (bIsLeaf && lKeyCnt > 1) || (!bIsLeaf && lKeyCnt > 0) ) + bDone = xbTrue; + else + npTag->npCurNode = npTag->npCurNode->npPrev; + } + + } else if( npTag->npCurNode->iCurKeyNo == (xbUInt32) lOrigKeyCnt - 1 ){ + + // scenario 2 + // if last two keys identical, then nothing to do, else go up looking for a key to change + if( memcmp( GetKeyData( npTag->npCurNode, npTag->npCurNode->iCurKeyNo, npTag->iKeyItemLen ), + GetKeyData( npTag->npCurNode, npTag->npCurNode->iCurKeyNo-1, npTag->iKeyItemLen ), + (size_t) npTag->iKeyLen )){ + + xbIxNode *pNode = npTag->npCurNode->npPrev; + char *pSrc = GetKeyData( npTag->npCurNode, npTag->npCurNode->iCurKeyNo-1, npTag->iKeyItemLen ); + + while( pNode && pNode->ulBlockNo != npTag->ulRootPage && pNode->iCurKeyNo == (xbUInt32) GetKeyCount( pNode ) ) + pNode = pNode->npPrev; + + if( pNode ){ + if( pNode->iCurKeyNo < (xbUInt32) GetKeyCount( pNode )){ + char *pTrg = pNode->cpBlockData; + pTrg += 12 + (pNode->iCurKeyNo * (xbUInt32) npTag->iKeyItemLen); + for( xbInt16 i = 0; i < npTag->iKeyLen; i++ ) + *pTrg++ = *pSrc++; + // write out the block + if(( iRc = WriteBlock( pNode->ulBlockNo, GetBlockSize(), pNode->cpBlockData )) != XB_NO_ERROR ){ + iErrorStop = 140; + throw iRc; + } + } + } + } + } + // restore node chain to pre delete status (which should be post add status) + npTag->npNodeChain = FreeNodeChain( npTag->npNodeChain ); + npTag->npNodeChain = npSaveNodeChain; + npTag->npCurNode = npSaveCurNode; + + // update the serial number + if(( iRc = TagSerialNo( 3, npTag )) != XB_NO_ERROR ){ + iErrorStop = 150; + throw iRc; + } + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbIxMdx::DeleteKey() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + if( npSaveNodeChain ){ + npTag->npNodeChain = npSaveNodeChain; + npSaveNodeChain = FreeNodeChain( npSaveNodeChain ); + npTag->npCurNode = npSaveCurNode; + } + } + return iRc; +} + +/***********************************************************************/ +//! @brief Delete a given tag +/*! + \param vpTag Input tag ptr for tag to be deleted
+ \returns Return Codes
+ 1 = Deleted entire MDX file, only had one tag + +*/ + +xbInt16 xbIxMdx::DeleteTag( void *vpTag ){ + + xbInt16 iRc = 0; + xbInt16 iErrorStop = 0; + xbMdxTag * mpTag = (xbMdxTag *) vpTag; + xbIxNode *n = NULL; + xbBool bLoneTag = xbFalse; + + try{ + + if( !vpTag ){ + iErrorStop = 100; + iRc = XB_INVALID_TAG; + throw iRc; + } + + char cSaveHasFilter = mpTag->cHasFilter; + char cSaveKeyFmt3 = mpTag->cKeyFmt3; + xbString sSaveKey = mpTag->sKeyExp->Str(); + + if( iTagUseCnt == 1 ){ + // std::cout << "xbIxMdx::DeleteTag - one tag found, delete the mdx file\n"; + + // close the mdx file + if(( iRc = xbIxMdx::Close()) != XB_NO_ERROR ){ + iErrorStop = 110; + throw iRc; + } + + // delete the file + xbRemove(); + + // init variables - needed? + // Init(); + // iRc > 0 defines this as the only tag in an MDX file, MDX file deleted. + // signals to the calling process to drop the MDX file from the + // list of updateable indices. + bLoneTag = xbTrue; + + } else { + + // harvest tag nodes + + if(( iRc = HarvestTagNodes( mpTag, xbTrue )) != XB_NO_ERROR ){ + iErrorStop = 120; + throw iRc; + } + + // remove an entry from tag table + // which tag is this? + xbInt16 iTagNo = 0; + xbMdxTag *mp = mdxTagTbl; + xbMdxTag *mpPrev = NULL; + while( mp && mp->ulTagHdrPageNo != mpTag->ulTagHdrPageNo ){ + iTagNo++; + mpPrev = mp; + mp = mp->next; + } + + // remove it from the linked list of tags + if( !mpPrev ){ + mdxTagTbl = mp->next; + } else { + mpPrev->next = mp->next; + } + if( mp ){ + if( mp->cpKeyBuf ) free( mp->cpKeyBuf ); + if( mp->cpKeyBuf2 ) free( mp->cpKeyBuf2 ); + if( mp->exp ) delete mp->exp; + if( mp->filter ) delete mp->filter; + if( mp->sKeyExp ) delete mp->sKeyExp; + if( mp->sFiltExp ) delete mp->sFiltExp; + if( mp->sTagName ) delete mp->sTagName; + free( mp ); + } + xbInt32 iTarg = iTagNo * 32; + xbInt32 iSrc = iTarg + 32; + xbInt32 iLen = (iTagUseCnt - iTagNo) * 32; + + if(( iRc = xbFseek( 544, SEEK_SET )) != XB_NO_ERROR ){ + iErrorStop = 160; + throw iRc; + } + char Buf[1536]; // 47 tags + 1 in case tag #47 is deleted + memset( Buf, 0x00, 1536 ); + if(( iRc = xbFread( Buf, 1504, 1 )) != XB_NO_ERROR ){ + iErrorStop = 170; + throw iRc; + } + char *pTrg = Buf; + pTrg += iTarg; + char *pSrc = Buf; + pSrc += iSrc; + for( xbInt32 i = 0; i < iLen; i++ ) + *pTrg++ = *pSrc++; + + if(( iRc = xbFseek( 544, SEEK_SET )) != XB_NO_ERROR ){ + iErrorStop = 180; + throw iRc; + } + if(( iRc = xbFwrite( Buf, 1504, 1 )) != XB_NO_ERROR ){ + iErrorStop = 190; + throw iRc; + } + + iTagUseCnt--; + if(( iRc = WriteHeadBlock( 1 )) != XB_NO_ERROR ){ + iErrorStop = 200; + throw iRc; + } + + /* update the btree pointers */ + CalcBtreePointers(); + char bBuf[3]; + xbMdxTag *tteWork = mdxTagTbl; + + if(( iRc = xbFseek( 560, SEEK_SET )) != XB_NO_ERROR ){ + iErrorStop = 210; + throw iRc; + } + while( tteWork ){ + bBuf[0] = tteWork->cLeftChild; + bBuf[1] = tteWork->cRightChild; + bBuf[2] = tteWork->cParent; + + if(( iRc = xbFwrite( bBuf, 3, 1 )) != XB_NO_ERROR ){ + iErrorStop = 320; + throw iRc; + } + if( tteWork->next ){ + if(( iRc = xbFseek( 29, SEEK_CUR )) != XB_NO_ERROR ){ + iErrorStop = 330; + throw iRc; + } + } + tteWork = tteWork->next; + } + } + + // update the dbf file if needed, if discreet field with no filter + // printf( "cSaveKeyFmt3 = [%x] cSaveHasFilter=[%x] SaveKey = [%s]\n", cSaveKeyFmt3, cSaveHasFilter, sSaveKey.Str()); + + if( cSaveKeyFmt3 == 0x01 && !cSaveHasFilter ){ + xbInt16 iFldNo; + if(( iRc = dbf->GetFieldNo( sSaveKey, iFldNo )) != XB_NO_ERROR ){ + iErrorStop = 340; + throw iRc; + } + xbInt64 lOffset = 31 + ((iFldNo + 1) * 32 ); + + if(( iRc = dbf->xbFseek( lOffset, SEEK_SET )) != XB_NO_ERROR ){ + iErrorStop = 350; + iRc = XB_SEEK_ERROR; + throw iRc; + } + char cBuf[2]; + cBuf[0] = 0x00; + cBuf[1] = 0x00; + if(( iRc = dbf->xbFwrite( cBuf, 1, 1 )) != XB_NO_ERROR ){ + iErrorStop = 360; + throw iRc; + } + dbf->UpdateSchemaIxFlag( iFldNo, 0x00 ); + } + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbIxMdx::DeleteTag() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + if( n ) + free( n ); + } + if( bLoneTag && !iRc ) + return 1; + else + return iRc; +} + +/***********************************************************************/ +#ifdef XB_DEBUG_SUPPORT + +//! @brief Dump a given block for a tag +/*! + \param iOpt Output message destination
+ 0 = stdout
+ 1 = Syslog
+ 2 = Both
+ \param ulBlockNo Block number to dump + \param mpTag Index tag pointer + \returns Return Codes + +*/ + +xbInt16 xbIxMdx::DumpBlock( xbInt16 iOpt, xbUInt32 ulBlockNo, xbMdxTag *mpTag ){ + + xbInt16 iRc = 0; + xbInt16 iErrorStop = 0; + xbString s, s2; + xbBool bLeaf; + char *p; + + try{ + if(( iRc = GetBlock( mpTag, ulBlockNo, 0 )) != XB_NO_ERROR ){ + iErrorStop = 10; + throw iRc; + } + p = cNodeBuf; + xbInt32 lNoOfKeys = eGetInt32( p ); + p+=4; + xbUInt32 ulNode2 = eGetUInt32( p ); + + if( !mpTag ){ + // if no tag info, print what is available without tag info and exit + s.Sprintf( "--- BlkNo = %ld Page = %ld NoOfKeys = %ld Node2 (opt NextFreePage) = %ld", ulBlockNo, BlockToPage( ulBlockNo ), lNoOfKeys, ulNode2 ); + xbase->WriteLogMessage( s, iOpt ); + return XB_NO_ERROR; + } + + p+=4; + p+= mpTag->iKeyItemLen * lNoOfKeys; + if( eGetUInt32( p ) == 0 ){ + bLeaf = xbTrue; + // std::cout << "No of keys = " << lNoOfKeys << "\n"; + s.Sprintf( "--- Leaf Node KeyCnt %d\t Page %d\t Block %d", lNoOfKeys, BlockToPage( ulBlockNo ), ulBlockNo ); + } else { + bLeaf = xbFalse; + s.Sprintf( "--- Interior Node KeyCnt %d\t Page %d\t Block %d", lNoOfKeys, BlockToPage( ulBlockNo ), ulBlockNo ); + } + if( ulNode2 > 0 ) + s.Sprintf( "%s Node2 (opt NextFreePage) = %d", s.Str(), ulNode2 ); + + xbase->WriteLogMessage( s, iOpt ); + + xbInt32 l; + for( l = 0; l < lNoOfKeys; l++ ){ + p = cNodeBuf + (8 + (l * mpTag->iKeyItemLen )); + s.Sprintf( "%08ld\t", eGetUInt32( p )); + p+=4; + if( mpTag->cKeyType2 == 'C' ){ //CHAR + for( xbInt32 l = 0; l < (mpTag->iKeyItemLen-4); l++ ) + s += *p++; + s.Trim(); + } else if( mpTag->cKeyType2 == 'N' ){ // NUMERIC + xbBcd bcd( p ); + bcd.ToString( s2 ); + s += s2; + } else if( mpTag->cKeyType2 == 'D' ){ // DATE + xbInt32 lDate = (xbInt32) eGetDouble( p ); + xbDate d( lDate ); + s.Sprintf( "%s\t%ld\t(%s)", s.Str(), lDate, d.Str()); + } else { + s.Sprintf( "Unknown key type [%c]", mpTag->cKeyType2 ); + } + xbase->WriteLogMessage( s, iOpt ); + } + if( !bLeaf ){ + // interior node has one extra key at the right most position + p = cNodeBuf + (8 + (l * mpTag->iKeyItemLen )); + s.Sprintf( "\t%08ld", eGetUInt32( p )); + xbase->WriteLogMessage( s, iOpt ); + } + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbIxMdx::DumpBlock() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return iRc; +} + +/**************************************************************************************************************/ +//! @brief Dump free blocks. +/*! + Dump free blocks for index debugging purposes. + + \param iOpt Output message destination
+ 0 = stdout
+ 1 = Syslog
+ 2 = Both
+ \returns Return Codes + +*/ +xbInt16 xbIxMdx::DumpFreeBlocks( xbInt16 iOpt ){ + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + xbString s; + char *pBuf = NULL; + char *pNextPage; + xbUInt32 ulNextPage; + + try{ + + if(( iRc = ReadHeadBlock( 1 )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + + xbUInt32 ulLastBlock = PageToBlock( ulPageCnt ); + + pBuf = (char *) malloc( (size_t) GetBlockSize()); + if( !pBuf ){ + iErrorStop = 110; + iRc = XB_NO_MEMORY; + throw iRc; + } + + if( ulFirstFreePage > 0 ){ + xbUInt32 ulThisFreePage = ulFirstFreePage; + xbUInt32 ulNextFreePage = 0; + xbUInt32 ulCnt = 0; + xbase->WriteLogMessage( "*** Free Blocks ***", iOpt ); + s.Sprintf( "File Header - FirstFreePage = %ld Block = %ld", ulFirstFreePage, PageToBlock( ulFirstFreePage )); + xbase->WriteLogMessage( s, iOpt ); + while( ulThisFreePage > 0 ){ + if(( iRc = ReadBlock( PageToBlock( ulThisFreePage ), GetBlockSize(), pBuf )) != XB_NO_ERROR ){ + iErrorStop = 120; + throw iRc; + } + pNextPage = pBuf; + pNextPage+=4; + ulNextFreePage = eGetUInt32( pNextPage ); + s.Sprintf( "Free Page# = %ld\t(Block# = %ld)\tNext Free Page = %ld\t(Block = %ld)", ulThisFreePage, PageToBlock( ulThisFreePage ), ulNextFreePage, PageToBlock( ulNextFreePage )); + xbase->WriteLogMessage( s, iOpt ); + ulThisFreePage = ulNextFreePage; + ulCnt++; + } + s.Sprintf( "%ld free blocks (%ld pages)", ulCnt, BlockToPage( ulCnt )); + xbase->WriteLogMessage( s, iOpt ); + xbase->WriteLogMessage( "*** End Of Free Blocks ***", iOpt ); + } + + pNextPage = pBuf; + pNextPage+=4; + + s = "*** Beginning of Block2 Info ***"; + xbase->WriteLogMessage( s, iOpt ); + s = "ulBlock2 info. ulBlock2 is either one of a linked list of free nodes, or the id of the original node that this node was split from."; + xbase->WriteLogMessage( s, iOpt ); + s = "Stored in physical file as pages, processed in blocks"; + xbase->WriteLogMessage( s, iOpt ); + + xbUInt32 ulFirstBlock = 3; + + for( xbUInt32 ul = ulFirstBlock; ul < ulLastBlock; ul++ ){ + if(( iRc = ReadBlock( ul, GetBlockSize(), pBuf )) != XB_NO_ERROR ){ + iErrorStop = 120; + throw iRc; + } + ulNextPage = eGetUInt32( pNextPage ); + if( ulNextPage > 0 ){ + s.Sprintf( " Block# = %ld\tPage# = %ld\tulBlock2 = %ld\tulBlock2(Page) = %ld", ul, BlockToPage( ul ), PageToBlock( ulNextPage ), ulNextPage ); + xbase->WriteLogMessage( s, iOpt ); + } + } + s = "*** End of Block2 Info ***"; + xbase->WriteLogMessage( s, iOpt ); + + if( pBuf ) free( pBuf ); + } + + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbIxMdx::DumpFreeBlocks() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + if( pBuf ) free( pBuf ); + } + return iRc; +} + +/**************************************************************************************************************/ +//! @brief Dump interior and leaf blocks for a given tag. +/*! + Dump blocks for given tag for index debugging purposes. + + A page is 512 bytes
+ A block is one or more pages
+ The default mdx block size is 2 pages, or 1024 bytes
+ The first four pages or header pages
+ + \param iOpt Output message destination
+ 0 = stdout
+ 1 = Syslog
+ 2 = Both
+ \param vpTag Index tag pointer, defaults to all tags if null. + \returns Return Codes + +*/ + +xbInt16 xbIxMdx::DumpTagBlocks( xbInt16 iOpt, void * vpTag ){ + + xbInt16 iRc = 0; + xbInt16 iErrorStop = 0; + xbInt16 iCurTag = 0; + xbString s; + xbInt16 iBlockCtr = 0; + + try{ + + xbMdxTag * mpTag; + if( vpTag == NULL ) + mpTag = (xbMdxTag *) GetTag( iCurTag++ ); + else + mpTag = (xbMdxTag *) vpTag; + + if( mpTag == NULL ){ + iErrorStop = 100; + iRc = XB_INVALID_TAG; + throw iRc; + } + + xbIxNode *n; + xbString s; + xbString s2; + xbBool bDone = xbFalse; + + xbUInt32 ulBlkNo; + xbLinkListOrd ll; + xbLinkListNode * llN; + + ll.SetDupKeys( xbFalse ); + + s.Sprintf( "%s Root Page %ld (Block %ld)", mpTag->cTagName, mpTag->ulRootPage, PageToBlock( mpTag->ulRootPage ) ); + xbase->WriteLogMessage( s, iOpt ); + + // for each tag + while( !bDone ){ + + // clear out any history + ll.Clear(); + if( mpTag->npNodeChain ){ + mpTag->npNodeChain = FreeNodeChain( mpTag->npNodeChain ); + mpTag->npCurNode = NULL; + } + + while( GetNextKey( mpTag, 0 ) == 0 ){ + n = mpTag->npNodeChain; + + while(n){ + ll.InsertKey( n->ulBlockNo, (xbUInt32) n->iCurKeyNo ); + n = n->npNext; + } + } + llN = ll.GetHeadNode(); + + while( llN ){ + + ulBlkNo = llN->GetKey(); + xbIxMdx::DumpBlock( iOpt, ulBlkNo, mpTag ); + llN = llN->GetNextNode(); + iBlockCtr++; + } + + if( vpTag || iCurTag >= GetTagCount()) + bDone = xbTrue; + else + mpTag = (xbMdxTag *) GetTag( iCurTag++ ); + } + + s.Sprintf( "\nTotal Blocks: %d", iBlockCtr ); + xbase->WriteLogMessage( s, iOpt ); + + if( mpTag->npNodeChain ){ + mpTag->npNodeChain = FreeNodeChain( mpTag->npNodeChain ); + mpTag->npCurNode = NULL; + } + + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbIxMdx::DumpTagBlocks() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return iRc; +} +/***********************************************************************/ +//! @brief Dump index file header. +/*! + Dump a index file header for debugging purposes. + \param iOpt Output message destination
+ 0 = stdout
+ 1 = Syslog
+ 2 = Both
+ \param iFmtOpt Output Format
+ 0, 1 = Header info only
+ 2 = Tag info
+ 3 = Header && Tag info
+ \returns Return Codes +*/ +xbInt16 xbIxMdx::DumpHeader( xbInt16 iOpt, xbInt16 iFmtOpt ) +{ + + xbInt16 iRc = XB_NO_ERROR; + xbString s; + if(( iRc = ReadHeadBlock( 1 )) != XB_NO_ERROR ) + return iRc; + + char c, tfv, cDisplayMask = 1; + cDisplayMask = cDisplayMask << 7; + if( iFmtOpt != 2 && iFmtOpt != 4 ){ + s = "*** MDX Index Header ***"; + xbase->WriteLogMessage( s, iOpt ); + s = "Version = "; + tfv = cVersion; + for( c = 1; c<= 8; c++ ){ + //std::cout << (tfv & cDisplayMask ? '1' : '0'); + s+= (tfv & cDisplayMask ? '1' : '0'); + tfv <<= 1; + } + xbase->WriteLogMessage( s, iOpt ); + + s.Sprintf( "Create Date = %d/%d/%d", (int) cCreateMM, (int) cCreateDD, (int) cCreateYY % 100 ); + xbase->WriteLogMessage( s, iOpt ); + + s.Sprintf( "File Name = %s", sFileName.Str() ); + xbase->WriteLogMessage( s, iOpt ); + + s.Sprintf( "Block Factor = %d", iBlockFactor ); + xbase->WriteLogMessage( s, iOpt ); + + s.Sprintf( "Block Size = %d", GetBlockSize() ); + xbase->WriteLogMessage( s, iOpt ); + + s.Sprintf( "Prod Ix Flag = %d", (xbInt16) cProdIxFlag ); + xbase->WriteLogMessage( s, iOpt ); + + s.Sprintf( "Tag Entry Cnt = %d", (xbInt16) cTagEntryCnt ); + xbase->WriteLogMessage( s, iOpt ); + + s.Sprintf( "Tag Len = %d", iTagLen ); + xbase->WriteLogMessage( s, iOpt ); + + s.Sprintf( "Next Tag = %d", (xbInt16) cNextTag ); + xbase->WriteLogMessage( s, iOpt ); + + s.Sprintf( "Tag Use Cnt = %d", iTagUseCnt ); + xbase->WriteLogMessage( s, iOpt ); + + s.Sprintf( "Page Cnt = %d", ulPageCnt ); + xbase->WriteLogMessage( s, iOpt ); + + s.Sprintf( "First Free Page = %d", ulFirstFreePage ); + xbase->WriteLogMessage( s, iOpt ); + + s.Sprintf( "No Of Block Avail = %d\n", ulNoOfBlockAvail ); + xbase->WriteLogMessage( s, iOpt ); + + s.Sprintf( "Last update date = %d/%d/%d", (int) cCreateMM, (int) cCreateDD, (int) cCreateYY % 100 ); + xbase->WriteLogMessage( s, iOpt ); + + if( ulFirstFreePage > 0 ){ + xbString s; + xbUInt32 ulNfp = ulFirstFreePage; // next free page + xbInt16 lc = 0; + while( ulNfp && lc++ < 5 ){ + if( s.Len() > 0 ) + s += ","; + s.Sprintf( "%s%ld", s.Str(), ulNfp ); + if(( iRc = GetBlock( NULL, (xbUInt32) (ulNfp / (xbUInt32) iBlockFactor), 0 )) != 0 ) + return iRc; + ulNfp = eGetUInt32( cNodeBuf+4 ); + } + xbase->WriteLogMessage( s, iOpt ); + } + } + if( iFmtOpt > 1 ){ + xbMdxTag *tt = mdxTagTbl; + xbString s; + xbInt16 i = 0; + + if( tt ){ + while( tt ){ + i++; + if(( iRc = LoadTagDetail( 2, tt )) != XB_NO_ERROR ) + return iRc; + + s.Sprintf( "TTE (%d)\tName HdrPage\tFormat\tLeftChild\tRightChild\tParent\tKeyType", i ); + xbase->WriteLogMessage( s, iOpt ); + s.Sprintf( "TTE (%d)\t%-12s %d\t\t%d\t%d\t\t%d\t\t%d\t%c\n", i, tt->cTagName, tt->ulTagHdrPageNo, tt->cKeyFmt, tt->cLeftChild, tt->cRightChild, tt->cParent, tt->cKeyType ); + xbase->WriteLogMessage( s, iOpt ); + + s.Sprintf( "TTH (%d)\tRoot\tTagSize\tKeyFmt2\tType2\tKeyLen\tKeysPerBlock\tSecType\tKeyItemLen\tSerial#\tHasKeys\tFilter\tDesc\tUnique\tLchild\tRchild\tKeyFmt3\tTagDate", i ); + xbase->WriteLogMessage( s, iOpt ); + + s.Sprintf( "TTH (%d)\t%d\t%d\t%d\t%c\t%d\t%d\t\t%d\t%d\t\t%x\t%x\t%d\t%d\t%d\t%d\t%d\t%d\t%d/%d/%d", + i, tt->ulRootPage, tt->ulTagSize, tt->cKeyFmt2, tt->cKeyType2, tt->iKeyLen, tt->iKeysPerBlock, tt->iSecKeyType, tt->iKeyItemLen, tt->cSerialNo, tt->cHasKeys, tt->cHasFilter, + (((tt->cKeyFmt2 & 0x08) > 0) ? 1 : 0), // descending? + tt->cUnique, tt->ulLeftChild, tt->ulRightChild, tt->cKeyFmt3, (int) tt->cTagMM, (int) tt->cTagDD, (int) tt->cTagYY % 100 ); + + xbase->WriteLogMessage( s, iOpt ); + + s.Sprintf( "Key (%d) %s", i, tt->sKeyExp->Str()); + xbase->WriteLogMessage( s, iOpt ); + + if( tt->cHasFilter ){ + s.Sprintf( "Flt (%d) %s", i, tt->sFiltExp->Str()); + xbase->WriteLogMessage( s, iOpt ); + } + xbase->WriteLogMessage( "", iOpt ); + tt = tt->next; + + } + } + } + return iRc; +} + +/***********************************************************************/ +xbInt16 xbIxMdx::DumpIxForTag( void *vpTag, xbInt16 iOutputOpt ) +{ + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + xbInt16 iDepth = 0; + xbUInt32 lKeyCtr = 0; + xbInt32 iMinDepth = 999999; + xbInt32 iMaxDepth = 0; + + try{ + /* + get first node + while interior node + print the left key + level++ + go down one on the left + */ + + xbMdxTag * mpTag = (xbMdxTag *) vpTag; + if( mpTag->npNodeChain ){ + mpTag->npNodeChain = FreeNodeChain( mpTag->npNodeChain ); + mpTag->npCurNode = NULL; + } + + // Get the root + if(( iRc = LoadTagDetail( 2, mpTag )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + // lRootBlock is now available + if(( iRc = GetBlock( vpTag, (mpTag->ulRootPage / (xbUInt32) iBlockFactor), 1 )) != XB_NO_ERROR ){ + iErrorStop = 110; + throw iRc; + } + // if no keys on this node, then the index is empty + xbUInt32 ulKeyPtr = eGetUInt32( mpTag->npCurNode->cpBlockData ); + if( ulKeyPtr == 0 ){ + iErrorStop = 120; + iRc = XB_EMPTY; + throw iRc; + } + while( !IsLeaf( vpTag, mpTag->npCurNode )){ // go down the chain looking for a leaf node + PrintKey( vpTag, mpTag->npCurNode , 0, iDepth++, 'I', iOutputOpt ); + if(( iRc = GetKeyPtr( vpTag, 0, mpTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){ + iErrorStop = 130; + throw iRc; + } + if(( iRc = GetBlock( vpTag, (ulKeyPtr / (xbUInt32) iBlockFactor), 1 )) != XB_NO_ERROR ){ + iErrorStop = 140; + throw iRc; + } + } + if(( iRc = GetKeyPtr( vpTag, 0, mpTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){ + iErrorStop = 150; + throw iRc; + } + // loop through the leaf entries of left most leaf + if( iDepth < iMinDepth ) iMinDepth = iDepth; + if( iDepth > iMaxDepth ) iMaxDepth = iDepth; + xbUInt32 ulNoOfKeys = eGetUInt32( mpTag->npCurNode->cpBlockData ); + for( xbUInt32 ul = 0; ul < ulNoOfKeys; ul++ ){ + PrintKey( vpTag, mpTag->npCurNode , ul, iDepth, 'L', iOutputOpt ); + lKeyCtr++; + } + + // if head node = start node, return + if( mpTag->npCurNode->ulBlockNo == ( mpTag->ulRootPage / (xbUInt32) iBlockFactor )) + return XB_NO_ERROR; + + xbBool bEof = false; + while( !bEof ){ + + // go up the chain, looking for an interior node with more keys on it + xbIxNode * TempIxNode = mpTag->npCurNode; + mpTag->npCurNode = mpTag->npCurNode->npPrev; + mpTag->npCurNode->npNext = NULL; + TempIxNode->npPrev = NULL; + TempIxNode = FreeNodeChain( TempIxNode ); + iDepth--; + + while( mpTag->npCurNode->iCurKeyNo >= eGetUInt32( mpTag->npCurNode->cpBlockData ) && + mpTag->npCurNode->ulBlockNo != ( mpTag->ulRootPage / (xbUInt32) iBlockFactor) ){ + TempIxNode = mpTag->npCurNode; + mpTag->npCurNode = mpTag->npCurNode->npPrev; + mpTag->npCurNode->npNext = NULL; + TempIxNode->npPrev = NULL; + TempIxNode = FreeNodeChain( TempIxNode ); + iDepth--; + } + // if head node && right most key, return + if( mpTag->npCurNode->ulBlockNo == (mpTag->ulRootPage / (xbUInt32) iBlockFactor) && + mpTag->npCurNode->iCurKeyNo == eGetUInt32( mpTag->npCurNode->cpBlockData )) + bEof = true; + + if( !bEof ){ + mpTag->npCurNode->iCurKeyNo++; + if(( iRc = GetKeyPtr( vpTag, mpTag->npCurNode->iCurKeyNo, mpTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){ + iErrorStop = 160; + throw iRc; + } + PrintKey( vpTag, mpTag->npCurNode , mpTag->npCurNode->iCurKeyNo, iDepth++, 'I', iOutputOpt ); + + if(( iRc = GetBlock( vpTag, (ulKeyPtr / (xbUInt32) iBlockFactor), 1 )) != XB_NO_ERROR ){ + iErrorStop = 170; + throw iRc; + } + + // traverse down the left side of the tree + while( !IsLeaf( vpTag, mpTag->npCurNode )) // go down the chain looking for a leaf node + { + PrintKey( vpTag, mpTag->npCurNode , 0, iDepth++, 'I', iOutputOpt ); + if(( iRc = GetKeyPtr( vpTag, 0, mpTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){ + iErrorStop = 180; + throw iRc; + } + if(( iRc = GetBlock( vpTag, (ulKeyPtr / (xbUInt32) iBlockFactor), 1 )) != XB_NO_ERROR ){ + iErrorStop = 190; + throw iRc; + } + } + if( iDepth < iMinDepth ) iMinDepth = iDepth; + if( iDepth > iMaxDepth ) iMaxDepth = iDepth; + ulNoOfKeys = eGetUInt32( mpTag->npCurNode->cpBlockData ); + for( xbUInt32 ul = 0; ul < ulNoOfKeys; ul++ ){ + PrintKey( vpTag, mpTag->npCurNode , ul, iDepth, 'L', iOutputOpt ); + lKeyCtr++; + } + } + } + xbString s; + s.Sprintf( "Total keys = [%ld] Min Depth = [%d] Max Depth = [%d]", lKeyCtr, iMinDepth, iMaxDepth ); + xbase->WriteLogMessage( s.Str(), 2 ); + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbIxMdx::DumpIxForTag() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return iRc; +} +/**************************************************************************************************************/ +void xbIxMdx::DumpIxNodeChain( void *vpTag, xbInt16 iOutputOpt ) const +{ + xbMdxTag * mpTag = (xbMdxTag *) vpTag; + xbString s( "Dump Node Chain" ); + xbase->WriteLogMessage( s, 2 ); + + if( mpTag->npNodeChain ){ + xbIxNode *n = mpTag->npNodeChain; + xbInt16 iCtr = 0; + char cLeaf; + s.Sprintf( "Cnt\tThis Prev Next CurKeyNo BlockNo Page NoOfKeys Type" ); + xbase->WriteLogMessage( s, iOutputOpt ); + while( n ){ + IsLeaf( vpTag, n ) ? cLeaf = 'L' : cLeaf = 'I'; + s.Sprintf( "%d\t%08x %08x %08x %08d %08d %08d %08ld %c", + iCtr++, n, n->npPrev, n->npNext, n->iCurKeyNo, + n->ulBlockNo, n->ulBlockNo * (xbUInt32) iBlockFactor, + eGetUInt32( n->cpBlockData ), cLeaf ); + xbase->WriteLogMessage( s, 2 ); + n = n->npNext; + } + } else { + s = "Empty Node Chain"; + xbase->WriteLogMessage( s, 2 ); + } +} +#endif + +/***********************************************************************************************/ +xbInt16 xbIxMdx::FindKey( void *vpTag, xbDouble dKey, xbInt16 iRetrieveSw ){ + + xbMdxTag * mpTag = (xbMdxTag *) vpTag; + if( mpTag->cKeyType2 == 'N' ){ // mdx indices store numeric keys as bcd values + xbBcd bcd( dKey ); + return xbIx::FindKey( vpTag, bcd, iRetrieveSw ); + } else // this would be a julian date inquiry + return FindKey( vpTag, &dKey, 8, iRetrieveSw ); +} + +/***********************************************************************************************/ +// iRetrieveSw = 1 - position db file to index position +// 0 - do not position dbf file + +xbInt16 xbIxMdx::FindKey( void *vpTag, const void * vpKey, + xbInt32 lSearchKeyLen, xbInt16 iRetrieveSw ){ + + xbInt16 iRc = 0; + xbInt16 iErrorStop = 0; + + try{ + // clean up any previous table updates before moving on + if( iRetrieveSw ){ + if( dbf->GetDbfStatus() == XB_UPDATED ){ + if( dbf->GetAutoCommit() == 1 ){ + if(( iRc = dbf->Commit()) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + } else { + if(( iRc = dbf->Abort()) != XB_NO_ERROR ){ + iErrorStop = 110; + throw iRc; + } + } + } + } + + xbUInt32 ulNoOfKeys; + xbMdxTag * mpTag = (xbMdxTag *) vpTag; + char cKeyType = GetKeyType( vpTag ); + xbBool bDescending = mpTag->cKeyFmt2 & 0x08; + + if( mpTag->npNodeChain ){ + + // determine if the index has been updated since the last time it was used + time_t tFileTs; + if(( iRc = GetFileMtime( tFileTs )) != XB_NO_ERROR ){ + iErrorStop = 120; + throw iRc; + } + if( mpTag->tNodeChainTs < tFileTs ){ + mpTag->npNodeChain = FreeNodeChain( mpTag->npNodeChain ); + mpTag->npCurNode = NULL; + + } else { + // pop up the chain looking for appropriate starting point + xbBool bDone = false; + xbIxNode * TempIxNode; + while( mpTag->npCurNode && !bDone && + (mpTag->npCurNode->ulBlockNo != ( mpTag->ulRootPage / (xbUInt32) iBlockFactor ))){ // not root node + //if no keys on the node, pop up one + ulNoOfKeys = eGetUInt32( mpTag->npCurNode->cpBlockData ); + if( ulNoOfKeys == 0 ){ + TempIxNode = mpTag->npCurNode; + mpTag->npCurNode = mpTag->npCurNode->npPrev; + TempIxNode = FreeNodeChain( TempIxNode ); + + } else { + + iRc = CompareKey( cKeyType, vpKey, GetKeyData( mpTag->npCurNode, 0, mpTag->iKeyItemLen ), (size_t) lSearchKeyLen ); + if( (!bDescending && iRc <= 0) || (bDescending && iRc >= 0 )){ + TempIxNode = mpTag->npCurNode; + mpTag->npCurNode = mpTag->npCurNode->npPrev; + TempIxNode = FreeNodeChain( TempIxNode ); + } else { + // get the number of keys on the block and compare the key to the rightmost key + xbUInt32 ulKeyCtr = eGetUInt32( mpTag->npCurNode->cpBlockData ) - 1; // IsLeaf( vpTag, mpTag->npCurNode ); + iRc = CompareKey( cKeyType, vpKey, GetKeyData( mpTag->npCurNode, ulKeyCtr, mpTag->iKeyItemLen), (size_t) lSearchKeyLen ); + + if( (!bDescending && iRc > 0) || (bDescending && iRc < 0 )){ + TempIxNode = mpTag->npCurNode; + mpTag->npCurNode = mpTag->npCurNode->npPrev; + TempIxNode = FreeNodeChain( TempIxNode ); + } else { + bDone = true; + } + } + } + } + } + } + + // either started empty, or cleared due to file time diff + if( !mpTag->npNodeChain ){ + // Get the root + if(( iRc = LoadTagDetail( 2, mpTag )) != XB_NO_ERROR ){ + iErrorStop = 130; + throw iRc; + } + // lRootBlock is now available + if(( iRc = GetBlock( vpTag, (mpTag->ulRootPage / (xbUInt32) iBlockFactor), 1 )) != XB_NO_ERROR ){ + iErrorStop = 140; + throw iRc; + } + // if this is a leaf node and no keys on this node, then the index is empty + ulNoOfKeys = eGetUInt32( mpTag->npCurNode->cpBlockData ); + if( ulNoOfKeys == 0 && IsLeaf( vpTag, mpTag->npCurNode )){ +// iRc = XB_EMPTY; + iRc = XB_NOT_FOUND; + return iRc; + } + } + + // should be in the appropriate position in the node chain to continue the search from here + // run down through the interior nodes + xbInt16 iSearchRc = 0; + xbUInt32 ulKeyPtr = 0; + + while( vpTag && !IsLeaf( vpTag, mpTag->npCurNode ) ){ + // get the number of keys on the block and compare the key to the rightmost key + ulNoOfKeys = eGetUInt32( mpTag->npCurNode->cpBlockData ); + + if( ulNoOfKeys == 0 ){ + mpTag->npCurNode->iCurKeyNo = 0; + + } else { + + iRc = CompareKey( cKeyType, vpKey, GetKeyData( mpTag->npCurNode, ulNoOfKeys - 1, mpTag->iKeyItemLen), (size_t) lSearchKeyLen ); + if( (!bDescending && iRc > 0) || (bDescending && iRc < 0)){ + mpTag->npCurNode->iCurKeyNo = ulNoOfKeys; + } + else + { + mpTag->npCurNode->iCurKeyNo = (xbUInt32) BSearchBlock( cKeyType, mpTag->npCurNode, + (xbInt32) mpTag->iKeyItemLen, vpKey, (xbInt32) lSearchKeyLen, iSearchRc, bDescending ); + } + } + + if(( iRc = GetKeyPtr( vpTag, mpTag->npCurNode->iCurKeyNo, mpTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){ + iErrorStop = 150; + throw iRc; + } + + if(( iRc = GetBlock( vpTag, (ulKeyPtr / (xbUInt32)iBlockFactor), 1 )) != XB_NO_ERROR ){ + iErrorStop = 160; + throw iRc; + } + } + + ulNoOfKeys = eGetUInt32( mpTag->npCurNode->cpBlockData ); + xbInt16 iCompRc = 0; + + if( ulNoOfKeys == 0 ){ + // iCompRc = -1; + // iRc = XB_EMPTY; + iRc = XB_NOT_FOUND; + return iRc; + + } else { + + iRc = BSearchBlock( cKeyType, mpTag->npCurNode, mpTag->iKeyItemLen, vpKey, lSearchKeyLen, iCompRc, bDescending ); + // iCompRc + // 0 found + // < 0 eof encountered, search key > last key in file + // > 0 not found, positioned to next key + + if( iCompRc >= 0 ){ + mpTag->npCurNode->iCurKeyNo = (xbUInt32) iRc; + + + if( iRetrieveSw ){ + + xbUInt32 ulKey = mpTag->npCurNode->iCurKeyNo; + if( ulKey >= ulNoOfKeys ) // if position past end of keys, need to go back one and position to last key + ulKey--; + + // if(( iRc = GetKeyPtr( vpTag, mpTag->npCurNode->iCurKeyNo, mpTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){ + + if(( iRc = GetKeyPtr( vpTag, ulKey, mpTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){ + iErrorStop = 170; + throw iRc; + } + if(( iRc = dbf->GetRecord( ulKeyPtr )) != XB_NO_ERROR ){ + iErrorStop = 180; + throw iRc; + } + } + } + } + + if( iCompRc == 0 ) + return XB_NO_ERROR; + else if( iCompRc > 0 ) + return XB_NOT_FOUND; + else + return XB_EOF; + + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbIxMdx::FindKey() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return iRc; +} + +/***********************************************************************/ +xbInt16 xbIxMdx::FindKeyForCurRec( void * vpTag ) +{ + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + xbInt16 i = 0; + + try{ + if(( iRc = CreateKey( vpTag, 0 )) < XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbIxMdx::FindKeyForCurRec() Exception Caught. Error Stop = [%d] iRc = [%d] Tag=[%d]", iErrorStop, iRc, i ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return XB_NO_ERROR; +} + +/***********************************************************************/ +//! @brief Get dbf record number for given key number. +/*! + \param vpTag Tag to retrieve dbf rec number on. + \param iKeyNo Key number for retrieval + \param np Pointer to node + \param ulDbfPtr- Output dbf record number + \returns Return Codes +*/ +xbInt16 xbIxMdx::GetDbfPtr( void *vpTag, xbInt16 iKeyNo, xbIxNode *np, xbUInt32 &ulDbfPtr ) const { + + xbInt16 iRc = 0; + xbInt16 iErrorStop = 0; + + try{ + #ifdef XB_DEBUG_SUPPORT + // turn this off in production mode for better performance + xbUInt32 ulNoOfKeys = eGetUInt32 ( np->cpBlockData ); + if( iKeyNo < 0 || iKeyNo > (xbInt16) --ulNoOfKeys ){ + iErrorStop = 100; + throw XB_INVALID_KEYNO; + } + #endif + + xbMdxTag * mpTag = (xbMdxTag *) vpTag; + char *p = ( np->cpBlockData); + p += (8 + (iKeyNo * mpTag->iKeyItemLen)); + ulDbfPtr = eGetUInt32 ( p ); + + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbIxMdx::GetDbfPtr() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return iRc; +} + +/***********************************************************************/ +/*! + \param vpTag Tag to retrieve first key on. + \param iRetrieveSw xbTrue - Retrieve the record on success.
+ xbFalse - Don't retrieve record. + \returns Return Codes +*/ + +xbInt16 xbIxMdx::GetFirstKey( void *vpTag, xbInt16 iRetrieveSw = 0 ){ + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + + // convert the tag pointer to mdx tag pointer + xbMdxTag * mpTag = (xbMdxTag *) vpTag; + + + try{ + // clear out any history + if( mpTag->npNodeChain ){ + mpTag->npNodeChain = FreeNodeChain( mpTag->npNodeChain ); + mpTag->npCurNode = NULL; + } + // Get the root + if(( iRc = LoadTagDetail( 2, mpTag )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + + // lRootPage is available + if(( iRc = GetBlock( vpTag, (mpTag->ulRootPage / (xbUInt32) iBlockFactor), 1 )) != XB_NO_ERROR ){ + iErrorStop = 110; + throw iRc; + } + + // if no keys on this node, then the index is empty + // this is not true + + xbUInt32 ulKeyPtr = eGetUInt32( mpTag->npCurNode->cpBlockData ); + if( ulKeyPtr == 0 && IsLeaf( vpTag, mpTag->npCurNode )){ + iRc = XB_EMPTY; + return iRc; + } + + while( !IsLeaf( vpTag, mpTag->npCurNode )){ // go down the chain looking for a leaf node + if(( iRc = GetKeyPtr( vpTag, 0, mpTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){ + iErrorStop = 120; + throw iRc; + } + + if(( iRc = GetBlock( vpTag, (ulKeyPtr / (xbUInt32) iBlockFactor), 1 )) != XB_NO_ERROR ){ + iErrorStop = 130; + throw iRc; + } + } + + // retrieve record to data buf + if( iRetrieveSw ){ + if(( iRc = GetKeyPtr( vpTag, 0, mpTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){ + iErrorStop = 140; + throw iRc; + } + if(( iRc = dbf->GetRecord( ulKeyPtr )) != XB_NO_ERROR ){ + iErrorStop = 150; + throw iRc; + } + } + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbIxMdx::GetFirstKey() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return iRc; +} + +/***********************************************************************/ +//xbBool xbIxMdx::GetIndexUpdated() const { +// std::cout << "xbIxMdx::GetIndexUpdate() FIX ME \n"; +// return xbFalse; +//} + +/***********************************************************************/ +//! @brief Get the key expression for the given tag. +/*! + \param vpTag Tag to retrieve key expression from tag. + \returns Key expression. +*/ + +xbString &xbIxMdx::GetKeyExpression( const void * vpTag ) const{ + xbMdxTag *mpTag = (xbMdxTag *) vpTag; + return *mpTag->sKeyExp; +} + +/***********************************************************************/ +//! @brief Get the key expression for the given tag. +/*! + \param vpTag Tag to retrieve filter expression from tag (if it exists). + \returns Key filter. +*/ + +xbString &xbIxMdx::GetKeyFilter( const void * vpTag ) const{ + + xbMdxTag * mpTag = (xbMdxTag *) vpTag; + + if( mpTag->sFiltExp ) + return *mpTag->sFiltExp; + else + return sNullString; + +} +/**************************************************************************************************/ +xbInt16 xbIxMdx::GetKeyPtr( void *vpTag, xbInt16 iKeyNo, xbIxNode *np, xbUInt32 &ulKeyPtr ) const { + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + + try{ + + xbMdxTag *mdxTag = (xbMdxTag *) vpTag; + char *p = np->cpBlockData; + xbUInt32 ulKeyCnt = eGetUInt32( p ); + if( iKeyNo < 0 || iKeyNo > (xbInt16) ulKeyCnt ){ + iErrorStop = 100; + iRc = XB_INVALID_KEYNO; + throw iRc; + } + p+=8; // skip past first two four byte numeric fields + p+= (iKeyNo * mdxTag->iKeyItemLen); + ulKeyPtr = eGetUInt32( p ); + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbIxMdx::GetKeyPtr() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return iRc; +} +/***********************************************************************/ +//! @brief Returns key update status. +/*! + \param vpTag Tag to check status on. + \returns XB_UPD_KEY Key updated.
+ XB_DEL_KEY Key deleted.
+ XB_ADD_KEY Key added.
+ 0 No key updates + +*/ +inline xbInt16 xbIxMdx::GetKeySts( void *vpTag ) const{ + + xbMdxTag *mpTag = (xbMdxTag *) vpTag; + return mpTag->iKeySts; +} + +/***********************************************************************/ +char xbIxMdx::GetKeyType( const void *vpTag ) const { + xbMdxTag * mpTag = (xbMdxTag *) vpTag; + return mpTag->cKeyType; +} + +/***********************************************************************/ +//! @brief Get the last key for the given tag. +/*! + \param vpTag Tag to retrieve last key on. + \param iRetrieveSw xbTrue - Retrieve the record on success.
+ xbFalse - Don't retrieve record. + \returns Return Codes +*/ + +xbInt16 xbIxMdx::GetLastKey( void *vpTag, xbInt16 iRetrieveSw ){ + return GetLastKey( 0, vpTag, iRetrieveSw ); +} + +/***********************************************************************/ +//! @brief Get the last key for the given tag and starting node. +/*! + \param ulBlockNo Starting node + \param vpTag Tag to retrieve last key on. + \param iRetrieveSw xbTrue - Retrieve the record if key found.
+ xbFalse - Don't retrieve record. + \returns Return Codes +*/ + +xbInt16 xbIxMdx::GetLastKey( xbUInt32 ulBlockNo, void *vpTag, xbInt16 iRetrieveSw ){ + + // if UlNodeNo is zero, start at head node, otherwise start at ulNodeNo + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + + // convert the tag pointer to mdx tag pointer + xbMdxTag * mpTag = (xbMdxTag *) vpTag; + + try{ + xbUInt32 ulNoOfKeys = 0; + // clear out any history + if( mpTag->npNodeChain ){ + mpTag->npNodeChain = FreeNodeChain( mpTag->npNodeChain ); + mpTag->npCurNode = NULL; + } + // Get the root + if( ulBlockNo == 0 ){ + if(( iRc = LoadTagDetail( 2, mpTag )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + //if(( iRc = GetBlock( vpTag, (mpTag->ulRootPage / (xbUInt32) iBlockFactor), 1 )) != XB_NO_ERROR ){ + if(( iRc = GetBlock( vpTag, PageToBlock( mpTag->ulRootPage ), 1 )) != XB_NO_ERROR ){ + iErrorStop = 110; + throw iRc; + } + + } else { + if(( iRc = GetBlock( vpTag, ulBlockNo, 1 )) != XB_NO_ERROR ){ + iErrorStop = 120; + throw iRc; + } + } + + ulNoOfKeys = eGetUInt32( mpTag->npCurNode->cpBlockData ); + + if( ulNoOfKeys == 0 && IsLeaf( vpTag, mpTag->npCurNode )){ + iRc = XB_EMPTY; + return iRc; + } + + mpTag->npCurNode->iCurKeyNo = ulNoOfKeys; + ulNoOfKeys = eGetUInt32( mpTag->npCurNode->cpBlockData ); + + xbUInt32 ulKeyPtr = 0; + while( !IsLeaf( vpTag, mpTag->npCurNode )){ // go down the chain looking for a leaf node + // std::cout << "Considered an interior node\n"; + if(( iRc = GetKeyPtr( vpTag, ulNoOfKeys, mpTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){ + iErrorStop = 130; + throw iRc; + } + if(( iRc = GetBlock( vpTag, (ulKeyPtr / (xbUInt32) iBlockFactor), 1 )) != XB_NO_ERROR ){ + iErrorStop = 140; + throw iRc; + } + + ulNoOfKeys = eGetUInt32( mpTag->npCurNode->cpBlockData ); + mpTag->npCurNode->iCurKeyNo = ulNoOfKeys; + } + // leaf node has one fewer keys than the interior node + mpTag->npCurNode->iCurKeyNo--; + ulNoOfKeys = eGetUInt32( mpTag->npCurNode->cpBlockData ); + + // retrieve record to data buf + if( iRetrieveSw ){ + if(( iRc = GetKeyPtr( vpTag, (ulNoOfKeys-1), mpTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){ + iErrorStop = 150; + throw iRc; + } + if(( iRc = dbf->GetRecord( ulKeyPtr )) != XB_NO_ERROR ){ + iErrorStop = 160; + throw iRc; + } + } + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbIxMdx::GetLastKey() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return iRc; +} + +/***********************************************************************/ +//! @brief Get the last key for a block number. +/*! + \param vpTag Tag to retrieve first key on. + \param ulBlockNo Block number for key retrieval. + \param cpBuf output buffer for key placement + \returns Return Codes +*/ + +xbInt16 xbIxMdx::GetLastKeyForBlockNo( void *vpTag, xbUInt32 ulBlockNo, char *cpBuf ){ + + // returns the last key for a given block number + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + + xbMdxTag * npTag = (xbMdxTag *) vpTag; + + try{ + xbIxNode * npSaveNodeChain = npTag->npNodeChain; + xbIxNode * npSaveCurNode = npTag->npCurNode; + npTag->npNodeChain = NULL; + + if(( iRc = GetLastKey( ulBlockNo, npTag, 0 )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + memcpy( cpBuf, GetKeyData( npTag->npCurNode, GetKeyCount( npTag->npCurNode ) - 1, npTag->iKeyItemLen ), (size_t) npTag->iKeyLen ); + + // free memory + FreeNodeChain( npTag->npNodeChain ); + npTag->npNodeChain = npSaveNodeChain; + npTag->npCurNode = npSaveCurNode; + + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbIxMdx::GetLastKeyForBlockNo() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( iRc ) ); + } + return iRc; +} +/***********************************************************************/ +//! @brief Get the next key for the given tag. +/*! + \param vpTag Tag to retrieve next key on. + \param iRetrieveSw xbTrue - Retrieve the record on success.
+ xbFalse - Don't retrieve record. + \returns Return Codes +*/ + +xbInt16 xbIxMdx::GetNextKey( void * vpTag, xbInt16 iRetrieveSw ){ + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + + // convert the tag pointer to mdx tag pointer + xbMdxTag * mpTag = (xbMdxTag *) vpTag; + + try{ + if( !mpTag->npCurNode ) + return GetFirstKey( vpTag, iRetrieveSw ); + + // more keys on this node? + xbUInt32 ulKeyPtr; + if( (eGetUInt32( mpTag->npCurNode->cpBlockData ) - 1) > mpTag->npCurNode->iCurKeyNo ){ + mpTag->npCurNode->iCurKeyNo++; + + if(( iRc = GetKeyPtr( vpTag, mpTag->npCurNode->iCurKeyNo, mpTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + if( iRetrieveSw ){ + if(( iRc = dbf->GetRecord( ulKeyPtr )) != XB_NO_ERROR ){ + iErrorStop = 110; + throw iRc; + } else { + return iRc; + } + } else { + return iRc; + } + } + + // if at end head node, then at eof + if( mpTag->npCurNode->ulBlockNo == ( mpTag->ulRootPage / (xbUInt32) iBlockFactor )) + return XB_EOF; + + // This logic assumes that interior nodes have n+1 left node no's where n is the number of keys in the node + xbIxNode * TempIxNode = mpTag->npCurNode; + mpTag->npCurNode = mpTag->npCurNode->npPrev; + TempIxNode = FreeNodeChain( TempIxNode ); + + // While no more right keys && not head node, pop up one node + while( mpTag->npCurNode->iCurKeyNo >= eGetUInt32( mpTag->npCurNode->cpBlockData ) && + mpTag->npCurNode->ulBlockNo != ( mpTag->ulRootPage / (xbUInt32) iBlockFactor) ){ + + TempIxNode = mpTag->npCurNode; + mpTag->npCurNode = mpTag->npCurNode->npPrev; + TempIxNode = FreeNodeChain( TempIxNode ); + } + + // if head node && right most key, return eof + if( mpTag->npCurNode->ulBlockNo == (mpTag->ulRootPage / (xbUInt32) iBlockFactor) && + mpTag->npCurNode->iCurKeyNo == eGetUInt32( mpTag->npCurNode->cpBlockData )) + return XB_EOF; + + // move one to the right + mpTag->npCurNode->iCurKeyNo++; + if(( iRc = GetKeyPtr( vpTag, mpTag->npCurNode->iCurKeyNo, mpTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){ + iErrorStop = 120; + throw iRc; + } + + if(( iRc = GetBlock( vpTag, (ulKeyPtr / (xbUInt32) iBlockFactor), 1 )) != XB_NO_ERROR ){ + iErrorStop = 130; + throw iRc; + } + + // traverse down the left side of the tree + while( !IsLeaf( vpTag, mpTag->npCurNode )) // go down the chain looking for a leaf node + { + if(( iRc = GetKeyPtr( vpTag, 0, mpTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){ + iErrorStop = 140; + throw iRc; + } + if(( iRc = GetBlock( vpTag, (ulKeyPtr / (xbUInt32) iBlockFactor), 1 )) != XB_NO_ERROR ){ + iErrorStop = 150; + throw iRc; + } + } + // retrieve record to data buf + if( iRetrieveSw ){ + if(( iRc = GetKeyPtr( vpTag, 0, mpTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){ + iErrorStop = 160; + throw iRc; + } + if(( iRc = dbf->GetRecord( ulKeyPtr )) != XB_NO_ERROR ){ + iErrorStop = 170; + throw iRc; + } + } else { + return iRc; + } + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbIxMdx::GetNextKey() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return iRc; +} + +/***********************************************************************/ +//! @brief Get the previous key for the given tag. +/*! + \param vpTag Tag to retrieve previous key on. + \param iRetrieveSw xbTrue - Retrieve the record on success.
+ xbFalse - Don't retrieve record. + \returns Return Codes +*/ + +xbInt16 xbIxMdx::GetPrevKey( void * vpTag, xbInt16 iRetrieveSw ){ + + xbString s; + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + + // convert the tag pointer to mdx tag pointer + xbMdxTag * mpTag = (xbMdxTag *) vpTag; + + try{ + if( !mpTag->npCurNode ){ + return GetLastKey( 0, vpTag, iRetrieveSw ); + } + + xbUInt32 ulKeyPtr = 0; + // more keys on this assumed-leaf node? + + if( mpTag->npCurNode->iCurKeyNo > 0 ){ + mpTag->npCurNode->iCurKeyNo--; + + if(( iRc = GetKeyPtr( vpTag, mpTag->npCurNode->iCurKeyNo, mpTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + + if( iRetrieveSw ){ + if(( iRc = dbf->GetRecord( ulKeyPtr )) != XB_NO_ERROR ){ + iErrorStop = 110; + throw iRc; + } else { + return iRc; + } + } else { + return iRc; + } + } + + //if head node = start node, at eof + if( mpTag->npCurNode->ulBlockNo == ( mpTag->ulRootPage / (xbUInt32) iBlockFactor )) + return XB_BOF; + + // This logic assumes that interior nodes have n+1 left node no's where n is the number of keys in the node + xbIxNode * TempIxNode = mpTag->npCurNode; + mpTag->npCurNode = mpTag->npCurNode->npPrev; + TempIxNode = FreeNodeChain( TempIxNode ); + + // While no more left keys && not head node, pop up one node + while( mpTag->npCurNode->iCurKeyNo == 0 && + mpTag->npCurNode->ulBlockNo != ( mpTag->ulRootPage / (xbUInt32) iBlockFactor) ){ + TempIxNode = mpTag->npCurNode; + mpTag->npCurNode = mpTag->npCurNode->npPrev; + TempIxNode = FreeNodeChain( TempIxNode ); + } + + //if head node && left most key, return bof + if( mpTag->npCurNode->ulBlockNo == ( mpTag->ulRootPage / (xbUInt32) iBlockFactor) && mpTag->npCurNode->iCurKeyNo == 0 ) + return XB_BOF; + + // move one to the left + mpTag->npCurNode->iCurKeyNo--; + if(( iRc = GetKeyPtr( vpTag, mpTag->npCurNode->iCurKeyNo, mpTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){ + iErrorStop = 120; + throw iRc; + } + + if(( iRc = GetBlock( vpTag, (ulKeyPtr / (xbUInt32) iBlockFactor), 1 )) != XB_NO_ERROR ){ + iErrorStop = 130; + throw iRc; + } + + // traverse down the right side of the tree + xbUInt32 ulNoOfKeys; + while( !IsLeaf( vpTag, mpTag->npCurNode )) // go down the chain looking for a leaf node + { + ulNoOfKeys = eGetUInt32( mpTag->npCurNode->cpBlockData ); + mpTag->npCurNode->iCurKeyNo = ulNoOfKeys; + + if(( iRc = GetKeyPtr( vpTag, ulNoOfKeys, mpTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){ + iErrorStop = 140; + throw iRc; + } + if(( iRc = GetBlock( vpTag, (ulKeyPtr / (xbUInt32) iBlockFactor), 1 )) != XB_NO_ERROR ){ + iErrorStop = 150; + throw iRc; + } + } + + // ulNoOfKeys = eGetUInt32( mpTag->npCurNode->cpBlockData ); + mpTag->npCurNode->iCurKeyNo = eGetUInt32( mpTag->npCurNode->cpBlockData ) - 1; + + // retrieve record to data buf + if( iRetrieveSw ){ + if(( iRc = GetKeyPtr( vpTag, mpTag->npCurNode->iCurKeyNo, mpTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){ + iErrorStop = 160; + throw iRc; + } + if(( iRc = dbf->GetRecord( ulKeyPtr )) != XB_NO_ERROR ){ + iErrorStop = 170; + throw iRc; + } + } else { + return iRc; + } + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbIxMdx::GetPrevKey() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return iRc; +} + +/***********************************************************************/ +//! @brief GetReuseEmptyNode swuitch setting. +/*! + \returns xbFalse - Do not reuse empty MDX index nodes (Dbase 6. behavior). + xbTrue - Reuse empty MDX index nodes. +*/ + +xbBool xbIxMdx::GetReuseEmptyNodesSw() const { + return bReuseEmptyNodes; +} +/***********************************************************************/ +xbBool xbIxMdx::GetSortOrder( void *vpTag ) const { + + // return true if descending + xbMdxTag *mTag = (xbMdxTag *) vpTag; + if( mTag->cKeyFmt2 & 0x08 ) + return 0x01; + else + return 0x00; +} + +/***********************************************************************/ +//! @brief Get tag for tag number. +/*! + \param iTagNo - Zero based, which tag to retrieve. + \returns Pointer to mdx tag for a given tag number. +*/ + +void * xbIxMdx::GetTag( xbInt16 iTagNo ) const { + + xbMdxTag *tt = mdxTagTbl; + xbInt16 i = 0; + + while( i < iTagNo && tt->next ){ + tt = tt->next; + i++; + } + if( i == iTagNo ) + return (void *) tt; + else + return NULL; +} + +/***********************************************************************/ +//! @brief Get tag for tag name. +/*! + \sTagName - Tag name to retrieve. + \returns Pointer to mdx tag for a given tag number. +*/ + +void * xbIxMdx::GetTag( xbString &sTagName ) const { + + xbMdxTag *tt = mdxTagTbl; + + while( sTagName != tt->cTagName && tt->next ){ + tt = tt->next; + } + + if( sTagName == tt->cTagName ) + return (void *) tt; + else + return NULL; +} +/***********************************************************************/ +xbInt16 xbIxMdx::GetTagCount() const { + return iTagUseCnt; +} + +/***********************************************************************/ +void xbIxMdx::GetTagName( void *vpTag, xbString &sTagName ){ + + xbMdxTag *mpTag = (xbMdxTag *) vpTag; + sTagName = mpTag->sTagName->Str(); +} + +/***********************************************************************/ +//const char *xbIxMdx::GetTagName( void *vpTag, xbInt16 iOpt ) const { + +const char *xbIxMdx::GetTagName( void *vpTag, xbInt16 ) const { + xbMdxTag *mpTag = (xbMdxTag *) vpTag; + return mpTag->cTagName; +} + +/***********************************************************************/ +xbString &xbIxMdx::GetTagName( void *vpTag ) const { + + xbMdxTag *mpTag = (xbMdxTag *) vpTag; + return *mpTag->sTagName; +} + +/***********************************************************************/ +void *xbIxMdx::GetTagTblPtr() const { + return (void *) mdxTagTbl; +} + +/***********************************************************************/ +xbBool xbIxMdx::GetUnique( void *vpTag ) const { +//! @brief Determine unique setting for given tag. +/*! + \param vpTag Tag to retrieve expression from. + \returns xbTrue if unique key. +*/ + xbMdxTag *mTag = (xbMdxTag *) vpTag; + return mTag->cUnique; +} + +/***********************************************************************/ +//! @brief Harvest Empty Node. +/*! + Harvest empty MDX node and add it to the chain of link nodes + + \param mpTag Tag to harvest. + \param iOpt - 0 Don't write the node info to disk, handled elsewhere (don't write it twice) + 1 Write the update into to disk + \returns Return Codes +*/ +xbInt16 xbIxMdx::HarvestEmptyNode( xbMdxTag *mpTag, xbIxNode *npNode, xbInt16 iOpt, xbBool bHarvestRoot ){ + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + xbBool bRootPage = xbFalse; + xbInt32 iOffset = 0; + + try{ + + if( mpTag->ulRootPage == BlockToPage( npNode->ulBlockNo ) && !bHarvestRoot ){ + bRootPage = xbTrue; + } + memset( npNode->cpBlockData, 0x00, GetBlockSize()); + + char *pTrg = npNode->cpBlockData; + if( !bRootPage ){ + pTrg += 4; + ePutUInt32( pTrg, ulFirstFreePage ); + } + + if( bRootPage ){ + if( mpTag->cHasKeys ){ + + // std::cout << "setting has keys\n"; + + mpTag->cHasKeys = 0x00; + if(( iRc = xbFseek( ((mpTag->ulTagHdrPageNo * 512) + 246), SEEK_SET )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + if(( iRc = xbFwrite( &mpTag->cHasKeys, 1, 1 )) != XB_NO_ERROR ){ + iErrorStop = 110; + throw iRc; + } + // might need to update left sibling and right sibling here. + // Fields don't seem to be updated consistently by other xbase tools, + // for now, not updating + } + + } else { + + // update header + // seek to position byte 13 + ulFirstFreePage = BlockToPage( npNode->ulBlockNo ); + if(( iRc = xbFseek( 36, SEEK_SET )) != XB_NO_ERROR ){ + iErrorStop = 120; + throw iRc; + } + // write it + char c4[4]; + ePutUInt32( c4, ulFirstFreePage ); + if(( iRc = xbFwrite( c4, 4, 1 )) != XB_NO_ERROR ){ + iErrorStop = 130; + throw iRc; + } + } + + if( iOpt == 1 ){ + if(( iRc = xbFseek( (xbInt64) ((npNode->ulBlockNo * GetBlockSize() )) + iOffset, SEEK_SET )) != XB_NO_ERROR ){ + iErrorStop = 140; + throw iRc; + } + // write out the block + if(( iRc = xbFwrite( npNode->cpBlockData, GetBlockSize(), 1 )) != XB_NO_ERROR ){ + iErrorStop = 150; + throw iRc; + } + } + } + + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbIxMdx::HarvestEmptyNodeI() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return iRc; +} + +/***********************************************************************/ +//! @brief Harvest Tag Nodes. +/*! + Save all nodes for a given tag into the free node chain. + Used for reindexing or deleting a given tag. + + \param mpTag Tag for harvesting nodes + \param bHarvestRoot Set to True when deleting tag + \returns Return Codes +*/ + +xbInt16 xbIxMdx::HarvestTagNodes( xbMdxTag *mpTag, xbBool bHarvestRoot ){ + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + + xbUInt32 ulBlkNo; + xbLinkListOrd ll; + xbLinkListNode * llN; + xbIxNode * n; + + try{ + + ll.SetDupKeys( xbFalse ); + + // clear out any history + if( mpTag->npNodeChain ){ + mpTag->npNodeChain = FreeNodeChain( mpTag->npNodeChain ); + mpTag->npCurNode = NULL; + } + + while( GetNextKey( mpTag, 0 ) == 0 ){ + n = mpTag->npNodeChain; + while(n){ + ll.InsertKey( n->ulBlockNo, (xbUInt32) n->iCurKeyNo ); + n = n->npNext; + } + } + + if( bHarvestRoot ) + ll.InsertKey( PageToBlock( mpTag->ulTagHdrPageNo ), 0 ); + + llN = ll.GetHeadNode(); + if(( n = xbIx::AllocateIxNode( GetBlockSize())) == NULL ){ + iErrorStop = 100; + iRc = XB_NO_MEMORY; + throw iRc; + } + + while( llN ){ + ulBlkNo = llN->GetKey(); + + // read in a block for the block number + if(( iRc = ReadBlock( ulBlkNo, GetBlockSize(), n->cpBlockData )) != XB_NO_ERROR ){ + iErrorStop = 110; + throw iRc; + } + // harvest it + n->ulBlockNo = ulBlkNo; + if(( iRc = HarvestEmptyNode( mpTag, n, 1, bHarvestRoot )) != XB_NO_ERROR ){ + iErrorStop = 120; + throw iRc; + } + llN = llN->GetNextNode(); + } + n = FreeNodeChain( n ); + mpTag->npNodeChain = FreeNodeChain( mpTag->npNodeChain ); + mpTag->npCurNode = NULL; + + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbIxMdx::HarvestTagNodes() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return iRc; +} + +/***********************************************************************/ +//! @brief Insert key into interior node. +/*! + Insert key into non-full interior node.
+ Assumes valid inputs + + \param vpTag Tag in play. + \param npNode Node for insertion. + \param iSlotNo Slot number to insert key. + \param ulPtr Page number to insert. + \returns Return Codes +*/ + +xbInt16 xbIxMdx::InsertNodeI( void *vpTag, xbIxNode *npNode, xbInt16 iSlotNo, xbUInt32 ulPtr ){ + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + char *pNewKeyPos; + char *pTrg; + char *pLastKey = NULL; + xbMdxTag * npTag; + npTag = (xbMdxTag *) vpTag; + xbInt16 iCopyLen; + xbInt16 iNewKeyPos = 8; + + try{ + xbInt32 lKeyCnt = GetKeyCount( npNode ); + iNewKeyPos += (iSlotNo * npTag->iKeyItemLen); + char *pSrc = npNode->cpBlockData; + + if( iSlotNo < lKeyCnt ) + iCopyLen = ((lKeyCnt - iSlotNo) * npTag->iKeyItemLen) + 4; + else + iCopyLen = 0; + + xbUInt32 ulRqdBufSize = (xbUInt32) ((lKeyCnt + 1) * npTag->iKeyItemLen) + 12; + if( ulRqdBufSize > npNode->ulBufSize ){ + npNode->ulBufSize += (xbUInt32) npTag->iKeyItemLen; + npNode->cpBlockData = (char *) realloc( npNode->cpBlockData, (size_t) npNode->ulBufSize ); + if( !npNode->cpBlockData ){ + iErrorStop = 100; + iRc = XB_NO_MEMORY; + throw iRc; + } + } + + // if not appending to the end of the node, make some room, move things to the right + pNewKeyPos = npNode->cpBlockData; + pNewKeyPos += iNewKeyPos; + + if( iSlotNo < lKeyCnt ){ + pTrg = pNewKeyPos; + pTrg += npTag->iKeyItemLen; + memmove( pTrg, pNewKeyPos, (size_t) iCopyLen ); + } + + // get the right most key for the left part of the split node + xbUInt32 ulKeyPtr2; + if(( iRc = GetKeyPtr( vpTag, npNode->iCurKeyNo, npNode, ulKeyPtr2 )) != XB_NO_ERROR ){ + iErrorStop = 110; + throw iRc; + } + + // get the new right key value for the freshly split node + pLastKey = (char *) malloc((size_t) npTag->iKeyLen); + if(( iRc = GetLastKeyForBlockNo( vpTag, PageToBlock( ulKeyPtr2 ), pLastKey )) != XB_NO_ERROR ){ + iRc = 120; + throw iRc; + } + + // write the key values + pTrg = pNewKeyPos; + pTrg += 4; + pSrc = pLastKey; + for( xbInt16 i = 0; i < npTag->iKeyLen; i++ ) + *pTrg++ = *pSrc++; + + pTrg = pNewKeyPos; + //pTrg+= npTag->iKeyItemLen - 4; + pTrg+= npTag->iKeyItemLen; + + ePutUInt32( pTrg, ulPtr); + ePutInt32( npNode->cpBlockData, ++lKeyCnt ); + + // write out the updated block to disk + if(( iRc = WriteBlock( npNode->ulBlockNo, GetBlockSize(), npNode->cpBlockData )) != XB_NO_ERROR ){ + iErrorStop = 130; + throw iRc; + } + if( pLastKey ) + free( pLastKey ); + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbIxMdx::InsertNodeI() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + if( pLastKey ) + free( pLastKey ); + } + return iRc; +} + +/***********************************************************************/ +//! @brief Insert key into leaf node. +/*! + Insert key into non-full leaf node.
+ Assumes valid inputs + + \param vpTag Tag in play. + \param npNode Node for insertion. + \param iSlotNo Slot number to insert key. + \param ulPtr Pointer number to insert. + \returns Return Codes +*/ +xbInt16 xbIxMdx::InsertNodeL( void *vpTag, xbIxNode *npNode, xbInt16 iSlotNo, char * cpKeyBuf, xbUInt32 ulPtr ){ + + // format of block data is + // 4 bytes number of keys on block + // 4 bytes - next free block or split block num + // repeating + // 4 bytes record number + // x bytes key data + + // Special processing note: when splitting node, new key is first inserted into full left node before + // the node is split. This routine will make additional room in the buffer for that scenario + + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + char *pNewKeyPos; // pointer to position in record for new key composite + char *pTrg; + + xbInt16 iNewKeyPos = 8; // position in data block where new key begins. + // is the position of the record number, where the fmt is + // [four byte rec number][actual key data] repeats + + xbMdxTag * npTag = (xbMdxTag *) vpTag; + xbInt16 iCopyLen; + + try{ + xbInt32 lKeyCnt = GetKeyCount( npNode ); + iNewKeyPos += (iSlotNo * npTag->iKeyItemLen); + + // length of number of keys that need to be moved to the right + if( iSlotNo < lKeyCnt ) + iCopyLen = (lKeyCnt - iSlotNo) * npTag->iKeyItemLen; + else + iCopyLen = 0; + + // +8 is to include the first two 4 byte fields in the block + xbUInt32 ulRqdBufSize = (xbUInt32) ((lKeyCnt + 1) * npTag->iKeyItemLen) + 8; + + if( ulRqdBufSize > npNode->ulBufSize ){ + + npNode->ulBufSize += (xbUInt32) npTag->iKeyItemLen; + npNode->cpBlockData = (char *) realloc( npNode->cpBlockData, (size_t) npNode->ulBufSize ); + + // init the newly acquired buffer space + char *p = npNode->cpBlockData; + p += (npNode->ulBufSize - (xbUInt32) npTag->iKeyItemLen); + memset( p, 0x00, (size_t) npTag->iKeyItemLen ); + + if( !npNode->cpBlockData ){ + iErrorStop = 100; + iRc = XB_NO_MEMORY; + throw iRc; + } + } + + // if not appending to end, move things right + + pNewKeyPos = npNode->cpBlockData; + pNewKeyPos += iNewKeyPos; + + if( iSlotNo < lKeyCnt ) { + pTrg = pNewKeyPos; + pTrg += npTag->iKeyItemLen; + memmove( pTrg, pNewKeyPos, (size_t) iCopyLen ); + + } + + // write rec number + ePutUInt32( pNewKeyPos, ulPtr ); + + // write the key value + pTrg = pNewKeyPos; + pTrg += 4; + char * pSrc = cpKeyBuf; + for( xbInt16 i = 0; i < npTag->iKeyLen; i++ ) + *pTrg++ = *pSrc++; + + // update number of keys on the node + ePutInt32( npNode->cpBlockData, ++lKeyCnt ); + + // determine length of node, zap everything to the right of it + xbUInt32 iStartPos = 8 + ((xbUInt32) lKeyCnt * (xbUInt32) npTag->iKeyItemLen ); + xbUInt32 iClearLen = npNode->ulBufSize - iStartPos; + + char *p = npNode->cpBlockData; + p += iStartPos; + memset( p, 0x00, iClearLen ); + + // write out the updated block to disk + if(( iRc = WriteBlock( npNode->ulBlockNo, GetBlockSize(), npNode->cpBlockData )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbIxMdx::InsertNodeL() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return iRc; +} +/***********************************************************************/ +inline xbBool xbIxMdx::IsLeaf( void *vpTag, xbIxNode *npNode ) const{ + + // for performance reasons, does no data checking + // will result in potentially hard to find segfaults if passing invalid npNode + + xbMdxTag *mTag = (xbMdxTag *) vpTag; + char *p = npNode->cpBlockData; + + xbInt32 lNoOfKeys = eGetInt32( p ); + // mdx interior nodes have a sibling number to the right of the right most key in the node + p+=8; + p+= mTag->iKeyItemLen * lNoOfKeys; + + if( eGetUInt32( p ) == 0 ){ + // std::cout << "leaf node\n"; + return true; + } else { + // std::cout << "interior node\n"; + return false; + } +} + +/***********************************************************************/ +xbInt16 xbIxMdx::KeyExists( void * vpTag ) +{ + // this method assumes the key has already been built + + xbMdxTag *mpTag = (xbMdxTag *) vpTag; + xbInt16 iRc = FindKey( vpTag, mpTag->cpKeyBuf, mpTag->iKeyLen, 0 ); + + if( iRc == 0 ) + return 1; + else + return 0; + +} + +/***********************************************************************/ +//! @brief Set position for key add. +/*! + This routine is called by the AddKey() method and is used to position + the node chain to the position the new key should be added to the index. + + \param npTag Pointer to npTag. + \param ulAddRecNo Record number to add. + \returns Return Codes +*/ +xbInt16 xbIxMdx::KeySetPosAdd( xbMdxTag *mpTag, xbUInt32 ulAddRecNo ){ + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + try{ + + iRc = FindKey( mpTag, mpTag->cpKeyBuf, mpTag->iKeyLen, 0 ); + if( iRc == XB_NOT_FOUND || iRc == XB_EMPTY ) + return XB_NO_ERROR; // good position + else if( iRc != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + // get here if key was found, get the right most instance of any non unique key for append, find correct spot for update + if( GetUnique( mpTag ) == 0 ){ + + xbUInt32 ulCurRecNo; + if(( iRc = GetDbfPtr( mpTag, mpTag->npCurNode->iCurKeyNo, mpTag->npCurNode, ulCurRecNo )) != XB_NO_ERROR ){ + iErrorStop = 110; + throw iRc; + } + xbBool bKeysMatch = xbTrue; + + while( bKeysMatch && ulAddRecNo > ulCurRecNo && iRc == XB_NO_ERROR ){ + if(( iRc = GetNextKey( mpTag, 0 )) == XB_NO_ERROR ){ + if( memcmp( GetKeyData( mpTag->npCurNode, mpTag->npCurNode->iCurKeyNo, mpTag->iKeyItemLen ), mpTag->cpKeyBuf, (size_t) mpTag->iKeyLen )) + bKeysMatch = xbFalse; + else{ + if(( iRc = GetDbfPtr( mpTag, mpTag->npCurNode->iCurKeyNo, mpTag->npCurNode, ulCurRecNo )) != XB_NO_ERROR ){ + iErrorStop = 120; + throw iRc; + } + } + } + } + } + if( iRc == XB_EOF ){ // eof condition + if(( iRc = GetLastKey( 0, mpTag, 0 )) != XB_NO_ERROR ){ + iErrorStop = 130; + throw iRc; + } + mpTag->npCurNode->iCurKeyNo++; + return XB_NO_ERROR; + } + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbIxMdx::KeySetPosAdd() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return iRc; +} + +/***********************************************************************/ +//! @brief Set position for key delete. +/*! + This routine is called by the DeleteKey() method and is used to position + the node chain to the position the old key should be deleted from the index. + + \param npTag Pointer to npTag. + \returns Return Codes +*/ + +xbInt16 xbIxMdx::KeySetPosDel( xbMdxTag *npTag ){ + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + xbString sMsg; + + try{ + + iRc = FindKey( npTag, npTag->cpKeyBuf2, npTag->iKeyLen, 0 ); + if( iRc == XB_NOT_FOUND || iRc == XB_EMPTY ) + return XB_NO_ERROR; // good pos ition + else if( iRc != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + + xbUInt32 ulIxRecNo; + if(( iRc = GetDbfPtr( npTag, npTag->npCurNode->iCurKeyNo, npTag->npCurNode, ulIxRecNo )) != XB_NO_ERROR ){ + iErrorStop = 110; + throw iRc; + } + + if( ulIxRecNo == dbf->GetCurRecNo()) + return XB_NO_ERROR; + + if( GetUnique( npTag ) == 1 ){ + iErrorStop = 120; + iRc = XB_NOT_FOUND; + throw iRc; + } + + xbBool bFound = xbFalse; + xbBool bKeysMatch = xbTrue; + while( bKeysMatch && !bFound && iRc == XB_NO_ERROR ){ + + if(( iRc = GetNextKey( npTag, 0 )) != XB_NO_ERROR ){ + iErrorStop = 130; + throw iRc; + } + if( memcmp( GetKeyData( npTag->npCurNode, npTag->npCurNode->iCurKeyNo, npTag->iKeyItemLen ), npTag->cpKeyBuf2, (size_t) npTag->iKeyLen )){ + bKeysMatch = xbFalse; + } else { + if(( iRc = GetDbfPtr( npTag, npTag->npCurNode->iCurKeyNo, npTag->npCurNode, ulIxRecNo )) != XB_NO_ERROR ){ + iErrorStop = 140; + throw iRc; + } + if( ulIxRecNo == dbf->GetCurRecNo()) + bFound = xbTrue; + } + } + + if( bFound ) + return XB_NO_ERROR; + else + return XB_NOT_FOUND; + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbIxMdx::KeySetPosDel() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return iRc; +} + +/***********************************************************************/ +//! @brief Returns key update status. +/*! + \param vpTag Tag to check status on. + \returns xbtrue - Key was updated.
xbFalse - Key not updated. +*/ +/* +inline xbBool xbIxMdx::KeyFiltered( void *vpTag ) const{ + + xbMdxTag *mpTag = (xbMdxTag *) vpTag; + return mpTag->bKeyFiltered; +} +*/ +/***********************************************************************/ +xbInt16 xbIxMdx::LoadTagDetail( xbInt16 iOption, xbMdxTag *tte ){ + + // option 1 - Load the entire tag detail + // option 2 - Load the dynamic variables only + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + size_t iReadSize; + char *buf = NULL; + char *p; + + try{ + // set the read length based on the option + if( iOption == 1 ) + iReadSize = 1024; + + else if( iOption == 2 ) + // iReadSize = 4; + iReadSize = 260; + else{ + iRc = XB_INVALID_OPTION; + iErrorStop = 100; + throw iRc; + } + if(( buf = (char *) calloc( 1, (size_t) iReadSize )) == NULL ){ + iErrorStop = 110; + iRc = XB_NO_MEMORY; + throw iRc; + } + if(( iRc = ReadBlock( tte->ulTagHdrPageNo,(xbUInt32) (GetBlockSize() / (xbUInt16) iBlockFactor), + iReadSize, buf )) != XB_NO_ERROR ){ + free( buf ); + iErrorStop = 30; + throw iRc; + } + + p = buf; + tte->ulRootPage = eGetUInt32( p ); + + if( iOption == 1 ){ + p+=4; + tte->ulTagSize = eGetUInt32( p ); + p+=4; + tte->cKeyFmt2 = *p; + p++; + tte->cKeyType2 = *p; + p+=3; + tte->iKeyLen = eGetInt16( p ); + p+=2; + tte->iKeysPerBlock = *p; + p+=2; + tte->iSecKeyType = eGetInt16( p ); + p+=2; + tte->iKeyItemLen = eGetInt16( p ); + p+=2; + tte->cSerialNo = *p; + p+=3; + tte->cUnique = *p; + p++; + + // next line assumes expression is a null terminated string in the block + tte->sKeyExp = new xbString(); + tte->sKeyExp->Sprintf( "%s", p ); + + p+=221; + tte->cHasFilter = *p; + p+=1; + tte->cHasKeys = *p; + p+=2; + tte->ulLeftChild = eGetUInt32( p ); + p+=4; + tte->ulRightChild = eGetUInt32( p ); + p+=5; + tte->cTagYY = *p; + p++; + tte->cTagMM = *p; + p++; + tte->cTagDD = *p; + // p+=223; + + p+=221; + tte->cKeyFmt3 = *p; + + if( tte->cHasFilter ){ + p+=282; + tte->sFiltExp = new xbString(); + tte->sFiltExp->Sprintf( "%s", p ); + tte->filter = new xbExp( xbase, dbf ); + if(( iRc = tte->filter->ParseExpression( tte->sFiltExp->Str())) != XB_NO_ERROR ){ + iErrorStop = 120; + throw iRc; + } + } + tte->npNodeChain = NULL; + tte->npCurNode = NULL; + tte->cpKeyBuf = (char *) malloc( (size_t) tte->iKeyLen + 1 ); + tte->cpKeyBuf2 = (char *) malloc( (size_t) tte->iKeyLen + 1 ); + tte->exp = new xbExp( xbase, dbf ); + if(( iRc = tte->exp->ParseExpression( tte->sKeyExp->Str() )) != XB_NO_ERROR ){ + iErrorStop = 130; + throw iRc; + } + } else if( iOption == 2 ){ + // refresh the dynamic tag variables + p+=4; + tte->ulTagSize = eGetUInt32( p ); + p+= 16; + tte->cSerialNo = *p; + p+= 226; + tte->cHasKeys = *p; + p+=2; + tte->ulLeftChild = eGetUInt32( p ); + p+=4; + tte->ulRightChild = eGetUInt32( p ); + p+=5; + tte->cTagYY = *p; + p++; + tte->cTagMM = *p; + p++; + tte->cTagDD = *p; + } + if( buf ) + free( buf ); + + } + catch (xbInt16 iRc ){ + if( buf ) + free( buf ); + xbString sMsg; + sMsg.Sprintf( "xbIxMdx::LoadTagDetail() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return iRc; +} + +/***********************************************************************/ +xbInt16 xbIxMdx::LoadTagTable() +{ + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + char * buf = NULL; + + //std::cout << "xbIxMdx::LoadTagTable() tag use cnt = " << iTagUseCnt << "\n"; + + try{ + + if( iTagUseCnt > 46 ){ + iErrorStop = 100; + iRc = XB_INVALID_INDEX; + throw iRc; + } + + xbInt16 iBufSize = (xbInt16) iTagLen * iTagUseCnt; + + if(( buf = (char *) malloc( (size_t) iBufSize )) == NULL ){ + iErrorStop = 110; + iRc = XB_NO_MEMORY; + throw iRc; + } + + if(( iRc = xbFseek( 544, SEEK_SET )) != XB_NO_ERROR ){ + iErrorStop = 120; + throw iRc; + } + + if(( iRc = xbFread( buf, (size_t) iBufSize, 1 )) != XB_NO_ERROR ){ + iErrorStop = 130; + throw iRc; + } + + xbInt16 iPos; + char *p; + xbMdxTag *tte; + xbMdxTag *ttel = NULL; + + for( xbInt16 i = 0; i < iTagUseCnt; i++ ){ + iPos = i * iTagLen; + p = buf + iPos; + + if(( tte = (xbMdxTag *) calloc( 1, (size_t) sizeof( xbMdxTag ))) == NULL ){ + iErrorStop = 140; + iRc = XB_NO_MEMORY; + throw iRc; + } + + // set the current tag to the first tag in the table + if( !vpCurTag ) + xbIx::SetCurTag( (void *) tte ); + + if( mdxTagTbl ) + ttel->next = tte; + else + mdxTagTbl = tte; + + ttel = tte; + tte->next = NULL; + tte->ulTagHdrPageNo = eGetUInt32( p ); + + p += 4; + for( xbUInt32 i = 0; i < 11; i ++ ) + tte->cTagName[i] = *p++; + + tte->cTagName[11] = 0x00; + tte->cKeyFmt = *p++; + tte->cLeftChild = *p++; + tte->cRightChild = *p++; + tte->cParent = *p++; + tte->c2 = *p++; + tte->cKeyType = *p; + tte->sTagName = new xbString(); + tte->sTagName->Set( tte->cTagName ); + tte->sTagName->Trim(); + + if(( iRc = LoadTagDetail( 1, tte )) != XB_NO_ERROR ){ + iErrorStop = 150; + throw iRc; + } + } + if( buf ) + free( buf ); + } + catch (xbInt16 iRc ){ + if( buf ) + free( buf ); + xbString sMsg; + sMsg.Sprintf( "xbIxMdx::LoadTagTable() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + if( iErrorStop == 100 ){ + sMsg.Sprintf( "xbIxMdx::LoadTagTable() Invalid Tag Count: %d", iTagUseCnt ); + xbase->WriteLogMessage( sMsg.Str()); + } + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return iRc; +} + +/**************************************************************************************************************/ +//! @brief Calculate the block number for a given page. +/*! + This routine is called by any function needing to calculate the block number for a given page. + Page numbers are stored internally in the physical file, and the library reads and writes in + blocks of one or more pages. + + Assumes valid data input + + \param ulPageNo Page Number + \returns Calculated block number. +*/ + +inline xbUInt32 xbIxMdx::PageToBlock( xbUInt32 ulPageNo ){ + return ulPageNo / (xbUInt32) iBlockFactor; +} + + + +/**************************************************************************************************************/ +#ifdef XB_DEBUG_SUPPORT +xbInt16 xbIxMdx::PrintKey( void *vpTag, xbIxNode *npNode, xbInt16 iKeyNo, xbInt16 iDepth, char cType, xbInt16 iOutputOpt ){ + + xbString sPre; + sPre.Sprintf( "%c ", cType ); + for( xbInt16 i = 0; i < iDepth; i++ ) + sPre += "|"; + + xbString sPost; + sPost.Sprintf( "\tThisBlock=[%ld] KeyNo=[%d] Depth=[%d]", npNode->ulBlockNo, iKeyNo, iDepth ); + + xbMdxTag * mpTag = (xbMdxTag *) vpTag; + char *p = npNode->cpBlockData + (8 + (iKeyNo * mpTag->iKeyItemLen )); + + xbString sKeyPtr; + xbUInt32 ulNoOfKeys = 0; + if( cType == 'I' ) { // interior + sKeyPtr.Sprintf( " ptr=[%ld]", eGetUInt32( p )); + ulNoOfKeys = eGetUInt32( mpTag->npCurNode->cpBlockData ); + } + else if( cType == 'L' ) // leaf + sKeyPtr.Sprintf( " rec=[%ld]", eGetUInt32( p )); + p += 4; + + xbString s; + if(( cType == 'I' && iKeyNo < (xbInt16) ulNoOfKeys) || cType == 'L' ){ + if( mpTag->cKeyType2 == 'C' ){ //CHAR + for( xbInt32 l = 0; l < (mpTag->iKeyItemLen-4); l++ ) + s += *p++; + + } else if( mpTag->cKeyType2 == 'N' ){ // NUMERIC + xbBcd bcd( p ); + xbString s2; + bcd.ToString( s2 ); + s += s2; + + } else if( mpTag->cKeyType2 == 'D' ){ // DATE + xbInt32 lDate = (xbInt32) eGetDouble( p ); + xbDate d( lDate ); + //xbString s2; + //d.JulToDate8( lDate, s2 ); + s.Sprintf( "%s%s", s.Str(), d.Str()); + } + } else { + s = "Rightmost InteriorNode Pointer"; + } + + xbString sOut( sPre ); + sOut += s; + sOut += sPost; + sOut += sKeyPtr; + + xbase->WriteLogMessage( sOut, iOutputOpt ); + return XB_NO_ERROR; +} +#endif + +/***********************************************************************/ +//! @brief ReadHeadBlock. +/*! + Read values off head block in MDX file + \param iOpt 0 - Read entire block, initialize as needed.
+ 1 - Read in only dynamic section of block
+ \returns Return Codes + +*/ + +xbInt16 xbIxMdx::ReadHeadBlock( xbInt16 iOpt ) +{ + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + + try{ + if( !FileIsOpen()){ + iRc = XB_NOT_OPEN; + iErrorStop = 100; + throw iRc; + } + char sBuf[48]; + memset( sBuf, 0x00, 48 ); + + if( iOpt == 0 ){ + if(( iRc = xbFseek( 0, SEEK_SET )) != XB_NO_ERROR ){ + iErrorStop = 110; + throw iRc; + } + + if(( iRc = xbFread( sBuf, 47, 1 )) != XB_NO_ERROR ){ + iErrorStop = 120; + throw iRc; + } + } else { + + if(( iRc = xbFseek( 28, SEEK_SET )) != XB_NO_ERROR ){ + iErrorStop = 130; + throw iRc; + } + + if(( iRc = xbFread( sBuf, 19, 1 )) != XB_NO_ERROR ){ + iErrorStop = 140; + throw iRc; + } + } + + char *p = sBuf; + if( iOpt == 0 ){ + cVersion = *p++; + cCreateYY = *p++; + cCreateMM = *p++; + cCreateDD = *p++; + sFileName.Assign( p, 1, 16 ); + p+=16; + iBlockFactor = eGetInt16( p ); + p+=2; + SetBlockSize( (xbUInt32) eGetInt16( p )); + p+=2; + cProdIxFlag = *p++; + cTagEntryCnt = *p++; + iTagLen = *p; + p+=2; + + iTagUseCnt = eGetInt16( p ); + //lTagUseCnt = eGetInt32( p ); + //p+=4; + p+=2; + cNextTag = *p++; + c1B = *p++; + + ulPageCnt = eGetUInt32( p ); + p+=4; + ulFirstFreePage = eGetUInt32( p ); + p+=4; + ulNoOfBlockAvail = eGetUInt32( p ); + p+=4; + cUpdateYY = *p++; + cUpdateMM = *p++; + cUpdateDD = *p; + + if( cNodeBuf ) + free( cNodeBuf ); + + if(( cNodeBuf = (char *) malloc( (size_t) GetBlockSize())) == NULL ){ + iErrorStop = 150; + throw XB_NO_MEMORY; + } + + if(( iRc = xbIxMdx::LoadTagTable()) != XB_NO_ERROR ){ + iErrorStop = 160; + throw iRc; + } + + } else { + iTagUseCnt = eGetInt16( p ); + p+=4; + ulPageCnt = eGetUInt32( p ); + p+=4; + ulFirstFreePage = eGetUInt32( p ); + p+=4; + ulNoOfBlockAvail = eGetUInt32( p ); + p+=4; + cUpdateYY = *p++; + cUpdateMM = *p++; + cUpdateDD = *p; + } + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbIxMdx::ReadHeadBlock() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + if( cNodeBuf ) + free( cNodeBuf ); + } + return iRc; +} + +/***********************************************************************/ +//! @brief Reindex +/*! + Reindex specifed tag or all tags + \param **vpTag &tag - Tag to reindex.
+ NULL - Reindex all tags
+ \returns Return Codes + + If this method fails, the index is left in an undefined state + +*/ + +xbInt16 xbIxMdx::Reindex( void **vpTag ){ + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + xbMdxTag * mpTag; + #ifdef XB_LOCKING_SUPPORT + xbBool bLocked = xbFalse; + #endif + + if( vpTag ) + mpTag = (xbMdxTag *) *vpTag; + else + mpTag = NULL; + + struct tagInfo{ + xbBool bUnique; + xbBool bDesc; + char sTagName[11]; + xbString *sKeyExp; + xbString *sFiltExp; + tagInfo *next; + }; + tagInfo *ti = NULL; + + try{ + + #ifdef XB_LOCKING_SUPPORT + if( dbf->GetAutoLock() && !dbf->GetTableLocked() ){ + if(( iRc = dbf->LockTable( XB_LOCK )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + bLocked = xbTrue; + } + #endif + + if( mpTag == NULL ){ + // do all tags + xbMdxTag *tt = mdxTagTbl; + tagInfo *pHead = NULL; + tagInfo *pEnd = NULL; + + if( tt ){ + while( tt ){ + ti = (tagInfo *) calloc( 1, sizeof( tagInfo )); + ti->bUnique = tt->cUnique ? 1 : 0; + ti->bDesc = (((tt->cKeyFmt2 & 0x08) > 0) ? 1 : 0); + memcpy( ti->sTagName, tt->cTagName, 11 ); + ti->sKeyExp = new xbString( tt->sKeyExp->Str()); + if( tt->cHasFilter ) + ti->sFiltExp = new xbString( tt->sFiltExp->Str()); + else + ti->sFiltExp = new xbString( "" ); + if( !pHead ) + pHead = ti; + else + pEnd->next = ti; + pEnd = ti; + tt = tt->next; + } + } + + // get the file name and save it + xbString sMdxFileName = GetFqFileName(); + + // close the mdx file + if(( iRc = xbIxMdx::Close()) != XB_NO_ERROR ){ + iErrorStop = 110; + throw iRc; + } + + // delete the file + xbRemove(); + + // init variables + Init(); + + tagInfo *p = pHead; + tagInfo *pDel; + + // create new file & add the tags + while( p ){ + + if(( iRc = CreateTag( p->sTagName, p->sKeyExp->Str(), p->sFiltExp->Str(), p->bDesc, p->bUnique, xbTrue, vpTag )) != XB_NO_ERROR ){ + iErrorStop = 120; + throw iRc; + } + delete p->sKeyExp; + delete p->sFiltExp; + pDel = p; + p = p->next; + free( pDel ); + } + } else { + if(( iRc = HarvestTagNodes( mpTag )) != XB_NO_ERROR ){ + iErrorStop = 130; + throw iRc; + } + } + + xbUInt32 ulRecCnt = 0; + if(( iRc = dbf->GetRecordCnt( ulRecCnt )) != XB_NO_ERROR ){ + iErrorStop = 140; + throw iRc; + } + xbInt16 iCurTag = 0; + xbBool bDone = xbFalse; + + for( xbUInt32 ulRec = 1; ulRec <= ulRecCnt; ulRec++ ){ + if(( iRc = dbf->GetRecord( ulRec )) != XB_NO_ERROR ){ + iErrorStop = 150; + throw iRc; + } + + bDone = xbFalse; + iCurTag = 0; + if( !vpTag ) + mpTag = (xbMdxTag *) GetTag( iCurTag++ ); + + while( !bDone ){ + // do the tag things + // CreateKey + if(( iRc = CreateKey( mpTag, 1 )) != XB_NO_ERROR ){ + iErrorStop = 160; + throw iRc; + } + if( mpTag->iKeySts == XB_ADD_KEY ){ + if( mpTag->cUnique ){ + if(( iRc = CheckForDupKey( mpTag )) != XB_NO_ERROR ){ + iErrorStop = 170; + throw iRc; + } + } + if(( iRc = AddKey( mpTag, ulRec )) != XB_NO_ERROR ){ + iErrorStop = 180; + throw iRc; + } + } + if( vpTag || iCurTag >= GetTagCount()) + bDone = xbTrue; + else + mpTag = (xbMdxTag *) GetTag( iCurTag++ ); + } + } + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbIxMdx::ReIndex() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + this->DeleteTag( mpTag ); + } + + #ifdef XB_LOCKING_SUPPORT + if( bLocked ){ + dbf->LockTable( XB_UNLOCK ); + } + #endif + return iRc; +} + +/***********************************************************************/ +xbInt16 xbIxMdx::SetCurTag( xbString &sTagName ) { + + xbMdxTag *tt = (xbMdxTag *) GetTag( sTagName ); + if( tt ){ + xbIx::SetCurTag((void *) tt ); + return XB_NO_ERROR; + } else + return XB_INVALID_TAG; +} + +/***********************************************************************/ +xbInt16 xbIxMdx::SetCurTag( xbInt16 iTagNo ) { + + xbMdxTag *tt = (xbMdxTag *) GetTag( iTagNo ); + if( tt ){ + xbIx::SetCurTag((void *) tt ); + return XB_NO_ERROR; + } else + return XB_INVALID_TAG; +} + +/***********************************************************************/ +//! @brief SetReuseEmptyNode switch setting. +/*! + \param bEmptyNodeSw xbFalse - Do not reuse empty MDX index nodes (Dbase 6. behavior). + xbTrue - Reuse empty MDX index nodes. +*/ + +void xbIxMdx::SetReuseEmptyNodesSw( xbBool bEmptyNodesSw ) { + bReuseEmptyNodes = bEmptyNodesSw; +} + +/***********************************************************************/ +//! @brief Split an interior node +/*! + + This routine splits an interior node into two nodes, divided by dSplitFactor.
+ This behaves differently than V7 Dbase. V7 does not balance the nodes.
+ For V7, if adding a key to the end of a node, it will create a right node + with only one key, and the left node is still full.

+ + Possible performance improvement options.
+ Two modes when splitting:
+ a) Split nodes in the middle - good for random access applications
+ b) Split off right node with only one key - good for applications with + expectation of ascending keys added moving forward.
+ + This routine first inserts the key into the left node in the appropriate location + then splits the node based on the split factor setting. + + \param vpTag Tag in play. + \param npLeft Left node to split. + \param npRight Right node to split. + \param iSlotNo Slot number for split. + \param ulPtr Pointer number to insert. + \returns Return Codes +*/ + +xbInt16 xbIxMdx::SplitNodeI( void *vpTag, xbIxNode * npLeft, xbIxNode *npRight, xbInt16 iSlotNo, xbUInt32 ulPtr ){ + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + xbMdxTag * npTag = (xbMdxTag *) vpTag; + xbDouble dSplitFactor = .5; // split the nodes 50/50 + xbInt16 iLen; + char *pSrc; + char *pTrg; + + try{ + xbInt32 lKeyCnt = GetKeyCount( npLeft ); + xbInt32 lNewLeftKeyCnt = (xbInt32) ((lKeyCnt + 1) * dSplitFactor); + xbInt32 lNewRightKeyCnt = lKeyCnt - lNewLeftKeyCnt; + if(( iRc = InsertNodeI( vpTag, npLeft, iSlotNo, ulPtr )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + + // move the right half of the left node to the right node + pSrc = npLeft->cpBlockData; + pSrc += 8 + ((lNewLeftKeyCnt+1) * npTag->iKeyItemLen); + pTrg = npRight->cpBlockData; + pTrg += 8; + iLen = (lNewRightKeyCnt * npTag->iKeyItemLen) + 4; + memmove( pTrg, pSrc, (size_t) iLen ); + + // eliminate chattle on the right + iLen = 12 + (lNewLeftKeyCnt * npTag->iKeyItemLen); + pSrc = npLeft->cpBlockData; + pSrc += iLen; + memset( pSrc, 0x00, npLeft->ulBufSize - (xbUInt32) iLen ); + + // write the new key counts into the nodes + pTrg = npLeft->cpBlockData; + ePutInt32( pTrg, lNewLeftKeyCnt ); + pTrg = npRight->cpBlockData; + ePutInt32( pTrg, lNewRightKeyCnt ); + + // write out the block + if(( iRc = WriteBlock( npLeft->ulBlockNo, GetBlockSize(), npLeft->cpBlockData )) != XB_NO_ERROR ){ + iErrorStop = 110; + throw iRc; + } + // write out the block + if(( iRc = WriteBlock( npRight->ulBlockNo, GetBlockSize(), npRight->cpBlockData )) != XB_NO_ERROR ){ + iErrorStop = 120; + throw iRc; + } + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbIxMdx::SplitNodeI() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return iRc; +} + +/***********************************************************************/ +//! @brief Split a leaf node. +/*! + This routine splits an index leaf into two nodes, divided by dSplitFactor.
+ + Possible performance improvement options.
+ Two modes when splitting:
+ a) Split nodes in the middle - good for random access applications
+ b) Split off right node with only one key - good for applications with + expectation of ascending keys added moving forward.
+ + This routine first inserts the key into the left node in the appropriate location + then splits the node based on the split factor setting. + + \param vpTag Tag in play. + \param npLeft Left node to split. + \param npRight Right node to split. + \param iSlotNo Slot number for split. + \param ulPtr Pointer number to insert. + \returns Return Codes +*/ + +xbInt16 xbIxMdx::SplitNodeL( void *vpTag, xbIxNode * npLeft, xbIxNode *npRight, + xbInt16 iSlotNo, char * cpKeyBuf, xbUInt32 ulPtr ){ + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + xbDouble dSplitFactor = .5; // can adjust performance with this number + xbMdxTag *mpTag = (xbMdxTag *) vpTag; + xbString sMsg; + + xbInt16 iLen; + char *pSrc; + char *pTrg; + + // std::cout << "In xbIxMdx::SplitNodeL()\n"; + try{ + xbInt32 lKeyCnt = GetKeyCount( npLeft ); + xbInt32 lNewLeftKeyCnt = (xbInt32) ((lKeyCnt + 1) * dSplitFactor) + 1; + xbInt32 lNewRightKeyCnt = lKeyCnt + 1 - lNewLeftKeyCnt; + + if(( iRc = InsertNodeL( vpTag, npLeft, iSlotNo, cpKeyBuf, ulPtr )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + + // move right half off of left node to the right node + pSrc = npLeft->cpBlockData; + pSrc += 8 + (lNewLeftKeyCnt * mpTag->iKeyItemLen); + pTrg = npRight->cpBlockData; + pTrg += 8; + iLen = lNewRightKeyCnt * mpTag->iKeyItemLen; + memmove( pTrg, pSrc, (size_t) iLen ); + + // write the new key counts into the nodes + pTrg = npLeft->cpBlockData; + ePutInt32( pTrg, lNewLeftKeyCnt ); + pTrg = npRight->cpBlockData; + ePutInt32( pTrg, lNewRightKeyCnt ); + + // zero out the next key number so this node is not confused with interior node + iLen = 8 + (lNewLeftKeyCnt * mpTag->iKeyItemLen); + pSrc = npLeft->cpBlockData; + pSrc += iLen; + memset( pSrc, 0x00, npLeft->ulBufSize - (xbUInt32) iLen ); + + // write out the left block + if(( iRc = WriteBlock( npLeft->ulBlockNo, GetBlockSize(), npLeft->cpBlockData )) != XB_NO_ERROR ){ + iErrorStop = 110; + throw iRc; + } + // write out the right block + if(( iRc = WriteBlock( npRight->ulBlockNo, GetBlockSize(), npRight->cpBlockData )) != XB_NO_ERROR ){ + iErrorStop = 120; + throw iRc; + } + + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbIxMdx::SplitNodeL() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return iRc; +} +/**************************************************************************************************************/ +//! @brief TagSerialNo. +/*! + This routine is used internally for reading or updating the serial number on a given tag when the tag. + + \param iOption 1 - Read tag serial number off disk, save in structure
+ 2 - Write serial number from memory to disk
+ 3 - Read serial number off disk, increment, write updated number to disk
+ mpTag - Pointer to tag for serial number update + \returns Return Codes +*/ + +xbInt16 xbIxMdx::TagSerialNo( xbInt16 iOption, xbMdxTag * mpTag ){ + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + + try{ + xbInt64 lPos = (mpTag->ulTagHdrPageNo * 512) + 20; + + if( iOption != 2 ){ + if(( iRc = xbFseek( lPos, SEEK_SET )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + if(( iRc = xbFgetc( mpTag->cSerialNo )) != XB_NO_ERROR ){ + iErrorStop = 110; + throw iRc; + } + } + + if( iOption == 3 ) + mpTag->cSerialNo++; + + if( iOption != 1 ){ + if(( iRc = xbFseek( lPos, SEEK_SET )) != XB_NO_ERROR ){ + iErrorStop = 120; + throw iRc; + } + if(( iRc = xbFwrite( &mpTag->cSerialNo, 1, 1 )) != XB_NO_ERROR ){ + iErrorStop = 130; + throw iRc; + } + } + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbIxMdx::UpdateSerialNo() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return iRc; +} + +/***********************************************************************/ +//! @brief UpdateTagKey +/*! + This routine updates a key or a given tag. + The file header is considered to be the first 2048 bytes in the file. + + \param cAction A - Add a key.
+ D - Delete a key.
+ R - Revise a key.
+ \param vpTg - Pointer to tag.
+ \param ulRecNo - Record number association with the action.
+ \returns Return Codes +*/ + +xbInt16 xbIxMdx::UpdateTagKey( char cAction, void *vpTag, xbUInt32 ulRecNo ){ + + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + xbMdxTag *npTag = (xbMdxTag *) vpTag; + + try{ + // save off any needed fields for updating + xbUInt32 ulTagSizeSave = npTag->ulTagSize; + + if( cAction == 'D' || cAction == 'R' ){ + // std::cout << "UpdateTagKey-delete going to DeleteKey \n"; + if(( iRc = DeleteKey( vpTag )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + } + + if( cAction == 'A' || cAction == 'R' ){ + if(( iRc = AddKey( vpTag, ulRecNo )) != XB_NO_ERROR ){ + iErrorStop = 110; + throw iRc; + } + } + if( ulTagSizeSave != npTag->ulTagSize ){ + if(( iRc = UpdateTagSize( npTag, npTag->ulTagSize )) != XB_NO_ERROR) { + iErrorStop = 120; + throw iRc; + } + } + // update the serial number + if(( iRc = TagSerialNo( 3, npTag )) != XB_NO_ERROR ){ + iErrorStop = 130; + throw iRc; + } + + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbIxMdx::UpdateTagKey() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return iRc; +} + +/**************************************************************************************************************/ +//! @brief Write head block. +/*! + This routine updates the MDX file header and commits changes to disk. + The file header is considered to be the first 2048 bytes in the file. + + \param iOption 0 - Entire 2048 byte header, used for creating a new mdx file.
+ 1 - Bytes 28 through 46, used when adding or deleting a tag.
+ 2 - Bytes 32 through 46, used after updating keys in the file. + \returns Return Codes +*/ + + +xbInt16 xbIxMdx::WriteHeadBlock( xbInt16 iOption ){ + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + + try{ + xbDate d; + d.Sysdate(); // set to system date, today + cUpdateYY = (char) d.YearOf() - 1900; + cUpdateMM = (char) d.MonthOf(); + cUpdateDD = (char) d.DayOf( XB_FMT_MONTH ); + + if( iOption > 0 ){ + char buf[48]; + memset( buf, 0x00, 48 ); + xbUInt32 ulStartPos = 0; + xbUInt32 ulLen = 0; + + if( iOption == 1 ){ + ePutInt16( &buf[28], iTagUseCnt ); + buf[30] = cNextTag; + buf[31] = 0x1b; + ulStartPos = 28; + ulLen = 19; + } else { + ulStartPos = 32; + ulLen = 16; + } + + ePutUInt32( &buf[32], ulPageCnt ); + ePutUInt32( &buf[36], ulFirstFreePage ); + ePutUInt32( &buf[40], ulNoOfBlockAvail ); + buf[44] = cUpdateYY; + buf[45] = cUpdateMM; + buf[46] = cUpdateDD; + + if(( iRc = xbFseek( ulStartPos, SEEK_SET )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + + if(( iRc = xbFwrite( &buf[ulStartPos], ulLen, 1 )) != XB_NO_ERROR ){ + iErrorStop = 110; + throw iRc; + } + + } else if( iOption == 0 ){ + char buf[2048]; + memset( buf, 0x00, 2048 ); + + buf[0] = cVersion; + cCreateYY = cUpdateYY; + cCreateMM = cUpdateMM; + cCreateDD = cUpdateDD; + buf[1] = cCreateYY; + buf[2] = cCreateMM; + buf[3] = cCreateDD; + + + for( xbUInt32 l = 0; l < sFileName.Len() && l < 10; l++ ){ + buf[l+4] = sFileName[l+1]; + } + + ePutInt16( &buf[20], iBlockFactor ); + ePutInt16( &buf[22], (xbInt16) GetBlockSize() ); + + buf[24] = cProdIxFlag; + buf[25] = cTagEntryCnt; + ePutInt16 ( &buf[26], iTagLen ); + ePutInt16 ( &buf[28], iTagUseCnt ); + buf[30] = cNextTag; + buf[31] = c1B; + ePutUInt32( &buf[32], ulPageCnt ); + ePutUInt32( &buf[36], ulFirstFreePage ); + ePutUInt32( &buf[40], ulNoOfBlockAvail ); + buf[44] = cUpdateYY; + buf[45] = cUpdateMM; + buf[46] = cUpdateDD; + + // not sure what the following "1" is for in a sea of zeroes.... + // maybe it's current tag or default tag or something along those lines? + buf[529] = 0x01; + + xbRewind(); + if(( iRc = xbFwrite( buf, 2048, 1 )) != XB_NO_ERROR ){ + iErrorStop = 120; + throw iRc; + } + } else { + iErrorStop = 130; + iRc = XB_INVALID_OPTION; + throw iRc; + } + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbIxMdx::WriteHeadBlock() Exception Caught. Error Stop = [%d] iRc = [%d] option = [%d]", iErrorStop, iRc, iOption ); + xbase->WriteLogMessage( sMsg ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return iRc; +} +/***********************************************************************/ +xbInt16 xbIxMdx::UpdateTagSize( xbMdxTag *mpTag, xbUInt32 ulTagSz ){ + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + char buf[4]; + try{ + ePutUInt32( &buf[0], ulTagSz ); + if(( iRc = xbFseek( (xbInt64) ((mpTag->ulTagHdrPageNo *512 )+ 4), SEEK_SET )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + if(( iRc = xbFwrite( &buf[0], 4, 1 )) != XB_NO_ERROR ){ + iErrorStop = 110; + throw iRc; + } + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbIxMdx::UpdateTagSize() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return iRc; +} + + +/***********************************************************************/ +//void xbIxMdx::TestStub( char *s, void *vpTag ){ +void xbIxMdx::TestStub( char *, void * ){ +} +/***********************************************************************/ +} /* namespace */ +#endif /* XB_MDX_SUPPORT */ + diff --git a/src/core/xbixndx.cpp b/src/core/xbixndx.cpp new file mode 100755 index 0000000..b28dd9d --- /dev/null +++ b/src/core/xbixndx.cpp @@ -0,0 +1,2834 @@ +/* xbixndx.cpp + +XBase64 Software Library + +Copyright (c) 1997,2003,2014,2022,2023 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" + +#ifdef XB_NDX_SUPPORT + +namespace xb{ + + +/***********************************************************************/ +//! @brief Class constructor. +/*! + \param dbf Pointer to dbf instance. +*/ + +xbIxNdx::xbIxNdx( xbDbf *dbf ) : xbIx( dbf ){ + ndxTag = (xbNdxTag *) calloc( 1, sizeof( xbNdxTag )); + SetBlockSize( XB_NDX_BLOCK_SIZE ); + cNodeBuf = (char *) malloc( XB_NDX_BLOCK_SIZE ); +} +/***********************************************************************/ +//! @brief Class Destructor. +xbIxNdx::~xbIxNdx(){ + if( ndxTag ){ + ndxTag->npNodeChain = FreeNodeChain( ndxTag->npNodeChain ); + if( ndxTag->cpKeyBuf ) + free( ndxTag->cpKeyBuf ); + if( ndxTag->cpKeyBuf2 ) + free( ndxTag->cpKeyBuf2 ); + if( ndxTag->exp ){ + delete ndxTag->exp; + ndxTag->exp = NULL; + } + ndxTag->sKeyExpression.Set( NULL ); + ndxTag->sTagName.Set( NULL ); + free( ndxTag ); + ndxTag = NULL; + } + if( cNodeBuf ) + free( cNodeBuf ); +} +/***********************************************************************/ +//! @brief Add key. +/*! + Add key. If this is a unique index, this logic assumes the duplicate + check logic was already done. + + \param vpTag Tag to update. + \param ulRecNo Record number to add key for. + \returns Return Codes +*/ + +xbInt16 xbIxNdx::AddKey( void * vpTag, xbUInt32 ulRecNo ){ + + xbNdxTag * npTag; + vpTag ? npTag = (xbNdxTag *) vpTag : npTag = ndxTag; + if( GetUniqueKeyOpt() == XB_EMULATE_DBASE && npTag->bFoundSts ) + return XB_NO_ERROR; + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + xbInt16 iHeadNodeUpdateOpt = 2; + + + try{ + + if(( iRc = xbIxNdx::KeySetPosAdd( npTag, ulRecNo )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + xbInt32 lKeyCnt = GetKeyCount( npTag->npCurNode ); + if( lKeyCnt < npTag->iKeysPerBlock ){ + // Section A - add key to appropriate position if space available + if(( iRc = InsertNodeL( npTag, npTag->npCurNode, npTag->npCurNode->iCurKeyNo, npTag->cpKeyBuf, ulRecNo )) != XB_NO_ERROR ){ + iErrorStop = 110; + throw iRc; + } + + } else { + // land here with a full leaf node + iHeadNodeUpdateOpt = 1; + // section B - split the leaf node + xbIxNode * npRightNode = AllocateIxNode( GetBlockSize() + (xbUInt32) npTag->iKeyItemLen, 1 ); + if( !npRightNode ){ + iErrorStop = 120; + iRc = XB_NO_MEMORY; + throw iRc; + } + if(( iRc = SplitNodeL( npTag, npTag->npCurNode, npRightNode, npTag->npCurNode->iCurKeyNo, npTag->cpKeyBuf, ulRecNo )) != XB_NO_ERROR ){ + iErrorStop = 130; + throw iRc; + } + xbUInt32 ulTempBlockNo = npRightNode->ulBlockNo; + + // section C - go up the tree, splitting nodes as necessary + xbIxNode * npParent = npTag->npCurNode->npPrev; + while( npParent && GetKeyCount( npParent ) >= npTag->iKeysPerBlock ){ + npRightNode = FreeNodeChain( npRightNode ); + npRightNode = AllocateIxNode( GetBlockSize() + (xbUInt32) npTag->iKeyItemLen, 1 ); + if( !npRightNode ){ + iErrorStop = 140; + iRc = XB_NO_MEMORY; + throw iRc; + } + if(( iRc = SplitNodeI( npTag, npParent, npRightNode, npParent->iCurKeyNo, ulTempBlockNo )) != XB_NO_ERROR ){ + iErrorStop = 150; + throw iRc; + } + ulTempBlockNo = npRightNode->ulBlockNo; + npTag->npCurNode = npParent; + npParent = npParent->npPrev; + } + + // section D - if cur node is split root, create new root + if( npTag->npCurNode->ulBlockNo == npTag->ulRootBlock ){ + // xbase->WriteLogMessage( "Section d" ); + if(( iRc = AddKeyNewRoot( npTag, npTag->npCurNode, npRightNode )) != XB_NO_ERROR ){ + iErrorStop = 160; + throw iRc; + } + npRightNode = FreeNodeChain( npRightNode ); + + } else { + // else section E, put key in parent + if(( iRc = InsertNodeI( vpTag, npParent, npParent->iCurKeyNo, npRightNode->ulBlockNo )) != XB_NO_ERROR ){ + iErrorStop = 170; + throw iRc; + } + npRightNode = FreeNodeChain( npRightNode ); + } + } + + // update the header + if(( iRc = WriteHeadBlock( iHeadNodeUpdateOpt )) != XB_NO_ERROR ){ + iErrorStop = 180; + throw iRc; + } + + // ---- free whatever is left of the node chain here, this might not be right, might need to restore it to + // the point right after SetKeyPosAdd + npTag->npNodeChain = FreeNodeChain( ndxTag->npNodeChain ); + npTag->npCurNode = NULL; + + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbIxNdx::AddKey() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return iRc; +} + +/***********************************************************************/ +//! @brief Add new root node. +/*! + \param npTag Tag to update. + \param npLeft Left node. + \param npRight Right node. + \returns Return Codes +*/ +xbInt16 xbIxNdx::AddKeyNewRoot( xbNdxTag *npTag, xbIxNode *npLeft, xbIxNode *npRight ){ + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + xbString sMsg; + char *pLastKey = NULL; + + try{ + xbIxNode *npRoot = AllocateIxNode( GetBlockSize() + (xbUInt32) npTag->iKeyItemLen, 1 ); + if( !npRoot ){ + iErrorStop = 100; + iRc = XB_NO_MEMORY; + throw iRc; + } + npTag->ulRootBlock = npRoot->ulBlockNo; + pLastKey = (char *) malloc( (size_t) ndxTag->iKeyLen ); + if(( iRc = GetLastKeyForBlockNo( npTag, npLeft->ulBlockNo, pLastKey )) != XB_NO_ERROR ){ + iErrorStop = 110; + throw iRc; + } + char * pTrg = npRoot->cpBlockData; + + // set no of keys to 1 + ePutUInt32( pTrg, 1 ); + + // set the left node number + pTrg += 4; + ePutUInt32( pTrg, npLeft->ulBlockNo ); + + // set the key + pTrg+= 8; + memcpy( pTrg, pLastKey, (size_t) npTag->iKeyLen ); + + // set the right node number + pTrg+= (npTag->iKeyItemLen - 8); + ePutUInt32( pTrg, npRight->ulBlockNo ); + + // write out the block + if(( iRc = WriteBlock( npRoot->ulBlockNo, GetBlockSize(), npRoot->cpBlockData )) != XB_NO_ERROR ){ + iErrorStop = 120; + throw iRc; + } + if( pLastKey ) + free( pLastKey ); + NodeFree( npRoot ); + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbIxNdx::AddKeyNewRoot() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + if( pLastKey ) + free( pLastKey ); + } + return iRc; +} +/***********************************************************************/ +//! @brief Append node to node chain. +/*! + Append a node to the current node chain for a given tag. + + \param vpTag Tag to update. + \param npNode Node to add to node chain. + \returns void +*/ +void xbIxNdx::AppendNodeChain( void *vpTag, xbIxNode * npNode ){ + + xbNdxTag * npTag; + vpTag ? npTag = (xbNdxTag *) vpTag : npTag = ndxTag; + + if( npTag->npNodeChain == NULL ){ + npTag->npNodeChain = npNode; + npTag->npCurNode = npNode; + } else { + npNode->npPrev = npTag->npCurNode; + npTag->npCurNode->npNext = npNode; + npTag->npCurNode = npNode; + } + // time stamp the node chain + GetFileMtime( npTag->tNodeChainTs ); +} + +/***********************************************************************/ +//! @brief Allocate a node. +/*! + \param ulBufSize Buffer size. + \param iOpt 0 - Don't update the node block number on the node. + 1 - Set node block number to the next available block number. + \returns Pointer to new node. +*/ + +xbIxNode * xbIxNdx::AllocateIxNode( xbUInt32 ulBufSize, xbInt16 iOpt ){ + xbIxNode *n = xbIx::AllocateIxNode( ulBufSize ); + if( n && iOpt == 1 ) n->ulBlockNo = ndxTag->ulTotalBlocks++; + return n; +} +/***********************************************************************/ +//! @brief Check for duplicate key. +/*! + \param vpTag Tag to check. + \returns XB_KEY_NOT_UNIQUE
XB_NO_ERROR +*/ +xbInt16 xbIxNdx::CheckForDupKey( void *vpTag ) +{ + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + xbNdxTag *npTag = (xbNdxTag *) vpTag; + npTag->bFoundSts = xbFalse; + try{ + if( GetUnique()){ + if( npTag->iKeySts == XB_ADD_KEY || npTag->iKeySts == XB_UPD_KEY ) + if( KeyExists( vpTag )){ + if( GetUniqueKeyOpt() == XB_EMULATE_DBASE ){ + npTag->bFoundSts = xbTrue; + return 0; + } else { + return XB_KEY_NOT_UNIQUE; + } + } + } + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbIxNdx::CheckForDupKey() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return iRc; +} + +/***********************************************************************/ +//! @brief Check tag integrity. +/*! + Check a tag for accuracy. + + \param vpTag Tag to create key for. + \param iOpt Output message destination
+ 0 = Syslog
+ 1 = Stdout
+ 2 = Both
+ \returns Return Codes +*/ +xbInt16 xbIxNdx::CheckTagIntegrity( void *vpTag, xbInt16 iOpt ){ + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iRc2; + xbInt16 iRc3; + xbInt16 iErrorStop = 0; + xbUInt32 ulIxCnt = 0; + xbUInt32 ulThisRecNo = 0; + xbUInt32 ulPrevRecNo = 0; + xbBool bDone = false; + xbString sMsg; + char cKeyType; + char *pPrevKeyBuf = NULL; + + #ifdef XB_LOCKING_SUPPORT + xbBool bLocked = xbFalse; + #endif + + xbNdxTag * npTag; + vpTag ? npTag = (xbNdxTag *) vpTag : npTag = ndxTag; + + try{ + #ifdef XB_LOCKING_SUPPORT + if( dbf->GetAutoLock() && !dbf->GetTableLocked() ){ + if(( iRc = dbf->LockTable( XB_LOCK )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + bLocked = xbTrue; + } + #endif + + memset( npTag->cpKeyBuf2, 0x00, (size_t) npTag->iKeyLen ); + cKeyType = GetKeyType( vpTag ); + + sMsg.Sprintf( "Checking index type [%c]", cKeyType ); + xbase->WriteLogMessage( sMsg, iOpt ); + + pPrevKeyBuf = (char *) calloc( 1, (size_t) ndxTag->iKeyLen ); + + // for each key in the index, make sure it is trending in the right direction + iRc = GetFirstKey( vpTag, 0 ); + while( iRc == XB_NO_ERROR && !bDone ){ + ulIxCnt++; + iRc = GetNextKey( vpTag, 0 ); + if( iRc == XB_NO_ERROR ){ + // compare this key to prev key + iRc2 = CompareKey( cKeyType, GetKeyData( npTag->npCurNode, npTag->npCurNode->iCurKeyNo, npTag->iKeyItemLen ), + pPrevKeyBuf, (size_t) npTag->iKeyLen ); + + if( iRc2 < 0 ){ + sMsg.Sprintf( "Key sequence error at key number [%ld].", ulIxCnt ); + xbase->WriteLogMessage( sMsg, iOpt ); + iErrorStop = 110; + iRc = XB_INVALID_INDEX; + throw iRc; + } + + ulThisRecNo = 0; + if(( iRc3 = GetDbfPtr( vpTag, npTag->npCurNode->iCurKeyNo, npTag->npCurNode, ulThisRecNo )) != XB_NO_ERROR ){ + iErrorStop = 120; + throw iRc3; + } + + if( iRc2 == 0 && (ulThisRecNo <= ulPrevRecNo )){ + sMsg.Sprintf( "Dbf record number sequence error at key number [%ld].", iOpt ); + xbase->WriteLogMessage( sMsg, iOpt ); + iErrorStop = 130; + iRc = XB_INVALID_INDEX; + throw iRc; + } + // save this key info to prev key + memcpy( pPrevKeyBuf, GetKeyData( npTag->npCurNode, npTag->npCurNode->iCurKeyNo, npTag->iKeyItemLen ), (size_t) npTag->iKeyLen ); + ulPrevRecNo = ulThisRecNo; + } + } + + // verify the index count matches the tag count + xbUInt32 ulDbfCnt = 0; + if(( iRc = dbf->GetRecordCnt( ulDbfCnt )) != XB_NO_ERROR ){ + iErrorStop = 140; + throw iRc; + } + + if( GetUniqueKeyOpt() == XB_EMULATE_DBASE && GetUnique( vpTag )){ + // Can't compare counts if using XB_EMULATE_DBASE and it's a unique index + } else { + if( ulDbfCnt != ulIxCnt ){ + sMsg.Sprintf( "CheckTagIntegrity() Index entry count [%ld] does not match dbf record count [%ld]", ulIxCnt, ulDbfCnt ); + xbase->WriteLogMessage( sMsg, iOpt ); + iErrorStop = 150; + iRc = XB_INVALID_INDEX; + throw iRc; + } + + // verify each record in the dbf file has a corresponding index entry + xbUInt32 j = 0; + while( j < ulDbfCnt ){ + if(( iRc = dbf->GetRecord( ++j )) != XB_NO_ERROR ){ + iErrorStop = 160; + throw iRc; + } + if(( iRc = FindKeyForCurRec( vpTag )) != XB_NO_ERROR ){ + ulThisRecNo = j; + iErrorStop = 170; + throw iRc; + } + } + sMsg.Sprintf( "CheckTagIntegrity() Index entry count [%ld] matches dbf record count [%ld]", ulIxCnt, ulDbfCnt ); + xbase->WriteLogMessage( sMsg, iOpt ); + } + if( pPrevKeyBuf ) + free( pPrevKeyBuf ); + + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbIxNdx::CheckTagIntegrity() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg, iOpt ); + xbase->WriteLogMessage( GetErrorMessage( iRc ), iOpt ); + if( pPrevKeyBuf ) + free( pPrevKeyBuf ); + + if( iErrorStop == 170 ){ + sMsg.Sprintf( "xbIxNdx::CheckTagIntegrity() Missing index entry for record [%d]", ulThisRecNo ); + xbase->WriteLogMessage( sMsg, iOpt ); + } + } + + #ifdef XB_LOCKING_SUPPORT + if( bLocked ){ + dbf->LockTable( XB_UNLOCK ); + } + #endif + return iRc; + +} +/***********************************************************************/ +//! @brief Create key for tag. +/*! + Append a node to the current node chain for a given tag. + + \param vpTag Tag to create key for. + \param iOpt 0 = Build a key for FindKey usage, only rec buf 0.
+ 1 = Append Mode, Create key for an append, only use rec buf 0, set updated switch.
+ 2 = Update Mode, Create old version and new version keys, check if different, set update switch appropriately. + \returns Return Codes +*/ + +xbInt16 xbIxNdx::CreateKey( void * vpTag, xbInt16 iOpt ){ + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + try{ + xbNdxTag * npTag; + vpTag ? npTag = (xbNdxTag *) vpTag : npTag = ndxTag; + + if(( iRc = npTag->exp->ProcessExpression( 0 )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + if( npTag->exp->GetReturnType() == XB_EXP_CHAR ){ + npTag->exp->GetStringResult( npTag->cpKeyBuf, (xbUInt32) npTag->iKeyLen ); + } + else if( npTag->exp->GetReturnType() == XB_EXP_NUMERIC ){ + xbDouble d; + npTag->exp->GetNumericResult( d ); + memcpy( npTag->cpKeyBuf, &d, 8 ); + } + else if( npTag->exp->GetReturnType() == XB_EXP_DATE ){ + xbDouble d; + npTag->exp->GetNumericResult( d ); + memcpy( npTag->cpKeyBuf, &d, 8 ); + } + + npTag->iKeySts = 0; + if( iOpt == 1 ) + npTag->iKeySts = XB_ADD_KEY; + + else if( iOpt == 2 ){ + if(( iRc = npTag->exp->ProcessExpression( 1 )) != XB_NO_ERROR ){ + iErrorStop = 110; + throw iRc; + } + if( npTag->exp->GetReturnType() == XB_EXP_CHAR ){ + npTag->exp->GetStringResult( npTag->cpKeyBuf2, (xbUInt32) npTag->iKeyLen ); + } else if( npTag->exp->GetReturnType() == XB_EXP_NUMERIC || npTag->exp->GetReturnType() == XB_EXP_DATE ){ + xbDouble d; + npTag->exp->GetNumericResult( d ); + memcpy( npTag->cpKeyBuf2, &d, 8 ); + } + if( memcmp( npTag->cpKeyBuf, npTag->cpKeyBuf2, (size_t) npTag->iKeyLen )) + npTag->iKeySts = XB_UPD_KEY; + } + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbIxNdx::CreateKey() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return iRc; +} +/***********************************************************************/ +//! @brief Create new tag. +/*! + This routine creates a new tag. Since NDX files have only one tag, + this creates a new NDX file. + + \param sName Tag Name, including .NDX suffix + \param sKey Key Expression + \param sFilter Filter expression. Not supported by NDX indices. + \param iDescending Not supported by NDX indices. + \param iUnique xbtrue - Unique.
xbFalse - Not unique. + \param iOverLay xbTrue - Overlay if file already exists.
xbFalse - Don't overlay. + \param vpTag Output from method Pointer to vptag pointer. + \returns Return Codes +*/ + +xbInt16 xbIxNdx::CreateTag( const xbString &sName, const xbString &sKey, + const xbString &, xbInt16, xbInt16 iUnique, xbInt16 iOverLay, void **vpTag ){ + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + xbNdxTag *npTag = ndxTag; + *vpTag = ndxTag; + + try{ + //xbString sMsg; + SetFileName( sName ); + + if( FileExists() && !iOverLay ) + return XB_FILE_EXISTS; + + if( FileIsOpen()){ + if(( iRc = xbTruncate(0)) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + if(( iRc = xbFclose()) != XB_NO_ERROR ){ + iErrorStop = 110; + throw iRc; + } + npTag->npNodeChain = FreeNodeChain( npTag->npNodeChain ); + npTag->npCurNode = NULL; + npTag->sKeyExpression.Set( "" ); + + if( npTag->cpKeyBuf ){ + free( npTag->cpKeyBuf ); + npTag->cpKeyBuf = NULL; + } + if( npTag->cpKeyBuf2 ){ + free( npTag->cpKeyBuf2 ); + npTag->cpKeyBuf2 = NULL; + } + } + if(( iRc = xbFopen( "w+b", dbf->GetShareMode())) != XB_NO_ERROR ){ + iErrorStop = 120; + throw iRc; + } + + //set up the key expression + npTag->exp = new xbExp( dbf->GetXbasePtr()); + if(( iRc = npTag->exp->ParseExpression( dbf, sKey )) != XB_NO_ERROR ){ + iErrorStop = 130; + throw iRc; + } + + switch( npTag->exp->GetReturnType()){ + case XB_EXP_CHAR: + npTag->cKeyType = 'C'; + npTag->iKeyType = 0; + npTag->iKeyLen = npTag->exp->GetResultLen(); + break; + + case XB_EXP_NUMERIC: + npTag->cKeyType = 'F'; + npTag->iKeyType = 1; + npTag->iKeyLen = 8; + break; + + case XB_EXP_DATE: + npTag->cKeyType = 'D'; + npTag->iKeyType = 1; + npTag->iKeyLen = 8; + break; + + default: + iErrorStop = 140; + iRc = XB_INVALID_INDEX; + throw iRc; + } + + npTag->iUnique = iUnique; + npTag->ulRootBlock = 1L; + //npTag->ulTotalBlocks = 2l; + npTag->ulTotalBlocks = 2L; + npTag->sKeyExpression = sKey; + + GetFileNamePart( npTag->sTagName ); + + if( npTag->iKeyLen > 100 ){ + iErrorStop = 150; + throw iRc; + } + + npTag->iKeyItemLen = npTag->iKeyLen + 8; + while(( npTag->iKeyItemLen % 4 )!= 0 ) npTag->iKeyItemLen++; + + npTag->iKeysPerBlock = (xbInt16) (GetBlockSize() - 8 ) / npTag->iKeyItemLen; + ndxTag->cpKeyBuf = (char *) malloc( (size_t) ndxTag->iKeyLen ); + ndxTag->cpKeyBuf2 = (char *) malloc( (size_t) ndxTag->iKeyLen ); + + if(( iRc = WriteHeadBlock(0)) != XB_NO_ERROR ){ + iErrorStop = 160; + throw iRc; + } + + //write out block binary zeroes + char buf[512]; + memset( buf, 0x00, 512 ); + if(( iRc = xbFwrite( buf, 1, 512 )) != XB_NO_ERROR ){ + iErrorStop = 170; + throw iRc; + } + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbIxNdx::CreateTag() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return iRc; +} + +/***********************************************************************/ +//! @brief Delete a key. +/*! + This routine deletes a key from a supplied node. + \param vpTag Tag to delete key on. + \param npNode Node to delete key on. + \param iSlotNo Slot number of key to delete. + \returns Return Codes +*/ + +xbInt16 xbIxNdx::DeleteFromNode( void *vpTag, xbIxNode * npNode, xbInt16 iSlotNo ) +{ + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + xbNdxTag * npTag; + vpTag ? npTag = (xbNdxTag *) vpTag : npTag = ndxTag; + + try{ + + xbInt32 lKeyCnt = GetKeyCount( npNode ); + xbInt16 iLen = (lKeyCnt - iSlotNo - 1) * npTag->iKeyItemLen; + if( !IsLeaf( vpTag, npNode )) + iLen += 4; + + if( iLen > 0 ){ + char *pTrg = npNode->cpBlockData; + pTrg += (4 + (npTag->iKeyItemLen * (iSlotNo)) ); //lTrgPos; + char *pSrc = pTrg; + pSrc += npTag->iKeyItemLen; + memmove( pTrg, pSrc, (size_t) iLen ); + } + + // set the new number of keys + ePutUInt32( npNode->cpBlockData, (xbUInt32) lKeyCnt - 1 ); + + // write out the block + if(( iRc = WriteBlock( npNode->ulBlockNo, GetBlockSize(), npNode->cpBlockData )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbIxNdx::DeleteFromNode() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return XB_NO_ERROR; +} + +/***********************************************************************/ +//! @brief Delete a key. +/*! + This routine deletes a key. It assumes the key to delete + is the current key in the node chain. + + \param vpTag Tag to delete key on. + + \returns Return Codes +*/ + +xbInt16 xbIxNdx::DeleteKey( void *vpTag ){ + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + xbNdxTag * npTag; + vpTag ? npTag = (xbNdxTag *) vpTag : npTag = ndxTag; + + // save copy of node chain to reset to after delete completed + xbIxNode *npSaveNodeChain = npTag->npNodeChain; + npTag->npNodeChain = NULL; + xbIxNode * npSaveCurNode = npTag->npCurNode; + + try{ + + xbString sMsg; + + if(( iRc = xbIxNdx::KeySetPosDel( npTag )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + // Delete key needs to handle two scenarios + // 1 - if the delete is on the only key of a leaf node, then traverse up the tree, trimming as needed + // 2 - if the last key on a node is deleted, and the key value is not the same as the prev key value + // go up the tree looking for an interior node needing updated key value + + xbInt32 lOrigKeyCnt = GetKeyCount( npTag->npCurNode ); + if(( iRc = DeleteFromNode( npTag, npTag->npCurNode, npTag->npCurNode->iCurKeyNo )) != XB_NO_ERROR ){ + iErrorStop = 110; + throw iRc; + } + + if( lOrigKeyCnt == 1 ){ + // scenario 1 + xbBool bDone = xbFalse; + xbBool bIsLeaf = xbFalse; + xbInt32 lKeyCnt; + npTag->npCurNode = npTag->npCurNode->npPrev; + + while( npTag->npCurNode && !bDone ){ + lKeyCnt = GetKeyCount( npTag->npCurNode ); + bIsLeaf = IsLeaf( npTag, npTag->npCurNode ); + if( lKeyCnt > 0 ){ + if(( iRc = DeleteFromNode( npTag, npTag->npCurNode, npTag->npCurNode->iCurKeyNo )) != XB_NO_ERROR ){ + iErrorStop = 120; + throw iRc; + } + } + if( (bIsLeaf && lKeyCnt > 1) || (!bIsLeaf && lKeyCnt > 0) ) + bDone = xbTrue; + else + npTag->npCurNode = npTag->npCurNode->npPrev; + } + } else if( npTag->npCurNode->iCurKeyNo == (xbUInt32) lOrigKeyCnt - 1 ){ + + // scenario 2 + // if last two keys identical, then nothing to do, else go up looking for a key to change + if( memcmp( GetKeyData( npTag->npCurNode, npTag->npCurNode->iCurKeyNo, npTag->iKeyItemLen ), + GetKeyData( npTag->npCurNode, npTag->npCurNode->iCurKeyNo-1, npTag->iKeyItemLen ), + (size_t) npTag->iKeyLen )){ + + xbIxNode *pNode = npTag->npCurNode->npPrev; + char *pSrc = GetKeyData( npTag->npCurNode, npTag->npCurNode->iCurKeyNo-1, npTag->iKeyItemLen ); + + while( pNode && pNode->ulBlockNo != npTag->ulRootBlock && pNode->iCurKeyNo == (xbUInt32) GetKeyCount( pNode ) ) + pNode = pNode->npPrev; + if( pNode ){ + if( pNode->iCurKeyNo < (xbUInt32) GetKeyCount( pNode )){ + char *pTrg = pNode->cpBlockData; + pTrg += 12 + (pNode->iCurKeyNo * (xbUInt32) npTag->iKeyItemLen); + for( xbInt16 i = 0; i < npTag->iKeyLen; i++ ) + *pTrg++ = *pSrc++; + + // write out the block + if(( iRc = WriteBlock( pNode->ulBlockNo, GetBlockSize(), pNode->cpBlockData )) != XB_NO_ERROR ){ + iErrorStop = 130; + throw iRc; + } + } + } + } + } + + // restore node chain to pre delete status (which should be post add status) + npTag->npNodeChain = FreeNodeChain( npTag->npNodeChain ); + npTag->npNodeChain = npSaveNodeChain; + npTag->npCurNode = npSaveCurNode; + + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbIxNdx::DeleteKey() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + if( npSaveNodeChain ){ + npTag->npNodeChain = npSaveNodeChain; + npSaveNodeChain = FreeNodeChain( npSaveNodeChain ); + npTag->npCurNode = npSaveCurNode; + } + } + return iRc; +} + + +/***********************************************************************/ +//! @brief Delete tag. +/*! + In the case of an ndx tag, it deletes the ndx file as it contains + only one tag. + \returns Return Codes +*/ + +xbInt16 xbIxNdx::DeleteTag( void * ){ + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + //xbNdxTag * npTag; + //vpTag ? npTag = (xbNdxTag *) vpTag : npTag = ndxTag; + + try{ + + // if open, close it + if( FileIsOpen()){ + if(( iRc = Close()) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + } + // delete file + if(( iRc = xbRemove()) != XB_NO_ERROR ){ + iErrorStop = 110; + throw iRc; + } + + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbIxNdx::DeleteTag() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return iRc; +} +/***********************************************************************/ +#ifdef XB_DEBUG_SUPPORT + +//! @brief Dump a block for a given tag. +/*! + Dump blocks for given tag for debugging purposes. + \param iOpt Output message destination
+ 0 = stdout
+ 1 = Syslog
+ 2 = Both
+ \param vpTag - Not required for single tag NDX files. + \returns void +*/ + +xbInt16 xbIxNdx::DumpTagBlocks( xbInt16 iOpt, void * ){ + + xbInt16 iRc = 0; + xbInt16 iErrorStop = 0; + xbUInt32 lNoOfKeys; + char *p; + xbString s; + xbBool bIsLeaf = false; + + try{ + if( !FileIsOpen()){ + iRc = XB_NOT_OPEN; + iErrorStop = 100; + throw iRc; + } + + xbUInt32 ulStartBlock; + xbUInt32 ulEndBlock; + ulStartBlock = 1; + ulEndBlock = ndxTag->ulTotalBlocks; + + for( xbUInt32 lBlk = ulStartBlock; lBlk < ulEndBlock; lBlk++ ){ + + memset( cNodeBuf, 0x00, XB_NDX_BLOCK_SIZE ); + if(( iRc = ReadBlock( lBlk, XB_NDX_BLOCK_SIZE, cNodeBuf )) != XB_NO_ERROR ){ + iErrorStop = 110; + throw iRc; + } + p = cNodeBuf; + lNoOfKeys = eGetUInt32( p ); + + if( eGetUInt32( p + 4 ) > 0 ){ + bIsLeaf = false; + s.Sprintf( "Node # %ld - Interior Node - Key Type [%c] Key Count [%ld]", lBlk, ndxTag->cKeyType, lNoOfKeys ); + } else { + bIsLeaf = true; + s.Sprintf( "Node # %ld - Leaf Node - Key Type [%c] Key count [%ld]", lBlk, ndxTag->cKeyType, lNoOfKeys ); + } + xbase->WriteLogMessage( s, iOpt ); + xbase->WriteLogMessage( "Key Child Dbf Rec Key", iOpt ); + p += 4; + xbUInt32 ulLeftBranch; + xbUInt32 ulRecNo; + xbString sKey; + xbDouble d; + + xbUInt32 l; + for( l = 0; l < lNoOfKeys; l++ ){ + ulLeftBranch = eGetUInt32( p ); + p+= 4; + ulRecNo = eGetUInt32( p ); + p+= 4; + if( ndxTag->cKeyType == 'C' ){ + sKey.Assign( p, 1, (xbUInt32) ndxTag->iKeyLen ); + } else if( ndxTag->cKeyType == 'D' ){ + xbInt32 lDate = (xbInt32) eGetDouble( p ); + xbDate dt( lDate ); + //xbString s2; + //dt.JulToDate8( lDate, s2 ); + sKey.Sprintf( "%ld - %s", lDate, dt.Str()); + } else { + d = eGetDouble( p ); + sKey.Sprintf( "%f", d ); + } + p+= (ndxTag->iKeyItemLen-8); + + s.Sprintf( "%3d %9d %9d %s", l+1, ulLeftBranch, ulRecNo, sKey.Str() ); + xbase->WriteLogMessage( s, iOpt ); + } + if( !bIsLeaf ){ + ulLeftBranch = eGetUInt32( p ); + s.Sprintf( "%3d %9d", l+1, ulLeftBranch ); + xbase->WriteLogMessage( s, iOpt ); + } + } + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbIxNdx::DumpTagBlocks() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return iRc; +} +/***********************************************************************/ +//! @brief Dump index file header. +/*! + Dump a index file header for debugging purposes. + \param iOpt Output message destination
+ 0 = stdout
+ 1 = Syslog
+ 2 = Both
+ \returns Return Codes +*/ + +xbInt16 xbIxNdx::DumpHeader( xbInt16 iOpt, xbInt16 ){ + xbString s; + xbInt16 iRc; + + if(( iRc = ReadHeadBlock( 1 )) != XB_NO_ERROR ) + return iRc; + + s.Sprintf( "Index Header Node for %s", GetFileName().Str()); + xbase->WriteLogMessage( s, iOpt ); + s.Sprintf( "--------------------------------" ); + xbase->WriteLogMessage( s, iOpt ); + s.Sprintf( "Root block = %ld", ndxTag->ulRootBlock ); + xbase->WriteLogMessage( s, iOpt ); + s.Sprintf( "Total blocks = %ld", ndxTag->ulTotalBlocks ); + xbase->WriteLogMessage( s, iOpt ); + s.Sprintf( "Key types = %c,%d", ndxTag->cKeyType, ndxTag->iKeyType ); + xbase->WriteLogMessage( s, iOpt ); + s.Sprintf( "Key Length = %d", ndxTag->iKeyLen ); + xbase->WriteLogMessage( s, iOpt ); + s.Sprintf( "Keys Per Block = %d", ndxTag->iKeysPerBlock ); + xbase->WriteLogMessage( s, iOpt ); + s.Sprintf( "Key Item Len = %ld", ndxTag->iKeyItemLen ); + xbase->WriteLogMessage( s, iOpt); + s.Sprintf( "Serial No = %d", ndxTag->cSerNo ); + xbase->WriteLogMessage( s, iOpt); + s.Sprintf( "Unique = %d", ndxTag->iUnique ); + xbase->WriteLogMessage( s, iOpt ); + s.Sprintf( "KeyExpression = %s", ndxTag->sKeyExpression.Str() ); + xbase->WriteLogMessage( s, iOpt ); + return XB_NO_ERROR; +} +/***********************************************************************/ +//! @brief Dump the index for a tag. +/*! + Stub. + \returns XB_NO_ERROR +*/ +xbInt16 xbIxNdx::DumpIxForTag( void *, xbInt16 ) +{ + return XB_NO_ERROR; +} +/***********************************************************************/ +//! @brief Dump the index node chain. +/*! + Dump the index node chain for debugging purposes. + \param vpTag Tag of node chain to dump. + \param iOpt Output message destination
+ 0 = stdout
+ 1 = Syslog
+ 2 = Both
+ \returns void +*/ +void xbIxNdx::DumpIxNodeChain( void *vpTag, xbInt16 iOpt ) const +{ + + xbNdxTag * npTag; + vpTag ? npTag = (xbNdxTag *) vpTag : npTag = ndxTag; + + xbString s( "Dump Node Chain" ); + xbase->WriteLogMessage( s, iOpt ); + + if( npTag->npNodeChain ){ + xbIxNode *n = npTag->npNodeChain; + xbInt16 iCtr = 0; + char cLeaf; + s.Sprintf( "Cnt\tThis Prev Next CurKeyNo BlockNo NoOfKeys Type" ); + xbase->WriteLogMessage( s, iOpt ); + while( n ){ + IsLeaf( vpTag, n ) ? cLeaf = 'L' : cLeaf = 'I'; + s.Sprintf( "%d\t%08x %08x %08x %08d %08d %08d %c", + iCtr++, n, n->npPrev, n->npNext, n->iCurKeyNo, + n->ulBlockNo, eGetUInt32( n->cpBlockData ), cLeaf ); + xbase->WriteLogMessage( s, iOpt ); + n = n->npNext; + } + } else { + s = "Empty Node Chain"; + xbase->WriteLogMessage( s, iOpt ); + } +} +/***********************************************************************/ +//! @brief Dump node. +/*! + Dump a node for debugging purposes. + \param vpTag Tag of node chain to dump. + \param pNode Node to dump. + \param iOpt Output message destination
+ 0 = stdout
+ 1 = Syslog
+ 2 = Both
+ \returns XB_INVALID_OBJECT
XB_NO_ERROR +*/ + +xbInt16 xbIxNdx::DumpNode( void *vpTag, xbIxNode *pNode, xbInt16 iOpt ) const +{ + xbString s; + xbString sKey; + xbUInt32 lLeftBranch; + xbUInt32 lRecNo; + xbDouble d; + + if( !pNode ) + return XB_INVALID_OBJECT; + + xbIx::DumpNode( vpTag, pNode, iOpt ); + + xbNdxTag * npTag; + vpTag ? npTag = (xbNdxTag *) vpTag : npTag = ndxTag; + + xbUInt32 lNoOfKeys = eGetUInt32( pNode->cpBlockData ); + xbBool bIsLeaf = IsLeaf( vpTag, pNode ); + + if( bIsLeaf ) + xbase->WriteLogMessage( "Leaf node", iOpt ); + else + xbase->WriteLogMessage( "Interior node", iOpt ); + + s.Sprintf( "Key type = [%c] No Of Keys =[%d] Prev =[%x] Next =[%x]", npTag->cKeyType, lNoOfKeys, pNode->npPrev, pNode->npNext ); + xbase->WriteLogMessage( s, iOpt ); + + char *p = pNode->cpBlockData; + p += 4; + + xbUInt32 l; + for( l = 0; l < lNoOfKeys; l++ ){ + + lLeftBranch = eGetUInt32( p ); + p+= 4; + lRecNo = eGetUInt32( p ); + p+= 4; + + if( npTag->cKeyType == 'C' ){ + sKey.Assign( p, 1, (xbUInt32) npTag->iKeyLen ); + } else if( npTag->cKeyType == 'D' ){ + xbInt32 lDate = (xbInt32) eGetDouble( p ); + xbDate dt( lDate ); + sKey.Sprintf( "%ld - %s", lDate, dt.Str()); + } else { + d = eGetDouble( p ); + sKey.Sprintf( "%f", d ); + } + p+= (npTag->iKeyItemLen-8); + s.Sprintf( "%3d %9d %9d %s", l+1, lLeftBranch, lRecNo, sKey.Str() ); + xbase->WriteLogMessage( s, iOpt ); + } + if( !bIsLeaf ){ + lLeftBranch = eGetUInt32( p ); + s.Sprintf( "%3d %9d", l+1, lLeftBranch ); + xbase->WriteLogMessage( s.Str(), iOpt ); + } + return XB_NO_ERROR; +} +#endif +/***********************************************************************/ +//! @brief Find key +/*! + \param vpTag Pointer to tag to search. + \param vpKey Void pointer to key data to search on. + \param lSearchKeyLen Length of key to search for. + \param iRetrieveSw xbTrue - Retrieve the record if key found.
+ xbFalse - Don't retrieve record, check for key existence only. + \returns XB_NO_ERROR - Key found.
+ XB_NOT_FOUND - Key not found.
+ Return Codes +*/ +xbInt16 xbIxNdx::FindKey( void *vpTag, const void *vpKey, xbInt32 lSearchKeyLen, + xbInt16 iRetrieveSw ){ + + + xbInt16 iRc = 0; + xbInt16 iErrorStop = 0; + xbString sMsg; + // xbInt16 iFindSts; + try{ + // clean up any previous table updates before moving on + if( iRetrieveSw ){ + if( dbf->GetDbfStatus() == XB_UPDATED ){ + if( dbf->GetAutoCommit() == 1 ){ + if(( iRc = dbf->Commit()) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + } else { + if(( iRc = dbf->Abort()) != XB_NO_ERROR ){ + iErrorStop = 110; + throw iRc; + } + } + } + } + + xbUInt32 ulNoOfKeys; + xbNdxTag * npTag; + vpTag ? npTag = (xbNdxTag *) vpTag : npTag = ndxTag; + char cKeyType = npTag->cKeyType; + + if( npTag->npNodeChain ){ + + // determine if the index has been updated since the last time it was used + time_t tFileTs; + if(( iRc = GetFileMtime( tFileTs )) != XB_NO_ERROR ){ + iErrorStop = 120; + throw iRc; + } + if( npTag->tNodeChainTs < tFileTs ){ + npTag->npNodeChain = FreeNodeChain( npTag->npNodeChain ); + npTag->npCurNode = NULL; + if(( iRc = ReadHeadBlock( 1 )) != XB_NO_ERROR ){ + iErrorStop = 130; + throw iRc; + } + if(( iRc = GetBlock( npTag, (npTag->ulRootBlock ), 1, (xbUInt32) npTag->iKeyItemLen )) != XB_NO_ERROR ){ + iErrorStop = 140; + throw iRc; + } + } else { + // pop up the chain looking for appropriate starting point + xbBool bDone = false; + xbIxNode * TempIxNode; + while( npTag->npCurNode && !bDone && npTag->npCurNode->ulBlockNo != npTag->ulRootBlock ){ // not root node + iRc = CompareKey( cKeyType, vpKey, GetKeyData( npTag->npCurNode, 0, npTag->iKeyItemLen ), (size_t) lSearchKeyLen ); + if( iRc <= 0 ){ + TempIxNode = npTag->npCurNode; + npTag->npCurNode = npTag->npCurNode->npPrev; + TempIxNode = FreeNodeChain( TempIxNode ); + } else { + // get the number of keys on the block and compare the key to the rightmost key + xbUInt32 ulKeyCtr = eGetUInt32( npTag->npCurNode->cpBlockData ) - 1; + iRc = CompareKey( cKeyType, vpKey, GetKeyData( npTag->npCurNode, ulKeyCtr, npTag->iKeyItemLen), (size_t) lSearchKeyLen ); + + if( iRc > 0 ){ + TempIxNode = npTag->npCurNode; + npTag->npCurNode = npTag->npCurNode->npPrev; + TempIxNode = FreeNodeChain( TempIxNode ); + } else { + bDone = true; + } + } + } + } + } else { + if(( iRc = GetBlock( npTag, (npTag->ulRootBlock ), 1, (xbUInt32) npTag->iKeyItemLen )) != XB_NO_ERROR ){ + iErrorStop = 150; + throw iRc; + } + } + + + // if cur node is the base node and no keys on this node, then the index is empty + if( npTag->ulRootBlock == npTag->npCurNode->ulBlockNo ){ + ulNoOfKeys = eGetUInt32( npTag->npCurNode->cpBlockData ); + if( ulNoOfKeys == 0 && IsLeaf( npTag, npTag->npCurNode )){ + // iRc = XB_EMPTY; + + iRc = XB_NOT_FOUND; + return iRc; + } + } + + // should be in the appropriate position in the node chain to continue the search from here + // run down through the interior nodes + xbInt16 iSearchRc = 0; + xbUInt32 ulKeyPtr = 0; + + while( !IsLeaf( npTag, npTag->npCurNode ) ){ + + // get the number of keys on the block and compare the key to the rightmost key + ulNoOfKeys = eGetUInt32( npTag->npCurNode->cpBlockData ); + if( ulNoOfKeys == 0 ) // interior nodes can have zero keys, just a link to the next lower node + npTag->npCurNode->iCurKeyNo = 0; + else + { + iRc = CompareKey( cKeyType, vpKey, GetKeyData( npTag->npCurNode, ulNoOfKeys - 1, npTag->iKeyItemLen), (size_t) lSearchKeyLen ); + if( iRc > 0 ){ + npTag->npCurNode->iCurKeyNo = ulNoOfKeys; + } else { + npTag->npCurNode->iCurKeyNo = (xbUInt32) BSearchBlock( cKeyType, npTag->npCurNode, + (xbInt32) npTag->iKeyItemLen, vpKey, (xbInt32) lSearchKeyLen, iSearchRc ); + } + } + + if(( iRc = GetKeyPtr( npTag, npTag->npCurNode->iCurKeyNo, npTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){ + iErrorStop = 160; + throw iRc; + } + + if(( iRc = GetBlock( npTag, ulKeyPtr, 1, (xbUInt32) npTag->iKeyItemLen )) != XB_NO_ERROR ){ + iErrorStop = 170; + throw iRc; + } + } + + // should be on a the correct leaf node, it may or may not contain the actual key + ulNoOfKeys = eGetUInt32( npTag->npCurNode->cpBlockData ); + xbInt16 iCompRc = 0; + + if( ulNoOfKeys == 0 ){ + iRc = XB_NOT_FOUND; + return iRc; + } else { + + iRc = BSearchBlock( cKeyType, npTag->npCurNode, npTag->iKeyItemLen, vpKey, lSearchKeyLen, iCompRc ); + + // iCompRc + // 0 found + // < 0 eof encountered, search key > last key in file + // > 0 not found, positioned to next key + + + // std::cout << "xbIxNdx::FindKey -Rc = " << iRc << " CompRc = " << iCompRc << " NoOfKeys = " << ulNoOfKeys << " blk no = " << npTag->npCurNode->ulBlockNo << "\n"; + + if( iCompRc >= 0 ){ + npTag->npCurNode->iCurKeyNo = (xbUInt32) iRc; + if( iRetrieveSw ){ + xbUInt32 ulKey = npTag->npCurNode->iCurKeyNo; + if( ulKey >= ulNoOfKeys ) // if position past end of keys, need to go back one and position to last key + ulKey--; + + if(( iRc = GetDbfPtr( vpTag, ulKey, npTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){ + iErrorStop = 180; + throw iRc; + } + if(( iRc = dbf->GetRecord( ulKeyPtr )) != XB_NO_ERROR ){ + iErrorStop = 190; + throw iRc; + } + } + } + } + + if( iCompRc == 0 ) + return XB_NO_ERROR; + else if( iCompRc > 0 ) + return XB_NOT_FOUND; + else + return XB_EOF; + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbIxNdx::FindKey() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return iRc; +} +/***********************************************************************/ +//! @brief Find key for current record +/*! + This routine is called when updating a key. + + \param vpTag Pointer to tag to search. + XB_NOT_FOUND Key not found.
+ Return Codes +*/ + +xbInt16 xbIxNdx::FindKeyForCurRec( void * vpTag ) +{ + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + xbNdxTag * npTag; + vpTag ? npTag = (xbNdxTag *) vpTag : npTag = ndxTag; + + try{ + if(( iRc = CreateKey( vpTag, 0 )) < XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + + // find key + iRc = FindKey( vpTag, npTag->cpKeyBuf, npTag->iKeyLen, 0 ); + if( iRc == XB_NOT_FOUND || iRc == XB_EMPTY || iRc == XB_EOF ) + return iRc; + + if( iRc != XB_NO_ERROR ){ + iErrorStop = 110; + throw iRc; + } + + // if keys are unique, and the recrd number matches, then we are good + if( GetUnique() ) + return XB_NO_ERROR; + + // get here if key found and not unique, need to move forward looking for correct rec no + xbUInt32 ulDbfRecNo = dbf->GetCurRecNo(); + xbBool bKeysMatch = true; // keys match? + xbBool bCurRecsMatch = false; // cur recod number matches? + xbUInt32 ulIxRecNo = 0; + char cKeyType = GetKeyType( vpTag ); + + if(( iRc = GetDbfPtr( vpTag, npTag->npCurNode->iCurKeyNo, npTag->npCurNode, ulIxRecNo )) != XB_NO_ERROR ){ + iErrorStop = 120; + throw iRc; + } + if( ulIxRecNo == ulDbfRecNo ) + bCurRecsMatch = true; + + xbInt16 iCompRc; + while( !bCurRecsMatch && bKeysMatch ){ + + if(( iRc = GetNextKey( vpTag, 0 )) != XB_NO_ERROR ){ + iErrorStop = 130; + throw iRc; + } + + // do compare key here + iCompRc = CompareKey( cKeyType, npTag->cpKeyBuf, GetKeyData( npTag->npCurNode, 0, npTag->iKeyItemLen ), (size_t) npTag->iKeyLen ); + if( iCompRc != 0 ) + bKeysMatch = false; + else{ + if(( iRc = GetDbfPtr( vpTag, npTag->npCurNode->iCurKeyNo, npTag->npCurNode, ulIxRecNo )) != XB_NO_ERROR ){ + iErrorStop = 140; + throw iRc; + } + if( ulIxRecNo == ulDbfRecNo ) + bCurRecsMatch = true; + } + } + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbIxNdx::FindKeyForCurRec() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return XB_NO_ERROR; +} + +/***********************************************************************/ +//! @brief Get dbf record number for given key number. +/*! + \param vpTag Tag to retrieve dbf rec number on. + \param iKeyNo Key number for retrieval + \param np Pointer to node + \param ulDbfPtr- Output dbf record number + \returns Return Codes +*/ +xbInt16 xbIxNdx::GetDbfPtr( void *vpTag, xbInt16 iKeyNo, xbIxNode *np, xbUInt32 &ulDbfPtr ) const { + + xbInt16 iRc = 0; + xbInt16 iErrorStop = 0; + + try{ + #ifdef XB_DEBUG_SUPPORT + // turn this off in production mode for better performance + xbUInt32 ulNoOfKeys = eGetUInt32 ( np->cpBlockData ); + if( iKeyNo < 0 || iKeyNo > (xbInt16) --ulNoOfKeys ){ + iErrorStop = 100; + throw XB_INVALID_KEYNO; + } + #endif + + xbNdxTag * npTag; + vpTag ? npTag = (xbNdxTag *) vpTag : npTag = ndxTag; + char *p = ( np->cpBlockData); + p += (8 + (iKeyNo * npTag->iKeyItemLen)); + ulDbfPtr = eGetUInt32 ( p ); + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbIxNdx::GetDbfPtr() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return iRc; +} +/***********************************************************************/ +//! @brief Get the first key for the given tag. +/*! + \param vpTag Tag to retrieve first key on. + \param iRetrieveSw xbTrue - Retrieve the record on success.
+ xbFalse - Don't retrieve record. + \returns Return Codes +*/ + +xbInt16 xbIxNdx::GetFirstKey( void *vpTag, xbInt16 iRetrieveSw ){ + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + xbNdxTag * npTag; + vpTag ? npTag = (xbNdxTag *) vpTag : npTag = ndxTag; + + try{ + // clear out any history + if( npTag->npNodeChain ){ + npTag->npNodeChain = FreeNodeChain( npTag->npNodeChain ); + npTag->npCurNode = NULL; + } + if(( iRc = ReadHeadBlock( 1 )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + // lRootBlock is now available + if(( iRc = GetBlock( npTag, npTag->ulRootBlock, 1, (xbUInt32) npTag->iKeyItemLen )) != XB_NO_ERROR ){ + iErrorStop = 110; + throw iRc; + } + // if no keys on this node, and it's a leaf node then the index is empty + xbUInt32 ulKeyPtr = eGetUInt32( npTag->npCurNode->cpBlockData ); + if( ulKeyPtr == 0 && IsLeaf( npTag, npTag->npCurNode )){ + iRc = XB_EMPTY; + return iRc; + } + while( !IsLeaf( npTag, npTag->npCurNode )) // go down the chain looking for a leaf node + { + if(( iRc = GetKeyPtr( npTag, 0, npTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){ + iErrorStop = 120; + throw iRc; + } + if(( iRc = GetBlock( npTag, ulKeyPtr, 1, (xbUInt32) npTag->iKeyItemLen )) != XB_NO_ERROR ){ + iErrorStop = 130; + throw iRc; + } + } + if( iRetrieveSw ){ + if(( iRc = GetDbfPtr( npTag, 0, npTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){ + iErrorStop = 140; + throw iRc; + } + if(( iRc = dbf->GetRecord( ulKeyPtr )) != XB_NO_ERROR ){ + iErrorStop = 150; + throw iRc; + } + } + } + catch( xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbIxNdx::GetFirstKey() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return iRc; +} +/***********************************************************************/ +//! @brief Get the key expression for the given tag. +/*! + \param vpTag Tag to retrieve expression from. + \returns Key expression. +*/ + +xbString &xbIxNdx::GetKeyExpression( const void * vpTag ) const{ + xbNdxTag * npTag; + vpTag ? npTag = (xbNdxTag *) vpTag : npTag = ndxTag; + return npTag->sKeyExpression; +} + + +/***********************************************************************/ +//! @brief Get the key filter for the given tag. +/*! + NDX index files do not support filters. This returns NULL. + \returns NULL. +*/ + +xbString &xbIxNdx::GetKeyFilter( const void * ) const{ + return sNullString; +} +/***********************************************************************/ +//! @brief Get the key length for the given tag. +/*! + \param vpTag Tag to retrieve key length for. + \returns Length of key. +*/ +xbInt32 xbIxNdx::GetKeyLen( const void * vpTag ) const{ + xbNdxTag * npTag; + vpTag ? npTag = (xbNdxTag *) vpTag : npTag = ndxTag; + return npTag->iKeyLen; +} +/***********************************************************************/ +//! @brief Get child node number for given key number. +/*! + \param vpTag Tag to retrieve dbf rec number on. + \param iKeyNo Key number for retrieval + \param np Pointer to node + \param ulKeyPtr Output node number + \returns Return Codes +*/ + +xbInt16 xbIxNdx::GetKeyPtr( void *vpTag, xbInt16 iKeyNo, xbIxNode *np, xbUInt32 &ulKeyPtr ) const { + + xbInt16 iRc = 0; + xbInt16 iErrorStop = 0; + try{ + #ifdef XB_DEBUG_SUPPORT + // turn this off in production mode for better performance + xbUInt32 ulNoOfKeys = eGetUInt32 ( np->cpBlockData ); + if( iKeyNo < 0 || iKeyNo > (xbInt16) ulNoOfKeys ){ + iErrorStop = 100; + throw XB_INVALID_KEYNO; + } + #endif + xbNdxTag * npTag; + vpTag ? npTag = (xbNdxTag *) vpTag : npTag = ndxTag; + char *p = ( np->cpBlockData); + p += (4 + (iKeyNo * npTag->iKeyItemLen)); + ulKeyPtr = eGetUInt32 ( p ); + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbIxNdx::GetKeyPtr() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return iRc; +} + +/***********************************************************************/ +//! @brief Returns key update status. +/*! + \param vpTag Tag to check status on. + \returns XB_UPD_KEY Key updated.
+ XB_DEL_KEY Key deleted.
+ XB_ADD_KEY Key added.
+ 0 No key updates + +*/ +xbInt16 xbIxNdx::GetKeySts( void *vpTag ) const{ + xbNdxTag * npTag; + vpTag ? npTag = (xbNdxTag *) vpTag : npTag = ndxTag; + return npTag->iKeySts; +} +/***********************************************************************/ +//! @brief Get character key type for given tag. +/*! + \param vpTag Tag to retrieve key type for. + \returns Char key type. +*/ + +char xbIxNdx::GetKeyType( const void * vpTag ) const{ + xbNdxTag * npTag; + vpTag ? npTag = (xbNdxTag *) vpTag : npTag = ndxTag; + return npTag->cKeyType; +} + +/***********************************************************************/ +//! @brief Get numeric key type for given tag. +/*! + \param vpTag Tag to retrieve first key for. + \returns Numeric key type. +*/ +xbInt16 xbIxNdx::GetKeyTypeN( const void * vpTag ) const{ + xbNdxTag * npTag; + vpTag ? npTag = (xbNdxTag *) vpTag : npTag = ndxTag; + return npTag->iKeyType; +} +/***********************************************************************/ +//! @brief Get the last key for the given tag. +/*! + \param vpTag Tag to retrieve last key on. + \param iRetrieveSw xbTrue - Retrieve the record on success.
+ xbFalse - Don't retrieve record. + \returns Return Codes +*/ +xbInt16 xbIxNdx::GetLastKey( void *vpTag, xbInt16 iRetrieveSw ){ + return GetLastKey( 0, vpTag, iRetrieveSw ); +// return GetLastKey( 0, vpTag, 1 ); + +} +/***********************************************************************/ +//! @brief Get the last key for the given tag and starting node. +/*! + \param ulNodeNo Starting node + \param vpTag Tag to retrieve last key on. + \param iRetrieveSw xbTrue - Retrieve the record if key found.
+ xbFalse - Don't retrieve record. + \returns Return Codes +*/ +xbInt16 xbIxNdx::GetLastKey( xbUInt32 ulNodeNo, void *vpTag, xbInt16 iRetrieveSw ){ + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + xbUInt32 ulKeyPtr = 0; + xbUInt32 ulNoOfKeys = 0; + xbNdxTag * npTag; + vpTag ? npTag = (xbNdxTag *) vpTag : npTag = ndxTag; + + try{ + // clear out any history + if( npTag->npNodeChain ){ + npTag->npNodeChain = FreeNodeChain( npTag->npNodeChain ); + npTag->npCurNode = NULL; + } + if( ulNodeNo == 0 ){ + if(( iRc = ReadHeadBlock( 1 )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + // lRootBlock is now available + if(( iRc = GetBlock( npTag, npTag->ulRootBlock, 1, (xbUInt32) npTag->iKeyItemLen )) != XB_NO_ERROR ){ + iErrorStop = 110; + throw iRc; + } + } else { + if(( iRc = GetBlock( npTag, ulNodeNo, 1, (xbUInt32) npTag->iKeyItemLen )) != XB_NO_ERROR ){ + iErrorStop = 120; + throw iRc; + } + } + // if no keys on this node, then the index is empty + ulNoOfKeys = eGetUInt32( npTag->npCurNode->cpBlockData ); + if( ulNoOfKeys == 0 && IsLeaf( npTag, npTag->npCurNode )){ + iRc = XB_EMPTY; + return iRc; + } + npTag->npCurNode->iCurKeyNo = ulNoOfKeys; + while( !IsLeaf( npTag, npTag->npCurNode ) ){ // go down the chain looking for a leaf node + npTag->npCurNode->iCurKeyNo = eGetUInt32( npTag->npCurNode->cpBlockData ); + if(( iRc = GetKeyPtr( npTag, npTag->npCurNode->iCurKeyNo, npTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){ + iErrorStop = 130; + throw iRc; + } + if(( iRc = GetBlock( npTag, ulKeyPtr, 1, (xbUInt32) npTag->iKeyItemLen )) != XB_NO_ERROR ){ + iErrorStop = 140; + throw iRc; + } + ulNoOfKeys = eGetUInt32( npTag->npCurNode->cpBlockData ); + npTag->npCurNode->iCurKeyNo = ulNoOfKeys; + } + // get here on a leaf node, it has one fewer iCurKeyNo + npTag->npCurNode->iCurKeyNo--; + if( iRetrieveSw ){ + ulNoOfKeys = eGetUInt32( npTag->npCurNode->cpBlockData ); + if(( iRc = GetDbfPtr( npTag, ulNoOfKeys - 1, npTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){ + iErrorStop = 150; + throw iRc; + } + if(( iRc = dbf->GetRecord( ulKeyPtr )) != XB_NO_ERROR ){ + iErrorStop = 160; + throw iRc; + } + } + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbIxNdx::GetLastKey() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return iRc; +} + +/***********************************************************************/ +//! @brief Get the last key for a block number. +/*! + \param vpTag Tag to retrieve first key on. + \param ulBlockNo Block number for key retrieval. + \param cpBuf output buffer for key placement + \returns Return Codes +*/ + +xbInt16 xbIxNdx::GetLastKeyForBlockNo( void *vpTag, xbUInt32 ulBlockNo, char *cpBuf ){ + + // returns the last key for a given block number + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + + xbNdxTag * npTag; + vpTag ? npTag = (xbNdxTag *) vpTag : npTag = ndxTag; + + try{ + xbIxNode * npSaveNodeChain = npTag->npNodeChain; + xbIxNode * npSaveCurNode = npTag->npCurNode; + npTag->npNodeChain = NULL; + + if(( iRc = GetLastKey( ulBlockNo, npTag, 0 )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + // set the key + memcpy( cpBuf, GetKeyData( npTag->npCurNode, GetKeyCount( npTag->npCurNode ) - 1, npTag->iKeyItemLen ), (size_t) npTag->iKeyLen ); + + // free memory + npTag->npNodeChain = FreeNodeChain( npTag->npNodeChain ); + npTag->npNodeChain = npSaveNodeChain; + npTag->npCurNode = npSaveCurNode; + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbIxNdx::GetLastKeyForBlockNo() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( iRc ) ); + } + return iRc; +} +/***********************************************************************/ +//! @brief Get the next key for the given tag. +/*! + \param vpTag Tag to retrieve next key on. + \param iRetrieveSw xbTrue - Retrieve the record on success.
+ xbFalse - Don't retrieve record. + \returns Return Codes +*/ + +xbInt16 xbIxNdx::GetNextKey( void * vpTag, xbInt16 iRetrieveSw ){ + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + xbNdxTag * npTag; + vpTag ? npTag = (xbNdxTag *) vpTag : npTag = ndxTag; + + try{ + if( !npTag->npNodeChain ) + return GetFirstKey( vpTag, iRetrieveSw ); + + // more keys on this node? if yes, get the next one to the right + xbUInt32 ulKeyPtr; + if((eGetUInt32( npTag->npCurNode->cpBlockData ) -1) > npTag->npCurNode->iCurKeyNo ){ + npTag->npCurNode->iCurKeyNo++; + if(( iRc = GetDbfPtr( npTag, npTag->npCurNode->iCurKeyNo, npTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + if( iRetrieveSw ){ + if(( iRc = dbf->GetRecord( ulKeyPtr )) != XB_NO_ERROR ){ + iErrorStop = 110; + throw iRc; + } else { + return iRc; + } + } else { + return iRc; + } + } + // if at end of head node, then eof + if( npTag->npCurNode->ulBlockNo == npTag->ulRootBlock ) + return XB_EOF; + + // This logic assumes that interior nodes have n+1 left node no's where n is he the number of keys in the node + xbIxNode * TempIxNode = npTag->npCurNode; + npTag->npCurNode = npTag->npCurNode->npPrev; + TempIxNode = FreeNodeChain( TempIxNode ); + + while( npTag->npCurNode->iCurKeyNo >= eGetUInt32( npTag->npCurNode->cpBlockData ) && + (npTag->npCurNode->ulBlockNo != npTag->ulRootBlock )){ + TempIxNode = npTag->npCurNode; + npTag->npCurNode = npTag->npCurNode->npPrev; + TempIxNode = FreeNodeChain( TempIxNode ); + } + + // head node and at end of head node, then eof + if( npTag->npCurNode->ulBlockNo == npTag->ulRootBlock && + npTag->npCurNode->iCurKeyNo == eGetUInt32( npTag->npCurNode->cpBlockData )) + return XB_EOF; + + // move one to the right + npTag->npCurNode->iCurKeyNo++; + + if(( iRc = GetKeyPtr( npTag, npTag->npCurNode->iCurKeyNo, npTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){ + iErrorStop = 120; + throw iRc; + } + + if(( iRc = GetBlock( npTag, ulKeyPtr, 1, (xbUInt32) npTag->iKeyItemLen )) != XB_NO_ERROR ){ + iErrorStop = 130; + throw iRc; + } + while( !IsLeaf( npTag, npTag->npCurNode )) // go down the chain looking for a leaf node + { + if(( iRc = GetKeyPtr( npTag, 0, npTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){ + iErrorStop = 140; + throw iRc; + } + if(( iRc = GetBlock( npTag, ulKeyPtr, 1, (xbUInt32) npTag->iKeyItemLen )) != XB_NO_ERROR ){ + iErrorStop = 150; + throw iRc; + } + } + if( iRetrieveSw ){ + if(( iRc = GetDbfPtr( npTag, 0, npTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){ + iErrorStop = 160; + throw iRc; + } + if(( iRc = dbf->GetRecord( ulKeyPtr )) != XB_NO_ERROR ){ + iErrorStop = 170; + throw iRc; + } + } + } + catch( xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbIxNdx::GetNextKey() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return iRc; +} +/***********************************************************************/ +//! @brief Get the previous key for the given tag. +/*! + \param vpTag Tag to retrieve previous key on. + \param iRetrieveSw xbTrue - Retrieve the record on success.
+ xbFalse - Don't retrieve record. + \returns Return Codes +*/ +xbInt16 xbIxNdx::GetPrevKey( void * vpTag, xbInt16 iRetrieveSw ){ + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + xbNdxTag * npTag; + vpTag ? npTag = (xbNdxTag *) vpTag : npTag = ndxTag; + + // This method assumes last index call landed on a valid key. + // If last call resulted in an error, this method will returns XB_BOF + + try{ + if( !npTag->npNodeChain ) + return GetLastKey( 0, vpTag, iRetrieveSw ); + + xbUInt32 ulKeyPtr; + if( npTag->npCurNode->iCurKeyNo > 0 ){ + npTag->npCurNode->iCurKeyNo--; + + if(( iRc = GetDbfPtr( npTag, npTag->npCurNode->iCurKeyNo, npTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + if( iRetrieveSw ){ + if(( iRc = dbf->GetRecord( ulKeyPtr )) != XB_NO_ERROR ){ + iErrorStop = 110; + throw iRc; + } else { + return iRc; + } + } + } + + // next two lines might have been an issue + if( npTag->npCurNode->ulBlockNo == npTag->ulRootBlock && GetKeyCount( npTag->npCurNode ) == 0 && IsLeaf( npTag, npTag->npCurNode )) + return XB_EMPTY; + + if( npTag->npCurNode->ulBlockNo == npTag->ulRootBlock ) + return XB_BOF; + + // This logic assumes that interior nodes have n+1 left node no's where n is he the number of keys in the node + xbIxNode * TempIxNode = npTag->npCurNode; + npTag->npCurNode = npTag->npCurNode->npPrev; + TempIxNode = FreeNodeChain( TempIxNode ); + + while( npTag->npCurNode->iCurKeyNo == 0 && + (npTag->npCurNode->ulBlockNo != npTag->ulRootBlock )){ + TempIxNode = npTag->npCurNode; + npTag->npCurNode = npTag->npCurNode->npPrev; + TempIxNode = FreeNodeChain( TempIxNode ); + } + + // head node and at end of head node, then bof + if( npTag->npCurNode->ulBlockNo == npTag->ulRootBlock && + npTag->npCurNode->iCurKeyNo == 0 ) + return XB_BOF; + + // move one to the left + npTag->npCurNode->iCurKeyNo--; + + if(( iRc = GetKeyPtr( npTag, npTag->npCurNode->iCurKeyNo, npTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){ + iErrorStop = 120; + throw iRc; + } + + if(( iRc = GetBlock( npTag, ulKeyPtr, 1, (xbUInt32) npTag->iKeyItemLen )) != XB_NO_ERROR ){ + iErrorStop = 130; + throw iRc; + } + + while( !IsLeaf( npTag, npTag->npCurNode )){ // go down the chain looking for a leaf node + npTag->npCurNode->iCurKeyNo = eGetUInt32( npTag->npCurNode->cpBlockData ); + if(( iRc = GetKeyPtr( npTag, npTag->npCurNode->iCurKeyNo, npTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){ + iErrorStop = 140; + throw iRc; + } + if(( iRc = GetBlock( npTag, ulKeyPtr, 1, (xbUInt32) npTag->iKeyItemLen )) != XB_NO_ERROR ){ + iErrorStop = 150; + throw iRc; + } + } + + npTag->npCurNode->iCurKeyNo = eGetUInt32( npTag->npCurNode->cpBlockData ) - 1; + if( iRetrieveSw ){ + if(( iRc = GetDbfPtr( npTag, npTag->npCurNode->iCurKeyNo, npTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){ + iErrorStop = 160; + throw iRc; + } + if(( iRc = dbf->GetRecord( ulKeyPtr )) != XB_NO_ERROR ){ + iErrorStop = 170; + throw iRc; + } + } + } + + catch( xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbIxNdx::GetPrevKey() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return iRc; +} +/***********************************************************************/ +//! @brief Get the sort order for given tag. +/*! + Ndx indices only support ascending keys. + \returns 0 +*/ +xbBool xbIxNdx::GetSortOrder( void * ) const{ + return 0; +} +/***********************************************************************/ +//! @brief Get tag for tag number. +/*! + \returns Pointer to ndx tag. +*/ +void * xbIxNdx::GetTag( xbInt16 ) const{ + return ndxTag; +} +/***********************************************************************/ +//! @brief Get tag for tag name. +/*! + \returns Pointer to ndx tag. +*/ +void * xbIxNdx::GetTag( xbString & ) const{ + return ndxTag; +} + +/***********************************************************************/ +//! @brief Get tag count. +/*! + NDX index files contain one tag. + \returns 1 +*/ + +xbInt16 xbIxNdx::GetTagCount() const{ + return 1; +} +/***********************************************************************/ +//! @brief Get tag name. +/*! + \returns Tag name. +*/ +xbString &xbIxNdx::GetTagName( void * ) const { +// char * xbIxNdx::GetTagName( void * ) const { + + return ndxTag->sTagName; + +} +/***********************************************************************/ +//! @brief Get tag name. +/*! + \returns Tag name. +*/ +const char * xbIxNdx::GetTagName( void *, xbInt16 ) const { + return ndxTag->sTagName; +} + +/***********************************************************************/ +//! @brief Get the unique setting for given tag. +/*! + \param vpTag Tag to unique setting on. + \returns xbTrue - Unique index.
xbFalse - Not unique index. +*/ +xbBool xbIxNdx::GetUnique( void * vpTag ) const{ + xbNdxTag * npTag; + vpTag ? npTag = (xbNdxTag *) vpTag : npTag = ndxTag; + return npTag->iUnique; +} + +/***********************************************************************/ +//! @brief Insert key into interior node. +/*! + Insert key into non-full interior node.
+ Assumes valid inputs + + \param vpTag Tag in play. + \param npNode Node for insertion. + \param iSlotNo Slot number to insert key. + \param ulPtr Pointer number to insert. + \returns Return Codes +*/ + +xbInt16 xbIxNdx::InsertNodeI( void *vpTag, xbIxNode *npNode, xbInt16 iSlotNo, xbUInt32 ulPtr ){ + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + char *pTrg; + xbInt16 iSrcPos; + char *pLastKey = NULL; + xbNdxTag * npTag; + vpTag ? npTag = (xbNdxTag *) vpTag : npTag = ndxTag; + + try{ + // update number of keys on the node + xbInt32 lKeyCnt = GetKeyCount( npNode ); + iSrcPos = 12 + (iSlotNo * npTag->iKeyItemLen); + + char *pSrc = npNode->cpBlockData; + pSrc += iSrcPos; + + // if not appending to the end of the node, make some room, move things to the right + if( iSlotNo < lKeyCnt ) { + xbInt16 iCopyLen = ((lKeyCnt - iSlotNo) * npTag->iKeyItemLen) - 4; + pTrg = pSrc; + pTrg += npTag->iKeyItemLen; + memmove( pTrg, pSrc, (size_t) iCopyLen ); + } + + // get the right most key for the left part of the split node + xbUInt32 ulKeyPtr2; + if(( iRc = GetKeyPtr( vpTag, npNode->iCurKeyNo, npNode, ulKeyPtr2 )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + // get the new right key value for the freshly split node + pLastKey = (char *) malloc((size_t) ndxTag->iKeyLen); + if(( iRc = GetLastKeyForBlockNo( vpTag, ulKeyPtr2, pLastKey )) != XB_NO_ERROR ){ + iRc = 110; + throw iRc; + } + // write the key value + pTrg = pSrc; + char *pTrg2 = pSrc; + pSrc = pLastKey; + for( xbInt16 i = 0; i < npTag->iKeyLen; i++ ) + *pTrg++ = *pSrc++; + + pTrg2 += (npTag->iKeyItemLen - 8); + ePutUInt32( pTrg2, ulPtr ); + ePutInt32( npNode->cpBlockData, ++lKeyCnt ); + + // write out the updated block to disk + if(( iRc = WriteBlock( npNode->ulBlockNo, GetBlockSize(), npNode->cpBlockData )) != XB_NO_ERROR ){ + iErrorStop = 120; + throw iRc; + } + if( pLastKey ) + free( pLastKey ); + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbIxNdx::InsertNodeI() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + if( pLastKey ) + free( pLastKey ); + } + return iRc; +} +/***********************************************************************/ +//! @brief Insert key into leaf node. +/*! + Insert key into non-full leaf node.
+ Assumes valid inputs + + \param vpTag Tag in play. + \param npNode Node for insertion. + \param iSlotNo Slot number to insert key. + \param ulPtr Pointer number to insert. + \returns Return Codes +*/ +xbInt16 xbIxNdx::InsertNodeL( void *vpTag, xbIxNode *npNode, xbInt16 iSlotNo, + char * cpKeyBuf, xbUInt32 ulPtr ){ + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + char *pSrc; + char *pTrg; + char *pKeyPos; + xbString sMsg; + xbNdxTag * npTag; + vpTag ? npTag = (xbNdxTag *) vpTag : npTag = ndxTag; + + try{ + xbInt32 lKeyCnt = GetKeyCount( npNode ); + xbInt16 iKeyPos = 4 + iSlotNo * npTag->iKeyItemLen; + pKeyPos = npNode->cpBlockData; + pKeyPos += iKeyPos; + + // if not appending to end, make space, move things right + if( iSlotNo < lKeyCnt ) { + xbInt16 iCopyLen = (lKeyCnt - iSlotNo) * npTag->iKeyItemLen; + pTrg = pKeyPos; + pTrg += npTag->iKeyItemLen; + memmove( pTrg, pKeyPos, (size_t) iCopyLen ); + } + // if leaf, write rec number + pTrg = pKeyPos; + memset( pTrg, 0x00, 4 ); + pTrg += 4; + ePutUInt32( pTrg, ulPtr ); + pTrg += 4; + + // write the key value + pSrc = cpKeyBuf; + for( xbInt16 i = 0; i < npTag->iKeyLen; i++ ) + *pTrg++ = *pSrc++; + + // update number of keys on the node + ePutInt32( npNode->cpBlockData, ++lKeyCnt ); + + // write out the updated block to disk + if(( iRc = WriteBlock( npNode->ulBlockNo, GetBlockSize(), npNode->cpBlockData )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbIxNdx::InsertNodeL() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return iRc; +} +/***********************************************************************/ +//! @brief Determine node leaf status +/*! + \param npNode Node to examine. + \returns xbTrue - Leaf node.
xbFalse - Interior node. +*/ +xbBool xbIxNdx::IsLeaf( void *, xbIxNode *npNode ) const { + xbUInt32 ulBlock = eGetUInt32 ( npNode->cpBlockData+4 ); + if( ulBlock > 0 ) // if the second four bytes are a number, it's an interior node + return false; + else + return true; +} +/***********************************************************************/ +//! @brief Determine if key exists. +/*! + This method assumes the key has already been built and is in either + cpKeyBuf or dKey. + + \param vpTag - Pointer to tag. + \returns xbTrue - Key exists.
xbFalse - Key does not exist. +*/ +xbInt16 xbIxNdx::KeyExists( void * vpTag ){ + + xbNdxTag * npTag; + vpTag ? npTag = (xbNdxTag *) vpTag : npTag = ndxTag; + + xbInt16 iRc = FindKey( vpTag, npTag->cpKeyBuf, npTag->iKeyLen, 0 ); + if( iRc == 0 ) + return 1; + else + return 0; +} + +/***********************************************************************/ +//! @brief Set position for key add. +/*! + This routine is called by the AddKey() method and is used to position + the node chain to the position the new key should be added to the index. + + \param npTag Pointer to npTag. + \param ulAddRecNo Record number to add. + \returns Return Codes +*/ +xbInt16 xbIxNdx::KeySetPosAdd( xbNdxTag *npTag, xbUInt32 ulAddRecNo ){ + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + try{ + + iRc = FindKey( npTag, npTag->cpKeyBuf, npTag->iKeyLen, 0 ); + if( iRc == XB_NOT_FOUND || iRc == XB_EMPTY ) + return XB_NO_ERROR; // good position + + else if( iRc != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + // get here if key was found, get the right most instance of any non unique key for append, find correct spot for update + if( GetUnique() == 0 ){ + xbUInt32 ulCurRecNo; + if(( iRc = GetDbfPtr( npTag, npTag->npCurNode->iCurKeyNo, npTag->npCurNode, ulCurRecNo )) != XB_NO_ERROR ){ + iErrorStop = 110; + throw iRc; + } + xbBool bKeysMatch = xbTrue; + while( bKeysMatch && ulAddRecNo > ulCurRecNo && iRc == XB_NO_ERROR ){ + if(( iRc = GetNextKey( npTag, 0 )) == XB_NO_ERROR ){ + if( memcmp( GetKeyData( npTag->npCurNode, npTag->npCurNode->iCurKeyNo, npTag->iKeyItemLen ), npTag->cpKeyBuf, (size_t) npTag->iKeyLen )) + bKeysMatch = xbFalse; + else{ + if(( iRc = GetDbfPtr( npTag, npTag->npCurNode->iCurKeyNo, npTag->npCurNode, ulCurRecNo )) != XB_NO_ERROR ){ + iErrorStop = 120; + throw iRc; + } + } + } + } + } + if( iRc == XB_EOF ){ // eof condition + if(( iRc = GetLastKey( 0, npTag, 0 )) != XB_NO_ERROR ){ + iErrorStop = 130; + throw iRc; + } + npTag->npCurNode->iCurKeyNo++; + return XB_NO_ERROR; + } + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbIxNdx::KeySetPos() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return iRc; +} + +/***********************************************************************/ +//! @brief Set position for key add. +/*! + This routine is called by the DeleteKey() method and is used to position + the node chain to the position the old key should be deleted from the index. + + \param npTag Pointer to npTag. + \returns Return Codes +*/ + +xbInt16 xbIxNdx::KeySetPosDel( xbNdxTag *npTag ){ + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + xbString sMsg; + + try{ + iRc = FindKey( NULL, npTag->cpKeyBuf2, npTag->iKeyLen, 0 ); + if( iRc == XB_NOT_FOUND || iRc == XB_EMPTY ) + return XB_NO_ERROR; // good position + else if( iRc != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + xbUInt32 ulIxRecNo; + if(( iRc = GetDbfPtr( npTag, npTag->npCurNode->iCurKeyNo, npTag->npCurNode, ulIxRecNo )) != XB_NO_ERROR ){ + iErrorStop = 110; + throw iRc; + } + if( ulIxRecNo == dbf->GetCurRecNo()) + return XB_NO_ERROR; + if( GetUnique() == 1 ){ + iErrorStop = 120; + iRc = XB_NOT_FOUND; + throw iRc; + } + xbBool bFound = xbFalse; + xbBool bKeysMatch = xbTrue; + while( bKeysMatch && !bFound && iRc == XB_NO_ERROR ){ + if(( iRc = GetNextKey( npTag, 0 )) != XB_NO_ERROR ){ + iErrorStop = 130; + throw iRc; + } + if( memcmp( GetKeyData( npTag->npCurNode, npTag->npCurNode->iCurKeyNo, npTag->iKeyItemLen ), npTag->cpKeyBuf2, (size_t) npTag->iKeyLen )){ + bKeysMatch = xbFalse; + } else { + if(( iRc = GetDbfPtr( npTag, npTag->npCurNode->iCurKeyNo, npTag->npCurNode, ulIxRecNo )) != XB_NO_ERROR ){ + iErrorStop = 140; + throw iRc; + } + if( ulIxRecNo == dbf->GetCurRecNo()) + bFound = xbTrue; + } + } + if( bFound ) + return XB_NO_ERROR; + else + return XB_NOT_FOUND; + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbIxNdx::KeySetPosDel() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return iRc; +} +/***********************************************************************/ +//! @brief Returns key filter status. +/*! + \param vpTag Tag to check status on. + \returns xbtrue - Key was updated.
xbFalse - Key not updated. + + Always true for NDX style indices. +*/ +//inline xbBool xbIxNdx::KeyFiltered( void *vpTag ) const{ +// return xbTrue; +//} + +/***********************************************************************/ +//! @brief Read head block of index file. +/*! + \param iOpt 0 - Read in entire block + 1 - Read in only dynamic section of block + \returns Return Codes +*/ +xbInt16 xbIxNdx::ReadHeadBlock( xbInt16 iOpt = 0 ) { + + xbInt16 iRc = 0; + xbInt16 iErrorStop = 0; + try{ + if( !FileIsOpen()){ + iRc = XB_NOT_OPEN; + iErrorStop = 100; + throw iRc; + } + xbInt16 iLen; + iOpt == 0 ? iLen = 512 : iLen = 21; + + if(( iRc = ReadBlock( (xbUInt32) 0, (size_t) iLen, cNodeBuf )) != XB_NO_ERROR ){ + iErrorStop = 110; + throw iRc; + } + char *p = cNodeBuf; + ndxTag->ulRootBlock = eGetUInt32( p ); p+=4; + ndxTag->ulTotalBlocks = eGetUInt32( p ); p+=5; + if( iOpt == 0 ){ + ndxTag->cKeyType = *p; p+=3; + ndxTag->iKeyLen = eGetInt16( p ); p+=2; + ndxTag->iKeysPerBlock = eGetInt16( p ); p+=2; + ndxTag->iKeyType = eGetInt16( p ); p+=2; + ndxTag->iKeyItemLen = eGetInt16( p ); p+=2; + ndxTag->cSerNo = *p; p+=3; + ndxTag->iUnique = *p; p++; + ndxTag->sKeyExpression.Set( p ); + + if( ndxTag->exp ) + delete ndxTag->exp; + + ndxTag->exp = new xbExp( xbase, dbf ); + if(( iRc = ndxTag->exp->ParseExpression( ndxTag->sKeyExpression.Str() )) != XB_NO_ERROR ){ + iErrorStop = 120; + throw iRc; + } + + if( ndxTag->cpKeyBuf ) + free( ndxTag->cpKeyBuf ); + if( ndxTag->cpKeyBuf2 ) + free( ndxTag->cpKeyBuf2 ); + + ndxTag->cpKeyBuf = (char *) malloc( (size_t) ndxTag->iKeyLen ); + ndxTag->cpKeyBuf2 = (char *) malloc( (size_t) ndxTag->iKeyLen ); + + if( ndxTag->sTagName == "" ) + GetFileNamePart( ndxTag->sTagName ); + + } else { + p+= 11; + ndxTag->cSerNo = *p; + } + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbIxNdx::ReadHeadBlock() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return iRc; +} + +/***********************************************************************/ +//! @brief Reindex a tag. +/*! + \param vpTag Pointer to tag pointer. + \returns Return Codes +*/ + +xbInt16 xbIxNdx::Reindex( void **vpTag ){ + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + xbNdxTag *npTag = ndxTag; + + try{ + xbInt16 iUnique = GetUnique( *vpTag ); + + npTag->npNodeChain = FreeNodeChain( npTag->npNodeChain ); + npTag->npCurNode = NULL; + npTag->ulRootBlock = 1L; + npTag->ulTotalBlocks = 2L; + + if(( iRc = xbTruncate( 1024 )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + + if(( iRc = WriteHeadBlock( 1 )) != XB_NO_ERROR ){ + iErrorStop = 110; + throw iRc; + } + char buf[512]; + memset( buf, 0x00, 512 ); + + if(( iRc = WriteBlock( 1, 0, buf )) != XB_NO_ERROR ){ + iErrorStop = 120; + throw iRc; + } + + xbUInt32 ulRecCnt = 0; + if(( iRc = dbf->GetRecordCnt( ulRecCnt )) != XB_NO_ERROR ){ + iErrorStop = 130; + throw iRc; + } + + for( xbUInt32 l = 1; l <= ulRecCnt; l++ ){ + if(( iRc = dbf->GetRecord( l )) != XB_NO_ERROR ){ + iErrorStop = 140; + throw iRc; + } + + if(( iRc = CreateKey( npTag, 1 )) != XB_NO_ERROR ){ + iErrorStop = 150; + throw iRc; + } + + if( iUnique ){ + // iRc = CheckForDupKey( vpTag2 ); + iRc = CheckForDupKey( npTag ); + if( iRc != 0 ){ + if( iRc < 0 ){ + iErrorStop = 160; + throw iRc; + } + return XB_KEY_NOT_UNIQUE; + } + } + + if(( iRc = AddKey( npTag, l )) != XB_NO_ERROR ){ + iErrorStop = 150; + throw iRc; + } + } + *vpTag = npTag; + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbIxNdx::Reindex() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + this->DeleteTag( NULL ); // Don't leave the index in an incomplete state + } + return iRc; +} +/***********************************************************************/ +//! @brief Set current tag. +/*! + For ndx indices, there is only one tag. + \returns XB_NO_ERROR. +*/ +xbInt16 xbIxNdx::SetCurTag( xbInt16 ) { + xbIx::SetCurTag( ndxTag ); + return XB_NO_ERROR; +} +/***********************************************************************/ +//! @brief Set current tag. +/*! + For ndx indices, there is only one tag. + \returns XB_NO_ERROR. +*/ +xbInt16 xbIxNdx::SetCurTag( xbString & ) { + xbIx::SetCurTag( ndxTag ); + dbf->SetCurTag( "NDX", this, GetTag(0) ); + return XB_NO_ERROR; +} + +/***********************************************************************/ +//! @brief Split an interior node +/*! + + This routine splits an interior node into two nodes, divided by dSplitFactor.
+ This behaves differently than V7 Dbase. V7 does not balance the nodes.
+ For V7, if adding a key to the end of a node, it will create a right node + with only one key, and the left node is still full.

+ + Possible performance improvement options.
+ Two modes when splitting:
+ a) Split nodes in the middle - good for random access applications
+ b) Split off right node with only one key - good for applications with + expectation of ascending keys added moving forward.
+ + This routine first inserts the key into the left node in the appropriate location + then splits the node based on the split factor setting. + + \param vpTag Tag in play. + \param npLeft Left node to split. + \param npRight Right node to split. + \param iSlotNo Slot number for split. + \param ulPtr Pointer number to insert. + \returns Return Codes +*/ + +xbInt16 xbIxNdx::SplitNodeI( void *vpTag, xbIxNode * npLeft, xbIxNode *npRight, xbInt16 iSlotNo, xbUInt32 ulPtr ){ + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + xbNdxTag * npTag; + vpTag ? npTag = (xbNdxTag *) vpTag : npTag = ndxTag; + xbDouble dSplitFactor = .5; // split the nodes 50/50 + xbString sMsg; + + try{ + xbInt32 lKeyCnt = GetKeyCount( npLeft ); + xbInt32 lNewLeftKeyCnt = (xbInt32) ((lKeyCnt + 1) * dSplitFactor) + 1; + xbInt32 lNewRightKeyCnt = lKeyCnt - lNewLeftKeyCnt; + xbInt16 iSrcPos; + xbInt16 iCopyLen; + char *pSrc; + char *pTrg; + + // insert the key into the left node + if(( iRc = InsertNodeI( vpTag, npLeft, iSlotNo, ulPtr )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + + // move the right half of the left node to the right node + iSrcPos = ((lNewLeftKeyCnt + 1) * npTag->iKeyItemLen) + 4; + iCopyLen = (lNewRightKeyCnt * npTag->iKeyItemLen) + 4; + pSrc = npLeft->cpBlockData; + pSrc += iSrcPos; + pTrg = npRight->cpBlockData; + pTrg += 4; + memmove( pTrg, pSrc, (size_t) iCopyLen ); + + // write the new key counts into the nodes + pTrg = npLeft->cpBlockData; + ePutInt32( pTrg, lNewLeftKeyCnt ); + pTrg = npRight->cpBlockData; + ePutInt32( pTrg, lNewRightKeyCnt ); + + // write the new key counts into the nodes + pTrg = npLeft->cpBlockData; + ePutInt32( pTrg, lNewLeftKeyCnt ); + pTrg = npRight->cpBlockData; + ePutInt32( pTrg, lNewRightKeyCnt ); + + // write out the block + if(( iRc = WriteBlock( npLeft->ulBlockNo, GetBlockSize(), npLeft->cpBlockData )) != XB_NO_ERROR ){ + iErrorStop = 110; + throw iRc; + } + // write out the block + if(( iRc = WriteBlock( npRight->ulBlockNo, GetBlockSize(), npRight->cpBlockData )) != XB_NO_ERROR ){ + iErrorStop = 120; + throw iRc; + } + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbIxNdx::SplitNodeI() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return iRc; +} + +/***********************************************************************/ +//! @brief Split a leaf node. +/*! + This routine splits an index leaf into two nodes, divided by dSplitFactor.
+ This behaves differently than V7 Dbase. V7 does not balance the nodes.
+ For V7, if adding a key to the end of a node, it will create a right node + with only one key, and the left node is still full.

+ + Possible performance improvement options.
+ Two modes when splitting:
+ a) Split nodes in the middle - good for random access applications
+ b) Split off right node with only one key - good for applications with + expectation of ascending keys added moving forward.
+ + This routine first inserts the key into the left node in the appropriate location + then splits the node based on the split factor setting. + + \param vpTag Tag in play. + \param npLeft Left node to split. + \param npRight Right node to split. + \param iSlotNo Slot number for split. + \param ulPtr Pointer number to insert. + \returns Return Codes +*/ + +xbInt16 xbIxNdx::SplitNodeL( void *vpTag, xbIxNode * npLeft, xbIxNode *npRight, + xbInt16 iSlotNo, char * cpKeyBuf, xbUInt32 ulPtr ){ + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + xbDouble dSplitFactor = .5; + xbNdxTag *npTag; + vpTag ? npTag = (xbNdxTag *) vpTag : npTag = ndxTag; + + xbString sMsg; + try{ + xbInt32 lKeyCnt = GetKeyCount( npLeft ); + xbInt32 lNewLeftKeyCnt = (xbInt32) ((lKeyCnt + 1) * dSplitFactor) + 1; + xbInt32 lNewRightKeyCnt = lKeyCnt + 1 - lNewLeftKeyCnt; + + // xbInt16 iSrcPos; + xbInt16 iLen; + char *pSrc = npLeft->cpBlockData; + char *pTrg; + + if(( iRc = InsertNodeL( vpTag, npLeft, iSlotNo, cpKeyBuf, ulPtr )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + + // move right half off of left node to the right node + pSrc = npLeft->cpBlockData; + pSrc += ((lNewLeftKeyCnt * npTag->iKeyItemLen)+4); + pTrg = npRight->cpBlockData; + pTrg += 4; + iLen = lNewRightKeyCnt * npTag->iKeyItemLen; + memmove( pTrg, pSrc, (size_t) iLen ); + + // write the new key counts into the nodes + pTrg = npLeft->cpBlockData; + ePutInt32( pTrg, lNewLeftKeyCnt ); + pTrg = npRight->cpBlockData; + ePutInt32( pTrg, lNewRightKeyCnt ); + + // write out the left block + if(( iRc = WriteBlock( npLeft->ulBlockNo, GetBlockSize(), npLeft->cpBlockData )) != XB_NO_ERROR ){ + iErrorStop = 110; + throw iRc; + } + // write out the right block + if(( iRc = WriteBlock( npRight->ulBlockNo, GetBlockSize(), npRight->cpBlockData )) != XB_NO_ERROR ){ + iErrorStop = 120; + throw iRc; + } + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbIxNdx::SplitNodeL() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return iRc; +} + +/***********************************************************************/ +//! @brief UpdateTagKey +/*! + This routine updates a key or a given tag. + The file header is considered to be the first 2048 bytes in the file. + + \param cAction A - Add a key.
+ D - Delete a key.
+ R - Revise a key.
+ \param vpTg - Pointer to tag.
+ \param ulRecNo - Record number association with the action.
+ \returns Return Codes +*/ + +xbInt16 xbIxNdx::UpdateTagKey( char cAction, void *vpTag, xbUInt32 ulRecNo ){ + + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + // ..xbNdxTag *npTag = (xbMdxTag *) vpTag; + + try{ + // save off any needed fileds for updating + // xbUInt32 ulTagSizeSave = mpTag->ulTagSize; + //xbUInt32 ulLeftChildSave = mpTag->ulLeftChild; + //xbUInt32 ulRightChildSave = mpTag->ulRightChild; + + + if( cAction == 'D' || cAction == 'R' ){ +// std::cout << "UpdateTagKey delete\n"; + if(( iRc = DeleteKey( vpTag )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + } + + if( cAction == 'A' || cAction == 'R' ){ + if(( iRc = AddKey( vpTag, ulRecNo )) != XB_NO_ERROR ){ + iErrorStop = 110; + throw iRc; + } + } + + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbIxNdx::UpdateTagKey() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return iRc; +} +/***********************************************************************/ +//! @brief Write head block. +/*! + Commit the index head node to disk. + \param iOpt 0 - Entire header.
+ 1 - Update root block, number of blocks and seq number.
+ 2 - Update sequence number only
+ \returns +*/ + +xbInt16 xbIxNdx::WriteHeadBlock( xbInt16 iOpt ){ + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + + try{ + if( iOpt == 2 ){ + + // increment the serial number + if( ndxTag->cSerNo >= 0 && ndxTag->cSerNo < 127 ) + ndxTag->cSerNo++; + else + ndxTag->cSerNo = 0; + + if(( iRc = xbFseek( 20, SEEK_SET )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + if(( iRc = xbFputc( ndxTag->cSerNo )) != XB_NO_ERROR ){ + iErrorStop = 110; + throw iRc; + } + } else if( iOpt == 1 ){ + xbRewind(); + char buf[8]; + ePutUInt32( &buf[0], ndxTag->ulRootBlock ); + ePutUInt32( &buf[4], ndxTag->ulTotalBlocks ); + if(( iRc = xbFwrite( buf, 8, 1 )) != XB_NO_ERROR ){ + iErrorStop = 120; + throw iRc; + } + return WriteHeadBlock( 2 ); + + } else if ( iOpt == 0 ){ + + char buf[512]; + memset( buf, 0x00, 512 ); + ePutUInt32( &buf[0], ndxTag->ulRootBlock ); + ePutUInt32( &buf[4], ndxTag->ulTotalBlocks ); + buf[9] = ndxTag->cKeyType; + buf[11] = 0x1B; + ePutInt16( &buf[12], ndxTag->iKeyLen ); + ePutInt16( &buf[14], ndxTag->iKeysPerBlock ); + ePutInt16( &buf[16], ndxTag->iKeyType ); + ePutInt16( &buf[18], ndxTag->iKeyItemLen ); + if( ndxTag-> iUnique ) buf[23] = 0x01; + + for( xbUInt32 i = 0; i < ndxTag->sKeyExpression.Len(); i++ ) + buf[i+24] = ndxTag->sKeyExpression.GetCharacter(i+1); + + xbRewind(); + if(( iRc = xbFwrite( buf, 512, 1 )) != XB_NO_ERROR ){ + iErrorStop = 130; + throw iRc; + } + } else { + iRc = XB_INVALID_OPTION; + throw iRc; + } + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbIxNdx::WriteHeadBlock() Exception Caught. Error Stop = [%d] iRc = [%d] option = [%d] ser=[%d]", iErrorStop, iRc, iOpt, ndxTag->cSerNo ); + xbase->WriteLogMessage( sMsg ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return iRc; +} +/***********************************************************************/ +} /* namespace */ +#endif /* XB_NDX_SUPPORT */ + + + diff --git a/src/core/xbixtdx.cpp b/src/core/xbixtdx.cpp new file mode 100755 index 0000000..4137725 --- /dev/null +++ b/src/core/xbixtdx.cpp @@ -0,0 +1,661 @@ +/* xbixtdx.cpp + +XBase64 Software Library + +Copyright (c) 1997,2003,2014,2022,2023 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 + +This module handles temporary index logic + +*/ + +#include "xbase.h" + + + +#ifdef XB_TDX_SUPPORT + + +namespace xb{ + +/************************************************************************/ +xbIxTdx::xbIxTdx( xbDbf *dbf ) : xbIxMdx( dbf ) { +//xbIxMdx::xbIxMdx( xbDbf *dbf ) : xbIx( dbf ){ + +// std::cout << "xbIxTdx::Constructor()\n"; + + // Init(); not needed, called in xbMdx + + +} + +/************************************************************************/ +xbIxTdx::~xbIxTdx() { + +// std::cout << "xbIxTdx::Destructor()\n"; + +} + +/***********************************************************************/ +xbInt16 xbIxTdx::Close(){ + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + +std::cout << "xbIxTdx::Close\n"; + + try{ + if(( iRc = xbIxMdx::Close()) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + if(( iRc = xbRemove()) != XB_NO_ERROR ){ + iErrorStop = 110; + throw iRc; + } + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbIxTdx::Close() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return iRc; +} + +/***********************************************************************/ +//! @brief Create new tag. +/*! + This routine creates a new tag. When complete, sets the cur tag pointer to + the newly created tag. + + + \param sName Tag Name, including .MDX suffix + \param sKey Key Expression + \param sFilter Filter expression. + \param iDescending + \param iUnique xbtrue - Unique.
xbFalse - Not unique. + \param iOverLay xbTrue - Overlay if file already exists.
xbFalse - Don't overlay. + \param vpTag Output from method Pointer to vptag pointer. + \returns
Return Codes +*/ + + +xbInt16 xbIxTdx::CreateTag( const xbString &sName, const xbString &sKey, const xbString &sFilter, xbInt16 iDescending, xbInt16 iUnique, xbInt16 iOverlay, void **vpTag ){ + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + xbMdxTag *tte = NULL; + + std::cout << "xbIxTdx::CreateTag()\n"; + + + // std::cout << "CreateTag() name=[" << sName.Str() << "] key=[" << sKey.Str() << "] sFilter=[" << sFilter.Str() << "]\n"; + // std::cout << "TagUseCnt = " << iTagUseCnt << std::endl; + + + try{ + // verify room for new tag + if( !( iTagUseCnt < 47 )){ + iErrorStop = 100; + iRc = XB_LIMIT_REACHED; + throw iRc; + } + + // verify valid tag name + xbString sWorker = sName; + sWorker.Trim(); + if( sWorker.Len() > 10 ){ + iErrorStop = 110; + iRc = XB_INVALID_TAG; + throw iRc; + } + + // verify tag not already defined + if( iTagUseCnt > 0 ){ + if( GetTag( sWorker )){ + iErrorStop = 120; + iRc = XB_INVALID_TAG; + throw iRc; + } + } + + // allocate a tag structure here + if(( tte = (xbMdxTag *) calloc( 1, (size_t) sizeof( xbMdxTag ))) == NULL ){ + iErrorStop = 130; + iRc = XB_NO_MEMORY; + throw iRc; + } + *vpTag = tte; + tte->sTagName = new xbString( sWorker ); + + //set up the key expression + sWorker = sFilter; + sWorker.Trim(); + if( sWorker.Len() > 0 ){ + if( sWorker.Len() == 0 || sWorker.Len() > 220 ){ + iRc = XB_INVALID_TAG; + iErrorStop = 140; + throw iRc; + } + tte->sFiltExp = new xbString( sWorker ); + tte->filter = new xbExp( dbf->GetXbasePtr()); + if(( iRc = tte->filter->ParseExpression( dbf, sWorker )) != XB_NO_ERROR ){ + iErrorStop = 150; + throw iRc; + } + + // tte->filter->DumpTree( 1 ); + + if((tte->filter->GetReturnType()) != 'L' ){ + iRc = XB_INVALID_TAG; + iErrorStop = 160; + throw iRc; + } + tte->cHasFilter = 0x01; + } + + //set up the key expression + sWorker = sKey; + sWorker.Trim(); + if( sWorker.Len() == 0 || sWorker.Len() > 100 ){ + iRc = XB_INVALID_TAG; + iErrorStop = 170; + throw iRc; + } + tte->sKeyExp = new xbString( sWorker ); + tte->exp = new xbExp( dbf->GetXbasePtr()); + if(( iRc = tte->exp->ParseExpression( dbf, sWorker )) != XB_NO_ERROR ){ + iErrorStop = 180; + throw iRc; + } + + xbDate d; + d.Sysdate(); + if( iTagUseCnt == 0 ){ + // first tag, new mdx file + // create the file name + + // create temp file + xbString sIxFileName; + if(( iRc = CreateUniqueFileName( GetTempDirectory(), "TDX", sIxFileName )) != XB_NO_ERROR ){ + iErrorStop = 190; + throw iRc; + } + + // copy the file name to the class variable + this->SetFileName( sIxFileName ); + if( FileExists() && !iOverlay ){ + iErrorStop = 200; + iRc = XB_FILE_EXISTS; + throw iRc; + } + + // first tag, need to create the file + if(( iRc = xbFopen( "w+b", dbf->GetShareMode())) != XB_NO_ERROR ){ + iErrorStop = 210; + throw iRc; + } + cVersion = 2; + cCreateYY = (char) d.YearOf() - 1900; + cCreateMM = (char) d.MonthOf(); + cCreateDD = (char) d.DayOf( XB_FMT_MONTH ); + + GetFileNamePart( sFileName ); + sFileName.ToUpperCase(); + + SetBlockSize( (xbUInt32) dbf->GetCreateMdxBlockSize()); + iBlockFactor = GetBlockSize() / 512; + + cProdIxFlag = 0; // MDX is 1 + cTagEntryCnt = 48; + iTagLen = 32; + ulPageCnt = 4; + ulFirstFreePage = 0; + ulNoOfBlockAvail = 0; + cNextTag = 1; + c1B = 0x1B; + cUpdateYY = cCreateYY; + cUpdateMM = cCreateMM; + cUpdateDD = cCreateDD; + + if(( iRc = WriteHeadBlock( 0 )) != XB_NO_ERROR ){ + iErrorStop = 220; + throw iRc; + } + } + + // populate the tag table entry structure + tte->ulTagHdrPageNo = ulPageCnt; + ulPageCnt += (xbUInt32) iBlockFactor; + tte->sTagName->strncpy( tte->cTagName, 10 ); + + // cKeyFmt is always 0x10; + // tested 2+ZIPCD CITY+STATE or just standalone field - always 0x10 + tte->cKeyFmt = 0x10; // = CalcTagKeyFmt( *tte->exp ); + + switch( tte->exp->GetReturnType()){ + case XB_EXP_CHAR: + tte->cKeyType = 'C'; + tte->iKeyLen = tte->exp->GetResultLen(); + tte->iSecKeyType = 0; + break; + + case XB_EXP_NUMERIC: + tte->cKeyType = 'N'; + tte->iKeyLen = 12; + tte->iSecKeyType = 0; + break; + + case XB_EXP_DATE: + tte->cKeyType = 'D'; + tte->iKeyLen = 8; + tte->iSecKeyType = 1; + break; + + default: + iErrorStop = 200; + iRc = XB_INVALID_INDEX; + throw iRc; + } + + tte->cpKeyBuf = (char *) malloc( (size_t) tte->iKeyLen + 1 ); + tte->cpKeyBuf2 = (char *) malloc( (size_t) tte->iKeyLen + 1 ); + +// if( 0 ){ +// printf( "ulTagHdrPageNo=[%d] cTagName=[%-11s], cLeftChild=[%d] cRightChild=[%d] cParent=[%d] cKeyType=[%c]\n\n", +// tte->ulTagHdrPageNo, tte->cTagName, tte->cLeftChild, tte->cRightChild, tte->cParent, tte->cKeyType ); +// } + + // write the new tte entry here + char tteBuf[21]; + memset( tteBuf, 0x00, 21 ); + + ePutUInt32( &tteBuf[0], tte->ulTagHdrPageNo ); + for( xbUInt32 l = 0; l < tte->sTagName->Len() && l < 10; l++ ){ + tteBuf[l+4] = tte->sTagName->GetCharacter(l+1); + } + tteBuf[15] = tte->cKeyFmt; + tteBuf[19] = 0x02; // appears to always be a 0x02 + tteBuf[20] = tte->cKeyType; + + if(( iRc = xbFseek( (iTagUseCnt * 32) + 544, SEEK_SET )) != XB_NO_ERROR ){ + iErrorStop = 230; + throw iRc; + } + if(( iRc = xbFwrite( tteBuf, 21, 1 )) != XB_NO_ERROR ){ + iErrorStop = 240; + throw iRc; + } + + + // Begin Tag Header + tte->ulRootPage = ulPageCnt; + tte->ulTagSize = (xbUInt32) iBlockFactor; + ulPageCnt += 2; + tte->cKeyFmt2 = 0x10; + if( iDescending ) + tte->cKeyFmt2 += 0x08; + if( iUnique ){ + tte->cKeyFmt2 += 0x40; + tte->cUnique = 0x01; + } + + tte->cTag11 = 0x1B; // always 0x1b ? + tte->cSerialNo = 0x01; // version incremented with each tag update + tte->ulLeftChild = tte->ulRootPage; + tte->ulRightChild = tte->ulRootPage; + + tte->cTagYY = (char) d.YearOf() - 1900; + tte->cTagMM = (char) d.MonthOf(); + tte->cTagDD = (char) d.DayOf( XB_FMT_MONTH ); + + tte->cKeyType2 = tte->cKeyType; + tte->iKeyItemLen = tte->iKeyLen + 4; + while(( tte->iKeyItemLen % 4 ) != 0 ) tte->iKeyItemLen++; + + tte->iKeysPerBlock = (xbInt16) (GetBlockSize() - 12) / tte->iKeyItemLen; + + //std::cout << "-------------- create tag info\n"; + //std::cout << "keylen=" << tte->iKeyLen << " iKeyItemLen = " << tte->iKeyItemLen << " keys per block calc = " << tte->iKeysPerBlock << "\n"; + + tte->cKeyFmt3 = CalcTagKeyFmt( *tte->exp ); + +// printf( "ulRootPage=[%d] cKeyFmt2=[%d] cKeyType2=[%d] iKeyLen=[%d]iKeysPerBlock=[%d]\n", tte->ulRootPage, tte->cKeyFmt2, tte->cKeyType2, tte->iKeyLen, tte->iKeysPerBlock ); +// printf( "iSecKeyType=[%d] iKeyItemLen=[%d] cUnique=[%d] \n", tte->iSecKeyType, tte->iKeyItemLen, tte->cUnique ); + + char *pBuf; + if(( pBuf = (char *) calloc( 1, (size_t) GetBlockSize())) == NULL ){ + iErrorStop = 230; + iRc = XB_NO_MEMORY; + throw iRc; + } + char *wPtr; + wPtr = pBuf; + ePutUInt32( wPtr, tte->ulRootPage ); + + wPtr += 4; + ePutUInt32( wPtr, tte->ulTagSize ); + + wPtr += 4; + *wPtr = tte->cKeyFmt2; + + wPtr++; + *wPtr = tte->cKeyType2; + + wPtr += 2; + *wPtr = tte->cTag11; + + wPtr += 1; + ePutInt16( wPtr, tte->iKeyLen ); + + wPtr += 2; + ePutInt16( wPtr, tte->iKeysPerBlock ); + + wPtr += 2; + ePutInt16( wPtr, tte->iSecKeyType ); + + wPtr += 2; + ePutInt16( wPtr, tte->iKeyItemLen ); + + wPtr += 2; + *wPtr = tte->cSerialNo; + + wPtr += 3; + *wPtr = tte->cUnique; + + wPtr++; + for( xbUInt32 l = 0; l < tte->sKeyExp->Len(); l++ ) + *wPtr++ = tte->sKeyExp->GetCharacter(l+1); + + wPtr = pBuf; + + tte->cHasKeys = 0x00; + pBuf[246] = tte->cHasKeys; + + wPtr += 248; + ePutUInt32( wPtr, tte->ulLeftChild ); + wPtr += 4; + ePutUInt32( wPtr, tte->ulRightChild ); + + pBuf[257] = tte->cTagYY; + pBuf[258] = tte->cTagMM; + pBuf[259] = tte->cTagDD; + pBuf[480] = tte->cKeyFmt3; + + if( sFilter.Len() > 0 ){ + pBuf[245] = tte->cHasFilter; + wPtr = pBuf; + wPtr += 762; + for( xbUInt32 l = 0; l < sFilter.Len(); l++ ) + *wPtr++ = sFilter.GetCharacter(l+1); + } + + if(( iRc = xbFseek( tte->ulTagHdrPageNo * 512, SEEK_SET )) != XB_NO_ERROR ){ + iErrorStop = 250; + throw iRc; + } + + if(( iRc = xbFwrite( pBuf, GetBlockSize(), 1 )) != XB_NO_ERROR ){ + iErrorStop = 260; + throw iRc; + } + + memset( pBuf, 0x00, GetBlockSize() ); + if(( iRc = xbFwrite( pBuf, GetBlockSize(), 1 )) != XB_NO_ERROR ){ + iErrorStop = 270; + throw iRc; + } + + iTagUseCnt++; + cNextTag++; + + + if(( iRc = WriteHeadBlock( 1 )) != XB_NO_ERROR ){ + iErrorStop = 280; + throw iRc; + } + + // add the new entry to the end of the list of tags + if( mdxTagTbl == NULL ){ + mdxTagTbl = tte; + } else { + xbMdxTag *tteL = mdxTagTbl; + while( tteL->next ) + tteL = tteL->next; + tteL->next = tte; + } + + /* update the btree pointers */ + CalcBtreePointers(); + char bBuf[3]; + xbMdxTag *tteWork = mdxTagTbl; + + if(( iRc = xbFseek( 560, SEEK_SET )) != XB_NO_ERROR ){ + iErrorStop = 290; + throw iRc; + } + while( tteWork ){ + bBuf[0] = tteWork->cLeftChild; + bBuf[1] = tteWork->cRightChild; + bBuf[2] = tteWork->cParent; + + if(( iRc = xbFwrite( bBuf, 3, 1 )) != XB_NO_ERROR ){ + iErrorStop = 300; + throw iRc; + } + if( tteWork->next ){ + if(( iRc = xbFseek( 29, SEEK_CUR )) != XB_NO_ERROR ){ + iErrorStop = 310; + throw iRc; + } + } + tteWork = tteWork->next; + } + free( pBuf ); + + } + + catch (xbInt16 iRc ){ + if( tte ){ + if( tte->cpKeyBuf ) + free( tte->cpKeyBuf ); + if( tte->cpKeyBuf2 ) + free( tte->cpKeyBuf2 ); + if( tte->exp ) + delete tte->exp; + if( tte->filter ) + delete tte->filter; + if( tte->sKeyExp ) + delete tte->sKeyExp; + if( tte->sFiltExp ) + delete tte->sFiltExp; + if( tte->sTagName ) + delete tte->sTagName; + free( tte ); + } + xbString sMsg; + sMsg.Sprintf( "xbIxTdx::CreateTag() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + + return iRc; +}; + +/***********************************************************************/ +//! @brief Delete a given tag +/*! + \param vpTag Input tag ptr for tag to be deleted
+ \returns Return Codes
+ 1 = Deleted entire MDX file, only had one tag + +*/ + +xbInt16 xbIxTdx::DeleteTag( void *vpTag ){ + + xbInt16 iRc = 0; + xbInt16 iErrorStop = 0; + xbMdxTag * mpTag = (xbMdxTag *) vpTag; + xbIxNode *n = NULL; + xbBool bLoneTag = xbFalse; + + try{ + + if( !vpTag ){ + iErrorStop = 100; + iRc = XB_INVALID_TAG; + throw iRc; + } + + // char cSaveHasFilter = mpTag->cHasFilter; + // char cSaveKeyFmt3 = mpTag->cKeyFmt3; + // xbString sSaveKey = mpTag->sKeyExp->Str(); + + if( iTagUseCnt == 1 ){ + // std::cout << "xbIxTdx::DeleteTag - one tag found, delete the mdx file\n"; + + // close the mdx file + if(( iRc = xbIxMdx::Close()) != XB_NO_ERROR ){ + iErrorStop = 110; + throw iRc; + } + + // delete the file + xbRemove(); + + // init variables - needed? + // Init(); + // iRc > 0 defines this as the only tag in an MDX file, MDX file deleted. + // signals to the calling process to drop the MDX file from the + // list of updateable indices. + bLoneTag = xbTrue; + + } else { + + // harvest tag nodes + + if(( iRc = HarvestTagNodes( mpTag, xbTrue )) != XB_NO_ERROR ){ + iErrorStop = 120; + throw iRc; + } + + // remove an entry from tag table + // which tag is this? + xbInt16 iTagNo = 0; + xbMdxTag *mp = mdxTagTbl; + xbMdxTag *mpPrev = NULL; + while( mp && mp->ulTagHdrPageNo != mpTag->ulTagHdrPageNo ){ + iTagNo++; + mpPrev = mp; + mp = mp->next; + } + + // remove it from the linked list of tags + if( !mpPrev ){ + mdxTagTbl = mp->next; + } else { + mpPrev->next = mp->next; + } + if( mp ){ + if( mp->cpKeyBuf ) free( mp->cpKeyBuf ); + if( mp->cpKeyBuf2 ) free( mp->cpKeyBuf2 ); + if( mp->exp ) delete mp->exp; + if( mp->filter ) delete mp->filter; + if( mp->sKeyExp ) delete mp->sKeyExp; + if( mp->sFiltExp ) delete mp->sFiltExp; + if( mp->sTagName ) delete mp->sTagName; + free( mp ); + } + xbInt32 iTarg = iTagNo * 32; + xbInt32 iSrc = iTarg + 32; + xbInt32 iLen = (iTagUseCnt - iTagNo) * 32; + + if(( iRc = xbFseek( 544, SEEK_SET )) != XB_NO_ERROR ){ + iErrorStop = 160; + throw iRc; + } + char Buf[1536]; // 47 tags + 1 in case tag #47 is deleted + memset( Buf, 0x00, 1536 ); + if(( iRc = xbFread( Buf, 1504, 1 )) != XB_NO_ERROR ){ + iErrorStop = 170; + throw iRc; + } + char *pTrg = Buf; + pTrg += iTarg; + char *pSrc = Buf; + pSrc += iSrc; + for( xbInt32 i = 0; i < iLen; i++ ) + *pTrg++ = *pSrc++; + + if(( iRc = xbFseek( 544, SEEK_SET )) != XB_NO_ERROR ){ + iErrorStop = 180; + throw iRc; + } + if(( iRc = xbFwrite( Buf, 1504, 1 )) != XB_NO_ERROR ){ + iErrorStop = 190; + throw iRc; + } + + iTagUseCnt--; + if(( iRc = WriteHeadBlock( 1 )) != XB_NO_ERROR ){ + iErrorStop = 200; + throw iRc; + } + + // update the btree pointers + CalcBtreePointers(); + char bBuf[3]; + xbMdxTag *tteWork = mdxTagTbl; + + if(( iRc = xbFseek( 560, SEEK_SET )) != XB_NO_ERROR ){ + iErrorStop = 210; + throw iRc; + } + while( tteWork ){ + bBuf[0] = tteWork->cLeftChild; + bBuf[1] = tteWork->cRightChild; + bBuf[2] = tteWork->cParent; + + if(( iRc = xbFwrite( bBuf, 3, 1 )) != XB_NO_ERROR ){ + iErrorStop = 320; + throw iRc; + } + if( tteWork->next ){ + if(( iRc = xbFseek( 29, SEEK_CUR )) != XB_NO_ERROR ){ + iErrorStop = 330; + throw iRc; + } + } + tteWork = tteWork->next; + } + } + + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbIxTdx::DeleteTag() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + if( n ) + free( n ); + } + if( bLoneTag && !iRc ) + return 1; + else + return iRc; +} + +/************************************************************************/ + + + +/************************************************************************/ +} /* namespace */ +#endif /* XB_TDX_SUPPORT */ \ No newline at end of file diff --git a/src/core/xblog.cpp b/src/core/xblog.cpp new file mode 100755 index 0000000..9443006 --- /dev/null +++ b/src/core/xblog.cpp @@ -0,0 +1,227 @@ +/* xblog.cpp + +XBase64 Software Library + +Copyright (c) 1997,2003,2014,2022,2023 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" +//#include + +#ifdef XB_LOGGING_SUPPORT + +namespace xb{ + +/******************************************************************************/ +//! @brief Constructor. +xbLog::xbLog() : xbFile( NULL ){ + + + // std::cout << "xbLog::xbLog(1) Directory = [" << GetLogDirectory() << "]\n"; + // std::cout << "xbLog::xbLog(1) Name = [" << GetLogFileName() << "]\n"; + + SetDirectory( GetLogDirectory()); + SetFileName ( GetLogFileName()); + + bLoggingStatus = xbFalse; + lLogSize = 100000; + + #ifdef XB_LOCKING_SUPPORT + iShareMode = XB_MULTI_USER; + #else + iShareMode = XB_SINGLE_USER; + #endif +} +/******************************************************************************/ +//! @brief Constructor. +/*! + \param sLogFileName - Log file name. +*/ +xbLog::xbLog( const xbString & sLogFileName ) : xbFile( NULL ){ + if( sLogFileName.GetPathSeparator()) + SetFqFileName( sLogFileName ); // file name includes a path + else + SetFileName( sLogFileName ); // no file path + + bLoggingStatus = xbFalse; + lLogSize = 100000; + + #ifdef XB_LOCKING_SUPPORT + iShareMode = XB_MULTI_USER; + #else + iShareMode = XB_SINGLE_USER; + #endif + +} +/******************************************************************************/ +//! @brief Deconstructor. +xbLog::~xbLog(){ + xbFclose(); +} +/******************************************************************************/ +//! @brief Get the current log status +/*! + \returns xbTrue - Logging turned on.
xbFalse - Logging turned off. +*/ +xbBool xbLog::LogGetStatus(){ + return bLoggingStatus; +} +/******************************************************************************/ +//! @brief Close the logfile. +/*! + \returns Return Codes +*/ +xbInt16 xbLog::LogClose(){ + return xbFclose(); +} +/******************************************************************************/ +//! @brief Set maximum log file size. +/*! + \param lSize - New maximum log file size. + \returns void +*/ +void xbLog::LogSetLogSize( size_t lSize ){ + lLogSize = lSize; +} +/******************************************************************************/ +//! @brief Set log status. +/*! + \param bStatus xbTrue - Turn logging on.
xbFalse - Turn logging off. + \returns void +*/ +void xbLog::LogSetStatus( xbBool bStatus ){ + if( bLoggingStatus && !bStatus ) + LogClose(); + bLoggingStatus = bStatus; +} +/******************************************************************************/ +//! @brief Open the logfile. +/*! + \returns Return Codes +*/ +xbInt16 xbLog::LogOpen(){ + xbInt16 rc; + +// std::cout << "*****\nxbLog::LogOpen(1) GetLogDirectory = " << GetLogDirectory() << "\n"; +// std::cout << "xbLog::LogOpen(1) GetLogFileName = " << GetLogFileName() << "\n"; +// std::cout << "xbLog::GetFqFileName(1) = " << GetFqFileName() << "\n\n"; + + // 4.1.3 added next two lines for dynamic log file name changing + SetDirectory( GetLogDirectory()); + SetFileName ( GetLogFileName()); + +// std::cout << "*****\nxbLog::LogOpen(2) GetLogDirectory = " << GetLogDirectory() << "\n"; +// std::cout << "xbLog::LogOpen(2) GetLogFileName = " << GetLogFileName() << "\n"; +// std::cout << "xbLog::GetFqFileName(2) = " << GetFqFileName() << "\n\n"; + + if(( rc = xbFopen( "a", iShareMode )) != XB_NO_ERROR ) + return rc; + xbFTurnOffFileBuffering(); + return XB_NO_ERROR; +} +/******************************************************************************/ +//! @brief Write a logfile message. +/*! + \param sLogEntryData - Message to write to the logfile. + \param iOutputOption 0 - Write to logfile.
+ 1 - Write to stdout.
+ 2 - Write to both logfile and stdout. + \returns Return Codes +*/ + +xbInt16 xbLog::LogWrite( const xbString &sLogEntryData, xbInt16 iOutputOption ){ + + if( bLoggingStatus == xbFalse ){ // logging turned off + return XB_NO_ERROR; + } + xbInt16 rc = 0; + if( iOutputOption != 1 && !FileIsOpen() ){ + if(( rc = LogOpen()) != XB_NO_ERROR ){ + fprintf( stderr, "Error - cant write to logfile\n" ); + return rc; + } + } + if( iOutputOption != 1 && lLogSize < xbFtell()){ + xbFputs( "Swapping to next log file" ); + xbFclose(); + xbString sBackupName; + sBackupName.Sprintf( "%s.bak", GetFqFileName().Str()); + if( FileExists( sBackupName )) + xbRemove( sBackupName ); + + xbRename( GetFqFileName(), sBackupName ); + xbFopen( "a", iShareMode ); + } + xbString sTimeStamp; + xbString sFled; // formatted log entry data + + if( iOutputOption != 1 ){ + #ifdef HAVE__LOCALTIME64_S_F + __time64_t timer; + struct tm tb; + _time64( &timer ); + _localtime64_s( &tb, &timer ); + tb.tm_year += 1900; + tb.tm_mon++; + sTimeStamp.Sprintf( "%4d-%02d-%02d %02d:%02d:%02d", tb.tm_year, tb.tm_mon, tb.tm_mday, tb.tm_hour, tb.tm_min, tb.tm_sec ); + #else + time_t timer; + struct tm *tb; + timer = time( NULL ); + tb = localtime( &timer ); + tb->tm_year += 1900; + tb->tm_mon++; + sTimeStamp.Sprintf( "%4d-%02d-%02d %02d:%02d:%02d", tb->tm_year, tb->tm_mon, tb->tm_mday, tb->tm_hour, tb->tm_min, tb->tm_sec ); + #endif + sFled.Sprintf( "%s - %s\n", sTimeStamp.Str(), sLogEntryData.Str() ); + } + + switch( iOutputOption ){ + case 0: + xbFputs( sFled ); + break; + case 1: + std::cout << sLogEntryData << std::endl; + break; + case 2: + xbFputs( sFled ); + std::cout << sLogEntryData << std::endl; + break; + } + return XB_NO_ERROR; +} +/******************************************************************************/ +//! @brief Write bytes to logfile. +/*! + \param ulByteCnt - Number of bytes to write to logfile. + \param p - Pointer to data to write to logfile. + \returns XB_NO_ERROR +*/ + +xbInt16 xbLog::LogWriteBytes( xbUInt32 ulByteCnt, const char *p ){ + + if( bLoggingStatus == xbFalse ) // logging turned off + return XB_NO_ERROR; + const char *p2 = p; + xbFputc( '[' ); + for( xbUInt32 l = 0; l < ulByteCnt; l++ ) + xbFputc( *p2++ ); + xbFputc( ']' ); + return XB_NO_ERROR; +} +/******************************************************************************/ +} // namespace +#endif // XB_LOGGING_ON + + + + diff --git a/src/core/xbmemo.cpp b/src/core/xbmemo.cpp new file mode 100755 index 0000000..406a77d --- /dev/null +++ b/src/core/xbmemo.cpp @@ -0,0 +1,219 @@ +/* xbmemo.cpp + +XBase64 Software Library + +Copyright (c) 1997,2003,2014,2022,2023 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 + + Base memo class +*/ + +#include "xbase.h" + +#ifdef XB_MEMO_SUPPORT + +namespace xb{ + +/***********************************************************************/ +//! @brief Class Constructor. +/*! + \param dbf Pointer to dbf construct. + \param sFileName Memo file name. +*/ + +xbMemo::xbMemo( xbDbf * dbf, xbString const &sFileName ) : xbFile( dbf->GetXbasePtr() ) { + this->dbf = dbf; /* pointer to the associated dbf class instance */ + // xbase = dbf->GetXbasePtr(); /* pointer to the engine */ + SetDirectory( dbf->GetDirectory()); + SetFileName( sFileName ); + mbb = NULL; + #ifdef XB_LOCKING_SUPPORT + bFileLocked = xbFalse; + #endif +} +/***********************************************************************/ +//! @brief Class Destructor. +xbMemo::~xbMemo(){ + if( mbb ) + free( mbb ); +} +/***********************************************************************/ +//! @brief Calculate the last data block number. +/*! + \param ulLastDataBlock Output - Last used block number in the file. + \returns Return Codes +*/ +xbInt16 xbMemo::CalcLastDataBlock( xbUInt32 & ulLastDataBlock ){ + + xbInt16 iRc = XB_NO_ERROR; + if(( iRc = xbFseek( 0, SEEK_END )) != XB_NO_ERROR ) + return iRc; + + ulLastDataBlock = (xbUInt32) xbFtell() / (xbUInt32) GetBlockSize(); + return XB_NO_ERROR; +} +/***********************************************************************/ +//! @brief Close the memo file. +/*! + \returns Return Codes +*/ +xbInt16 xbMemo::CloseMemoFile(){ + + if( mbb ){ + free( mbb ); + mbb = NULL; + } + return xbFclose(); +} + + +/***********************************************************************/ +//! @brief Get memo file type. +/*! + \returns 3 - Version 3 memo file.
+ 4 - Version 4 memo file. +*/ +xbInt16 xbMemo::GetMemoFileType(){ + return iMemoFileType; +} + +/***********************************************************************/ +//! @brief Get next block available from file header. +/*! + \param ulBlockNo Output - Next block number for appending data to memo file. + \returns Return Codes +*/ +xbInt16 xbMemo::GetHdrNextBlock( xbUInt32 & ulBlockNo ){ + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + try{ + if(( iRc = ReadDbtHeader( 0 )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + ulBlockNo = ulHdrNextBlock; + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbmemo::GetNextAvailableBlock() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return iRc; +} +/***********************************************************************/ +#ifdef XB_LOCKING_SUPPORT +//! @brief Lock memo file +/*! + + \param iLockFunction XB_LOCK
XB_UNLOCK + \returns Return Codes +*/ + +xbInt16 xbMemo::LockMemo( xbInt16 iLockFunction ){ + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + + try{ + if( iLockFunction == XB_LOCK ){ + + if( bFileLocked ) // already locked + return XB_NO_ERROR; + + if( dbf->GetLockFlavor() == LK_DBASE ){ + iRc = xbLock( XB_LOCK, LK4026531838, 1 ); + if( iRc != XB_NO_ERROR ){ + if( iRc == XB_LOCK_FAILED ) + return iRc; + else { + iErrorStop = 100; + throw iRc; + } + } else { + bFileLocked = xbTrue; + } + } + } else if( iLockFunction == XB_UNLOCK ){ + + if( !bFileLocked ) // already unlocked + return XB_NO_ERROR; + + if( dbf->GetLockFlavor() == LK_DBASE ){ + iRc = xbLock( XB_UNLOCK, LK4026531838, 1 ); + if( iRc != XB_NO_ERROR ){ + if( iRc == XB_LOCK_FAILED ) + return iRc; + else { + iErrorStop = 110; + throw iRc; + } + } else { + bFileLocked = xbFalse; + } + } + } else { + iErrorStop = 120; + iRc = XB_INVALID_OPTION; + throw iRc; + } + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbmemo::LockMemoFile() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return iRc; +} + +/***********************************************************************/ +//! @brief Get memo file lock status. +/*! + \returns xbTrue - Memo file is locked.
+ xbFalse - Memo file is not locked. +*/ +xbBool xbMemo::GetMemoLocked() const { + return bFileLocked; +} +#endif + +/***********************************************************************/ +//! @brief Update Next Node number in file header +/*! + \returns Return Codes +*/ +xbInt16 xbMemo::UpdateHeadNextNode(){ + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + try{ + char buf[4]; + ePutUInt32( buf, ulHdrNextBlock ); + if(( iRc = xbFseek( 0, SEEK_SET )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + if(( iRc = xbFwrite( &buf, 4, 1 )) != XB_NO_ERROR ){ + iErrorStop = 110; + throw iRc; + } + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbmemo::UpdateHeadeNextNode() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return iRc; +} +/***********************************************************************/ +} /* namespace */ +#endif /* XB_MEMO_SUPPORT */ + diff --git a/src/core/xbmemo3.cpp b/src/core/xbmemo3.cpp new file mode 100755 index 0000000..767e9d2 --- /dev/null +++ b/src/core/xbmemo3.cpp @@ -0,0 +1,585 @@ +/* xbmemo3.cpp + +XBase64 Software Library + +Copyright (c) 1997,2003,2014,2022,2023 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 + + This class is used for support dBASE V3 memo files + +*/ + +#include "xbase.h" + +#ifdef XB_MEMO_SUPPORT +#ifdef XB_DBF3_SUPPORT + +namespace xb{ + +/***********************************************************************/ +//! @brief Class Constructor. +/*! + \param dbf Pointer to dbf instance. + \param sFileName Memo file name. +*/ +xbMemoDbt3::xbMemoDbt3( xbDbf * dbf, xbString const & sFileName ) : xbMemo( dbf, sFileName ){ + iMemoFileType = 3; + SetBlockSize( 512 ); +} + +/***********************************************************************/ +//! @brief Class Deconstructor. +xbMemoDbt3::~xbMemoDbt3(){} + +/***********************************************************************/ +//! @brief Abort. +/*! + Abort any pending updates to the memo file. + \returns XB_NO_ERROR +*/ +xbInt16 xbMemoDbt3::Abort(){ + return XB_NO_ERROR; +}/***********************************************************************/ +//! @brief Commit changes to memo file. +/*! + \returns XB_NO_ERROR. +*/ +xbInt16 xbMemoDbt3::Commit(){ + return XB_NO_ERROR; +} +/***********************************************************************/ +//! @brief Create memo file. +/*! + \returns Return Codes +*/ + +xbInt16 xbMemoDbt3::CreateMemoFile(){ + + xbInt16 rc = XB_NO_ERROR; + char cBuf[4]; + if(( rc = xbFopen( "w+b", dbf->GetShareMode())) != XB_NO_ERROR ) + return rc; + ulHdrNextBlock = 1L; + ePutUInt32( cBuf, ulHdrNextBlock ); + if(( rc = xbFwrite( cBuf, 4, 1 ))!= XB_NO_ERROR ){ + xbFclose(); + return rc; + } + for(int i = 0; i < 12; i++ ) + xbFputc( 0x00 ); + xbFputc( 0x03 ); + for(int i = 0; i < 495; i++ ) + xbFputc( 0x00 ); + if(( mbb = (void *) malloc( 512 )) == NULL ){ + xbFclose(); + return XB_NO_MEMORY; + } + return XB_NO_ERROR; +} +/***********************************************************************/ +#ifdef XB_DEBUG_SUPPORT +//! @brief Dump memo file header. +/*! + \returns XB_NO_ERROR +*/ +xbInt16 xbMemoDbt3::DumpMemoFreeChain() { + std::cout << "Xbase version 3 file - no free block chain" << std::endl; + return XB_NO_ERROR; +} +#endif // XB_DEBUG_SUPPORT + +//! @brief Dump memo file header. +/*! + \returns XB_NO_ERROR +*/ +xbInt16 xbMemoDbt3::DumpMemoHeader(){ + xbInt16 rc = XB_NO_ERROR; + xbUInt64 stFileSize; + if(( rc = ReadDbtHeader( 1 )) != XB_NO_ERROR ) + return rc; + GetFileSize( stFileSize ); + std::cout << "Version 3 Memo Header Info" << std::endl; + std::cout << "Memo File Name = " << GetFqFileName() << std::endl; + std::cout << "Next Available Block = " << ulHdrNextBlock << std::endl; + std::cout << "Memo File Version = " << (xbInt16) cVersion << " ("; + BitDump( cVersion ); + std::cout << ")" << std::endl; + std::cout << "Block Size = " << GetBlockSize() << std::endl; + std::cout << "File Size = " << stFileSize << std::endl; + std::cout << "Block Count = " << stFileSize / GetBlockSize() << std::endl; + return XB_NO_ERROR; +} +/***********************************************************************/ +//! @brief Get a memo field for a given field number. +/*! + \param iFieldNo Field number to retrieve data for. + \param sMemoData Output - string containing memo field data. + \returns Return Codes +*/ + +xbInt16 xbMemoDbt3::GetMemoField( xbInt16 iFieldNo, xbString & sMemoData ){ + + xbInt16 iErrorStop = 0; + xbInt16 rc = XB_NO_ERROR; + xbUInt32 ulScnt; + char *sp, *spp; + xbUInt32 ulBlockNo; + xbBool bDone = xbFalse; + sMemoData = ""; + try{ + + if(( rc = dbf->GetULongField( iFieldNo, ulBlockNo )) < XB_NO_ERROR ){ + iErrorStop = 100; + throw rc; + } + + if( ulBlockNo == 0L ){ + sMemoData = ""; + return XB_NO_ERROR; + } + spp = NULL; + + while( !bDone ){ + if(( rc = ReadBlock( ulBlockNo++, GetBlockSize(), mbb )) != XB_NO_ERROR ){ + iErrorStop = 120; + throw rc; + } + ulScnt = 0; + sp = (char *) mbb; + while( ulScnt < 512 && !bDone ){ + if( *sp == 0x1a && *spp == 0x1a ) + bDone = xbTrue; + else{ + ulScnt++; spp = sp; sp++; + } + } + sMemoData.Append( (char *) mbb, ulScnt ); + } + sMemoData.ZapTrailingChar( 0x1a ); + } + catch (xbInt16 rc ){ + xbString sMsg; + sMsg.Sprintf( "xbMemoDbt3::GetMemoField() Exception Caught. Error Stop = [%d] rc = [%d]", iErrorStop, rc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( rc )); + } + return rc; +} +/***********************************************************************/ +//! @brief Get a memo field length for a given field number. +/*! + \param iFieldNo Field number to retrieve data for. + \param ulFieldLen Output - length of memo field data. + \returns Return Codes +*/ +xbInt16 xbMemoDbt3::GetMemoFieldLen( xbInt16 iFieldNo, xbUInt32 & ulFieldLen ){ + + xbInt16 iErrorStop = 0; + xbInt16 rc = XB_NO_ERROR; + xbInt16 iScnt; + char *sp, *spp; + xbUInt32 ulBlockNo; + xbInt16 iNotDone; + try{ + if(( rc = dbf->GetULongField( iFieldNo, ulBlockNo )) < XB_NO_ERROR ){ + iErrorStop = 100; + throw rc; + } + if( ulBlockNo == 0 ){ + ulFieldLen = 0; + return XB_NO_ERROR; + } + ulFieldLen = 0L; + spp = NULL; + iNotDone = 1; + while( iNotDone ){ + if(( rc = ReadBlock( ulBlockNo++, GetBlockSize(), mbb )) != XB_NO_ERROR ){ + iErrorStop = 110; + throw rc; + } + iScnt = 0; + sp = (char *) mbb; + while( iScnt < 512 && iNotDone ){ + if( *sp == 0x1a && *spp == 0x1a ) + iNotDone = 0; + else{ + ulFieldLen++; iScnt++; spp = sp; sp++; + } + } + } + if( ulFieldLen > 0 ) ulFieldLen--; + } + catch (xbInt16 rc ){ + xbString sMsg; + sMsg.Sprintf( "xbMemoDbt3::GetMemoFieldLen() Exception Caught. Error Stop = [%d] rc = [%d]", iErrorStop, rc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( rc )); + } + return rc; +} +/***********************************************************************/ +//! @brief Open memo file. +/*! + \returns Return Codes +*/ + +xbInt16 xbMemoDbt3::OpenMemoFile() { + xbInt16 rc = XB_NO_ERROR; + if(( rc = xbFopen( dbf->GetOpenMode(), dbf->GetShareMode())) != XB_NO_ERROR ) + return rc; + if(( mbb = (void *) malloc( 512 )) == NULL ){ + xbFclose(); + return XB_NO_MEMORY; + } + return XB_NO_ERROR; +} +/***********************************************************************/ +//! @brief Pack memo file. +/*! + This routine frees up any unused blocks in the file resulting from field updates. + Version 3 memo files do not reclaim unused space (Version 4 files do). + This routine cleans up the unused space. + \returns Return Codes +*/ +xbInt16 xbMemoDbt3::PackMemo( void (*memoStatusFunc ) ( xbUInt32 ulItemNum, xbUInt32 ulNumItems )) +{ + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + char * cBlock = NULL; + + #ifdef XB_LOCKING_SUPPORT + xbBool bTableLocked = xbFalse; + xbBool bMemoLocked = xbFalse; + #endif + + try{ + + #ifdef XB_LOCKING_SUPPORT + if( dbf->GetAutoLock() && !dbf->GetTableLocked() ){ + if(( iRc = dbf->LockTable( XB_LOCK )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } else { + bTableLocked = xbTrue; + } + if(( iRc = LockMemo( XB_LOCK )) != XB_NO_ERROR ){ + iErrorStop = 110; + throw iRc; + } else { + bMemoLocked = xbTrue; + } + } + #endif + + // create temp file + xbString sTempMemoName; + //if(( iRc = CreateUniqueFileName( GetDirectory(), "dbt", sTempMemoName )) != XB_NO_ERROR ){ + if(( iRc = CreateUniqueFileName( GetTempDirectory(), "DBT", sTempMemoName )) != XB_NO_ERROR ){ + iErrorStop = 120; + throw iRc; + } + + xbMemoDbt3 *pMemo = new xbMemoDbt3( dbf, sTempMemoName ); + if(( iRc = pMemo->CreateMemoFile()) != XB_NO_ERROR ){ + iErrorStop = 130; + throw iRc; + } + // for dbase III, block size is always 512, don't need to reset it + // for each record in dbf + xbUInt32 ulRecCnt; + if(( iRc = dbf->GetRecordCnt( ulRecCnt )) != XB_NO_ERROR ){ + iErrorStop = 140; + throw iRc; + } + xbInt32 lFldCnt = dbf->GetFieldCnt(); + char cFldType; + xbString sMemoFldData; + + for( xbUInt32 ulI = 1; ulI <= ulRecCnt; ulI++ ){ + + if(( iRc = dbf->GetRecord( ulI )) != XB_NO_ERROR ){ + iErrorStop = 150; + throw iRc; + } + if( (void *) memoStatusFunc ) + (*memoStatusFunc) ( ulI, ulRecCnt ); + + // for each memo field + for( xbInt32 lFc = 0; lFc < lFldCnt; lFc++ ){ + if(( iRc = dbf->GetFieldType( lFc, cFldType )) != XB_NO_ERROR ){ + iErrorStop = 160; + throw iRc; + } + + + if( cFldType == 'M' ){ + // copy it to work field + if(( iRc = dbf->GetMemoField( lFc, sMemoFldData )) != XB_NO_ERROR ){ + iErrorStop = 170; + throw iRc; + } + // write it to new field + if(( iRc = pMemo->UpdateMemoField( lFc, sMemoFldData )) != XB_NO_ERROR ){ + iErrorStop = 180; + throw iRc; + } + } + } + } + + //copy target back to source + xbUInt32 ulBlkSize = GetBlockSize(); + xbUInt64 ullFileSize; + if(( iRc = pMemo->GetFileSize( ullFileSize )) != XB_NO_ERROR ){ + iErrorStop = 190; + throw iRc; + } + // file size should be evenly divisible by block size + xbUInt32 ulBlkCnt; + + if( ullFileSize % ulBlkSize ){ + iErrorStop = 200; + throw iRc; + } else { + ulBlkCnt = (xbUInt32) (ullFileSize / ulBlkSize); + } + if(( iRc = xbTruncate( 0 )) != XB_NO_ERROR ){ + iErrorStop = 210; + throw iRc; + } + + if(( cBlock = (char *) malloc( (size_t) ulBlkSize )) == NULL ){ + iErrorStop = 220; + throw iRc; + } + + // can't rename files in a multiuser, cross platform environment, causes issues + // copy work table back to source table + for( xbUInt32 ulBc = 0; ulBc < ulBlkCnt; ulBc++ ){ + if(( iRc = pMemo->ReadBlock( ulBc, ulBlkSize, cBlock )) != XB_NO_ERROR ){ + iErrorStop = 230; + throw iRc; + } + if(( iRc = WriteBlock( ulBc, ulBlkSize, cBlock )) != XB_NO_ERROR ){ + iErrorStop = 240; + throw iRc; + } + } + + //close and delete target + if(( iRc = pMemo->xbFclose()) != XB_NO_ERROR ){ + iErrorStop = 250; + throw iRc; + } + + if(( iRc = pMemo->xbRemove()) != XB_NO_ERROR ){ + iErrorStop = 260; + throw iRc; + } + free( cBlock ); + delete pMemo; + } + catch (xbInt16 iRc ){ + free( cBlock ); + xbString sMsg; + sMsg.Sprintf( "xbMemoDbt3::PackMemo() Exception Caught. Error Stop = [%d] rc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + + #ifdef XB_LOCKING_SUPPORT + if( bTableLocked ) + dbf->LockTable( XB_UNLOCK ); + if( bMemoLocked ) + LockMemo( XB_UNLOCK ); + #endif + return iRc; +} + +/***********************************************************************/ +//! @brief Read dbt header file. +/*! + \param iOption 0 --> read only first four bytes
+ 1 --> read the entire thing + \returns Return Codes +*/ + +xbInt16 xbMemoDbt3::ReadDbtHeader( xbInt16 iOption ){ + char *p; + char MemoBlock[20]; + xbInt16 rc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + xbUInt32 ulReadSize; + + try{ + if( !FileIsOpen() ){ + iErrorStop = 100; + rc = XB_NOT_OPEN; + throw rc; + } + if( iOption == 0 ) + ulReadSize = 4; + else{ + xbUInt64 stFileSize = 0; + if(( rc = GetFileSize( stFileSize )) != XB_NO_ERROR ){ + iErrorStop = 110; + throw rc; + } + if( stFileSize < 4 ){ + iErrorStop = 120; + rc = XB_INVALID_BLOCK_NO; + throw rc; + } + else if( stFileSize > 20 ) + ulReadSize = 130; + else + ulReadSize = 4; + } + if( xbFseek( 0, SEEK_SET )){ + iErrorStop = 140; + rc = XB_SEEK_ERROR; + throw rc; + } + if(( xbFread( &MemoBlock, ulReadSize, 1 )) != XB_NO_ERROR ){ + iErrorStop = 150; + rc = XB_READ_ERROR; + throw rc; + } + p = MemoBlock; + ulHdrNextBlock = eGetUInt32( p ); + + if( iOption == 0) + return XB_NO_ERROR; + + if( ulReadSize >= 20 ){ + p+=16; + cVersion = *p; + } + } + catch (xbInt16 rc ){ + xbString sMsg; + sMsg.Sprintf( "xbMemoDbt3::ReadDbtHeader() Exception Caught. Error Stop = [%d] rc = [%d]", iErrorStop, rc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( rc )); + } + return rc; +} +/***********************************************************************/ +//! @brief Update header name. +/*! + \returns XB_NO_ERROR +*/ +xbInt16 xbMemoDbt3::UpdateHeaderName(){ + return XB_NO_ERROR; +} +/***********************************************************************/ +//! @brief Update a memo field for a given field number. +/*! + \param iFieldNo Field number to update data for. + \param sMemoData Data to update memo field data with. + \returns Return Codes +*/ + +xbInt16 xbMemoDbt3::UpdateMemoField( xbInt16 iFieldNo, const xbString & sMemoData ) { + + xbInt16 iErrorStop = 0; + xbInt16 rc = XB_NO_ERROR; + try{ + if( sMemoData == "" ){ + if(( rc = dbf->PutField( iFieldNo, "" )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw rc; + } + } else { + xbUInt32 ulDataLen = sMemoData.Len() + 2; + xbUInt32 ulBlocksNeeded = (ulDataLen / 512) + 1; + xbUInt32 ulLastDataBlock; + if(( rc = CalcLastDataBlock( ulLastDataBlock )) != XB_NO_ERROR ){ + iErrorStop = 110; + throw rc; + } + if(( rc = xbFseek( ((xbInt64) ulLastDataBlock * 512), SEEK_SET )) != XB_NO_ERROR ){ + iErrorStop = 120; + throw rc; + } + if(( rc = xbFwrite( sMemoData.Str(), sMemoData.Len(), 1 )) != XB_NO_ERROR ){ + iErrorStop = 130; + throw rc; + } + if(( rc = xbFputc( 0x1a, 2 )) != XB_NO_ERROR ){ + iErrorStop = 140; + throw rc; + } + if(( rc = xbFputc( 0x00, (xbInt32) ( ulBlocksNeeded * 512 ) - (xbInt32) ulDataLen )) != XB_NO_ERROR ){ + iErrorStop = 150; + throw rc; + } + if(( rc = dbf->PutULongField( iFieldNo, ulLastDataBlock )) != XB_NO_ERROR ){ + iErrorStop = 160; + throw rc; + } + ulHdrNextBlock = ulLastDataBlock + ulBlocksNeeded; + if(( rc = UpdateHeadNextNode()) != XB_NO_ERROR ){ + iErrorStop = 170; + throw rc; + } + } + } + catch (xbInt16 rc ){ + xbString sMsg; + sMsg.Sprintf( "xbMemoDbt3::UpdateMemoField() Exception Caught. Error Stop = [%d] rc = [%d]", iErrorStop, rc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( rc )); + } + return rc; +} + +/***********************************************************************/ +//! @brief Empty the memo file. +/*! + This routine clears everything out of the file. It does not address the + block pointers on the dbf file. + \returns Return Codes +*/ +xbInt16 xbMemoDbt3::Zap(){ + + xbInt16 iRc = 0; + xbInt16 iErrorStop = 0; + char cBuf[4]; + + try{ + ulHdrNextBlock = 1L; + ePutUInt32( cBuf, ulHdrNextBlock ); + + if(( iRc != xbFseek( 0, SEEK_SET )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + if(( iRc != xbFwrite( cBuf, 4, 1 ))!= XB_NO_ERROR ){ + iErrorStop = 110; + throw iRc; + } + if(( iRc != xbTruncate( 512 )) != XB_NO_ERROR ){ + iErrorStop = 120; + throw iRc; + } + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbMemoDbt3::Zap() Exception Caught. Error Stop = [%d] rc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return iRc; +} + +/***********************************************************************/ +} /* namespace */ +#endif /* XB_DBF3_SUPPORT */ +#endif /* XB_MEMO_SUPPORT */ diff --git a/src/core/xbmemo4.cpp b/src/core/xbmemo4.cpp new file mode 100755 index 0000000..9770806 --- /dev/null +++ b/src/core/xbmemo4.cpp @@ -0,0 +1,1336 @@ +/* xbmemo4.cpp + +XBase64 Software Library + +Copyright (c) 1997,2003,2014,2022,2023 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 + + This class is used for support dBASE V4 memo files + +*/ + +#include "xbase.h" + +#ifdef XB_MEMO_SUPPORT +#ifdef XB_DBF4_SUPPORT + +namespace xb{ + +/***********************************************************************/ +//! @brief Class Constructor. +/*! + \param dbf Pointer to dbf instance. + \param sFileName Memo file name. +*/ +xbMemoDbt4::xbMemoDbt4( xbDbf * dbf, xbString const & sFileName ) : xbMemo( dbf, sFileName ){ + iMemoFileType = 4; + SetBlockSize( dbf->GetCreateMemoBlockSize() ); +} + +/***********************************************************************/ +//! @brief Class Deconstructor. +xbMemoDbt4::~xbMemoDbt4(){} + +/***********************************************************************/ +//! @brief Abort. +/*! + Abort any pending updates to the memo file. + \returns XB_NO_ERROR +*/ +xbInt16 xbMemoDbt4::Abort(){ + + xbInt16 rc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + xbUInt32 ulBlockNo; + + try{ + xbUInt32 ulNodeCnt = llNewBlocks.GetNodeCnt(); + for( xbUInt32 l = 0; l < ulNodeCnt; l++ ){ + if(( rc = llNewBlocks.RemoveFromFront( ulBlockNo )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw rc; + } + if(( rc = FreeMemoBlockChain( ulBlockNo )) != XB_NO_ERROR ){ + iErrorStop = 110; + throw rc; + } + } + llOldBlocks.Clear(); + } + + catch (xbInt16 rc ){ + xbString sMsg; + sMsg.Sprintf( "xbMemoDbt4::Abort() Exception Caught. Error Stop = [%d] rc = [%d]", iErrorStop, rc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( rc )); + } + return rc; +} + +/***********************************************************************/ +//! @brief Commit changes to memo file. +/*! + Commit any pending updates to the memo file. + \returns XB_NO_ERROR. +*/ +xbInt16 xbMemoDbt4::Commit(){ + + xbInt16 rc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + xbUInt32 ulBlockNo; + + try{ + xbUInt32 ulNodeCnt = llOldBlocks.GetNodeCnt(); + for( xbUInt32 l = 0; l < ulNodeCnt; l++ ){ + if(( rc = llOldBlocks.RemoveFromFront( ulBlockNo )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw rc; + } + if(( rc = FreeMemoBlockChain( ulBlockNo )) != XB_NO_ERROR ){ + iErrorStop = 110; + throw rc; + } + } + llNewBlocks.Clear(); + } + catch (xbInt16 rc ){ + xbString sMsg; + sMsg.Sprintf( "xbMemoDbt4::Commit() Exception Caught. Error Stop = [%d] rc = [%d]", iErrorStop, rc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( rc )); + } + return rc; +} +/***********************************************************************/ +//! @brief Create memo file. +/*! + \returns Return Codes +*/ + +xbInt16 xbMemoDbt4::CreateMemoFile(){ + + xbInt16 iErrorStop = 0; + xbInt16 rc = XB_NO_ERROR; + char cBuf[4]; + + try{ + if(( rc = xbFopen( "w+b", dbf->GetShareMode() )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw rc; + } + + ulHdrNextBlock = 1L; + ePutUInt32( cBuf, ulHdrNextBlock ); + if(( rc = xbFwrite( cBuf, 4, 1 ))!= XB_NO_ERROR ){ + iErrorStop = 110; + xbFclose(); + throw rc; + } + for(int i = 0; i < 4; i++ ) + xbFputc( 0x00 ); + GetFileNamePart( sDbfFileNameWoExt ); + sDbfFileNameWoExt.PadRight( ' ', 8 ); // need at least eight bytes of name + sDbfFileNameWoExt = sDbfFileNameWoExt.Mid( 1, 8 ); // need no more than eight bytes of name + for( int i = 1; i < 9; i++ ) + xbFputc( sDbfFileNameWoExt[i] ); + + for(int i = 0; i < 4; i++ ) + xbFputc( 0x00 ); + + ePutInt16( cBuf, GetBlockSize()); + if(( rc = xbFwrite( cBuf, 2, 1 ))!= XB_NO_ERROR ){ + iErrorStop = 120; + xbFclose(); + throw rc; + } + for( xbUInt32 i = 0; i < (GetBlockSize() - 22); i++ ) + xbFputc( 0x00 ); + if(( mbb = (void *) malloc( GetBlockSize())) == NULL ){ + rc = XB_NO_MEMORY; + iErrorStop = 130; + return XB_NO_MEMORY; + } + } + catch (xbInt16 rc ){ + xbString sMsg; + sMsg.Sprintf( "xbMemoDbt4::CreateMemoFile() Exception Caught. Error Stop = [%d] rc = [%d]", iErrorStop, rc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( rc )); + xbFclose(); + } + return rc; +} +/***********************************************************************/ +#ifdef XB_DEBUG_SUPPORT +//! @brief Dump memo file header. +/*! + \returns XB_NO_ERROR +*/ + +xbInt16 xbMemoDbt4::DumpMemoFreeChain() +{ + xbInt16 rc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + xbUInt32 ulCurBlock, ulLastDataBlock; + + try{ + if(( rc = ReadDbtHeader(1)) != XB_NO_ERROR ){ + iErrorStop = 100; + throw rc; + } + if(( rc = CalcLastDataBlock( ulLastDataBlock )) != XB_NO_ERROR ){ + iErrorStop = 110; + throw rc; + } + + ulCurBlock = ulHdrNextBlock; + std::cout << "**********************************" << std::endl; + std::cout << "Head Node Next Block = " << ulCurBlock << std::endl;; + std::cout << "Total blocks in file = " << ulLastDataBlock << std::endl; + + while( ulCurBlock < ulLastDataBlock ){ + if(( rc = ReadBlockHeader( ulCurBlock, 2 )) != XB_NO_ERROR ){ + iErrorStop = 120; + throw rc; + } + std::cout << "**********************************" << std::endl; + std::cout << "This Free Block = [" << ulCurBlock << "] contains [" << ulFreeBlockCnt << "] block(s)" << std::endl; + std::cout << "Next Free Block = [" << ulNextFreeBlock << "]" << std::endl; + ulCurBlock = ulNextFreeBlock; + } + } + catch (xbInt16 rc ){ + xbString sMsg; + sMsg.Sprintf( "xbMemoDbt4::UpdateMemoField() Exception Caught. Error Stop = [%d] rc = [%d]", iErrorStop, rc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( rc )); + } + return XB_NO_ERROR; +} + +//! @brief Dump memo internals. +/*! + \returns XB_NO_ERROR +*/ + +xbInt16 xbMemoDbt4::DumpMemoInternals() { + + xbLinkListNode *llPtr; + xbInt16 iNodeCnt; + + llPtr = llOldBlocks.GetHeadNode(); + iNodeCnt = llOldBlocks.GetNodeCnt(); + + std::cout << "Link List Old Blocks - " << iNodeCnt << " nodes" << std::endl; + for( xbInt16 i = 0; i < iNodeCnt; i++ ){ + std::cout << llPtr->GetKey() << ","; + llPtr = llPtr->GetNextNode(); + } + std::cout << std::endl; + + llPtr = llNewBlocks.GetHeadNode(); + iNodeCnt = llNewBlocks.GetNodeCnt(); + std::cout << "Link List New Blocks - " << iNodeCnt << " nodes" << std::endl; + for( xbInt16 i = 0; i < iNodeCnt; i++ ){ + std::cout << llPtr->GetKey() << ","; + llPtr = llPtr->GetNextNode(); + } + std::cout << std::endl; + + return XB_NO_ERROR; +} +#endif // XB_DEBUG_SUPPORT + +/***********************************************************************/ +//! @brief Dump memo file header. +/*! + \returns XB_NO_ERROR +*/ +xbInt16 xbMemoDbt4::DumpMemoHeader(){ + + xbInt16 rc = XB_NO_ERROR; + xbUInt32 ulLastDataBlock; + CalcLastDataBlock( ulLastDataBlock ); + + if(( rc = ReadDbtHeader( 1 )) != XB_NO_ERROR ) + return rc; + std::cout << "Version 4 Memo Header Info" << std::endl; + std::cout << "Memo File Name = " << GetFqFileName() << std::endl; + std::cout << "Hdr Next Avail Block = " << ulHdrNextBlock << std::endl; + std::cout << "Block Size = " << GetBlockSize() << std::endl; + std::cout << "Dbf File Name wo Ext = " << sDbfFileNameWoExt.Str() << std::endl; + std::cout << "Last Data Block = " << ulLastDataBlock << std::endl; + return rc; +} + +/************************************************************************/ +//! @brief Find an empty set of blocks in the free block chain +/*! + This routine searches thruugh the free node chain in a dbase IV type + memo file searching for a place to grab some free blocks for reuse + + \param ulBlocksNeeded The size to look in the chain for. + \param ulLastDataBlock is the last data block in the file, enter 0 + for the routine to calculate it. + \param ulLocation The location it finds. + \param ulPreviousNode Block number of the node immediately previous to this node in the chain.
+ 0 if header node + \param bFound Output xbFalse - Spot not found in chain.
+ xbTrue - Spot found in chain. + \returns Return Codes +*/ +xbInt16 xbMemoDbt4::FindBlockSetInChain( xbUInt32 ulBlocksNeeded, + xbUInt32 &ulLastDataBlock, xbUInt32 &ulLocation, xbUInt32 &ulPrevNode, xbBool &bFound ){ + xbInt16 rc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + xbUInt32 ulCurNode; + + try{ + ulPrevNode = 0L; + if( ulLastDataBlock == 0 ){ + /* Determine last good data block */ + if(( rc = CalcLastDataBlock( ulLastDataBlock )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw rc; + } + } + if( ulHdrNextBlock < ulLastDataBlock ){ + ulCurNode = ulHdrNextBlock; + + if(( rc = ReadBlockHeader( ulHdrNextBlock, 2 )) != XB_NO_ERROR ){ + iErrorStop = 110; + throw rc; + } + while( ulBlocksNeeded > ulFreeBlockCnt && ulNextFreeBlock < ulLastDataBlock ){ + ulPrevNode = ulCurNode; + ulCurNode = ulNextFreeBlock; + if(( rc = ReadBlockHeader( ulNextFreeBlock, 2 )) != XB_NO_ERROR ){ + iErrorStop = 120; + throw rc; + } + } + if( ulBlocksNeeded <= ulFreeBlockCnt ){ + ulLocation = ulCurNode; + // PreviousNode = lPrevNode; + bFound = xbTrue; + } else { // no data found and at end of chain + ulPrevNode = ulCurNode; + bFound = xbFalse; + } + } else { + bFound = xbFalse; + } + } + catch (xbInt16 rc ){ + xbString sMsg; + sMsg.Sprintf( "xbMemoDbt4::FindBlockSetInChain() Exception Caught. Error Stop = [%d] rc = [%d]", iErrorStop, rc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( rc )); + } + return rc; +} +/***********************************************************************/ +//! @brief Free a block. +/*! + \returns Return Codes +*/ + +xbInt16 xbMemoDbt4::FreeMemoBlockChain( xbUInt32 ulBlockNo ){ + xbUInt32 ulLastDataBlock; + return FreeMemoBlockChain( ulBlockNo, ulLastDataBlock ); +} + +/***********************************************************************/ +//! @brief Free a block. +/*! + \param ulBlockNo The block number being deleted. + \param ulLastDataBlock Output - Last free block number,prior to this block. + \returns Return Codes +*/ +xbInt16 xbMemoDbt4::FreeMemoBlockChain( xbUInt32 ulBlockNo, xbUInt32 &ulLastDataBlock ) +{ + xbInt16 rc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + xbUInt32 ulNoOfFreedBlocks; + xbUInt32 ulLastFreeBlock = 0; + xbUInt32 ulLastFreeBlockCnt = 0; + xbUInt32 ulSaveNextFreeBlock; + + // iFieldNo - The field no m\bing deleted + // iBlockNo - The block number being deleted + // iNoOfFreeBlocks - The number of blocks being freed with this delete + // iLastDataBlock - The next block number to allocate if more blocks needed + // iHdrNextBlock - The head pointer in the main header block + // iNextFreeBlock - The block that is immediately following the current free block to be added + // iLastFreeBlock - Last free block number,prior to this block + // iLastFreeBlockCnt - Last free block number of blocks + + try{ + + if( ulBlockNo <= 0 ){ + iErrorStop = 100; + rc =XB_INVALID_BLOCK_NO; + throw rc; + } + + /* Load the first block */ + if(( rc = ReadBlockHeader( ulBlockNo, 1 )) != XB_NO_ERROR ){ + iErrorStop = 110; + throw rc; + } + + if( (ulFieldLen) % GetBlockSize() ) + ulNoOfFreedBlocks = ((ulFieldLen) / GetBlockSize()) + 1L; + else + ulNoOfFreedBlocks = (ulFieldLen) / GetBlockSize(); + + /* Determine last good data block */ + if(( rc = CalcLastDataBlock( ulLastDataBlock )) != XB_NO_ERROR ){ + iErrorStop = 120; + throw rc; + } + + if(( rc = ReadDbtHeader( 0 )) != XB_NO_ERROR ){ + iErrorStop = 130; + throw rc; + } + + // Not an empty node chain, position to correct location in chain + ulNextFreeBlock = ulHdrNextBlock; + while( ulBlockNo > ulNextFreeBlock && ulBlockNo < ulLastDataBlock ){ + ulLastFreeBlock = ulNextFreeBlock; + + if(( rc = ReadBlockHeader( ulNextFreeBlock, 2 )) != XB_NO_ERROR ){ + iErrorStop = 140; + return rc; + } + ulLastFreeBlockCnt = ulFreeBlockCnt; + } + + // One of two outcomes at this point + // A) This block is combined with the next free block chain, and points to the free chain after the next free block + // B) This block is not combined with the next free block chain, and points to the next block + // (which could be the last block + + // should next block should be concatonated onto the end of this set? + ulSaveNextFreeBlock = ulNextFreeBlock; + if(( ulBlockNo + ulNoOfFreedBlocks ) == ulNextFreeBlock && ulNextFreeBlock < ulLastDataBlock ){ + if(( rc = ReadBlockHeader( ulNextFreeBlock, 2 )) != XB_NO_ERROR ){ + iErrorStop = 150; + throw rc; + } + ulNoOfFreedBlocks += ulFreeBlockCnt; + ulSaveNextFreeBlock = ulNextFreeBlock; + } + + // if this is the first set of free blocks + if( ulLastFreeBlock == 0 ){ + // 1 - write out the current block + // 2 - update header block + // 3 - write header block + // 4 - update data field + + ePutUInt32( (char *) mbb, ulSaveNextFreeBlock ); + ePutUInt32( (char *) mbb+4, ulNoOfFreedBlocks ); + if(( rc = WriteBlock( ulBlockNo, 8, mbb )) != XB_NO_ERROR ){ + iErrorStop = 160; + throw rc; + } + + ulHdrNextBlock = ulBlockNo; + if(( rc = UpdateHeadNextNode()) != XB_NO_ERROR ){ + iErrorStop = 170; + throw rc; + } + return XB_NO_ERROR; + } + + /* determine if this block set should be added to the previous set */ + if(( ulLastFreeBlockCnt + ulLastFreeBlock ) == ulBlockNo ){ + if(( rc = ReadBlockHeader( ulLastFreeBlock, 2 )) != XB_NO_ERROR ){ + iErrorStop = 180; + throw rc; + } + ulFreeBlockCnt += ulNoOfFreedBlocks; + + ePutUInt32( (char *) mbb, ulSaveNextFreeBlock ); + ePutUInt32( (char *) mbb+4, ulFreeBlockCnt ); + if(( rc = WriteBlock( ulLastFreeBlock, 8, mbb )) != XB_NO_ERROR ){ + iErrorStop = 190; + throw rc; + } + return XB_NO_ERROR; + } + + /* insert into the chain */ + /* 1 - set the next bucket on the current node */ + /* 2 - write this node */ + /* 3 - go to the previous node */ + /* 4 - insert this nodes id into the previous node set */ + /* 5 - write previous node */ + + ePutUInt32( (char *) mbb, ulSaveNextFreeBlock ); + ePutUInt32( (char *) mbb+4, ulNoOfFreedBlocks ); + if(( rc = WriteBlock( ulBlockNo, 8, mbb )) != XB_NO_ERROR ){ + iErrorStop = 200; + throw rc; + } + + if(( rc = ReadBlockHeader( ulLastFreeBlock, 2 )) != XB_NO_ERROR ){ + iErrorStop = 210; + throw rc; + } + + ePutUInt32( (char *) mbb, ulBlockNo ); + if(( rc = WriteBlock( ulLastFreeBlock, 8, mbb )) != XB_NO_ERROR ){ + iErrorStop = 220; + throw rc; + } + } + catch (xbInt16 rc ){ + xbString sMsg; + sMsg.Sprintf( "xbMemoDbt4::DeleteMemoField() Exception Caught. Error Stop = [%d] rc = [%d]", iErrorStop, rc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( rc )); + } + return rc; +} +/************************************************************************/ +//! @brief Get a set of blocks from the free block chain. +/*! + This routine grabs a set of blocks out of the free block chain. + + \param ulBlocksNeeded The number of blocks requested. + \param ulLocation + \param ulPrevNode + \returns Return Codes +*/ + + +xbInt16 xbMemoDbt4::GetBlockSetFromChain( xbUInt32 ulBlocksNeeded, + xbUInt32 ulLocation, xbUInt32 ulPrevNode ) +{ + xbInt16 rc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + xbUInt32 ulNextFreeBlock2; + xbUInt32 ulNewFreeBlocks; + xbUInt32 ulSaveNextFreeBlock; + + try{ + if(( rc = ReadBlockHeader( ulLocation, 2 )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw rc; + } + + if( ulBlocksNeeded == ulFreeBlockCnt ){ // grab this whole set of blocks + if( ulPrevNode == 0 ){ // first in the chain + ulHdrNextBlock = ulNextFreeBlock; + if(( rc = UpdateHeadNextNode()) != XB_NO_ERROR ){ + iErrorStop = 110; + throw rc; + } + } + else // remove out of the middle or end + { + ulNextFreeBlock2 = ulNextFreeBlock; + if(( rc = ReadBlockHeader( ulPrevNode, 2 )) != XB_NO_ERROR ){ + iErrorStop = 120; + throw rc; + } + ulNextFreeBlock = ulNextFreeBlock2; + if(( rc = WriteBlockHeader( ulPrevNode, 2 )) != XB_NO_ERROR ){ + iErrorStop = 130; + throw rc; + } + } + } else { // only take a portion of this set + if( ulPrevNode == 0 ){ // first in the set + ulHdrNextBlock = ulLocation + ulBlocksNeeded; + if(( rc = UpdateHeadNextNode()) != XB_NO_ERROR ){ + iErrorStop = 140; + throw rc; + } + ulFreeBlockCnt -= ulBlocksNeeded; + if(( rc = WriteBlockHeader( ulHdrNextBlock, 2 )) != XB_NO_ERROR ){ + iErrorStop = 150; + throw rc; + } + } + else { // remove out of the middle or end + ulNewFreeBlocks = ulFreeBlockCnt - ulBlocksNeeded; + ulSaveNextFreeBlock = ulNextFreeBlock; + ulNextFreeBlock2 = ulLocation + ulBlocksNeeded; + + if(( rc = ReadBlockHeader( ulPrevNode, 2 )) != XB_NO_ERROR ){ + iErrorStop = 160; + throw rc; + } + ulNextFreeBlock = ulNextFreeBlock2; + if(( rc = WriteBlockHeader( ulPrevNode, 2 )) != XB_NO_ERROR ){ + iErrorStop = 170; + throw rc; + } + ulFreeBlockCnt = ulNewFreeBlocks; + ulNextFreeBlock = ulSaveNextFreeBlock; + if(( rc = WriteBlockHeader( ulNextFreeBlock2, 2 )) != XB_NO_ERROR ){ + iErrorStop = 180; + throw rc; + } + } + } + } + catch (xbInt16 rc ){ + xbString sMsg; + sMsg.Sprintf( "xbMemoDbt4::GetBlockSetFromChain() Exception Caught. Error Stop = [%d] rc = [%d]", iErrorStop, rc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( rc )); + } + return rc; +} +/***********************************************************************/ +//! @brief Get a memo field for a given field number. +/*! + \param iFieldNo Field number to retrieve data for. + \param sMemoData Output - string containing memo field data. + \returns Return Codes +*/ + +xbInt16 xbMemoDbt4::GetMemoField( xbInt16 iFieldNo, xbString & sMemoData ){ + + xbUInt32 ulBlockNo; + xbUInt32 ulMemoFieldLen; + xbUInt32 ulMemoFieldDataLen; + xbInt16 rc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + char *p = NULL; + + try{ + if(( rc = GetMemoFieldLen( iFieldNo, ulMemoFieldLen, ulBlockNo )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw rc; + } + + if( ulBlockNo == 0L || ulMemoFieldLen == 0L ) + sMemoData = ""; + else{ + ulMemoFieldDataLen = ulMemoFieldLen - 8; + + if(( p = (char *)calloc(1, ulMemoFieldDataLen+1)) == NULL ){ + iErrorStop = 110; + rc = XB_NO_MEMORY; + xbString sMsg; + sMsg.Sprintf( "xbMemoDbt4::GetMemoField() lBlockNo = %ld Data Len = [%ld]", ulBlockNo, ulMemoFieldDataLen + 1 ); + xbase->WriteLogMessage( sMsg.Str() ); + throw rc; + } + + // go to the first block of the memo field, skip past the first 8 bytes + if(( xbFseek( ( ulBlockNo * GetBlockSize() + 8 ), SEEK_SET )) != XB_NO_ERROR ){ + iErrorStop = 120; + rc = XB_SEEK_ERROR; + throw rc; + } + + // read the memo file data into buffer pointed to by "p" + if(( rc = xbFread( p, ulMemoFieldDataLen, 1 )) != XB_NO_ERROR ){ + iErrorStop = 130; + rc = XB_READ_ERROR; + throw rc; + } + // null terminate the string + char *p2; + p2 = p + ulMemoFieldDataLen; + *p2 = 0x00; + + // save it to the string + sMemoData.Set( p, ulMemoFieldDataLen + 1 ); + free( p ); + } + } + catch (xbInt16 rc ){ + xbString sMsg; + sMsg.Sprintf( "xbMemoDbt4::GetMemoField() Exception Caught. Error Stop = [%d] rc = [%d]", iErrorStop, rc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( rc )); + if( p ) + free( p ); + } + return rc; +} + +/***********************************************************************/ +//! @brief Get a memo field length for a given field number. +/*! + \param iFieldNo Field number to retrieve data for. + \param ulMemoFieldLen Output - length of memo field data. + \returns Return Codes +*/ +xbInt16 xbMemoDbt4::GetMemoFieldLen( xbInt16 iFieldNo, xbUInt32 &ulMemoFieldLen ){ + xbUInt32 ulBlockNo; + return GetMemoFieldLen( iFieldNo, ulMemoFieldLen, ulBlockNo ); +} + +/***********************************************************************/ +//! @brief Get a memo field length for a given field number. +/*! + \param iFieldNo Field number to retrieve data for. + \param ulMemoFieldLen Output - length of memo field data. + \param ulBlockNo Output - Starting block number. + \returns Return Codes +*/ + +xbInt16 xbMemoDbt4::GetMemoFieldLen( xbInt16 iFieldNo, xbUInt32 &ulMemoFieldLen, xbUInt32 &ulBlockNo ){ + + xbInt16 rc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + char cFieldType; + + try{ + + if(( rc = dbf->GetFieldType( iFieldNo, cFieldType )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw rc; + } + if( cFieldType != 'M' ){ + iErrorStop = 110; + rc = XB_INVALID_MEMO_FIELD; + throw rc; + } + if(( rc = dbf->GetULongField( iFieldNo, ulBlockNo )) < XB_NO_ERROR ){ + iErrorStop = 120; + throw rc; + } + if( ulBlockNo < 1 ){ + ulMemoFieldLen = 0; + return XB_NO_ERROR; + } + if(( rc = ReadBlockHeader( ulBlockNo, 1 )) != XB_NO_ERROR ){ + iErrorStop = 130; + throw rc; + } + ulMemoFieldLen = ulFieldLen; + } + catch (xbInt16 rc ){ + xbString sMsg; + sMsg.Sprintf( "xbMemoDbt4::GetMemoFieldLen() Exception Caught. Error Stop = [%d] rc = [%d]", iErrorStop, rc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( rc )); + } + return rc; +} +/***********************************************************************/ +//! @brief Open memo file. +/*! + \returns Return Codes +*/ + +xbInt16 xbMemoDbt4::OpenMemoFile() { + + xbInt16 iErrorStop = 0; + xbInt16 rc = XB_NO_ERROR; + + try{ + if(( rc = xbFopen( dbf->GetOpenMode(), dbf->GetShareMode())) != XB_NO_ERROR ){ + iErrorStop = 100; + throw rc; + } + if(( rc = ReadDbtHeader( 1 )) != XB_NO_ERROR ){ + iErrorStop = 110; + throw rc; + } + if(( mbb = (void *) malloc( GetBlockSize())) == NULL ){ + xbFclose(); + iErrorStop = 120; + rc = XB_NO_MEMORY; + throw rc; + } + } + catch (xbInt16 rc ){ + xbString sMsg; + sMsg.Sprintf( "xbMemoDbt4::OpenMemoFile() Exception Caught. Error Stop = [%d] rc = [%d]", iErrorStop, rc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( rc )); + } + return rc; +} +/***********************************************************************/ +//! @brief Pack memo file. +/*! + This routine frees up any unused blocks in the file resulting from field updates. + Version 3 memo files do not reclaim unused space (Version 4 files do). + This routine cleans up the unused space. + \returns Return Codes +*/ + +xbInt16 xbMemoDbt4::PackMemo( void (*memoStatusFunc ) ( xbUInt32 ulItemNum, xbUInt32 ulNumItems )){ + xbInt16 iRc = 0; + xbInt16 iErrorStop = 0; + char * cBlock = NULL; + + #ifdef XB_LOCKING_SUPPORT + xbBool bTableLocked = xbFalse; + xbBool bMemoLocked = xbFalse; + #endif + + try{ + #ifdef XB_LOCKING_SUPPORT + if( dbf->GetAutoLock() && !dbf->GetTableLocked() ){ + if(( iRc = dbf->LockTable( XB_LOCK )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } else { + bTableLocked = xbTrue; + } + if(( iRc = LockMemo( XB_LOCK )) != XB_NO_ERROR ){ + iErrorStop = 110; + throw iRc; + } else { + bMemoLocked = xbTrue; + } + } + #endif + + // create temp file + xbString sTempMemoName; + //if(( iRc = CreateUniqueFileName( GetDirectory(), "dbt", sTempMemoName )) != XB_NO_ERROR ){ + if(( iRc = CreateUniqueFileName( GetTempDirectory(), "DBT", sTempMemoName )) != XB_NO_ERROR ){ + iErrorStop = 120; + throw iRc; + } + + xbMemoDbt4 *pMemo = new xbMemoDbt4( dbf, sTempMemoName ); + if(( iRc = pMemo->CreateMemoFile()) != XB_NO_ERROR ){ + iErrorStop = 130; + throw iRc; + } + // for dbase III, block size is always 512, don't need to reset it + // for each record in dbf + xbUInt32 ulRecCnt; + if(( iRc = dbf->GetRecordCnt( ulRecCnt)) != XB_NO_ERROR ){ + iErrorStop = 140; + throw iRc; + } + xbInt32 lFldCnt = dbf->GetFieldCnt(); + char cFldType; + xbString sMemoFldData; + + for( xbUInt32 ulI = 1; ulI <= ulRecCnt; ulI++ ){ + if(( iRc = dbf->GetRecord( ulI )) != XB_NO_ERROR ){ + iErrorStop = 150; + throw iRc; + } + + if( (void *) memoStatusFunc) + (*memoStatusFunc)(ulI, ulRecCnt ); + + // for each memo field + for( xbInt32 lFc = 0; lFc < lFldCnt; lFc++ ){ + if(( iRc = dbf->GetFieldType( lFc, cFldType )) != XB_NO_ERROR ){ + iErrorStop = 160; + throw iRc; + } + if( cFldType == 'M' ){ + // copy it to work field + if(( iRc = dbf->GetMemoField( lFc, sMemoFldData )) != XB_NO_ERROR ){ + iErrorStop = 170; + throw iRc; + } + // write it to new field + if(( iRc = pMemo->UpdateMemoField( lFc, sMemoFldData )) != XB_NO_ERROR ){ + iErrorStop = 180; + throw iRc; + } + } + } + } + + //copy target back to source + xbUInt32 ulBlkSize = GetBlockSize(); + xbUInt64 ullFileSize; + if(( iRc = pMemo->GetFileSize( ullFileSize )) != XB_NO_ERROR ){ + iErrorStop = 190; + throw iRc; + } + // file size should be evenly divisible by block size + xbUInt32 ulBlkCnt; + + if( ullFileSize % ulBlkSize ){ + iErrorStop = 200; + throw iRc; + } else { + ulBlkCnt = (xbUInt32) (ullFileSize / ulBlkSize); + } + if(( iRc = xbTruncate( 0 )) != XB_NO_ERROR ){ + iErrorStop = 210; + throw iRc; + } + + if(( cBlock = (char *) malloc( ulBlkSize )) == NULL ){ + iErrorStop = 220; + throw iRc; + } + + // can't rename files in a multiuser, cross platform environment, causes issues + // copy work table back to source table + + for( xbUInt32 ulBc = 0; ulBc < ulBlkCnt; ulBc++ ){ + if(( iRc = pMemo->ReadBlock( ulBc, ulBlkSize, cBlock )) != XB_NO_ERROR ){ + iErrorStop = 230; + throw iRc; + } + if(( iRc = WriteBlock( ulBc, ulBlkSize, cBlock )) != XB_NO_ERROR ){ + iErrorStop = 240; + throw iRc; + } + } + + if(( xbFseek( 8, SEEK_SET )) != XB_NO_ERROR ){ + iErrorStop = 250; + iRc = XB_SEEK_ERROR; + throw iRc; + } + + for( int i = 1; i < 9; i++ ) + xbFputc( sDbfFileNameWoExt[i] ); + + //close and delete target + if(( iRc = pMemo->xbFclose()) != XB_NO_ERROR ){ + iErrorStop = 260; + throw iRc; + } + if(( iRc = pMemo->xbRemove()) != XB_NO_ERROR ){ + iErrorStop = 270; + throw iRc; + } + free( cBlock ); + delete pMemo; + + } + catch (xbInt16 iRc ){ + free( cBlock ); + + xbString sMsg; + sMsg.Sprintf( "xbMemoDbt4::PackMemo() Exception Caught. Error Stop = [%d] rc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + #ifdef XB_LOCKING_SUPPORT + if( bTableLocked ) + dbf->LockTable( XB_UNLOCK ); + if( bMemoLocked ) + LockMemo( XB_UNLOCK ); + #endif + return iRc; +} +/***********************************************************************/ +//! @brief Read block header. +/*! + \param ulBlockNo Block to read + \param iOption 1 - Read fields option 1 + 2 - Read fields option 2 + \returns Return Codes +*/ + +xbInt16 xbMemoDbt4::ReadBlockHeader( xbUInt32 ulBlockNo, xbInt16 iOption ) { + + xbInt16 rc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + + try{ + + if(( rc = ReadBlock( ulBlockNo, 8, mbb )) != XB_NO_ERROR ){ + iErrorStop = 100; + rc = XB_READ_ERROR; + } + if( iOption == 1 ){ + iField1 = eGetInt16((char *) mbb ); + iStartPos = eGetInt16((char *) mbb+2); + ulFieldLen = eGetUInt32((char *) mbb+4); + } + else if( iOption == 2 ){ + ulNextFreeBlock = eGetUInt32((char *) mbb ); + ulFreeBlockCnt = eGetUInt32((char *) mbb+4 ); + } + else{ + iErrorStop = 110; + rc = XB_INVALID_OPTION; + throw rc; + } + } + catch (xbInt16 rc ){ + xbString sMsg; + sMsg.Sprintf( "xbMemoDbt4::ReadBlockHeader() Exception Caught. Error Stop = [%d] rc = [%d]", iErrorStop, rc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( rc )); + } + return rc; +} +/***********************************************************************/ +//! @brief Read dbt header file. +/*! + \param iOption 0 --> read only first four bytes
+ 1 --> read the entire thing + \returns Return Codes +*/ + +xbInt16 xbMemoDbt4::ReadDbtHeader( xbInt16 iOption ) { + + xbInt16 iErrorStop = 0; + xbInt16 rc = XB_NO_ERROR; + xbInt16 iReadLen = 0; + char *p; + char MemoBlock[22]; + + try{ + if( !FileIsOpen() ){ + iErrorStop = 100; + rc = XB_NOT_OPEN; + throw rc; + } + if( xbFseek( 0, SEEK_SET )){ + iErrorStop = 110; + rc = XB_SEEK_ERROR; + throw rc; + } + if( iOption ) + iReadLen = 22; + else + iReadLen = 4; + + if(( xbFread( &MemoBlock, (size_t) iReadLen, 1 )) != XB_NO_ERROR ){ + iErrorStop = 120; + rc = XB_READ_ERROR; + throw rc; + } + + p = MemoBlock; + ulHdrNextBlock = eGetUInt32( p ); + if( iOption == 0) + return XB_NO_ERROR; + + p += 8; + sDbfFileNameWoExt = ""; + for( int i = 0; i < 8; i++ ) + sDbfFileNameWoExt += *p++; + + p += 4; + SetBlockSize( (xbUInt32) eGetInt16( p )); + + cVersion = MemoBlock[16]; + + } + catch (xbInt16 rc ){ + + xbString sMsg; + sMsg.Sprintf( "xbMemoDbt4::ReadDbtHeader() Exception Caught. Error Stop = [%d] rc = [%d]", iErrorStop, rc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( rc )); + } + return rc; +} + +/***********************************************************************/ +#ifdef XB_DEBUG_SUPPORT +//! @brief Read free block information from header. +/*! + This routing pulls any reusable block information for file header. + Not used with version 3 memo files - stub. + + \param ulBlockNo + \param ulNextBlock + \param ulFreeBlockCnt + \returns XB_NO_ERROR +*/ +xbInt16 xbMemoDbt4::ReadFreeBlockHeader( xbUInt32 ulBlockNo, xbUInt32 &ulNextBlock, xbUInt32 &ulFreeBlockCount ){ + + xbInt16 rc = XB_NO_ERROR; + rc = ReadBlockHeader( ulBlockNo, 2 ); + ulNextBlock = ulNextFreeBlock; + ulFreeBlockCount = ulFreeBlockCnt; + return rc; +} +#endif +/***********************************************************************/ +//! @brief Update header name. +/*! + \returns XB_NO_ERROR +*/ +xbInt16 xbMemoDbt4::UpdateHeaderName() { + + xbInt16 rc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + + GetFileNamePart( sDbfFileNameWoExt ); + sDbfFileNameWoExt.PadRight( ' ', 8 ); // need at least eight bytes of name + sDbfFileNameWoExt = sDbfFileNameWoExt.Mid( 1, 8 ); // need no more than eight bytes of name + + try{ + if(( rc = xbFseek( 8, SEEK_SET )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw rc; + } + + for( int i = 1; i < 9; i++ ){ + if(( rc = xbFputc( sDbfFileNameWoExt[i] )) != XB_NO_ERROR ){ + iErrorStop = 110; + throw rc; + } + } + } + catch (xbInt16 rc ){ + xbString sMsg; + sMsg.Sprintf( "xbMemoDbt4::UpdateHeaderName() Exception Caught. Error Stop = [%d] rc = [%d]", iErrorStop, rc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( rc )); + } + return rc; +} +/***********************************************************************/ +//! @brief Update a memo field length for a given field number. +/*! + \param iFieldNo Field number to update data for. + \param sMemoData Data to update memo field data with. + \returns Return Codes +*/ + +xbInt16 xbMemoDbt4::UpdateMemoField( xbInt16 iFieldNo, const xbString & sMemoData ) { + + xbInt16 iErrorStop = 0; + xbInt16 rc = XB_NO_ERROR; + xbUInt32 ulBlockNo; + + try{ + + if(( rc = dbf->GetULongField( iFieldNo, ulBlockNo )) < XB_NO_ERROR ){ + iErrorStop = 100; + throw rc; + } + + if( sMemoData == "" ){ + if( ulBlockNo == 0 ){ + /* if nothing to do, return */ + return XB_NO_ERROR; + } else { + + // if this is in the new blocks link list already, then this is not the first update for this memo field + // this would be second or third update on the field since the original change and not commited + // Since it won't be needed in either a Commmit() or Abort(), can be freed immediately + if( llNewBlocks.SearchFor( ulBlockNo ) > 0 ){ + if(( rc = FreeMemoBlockChain( ulBlockNo )) != XB_NO_ERROR ){ + iErrorStop = 110; + throw rc; + } + if(( llNewBlocks.RemoveByVal( ulBlockNo )) < XB_NO_ERROR ){ + iErrorStop = 120; + throw rc; + } + } else { + // first revision, save what it was in case of Abort() command + if(( llOldBlocks.InsertAtFront( ulBlockNo )) != XB_NO_ERROR ){ + iErrorStop = 130; + throw rc; + } + } + if(( rc = dbf->PutField( iFieldNo, "" )) != XB_NO_ERROR ){ + iErrorStop = 140; + throw rc; + } + } + } else { + // free up the old space + xbUInt32 ulLastDataBlock = 0L; + + if( ulBlockNo > 0 ){ + if( llNewBlocks.SearchFor( ulBlockNo ) > 0 ){ + + if(( rc = FreeMemoBlockChain( ulBlockNo, ulLastDataBlock )) != XB_NO_ERROR ){ + iErrorStop = 150; + throw rc; + } + } else { + // first revision, save what it was in case of Abort() command + if(( rc = llOldBlocks.InsertAtFront( ulBlockNo )) != XB_NO_ERROR ){ + iErrorStop = 160; + throw rc; + } + } + } + // should next line be unsigned 32 bit int? + xbUInt32 ulTotalLen = 8 + sMemoData.Len(); + xbUInt32 ulBlocksNeeded; + if( ulTotalLen % GetBlockSize()) + ulBlocksNeeded = ulTotalLen / GetBlockSize() + 1; + else + ulBlocksNeeded = ulTotalLen / GetBlockSize(); + + xbBool bUsedBlockFound; + xbUInt32 ulHeadBlock; + xbUInt32 ulPrevNode; + if(( rc = FindBlockSetInChain( ulBlocksNeeded, ulLastDataBlock, ulHeadBlock, ulPrevNode, bUsedBlockFound )) != XB_NO_ERROR ){ + iErrorStop = 170; + throw rc; + } + iField1 = -1; + iStartPos = 8; + ulFieldLen = sMemoData.Len() + 8; + + if( bUsedBlockFound ){ + + if(( rc = GetBlockSetFromChain( ulBlocksNeeded, ulHeadBlock, ulPrevNode )) != XB_NO_ERROR ){ + iErrorStop = 180; + throw rc; + } + + if(( rc = WriteBlockHeader( ulHeadBlock, 1 )) != XB_NO_ERROR ){ + iErrorStop = 190; + throw rc; + } + + if(( rc = xbFwrite( sMemoData.Str(), sMemoData.Len(), 1 )) != XB_NO_ERROR ){ + iErrorStop = 200; + throw rc; + } + } else { // append to the end + + if(( rc = WriteBlockHeader( ulLastDataBlock, 1 )) != XB_NO_ERROR ){ + iErrorStop = 220; + throw rc; + } + + if(( rc = xbFwrite( sMemoData.Str(), sMemoData.Len(), 1 )) != XB_NO_ERROR ){ + iErrorStop = 230; + throw rc; + } + + if(( rc = xbFputc( 0x00, (xbInt32)((ulBlocksNeeded * GetBlockSize()) - (sMemoData.Len() + 8)))) != XB_NO_ERROR ){ + iErrorStop = 240; + throw rc; + } + + if( ulLastDataBlock == ulHdrNextBlock ){ // this is first node to be added to the node chain + ulHdrNextBlock += ulBlocksNeeded; + ulHeadBlock = ulLastDataBlock; + if(( rc = UpdateHeadNextNode()) != XB_NO_ERROR ){ + iErrorStop = 250; + throw rc; + } + + } else { // adding memo data to the end of the file, but chain exists + + ulNextFreeBlock = ulLastDataBlock + ulBlocksNeeded; + ulHeadBlock = ulLastDataBlock; + if(( rc = WriteBlockHeader( ulPrevNode, 2 )) != XB_NO_ERROR ){ + iErrorStop = 260; + throw rc; + } + } + } + + if(( rc = llNewBlocks.InsertAtFront( ulHeadBlock )) != XB_NO_ERROR ){ // In case of Abort(), this block needs to be freed + iErrorStop = 270; + throw rc; + } + if(( rc = dbf->PutLongField( iFieldNo, (xbInt32) ulHeadBlock )) != XB_NO_ERROR ){ + iErrorStop = 280; + throw rc; + } + } + } + + catch (xbInt16 rc ){ + xbString sMsg; + sMsg.Sprintf( "xbMemoDbt4::UpdateMemoField() Exception Caught. Error Stop = [%d] rc = [%d]", iErrorStop, rc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( rc )); + } + return rc; +} +/***********************************************************************/ +//! @brief Write block header. +/*! + \param ulBlockNo Block to read + \param iOption 1 - Read fields option 1 + 2 - Read fields option 2 + \returns Return Codes +*/ + +xbInt16 xbMemoDbt4::WriteBlockHeader( xbUInt32 ulBlockNo, xbInt16 iOption ) { + + xbInt16 rc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + + try{ + if( iOption == 1 ){ + ePutInt16 ((char *) mbb, iField1 ); + ePutInt16 ((char *) mbb+2, iStartPos ); + ePutUInt32((char *) mbb+4, ulFieldLen ); + } + else if( iOption == 2 ){ + ePutUInt32((char *) mbb, ulNextFreeBlock ); + ePutUInt32((char *) mbb+4, ulFreeBlockCnt ); + } + else{ + iErrorStop = 100; + rc = XB_INVALID_OPTION; + throw rc; + } + + if(( rc = WriteBlock( ulBlockNo, 8, mbb )) != XB_NO_ERROR ){ + iErrorStop = 110; + rc = XB_READ_ERROR; + } + } + catch (xbInt16 rc ){ + xbString sMsg; + sMsg.Sprintf( "xbMemoDbt4::WriteHeader() Exception Caught. Error Stop = [%d] rc = [%d]", iErrorStop, rc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( rc )); + } + return rc; +} +/***********************************************************************/ +//! @brief Empty the memo file. +/*! + \returns Return Codes +*/ +xbInt16 xbMemoDbt4::Zap(){ + + xbInt16 iRc = 0; + xbInt16 iErrorStop = 0; + char cBuf[4]; + try{ + ulHdrNextBlock = 1L; + ePutUInt32( cBuf, ulHdrNextBlock ); + + if(( iRc != xbFseek( 0, SEEK_SET )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + if(( iRc != xbFwrite( cBuf, 4, 1 ))!= XB_NO_ERROR ){ + iErrorStop = 110; + throw iRc; + } + if(( iRc != xbTruncate( GetBlockSize())) != XB_NO_ERROR ){ + iErrorStop = 120; + throw iRc; + } + } + + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbMemoDbt4::Zap() Exception Caught. Error Stop = [%d] rc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return iRc; +}/***********************************************************************/ +} /* namespace */ +#endif /* XB_DBF4_SUPPORT */ +#endif /* XB_MEMO_SUPPORT */ + diff --git a/src/core/xbssv.cpp b/src/core/xbssv.cpp new file mode 100755 index 0000000..532f942 --- /dev/null +++ b/src/core/xbssv.cpp @@ -0,0 +1,658 @@ +/* xbssv.cpp + +XBase64 Software Library + +Copyright (c) 1997,2003,2014,2022,2023 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{ + +const xbErrorMessage xbErrorMessages[] = { + { XB_NO_ERROR, "No Error" }, + { XB_NO_MEMORY, "No Memory" }, + { XB_INVALID_OPTION, "Invalid Option" }, + { XB_INVALID_PARAMETER, "Invalid Parameter" }, + { XB_DUP_TABLE_OR_ALIAS, "Duplicate Alias/Table Name" }, + { XB_INVALID_NODELINK, "Invalid Node Link" }, + { XB_KEY_NOT_UNIQUE, "Key Not Unique" }, + { XB_MEMCPY_ERROR, "Memory copy failure" }, + { XB_FILE_EXISTS, "File Already Exists" }, + { XB_ALREADY_OPEN, "Database already open" }, + { XB_DBF_FILE_NOT_OPEN, "DBF File Not Open" }, + { XB_FILE_NOT_FOUND, "File not found" }, + { XB_FILE_TYPE_NOT_SUPPORTED, "Not an Xbase type database" }, + { XB_RENAME_ERROR, "Unable to rename file" }, + { XB_INVALID_OBJECT, "Invalid Object" }, + { XB_NOT_OPEN, "Database not open" }, + { XB_NOT_FOUND, "Not Found" }, + { XB_OPEN_ERROR, "Open Error" }, + { XB_CLOSE_ERROR, "Close Error" }, + { XB_SEEK_ERROR, "Seek Error" }, + { XB_READ_ERROR, "Read Error" }, + { XB_WRITE_ERROR, "Error writing to disk drive" }, + { XB_EOF, "End Of File" }, + { XB_BOF, "Beginning Of File" }, + { XB_INVALID_BLOCK_SIZE, "Invalid Block Size" }, + { XB_INVALID_BLOCK_NO, "Invalid Block Number" }, + { XB_INVALID_RECORD, "Invalid Record Number" }, + { XB_DELETE_FAILED, "Delete Failed" }, + { XB_INVALID_TABLE_NAME, "Invalid Table Name" }, + { XB_EMPTY, "Empty Table or Index" }, + { XB_LIMIT_REACHED, "Limit Reached" }, + { XB_BLOCKREAD_NOT_ENABLED, "Block Read Mode is not enabled" }, + { XB_DIRECTORY_ERROR, "Directory Read/Write error" }, + { XB_INVALID_FIELD_TYPE, "Unknown Field Type" }, + { XB_INVALID_FIELD_NO, "Invalid Field Number" }, + { XB_INVALID_DATA, "Invalid Data" }, + { XB_INVALID_FIELD_NAME, "Invalid Field Name" }, + { XB_INVALID_MEMO_FIELD, "Not a Memo field" }, + { XB_INVALID_FIELD, "Invalid Field" }, + { XB_INVALID_FIELD_LEN, "Invalid Field Length" }, + { XB_INVALID_DATE, "Invalid Date" }, + { XB_INVALID_LOCK_OPTION, "Invalid Lock Option" }, + { XB_LOCK_FAILED, "Lock Failed" }, + { XB_TABLE_NOT_LOCKED, "Table Not Locked" }, + { XB_PARSE_ERROR, "Parse Error" }, + { XB_INVALID_FUNCTION, "Invalid or Undefined Function" }, + { XB_INVALID_PARM, "Invalid Parm" }, + { XB_INCONSISTENT_PARM_LENS, "Inconsistent parm lengths" }, + { XB_INCOMPATIBLE_OPERANDS, "Incompatible operands" }, + { XB_UNBALANCED_PARENS, "Unbalanced Parens" }, + { XB_UNBALANCED_QUOTES, "Unbalanced Quotes" }, + { XB_INVALID_EXPRESSION, "Invalid expression" }, + { XB_INVALID_KEYNO, "Invalid Key Number" }, + { XB_INVALID_INDEX, "Index File Error" }, + { XB_INVALID_TAG, "Invalid index tag" }, + { XB_SYNTAX_ERROR, "Invalid SQL Syntax" }, + { XB_MAX_ERROR_NO, "End of Error List" } +}; +// see also xbretcod.h + +xbInt16 xbSsv::iEndianType = 0; +xbString xbSsv::sDefaultDateFormat = "MM/DD/YY"; + +xbInt16 xbSsv::iDefaultFileVersion = 4; +xbString xbSsv::sNullString = ""; +xbBool xbSsv::bDefaultAutoCommit = xbTrue; + +xbString xbSsv::sDataDirectory = PROJECT_DATA_DIR; +xbString xbSsv::sTempDirectory = PROJECT_TEMP_DIR; + +#ifdef XB_LOGGING_SUPPORT +xbString xbSsv::sLogDirectory = PROJECT_LOG_DIR; +xbString xbSsv::sLogFileName = PROJECT_DFLT_LOGFILE; +#endif // XB_LOGGING_SUPPORT + +#ifdef XB_LOCKING_SUPPORT +xbInt32 xbSsv::lDefaultLockWait = 100; +xbInt16 xbSsv::iDefaultLockRetries = 3; +xbBool xbSsv::bDefaultAutoLock = xbTrue; +xbInt16 xbSsv::iDefaultLockFlavor = 1; +xbBool xbSsv::bMultiUser = xbTrue; +#else +xbBool xbSsv::bMultiUser = xbFalse; +#endif // XB_LOCKING_SUPPORT + +#if defined (XB_NDX_SUPPORT) || defined (XB_MDX_SUPPORT) +xbInt16 xbSsv::iUniqueKeyOpt = XB_HALT_ON_DUPKEY; + // is one of XB_HALT_ON_DUPKEY || XB_EMULATE_DBASE +#endif // defined (XB_NDX_SUPPORT) || defined (XB_MDX_SUPPORT) + + +#ifdef XB_MDX_SUPPORT +xbInt16 xbSsv::iCreateMdxBlockSize = 1024; // 1024 is DBase 7 default size for MDX index blocks +#endif // XB_MDX_SUPPORT + +#ifdef XB_BLOCKREAD_SUPPORT +xbUInt32 xbSsv::ulDefaultBlockReadSize = 32768; // 32K buffer for block DBF datafile reads +#endif // XB_BLOCKREAD_SUPPORT + +/*************************************************************************/ +//! @brief Class Constructor. +xbSsv::xbSsv(){} +/*************************************************************************/ +void xbSsv::BitDump( unsigned char c ) const { + for( int i = 7; i >= 0; i-- ) + std::cout << (BitSet( c, i ) ? 1 : 0); +} +void xbSsv::BitDump( char c ) const { + BitDump( (unsigned char) c ); +} +/*************************************************************************/ +//! @brief Check a bit in a one byte field and see if it is set. +/*! + \param c One byte char field to examine. + \param iBitNo which bit to examine. + \returns xbTrue Bit is set
+ xbFalse Bit is not set +*/ +xbBool xbSsv::BitSet( unsigned char c, xbInt16 iBitNo ) const { + return c & 1 << iBitNo; +} +/*************************************************************************/ +//! @brief Display error message on console for a given error number. +/*! + \param iErrorCode Error number to reference +*/ +void xbSsv::DisplayError( xbInt16 iErrorCode ) const { + std::cout << (const char *) GetErrorMessage( iErrorCode ) << std::endl; +} +/*************************************************************************/ +//! @brief Get the default auto commit setting. +/*! + + When auto commit is enabled, the library will automatically post any updates + when moving off an updated record or closing files. + If auto commit is disabled, the application program will need to explicitly + update the tables using using dbf->Put() and dbf->AppendRecord(). + + \returns xbTrue if auto commit is turned on
+ xbFalse is auto commit is turned off +*/ + +xbBool xbSsv::GetDefaultAutoCommit() const { + return bDefaultAutoCommit; +} +/*************************************************************************/ +//! @brief Get the current data directory. +/*! + \returns xbString containing the current data directory + where the database files are stored. +*/ + +xbString &xbSsv::GetDataDirectory() const { + return sDataDirectory; +} +/*************************************************************************/ +//! @brief Get the default date format. +/*! + \returns xbString containing the default date format. +*/ + +xbString & xbSsv::GetDefaultDateFormat() const { + return sDefaultDateFormat; +} +/*************************************************************************/ +//! @brief Get the Endian type. +/*! + \returns B - Big endian
+ L - Little endian
+*/ +xbInt16 xbSsv::GetEndianType() const { + return iEndianType; +} +/*************************************************************************/ +//! @brief Get an error message. +/*! + \param iErrorCode ErrorCode is the error number of description to be returned. + \returns Returns a pointer to a string containing a text description for the error code. +*/ + +const char * xbSsv::GetErrorMessage( xbInt16 iErrorCode ) const{ + + if( iErrorCode > 0 || iErrorCode <= XB_MAX_ERROR_NO ) + return ""; + + xbBool bFound = xbFalse; + xbInt16 iCtr = 0; + while( !bFound ){ + if( xbErrorMessages[iCtr].iErrorNo == XB_MAX_ERROR_NO ) + return "Unknown Error"; + if( xbErrorMessages[iCtr].iErrorNo == iErrorCode ) + return xbErrorMessages[iCtr].sErrorText; + iCtr++; + } + return ""; +} +/*************************************************************************/ +//! @brief Get home directory. +/*! + \param sHomeDirOut - Output home directory for current user. +*/ + +void xbSsv::GetHomeDir( xbString &sHomeDirOut ){ + + #ifdef HAVE_GETENV_S_F + + char sPath[MAX_PATH]; + size_t lSize; + + sHomeDirOut = ""; + memset( sPath, 0x00, MAX_PATH ); + + getenv_s( &lSize, NULL, 0, "HOMEDRIVE" ); + if( lSize > 0 ){ + getenv_s( &lSize, sPath, lSize, "HOMEDRIVE" ); + sHomeDirOut = sPath; + memset( sPath, 0x00, MAX_PATH ); + } + + getenv_s( &lSize, NULL, 0, "HOMEPATH" ); + if( lSize > 0 ){ + getenv_s( &lSize, sPath, lSize, "HOMEPATH" ); + sHomeDirOut += sPath; + } + if( sHomeDirOut == "" ) + sHomeDirOut = "C:\xbase64"; + + #elif defined(WIN32) + sHomeDirOut.Sprintf( "%s%s", getenv( "HOMEDRIVE" ), getenv( "HOMEPATH" )); + + #else + sHomeDirOut.Sprintf( "%s", getenv( "HOME" )); + sHomeDirOut.Trim(); + if( sHomeDirOut == "" ) + sHomeDirOut.Sprintf( "%s", getpwuid( getuid())->pw_dir ); + #endif + + sHomeDirOut.Trim(); +} + + +/*************************************************************************/ +//! @brief Set the data directory. +/*! + \param sDataDirectory Set the data directory. +*/ + +void xbSsv::SetDataDirectory( const xbString &sDataDirectory ){ + this->sDataDirectory = sDataDirectory; + + #ifdef WIN32 + this->sDataDirectory.SwapChars( '/', '\\' ); + #else + this->sDataDirectory.SwapChars( '\\', '/' ); + #endif + +} + +/*************************************************************************/ +//! @brief Set the default date format. +/*! + \param sDefaultDateFormat Set the default date format. +*/ + +void xbSsv::SetDefaultDateFormat( const xbString &sDefaultDateFormat ) { + this->sDefaultDateFormat = sDefaultDateFormat; +} + +/*************************************************************************/ +//! @brief Set the default auto commit. +/*! + + Disabling auto commit requires the application execute explicit updates + using dbf->Put() and dbf->AppendRecord(). With auto commit on, the library + posts updates automatically when moving off the current record or closing + a file. + + \param bDefaultAutoCommit xbTrue - Enable default auto commit.
+ xbFalse - Disable default auto commit.
+*/ + +void xbSsv::SetDefaultAutoCommit( xbBool bDefaultAutoCommit ) { + this->bDefaultAutoCommit = bDefaultAutoCommit; +} +/*************************************************************************/ +//! @brief Set the endian type +/*! + This routine determines the Endian-ness at run time instead of + compile time as some processers (ie; Sparc,ARM) can be switched either way. + This routine is called automatically by the library at startup and does not + need to be called in an application program. + +*/ + +void xbSsv::SetEndianType() { + xbInt16 e = 1; + iEndianType = *(char *) &e; + if( iEndianType ) + iEndianType = 'L'; + else + iEndianType = 'B'; + return; +} + +/*************************************************************************/ +//! @brief Set the temp directory. +/*! + \param sTempDirectory Set the data direcroty. +*/ + +void xbSsv::SetTempDirectory( const xbString &sTempDirectory ){ + this->sTempDirectory = sTempDirectory; + + #ifdef WIN32 + this->sTempDirectory.SwapChars( '/', '\\' ); + #else + this->sTempDirectory.SwapChars( '\\', '/' ); + #endif + +} + +/*************************************************************************/ +//! @brief Get the OS dependent path separator. +/*! + \returns Returns '\' for windows environment, otherwise returns '/'. +*/ + +char xbSsv::GetPathSeparator() const { + #ifdef WIN32 + return '\\'; + #else + return '/'; + #endif +} + +/*************************************************************************/ +//! @brief Get the current temp directory. +/*! + \returns xbString containing the current data directory + where the database files are stored. +*/ + +xbString &xbSsv::GetTempDirectory() const { + return sTempDirectory; +} + +/*************************************************************************/ +#ifdef XB_LOGGING_SUPPORT + +//! @brief Get the default log file name. +/*! + \returns Returns the log file name. +*/ + +xbString & xbSsv::GetLogFileName() const { + return sLogFileName; +} + +/*************************************************************************/ +//! @brief Get the default log directory. +/*! + \returns Returns the log directory. +*/ + + +xbString & xbSsv::GetLogDirectory() const { + return sLogDirectory; +} +/*************************************************************************/ +//! @brief Set the default log directory name. +/*! + \param sLogDirectory Name of desired log directory. + +*/ + +void xbSsv::SetLogDirectory( const xbString &sLogDirectoryIn ){ + + this->sLogDirectory = sLogDirectoryIn; + #ifdef WIN32 + this->sLogDirectory.SwapChars( '/', '\\' ); + #else + this->sLogDirectory.SwapChars( '\\', '/' ); + #endif + +} + +//! @brief Set the log file name. +/*! + \param sLogFileName - Log File Name. + \return void +*/ + +void xbSsv::SetLogFileName( const xbString & sLogFileName ){ + + this->sLogFileName = sLogFileName; +} + + +#else + +xbString & xbSsv::GetLogFileName() const { + return sNullString; +} + + +xbString & xbSsv::GetLogDirectory() const { + +std::cout << "xbSsv::GetLogDirectory() returning null\n"; + + return sNullString; +} + +void xbSsv::SetLogDirectory( const xbString &sLogDirectory ){ + return; +} + +void xbSsv::SetLogFileName( const xbString & sLogFileName ){ + return; +} + +#endif + +/*************************************************************************/ + +#ifdef XB_LOCKING_SUPPORT + +//! @brief Get the default lock retries. +/*! + This is the number of lock attempts the libary will make before returning + failure if the file can not be locked. + \returns Default lock retry count. +*/ +xbInt16 xbSsv::GetDefaultLockRetries() const { + return iDefaultLockRetries; +} + +//! @brief Set the default lock retries. +/*! + \param iDefaultLockRetries - Number of lock attempts before returning failure. +*/ +void xbSsv::SetDefaultLockRetries( xbInt16 iDefaultLockRetries ) { + this->iDefaultLockRetries = iDefaultLockRetries; +} + +//! @brief Get the default auto lock setting. +/*! + When auto locking is turned on, the library automatically locks and unlocks + files and indices as needed in a multi user environment. + \returns Number of lock attempt settings. +*/ +xbBool xbSsv::GetDefaultAutoLock() const { + return bDefaultAutoLock; +} + + +//! @brief Set the default auto lock setting. +/*! + When auto locking is turned on, the library automatically locks and unlocks + files and indices as needed in a multi user environment. Locking is not required + in single a single user environment. + + \param bDefaultAutoLock xbTrue - Turn autolocking on
+ xbFalse - Turn autolocking off
+*/ +void xbSsv::SetDefaultAutoLock( xbBool bDefaultAutoLock ) { + this->bDefaultAutoLock = bDefaultAutoLock; +} + +//! @brief Enable default auto locking. +/*! + When auto locking is turned on, the library automatically locks and unlocks + files and indices as needed in a multi user environment. +*/ +void xbSsv::EnableDefaultAutoLock() { + this->bDefaultAutoLock = xbTrue; +} + +//! @brief Disable defalt auto locking. +/*! + When auto locking is turned off, the library does not automatically lock + and unlock files and indices as needed in a multi user environment. + Locking is not needed in a single user environment. + +*/ +void xbSsv::DisableDefaultAutoLock() { + this->bDefaultAutoLock = xbFalse; +} +/***************************************************************************/ +//! @brief Get default lock flavor +/*! + Currently one flavor. This routine is part of the structure to support + future additional locking scenarios for Clipper and Foxpro. + \returns 1 +*/ +xbInt16 xbSsv::GetDefaultLockFlavor() const { + return iDefaultLockFlavor; +} + +/***************************************************************************/ +//! @brief Set default lock flavor +/*! + Currently one flavor. This routine is part of the structure to support + future additional locking scenarios for Clipper and Foxpro. +*/ +void xbSsv::SetDefaultLockFlavor( xbInt16 iDefaultLockFlavor ) { + this->iDefaultLockFlavor = iDefaultLockFlavor; +} + +/***************************************************************************/ +//! @brief Set default lock wait +/*! + \param lLockWait Set default lock wait in milliseconds. +*/ +void xbSsv::SetDefaultLockWait( xbInt32 lLockWait ) { + this->lDefaultLockWait = lLockWait; +} + +/***************************************************************************/ +//! @brief Get default lock wait +/*! + \returns Lock wait time in milliseconds. + +*/ +xbInt32 xbSsv::GetDefaultLockWait() const { + return lDefaultLockWait; +} +#endif + +/***************************************************************************/ +//! @brief Get the multi user setting. +/*! + \returns xbTrue - Multi user mode turned on.
+ xbFalse - Multi user mode turned off.
+*/ +xbBool xbSsv::GetMultiUser() const { + return bMultiUser; +} + +//! @brief Get the multi user setting. +/*! + \param bMultiUser xbTrue - Turn on Multi user mode.
+ xbFalse - Turn off Multi user mode.
+*/ +void xbSsv::SetMultiUser( xbBool bMultiUser ) { + this->bMultiUser = bMultiUser; +} + + + +/************************************************************************/ +#ifdef XB_MDX_SUPPORT +//! @brief Get the mdx file block size used when creating a memo file. +/*! + \returns system default setting for MDX block size. +*/ +xbInt16 xbSsv::GetCreateMdxBlockSize() const { + return iCreateMdxBlockSize; +} + +/************************************************************************/ +//! @brief Create mdx block size. +/*! + This routine sets the mdx file block size at the system level. This value is + used when the mdx index file is initially created so if you want to change it, + this must be called before creating the table. + + \param iBlockSize - Block size, must be evenly divisible by 512 and <= 16384 + \returns XB_INVALID_BLOCK_SIZE
XB_NO_ERROR +*/ + +xbInt16 xbSsv::SetCreateMdxBlockSize( xbInt16 iBlockSize ){ + + if( iBlockSize < 512 || iBlockSize > 16384 || iBlockSize % 512 ) + return XB_INVALID_BLOCK_SIZE; + else + iCreateMdxBlockSize = iBlockSize; + + return XB_NO_ERROR; +} +#endif + +/************************************************************************/ +#if defined (XB_NDX_SUPPORT) || defined (XB_MDX_SUPPORT) +//! @brief Get Unique Key Opt +/*! + This routine returns the Unique Key Processing Option which is one of: + XB_HALT_ON_DUPKEY + XB_EMULATE_DBASE +*/ + +xbInt16 xbSsv::GetUniqueKeyOpt() const { + return iUniqueKeyOpt; +} + +//! @brief Set Unique Key Opt +/*! @brief Set Unique Key Opt + This routine Sets the Unique Key Processing Option which is one of: + XB_HALT_ON_DUPKEY + XB_EMULATE_DBASE +*/ +xbInt16 xbSsv::SetUniqueKeyOpt( xbInt16 iOpt ){ + if( iOpt == XB_HALT_ON_DUPKEY || iOpt == XB_EMULATE_DBASE ){ + iUniqueKeyOpt = iOpt; + return XB_NO_ERROR; + } else { + return XB_INVALID_OPTION; + } +} +#endif +/************************************************************************/ +#ifdef XB_BLOCKREAD_SUPPORT + +//! @brief Get Default Read Block Size +/*! + This routine returns the default read block size used when allocating + buffer space for block reads of table data. Initial setting is 32768 bytes. +*/ +xbUInt32 xbSsv::GetDefaultBlockReadSize() const { + return ulDefaultBlockReadSize; +} + + +//! @brief Set Default Read Block Size +/*! + This routine sets the default read block size used when allocating + buffer space for block reads of table data. Initial setting is 32768 bytes. +*/ +void xbSsv::SetDefaultBlockReadSize( xbUInt32 ulDfltBlockReadSize ){ + ulDefaultBlockReadSize = ulDfltBlockReadSize; +} + +#endif // XB_BLOCKREAD_SUPPORT +/************************************************************************/ + + + + +} /* namespace */ \ No newline at end of file diff --git a/src/core/xbstring.cpp b/src/core/xbstring.cpp new file mode 100755 index 0000000..89cefb6 --- /dev/null +++ b/src/core/xbstring.cpp @@ -0,0 +1,2000 @@ +/* xbstring.cpp + +XBase64 Software Library + +Copyright (c) 1997,2003,2014,2021,2022,2023 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 + +*/ + +//#ifdef __GNU LesserG__ +// #pragma implementation "xbstring.h" +//#endif + +#include "xbase.h" + + +namespace xb{ + +XBDLLEXPORT const char * xbString::NullString = ""; +XBDLLEXPORT char xbString::cJunkBuf; + +/************************************************************************/ +//! @brief Destructor + +xbString::~xbString(){ + if (data != NULL) + free(data); + +} + +/************************************************************************/ +//! @brief Constructor +/*! + \param ulSize - Allocation size. The allocation size is normally handled internally + by the class, but it can be set in this constructor. +*/ +xbString::xbString(xbUInt32 ulSize) { + data = (char *)calloc(1, ulSize); + this->size = ulSize; +// memset( data, 0x00, ulSize ); - redundant, initialized by calloc +} +/************************************************************************/ +//! @brief Constructor +/*! + \param c - Initialize string to c. +*/ +xbString::xbString(char c) { + data = (char *)calloc(1, 2); + data[0] = c; + data[1] = 0; + size = 2; +} +/************************************************************************/ +//! @brief Constructor +/*! + \param s - Initialize string to s. +*/ +xbString::xbString( const char *s ) { + + if( s == NULL ){ + size = 0; + data = NULL; + } else { + size = (xbUInt32) (strlen(s) + 1 ); + data = (char *) calloc( 1, size ); + xb_strcpy( data, s ); + } + // ctor(s); +} +/************************************************************************/ +//! @brief Constructor +/*! + \param d - Initiailize string to d. +*/ +xbString::xbString( xbDouble d ) { + data = NULL; + size = 0; + Sprintf("%f", d); +} +/************************************************************************/ +//! @brief Constructor +/*! + \param s Initialize string to s. + \param ulMaxLen Maximum length of string. Truncate any characters greater than ulMaxLen. +*/ +xbString::xbString( const char *s, xbUInt32 ulMaxLen ) { + xbUInt32 sSize = (xbUInt32) strlen( s ); + if( sSize < ulMaxLen ) + size = sSize; + else + size = ulMaxLen; + data = (char *)calloc(1, size+1); + for( xbUInt32 i = 0; i < size; i++ ) + data[i] = s[i]; + data[size] = '\0'; + size++; // account for null trailing byte + return; +} +/************************************************************************/ +//! @brief Constructor +/*! + \param s Initialize string to s. +*/ +xbString::xbString( const xbString &s ) { + ctor(s.Str()); +} + +/************************************************************************/ +//! @brief Operator const char * +/*! + \returns Pointer to string data. +*/ +xbString::operator const char *() const { + return data ? data : NullString; +} + +/************************************************************************/ +//! @brief Set operator = +/*! + \param s - Set the string to the string on the right of the equal sign. +*/ +xbString &xbString::operator=( const xbString &s ) { + return Set(s); +} +/************************************************************************/ +//! @brief Set operator = +/*! + \param s - Set the string to the string on the right of the equal sign. +*/ +xbString &xbString::operator=( const char *s ) { + return Set(s); +} + +/************************************************************************/ +//! @brief Stream insertion operator << +/*! + std::cout << MyString << std::endl; + + \param os Output stream + \param s String to send to output stream +*/ +std::ostream& operator<< ( std::ostream& os, const xbString & s ) { + return os << s.Str(); +} +/************************************************************************/ +//! @brief Append operator += +/*! + \param s - Append s to the string. +*/ +xbString &xbString::operator+=( const xbString &s ) { + + if (s.IsNull()) + return (*this); + + xbUInt32 Len = s.Len(); + xbUInt32 oldLen = this->Len(); + xbUInt32 newLen = Len + oldLen; + + data = (char *)realloc(data, newLen+1); + if( !data ) + return (*this); + + if(oldLen == 0) + data[0] = 0; + + char *t = data; + t+= oldLen; + for( xbUInt32 i = 0; i < Len; i++ ) + *t++ = s.GetCharacter(i+1); + + data[newLen] = '\0'; + size == 0 ? size += (Len + 1) : size += Len; + + return (*this); +} +/************************************************************************/ +//! @brief Append operator += +/*! + \param s - Append s to the string. +*/ +xbString &xbString::operator+=( const char *s ) { + + if (s == NULL) + return (*this); + xbUInt32 Len = (xbUInt32) strlen(s); + xbUInt32 oldLen = this->Len(); + xbUInt32 newLen = Len + oldLen; + data = (char *)realloc(data, newLen+1); + if(oldLen == 0) + data[0] = 0; + for( xbUInt32 i = 0; i < Len; i++ ) + data[i+oldLen] = s[i]; + data[newLen] = '\0'; + // size += Len; + size == 0 ? size+= (Len + 1) : size += Len; + return (*this); +} +/************************************************************************/ +//! @brief Append operator += +/*! + \param c - Append c to the string. +*/ +xbString &xbString::operator+=( char c ) { + xbUInt32 Len = 1; + xbUInt32 oldLen = this->Len(); + data = (char *)realloc(data, oldLen+Len+1); + data[oldLen] = c; + data[oldLen+1] = 0; + // size++; + size == 0 ? size += 2 : size++; + return (*this); +} +/************************************************************************/ +//! @brief Append operator -= +/*! + Append s to the right of this string, right trimming both strings. + \param s - Append s to the right of the string value. +*/ +xbString &xbString::operator-=( const xbString &s ) { + + Rtrim(); + if (s.IsNull()) + return (*this); + xbUInt32 Len = s.Len(); + xbUInt32 oldLen = this->Len(); + xbUInt32 newLen = Len + oldLen; + + data = (char *)realloc(data, newLen+1); + if(oldLen == 0) + data[0] = 0; + + for( xbUInt32 i = 0; i < Len; i++ ) + data[i+oldLen] = s.GetCharacter(i+1); + + data[newLen] = '\0'; + //size += Len; + size == 0 ? size += (Len+1) : size += Len; + Rtrim(); + return (*this); +} +/************************************************************************/ +//! @brief Append operator -= +/*! + Append s to the right of this string, right trimming both strings. + \param s - Append s to the right of the string value. +*/ +xbString &xbString::operator-=(const char *s) { + + Rtrim(); + if (s == NULL) + return (*this); + xbUInt32 Len = (xbUInt32) strlen(s); + xbUInt32 oldLen = this->Len(); + xbUInt32 newLen = Len + oldLen; + + data = (char *)realloc(data, newLen+1); + + if(oldLen == 0) + data[0] = 0; + + for( xbUInt32 i = 0; i < Len; i++ ) + data[i+oldLen] = s[i]; + data[newLen] = '\0'; + + //size += Len; + size == 0 ? size += (Len+1) : size += Len; + + Rtrim(); + return (*this); +} +/************************************************************************/ +//! @brief Append operator -= +/*! + Append c to the right of this string, trimming right space on this string first. + \param c - Append s to the right of the string value. +*/ +xbString &xbString::operator-=(const char c) { + Rtrim(); + xbUInt32 oldSize = size; + + // size += 1; + size == 0 ? size += 2 : size += 1; + + data = (char *)realloc( data, size ); + if( oldSize == 0 ) data[0] = 0; + data[size-2] = c; + data[size-1] = 0; + Trim(); + return (*this); +} +/************************************************************************/ +//! @brief Concatonate operator - +/*! + Concatonate left string with right string returning reference to new string. + Both strings are trimmed. + + \param s1 Right string operator. +*/ +xbString xbString::operator-(const xbString &s1) { + xbString tmp( data ); + tmp -= s1; + return tmp; +} +/************************************************************************/ +//! @brief Concatonate operator + +/*! + Concatonate left string with right string returning reference to new string. + + \param s1 Right string operator. +*/ +xbString xbString::operator+( const char *s1) { + xbString tmp( data ); + tmp += s1; + return tmp; +} +/************************************************************************/ +//! @brief Concatonate operator + +/*! + Concatonate left string with right string returning reference to new string. + + \param s1 Right string operator. +*/ +xbString xbString::operator+( const xbString &s1) { + xbString tmp( data ); + tmp += s1; + return tmp; +} +/************************************************************************/ +//! @brief Concatonate operator + +/*! + Concatonate left string with right string returning reference to new string. + + \param c Right string operator. +*/ + +xbString xbString::operator+( const char c) { + xbString tmp( data ); + tmp += c; + return tmp; +} +/************************************************************************/ +//! @brief operator [] +/*! + \param n - Offset into the string of the byte to retrieve. + \returns c - The character to return from the offset within the [] brackets. +*/ +char &xbString::operator[]( xbUInt32 n ) const { + if( n > 0 && n <= size ) + return data[n-1]; + else + return cJunkBuf; +} +/************************************************************************/ +//! @brief operator [] +/*! + \param n - Offset into the string of the byte to retrieve. + \returns c - The character to return from the offset within the [] brackets. +*/ +char &xbString::operator[]( xbInt32 n ) const { + if( n > 0 && n <= (xbInt32) size ) + return data[n-1]; + else + return cJunkBuf; +} +/************************************************************************/ +//! @brief operator == +/*! + \param s String to compare + \returns xbTrue - Strings match.
+ zbFalse - Strings don't match.
+ +*/ +xbBool xbString::operator==( const xbString &s ) const { + + if( data == NULL || data[0] == 0 ) { + if( s.data == NULL || s.data[0] == 0 ) + return true; + return false; + } else { + if( s.data == NULL || s.data[0] == 0 ) + return false; + return( strcmp(data,s.data) == 0 ? xbTrue : xbFalse ); + } +} +/************************************************************************/ +//! @brief operator == +/*! + \param s String to compare + \returns xbTrue - Strings match.
+ zbFalse - Strings don't match.
+*/ +xbBool xbString::operator==( const char *s ) const { + + if (s == NULL) { + if ( data == NULL) + return true; + return false; + } + if ((s[0] == 0) && data == NULL) + return true; + if ( data == NULL) + return false; + return( strcmp( data, s) == 0 ? xbTrue : xbFalse ); +} +/************************************************************************/ +//! @brief operator != +/*! + \param s String to compare + \returns xbTrue - Strings don't match.
+ xbFalse - Strings match.
+*/ +xbBool xbString::operator!=( const xbString &s ) const { + if( data == NULL || data[0] == 0 ) { + if( s.data == NULL || s.data[0] == 0 ) + return xbFalse; // NULL != NULL + return xbTrue; // NULL != !NULL + } else { + if( s.data == NULL || s.data[0] == 0 ) + return xbTrue; // !NULL != NULL + return( strcmp( data, s.data ) != 0 ? xbTrue : xbFalse ); // !NULL != !NULL + } +} +/************************************************************************/ +//! @brief operator != +/*! + \param s String to compare + \returns xbTrue - Strings don't match.
+ zbFalse - Strings match.
+*/ +xbBool xbString::operator!=( const char *s ) const { + if( s == NULL || s[0] == 0 ) { + if( data == NULL || data[0] == 0 ) + return xbFalse; // NULL != NULL + return xbTrue; // NULL != !NULL + } else { + if( s == NULL || s[0] == 0 ) + return xbTrue; // !NULL != NULL + return( strcmp( data, s ) != 0 ? xbTrue : xbFalse ); // !NULL != !NULL + } +} +/************************************************************************/ +//! @brief operator < +/*! + \param s String to compare + \returns xbTrue - Left string is less than the right string.
+ zbFalse - Left string is not less than the right string.
+*/ +xbBool xbString::operator< ( const xbString &s ) const { + + if( data == NULL || data[0] == 0 ) { + if( s.data == NULL || s.data[0] == 0 ) + return false; + return true; + } else { + if( s.data == NULL || s.data[0] == 0 ) + return false; + return ( strcmp(data, s.data) < 0 ? xbTrue : xbFalse ); + } +} +/************************************************************************/ +//! @brief operator > +/*! + \param s String to compare + \returns xbTrue - Left string is greater than the right string.
+ zbFalse - Left string is not greater than the right string.
+*/ +xbBool xbString::operator> ( const xbString &s ) const { + if( data == NULL || data[0] == 0 ) { + if( s.data == NULL || s.data[0] == 0 ) + return false; + return false; + } else { + if( s.data == NULL || s.data[0] == 0 ) + return true; + return( strcmp(data,s.data) > 0 ? xbTrue : xbFalse ); + } +} +/************************************************************************/ +//! @brief operator <= +/*! + \param s String to compare + \returns xbTrue - Left string is less than or equal to the right string.
+ zbFalse - Left string is not less than or equal to the right string.
+*/ +xbBool xbString::operator<=( const xbString &s ) const { + if( data == NULL || data[0] == 0 ) { + if( s.data == NULL || s.data[0] == 0 ) + return true; + return true; + } else { + if( s.data == NULL || s.data[0] == 0 ) + return false; + return( strcmp(data,s.data) <= 0 ? xbTrue : xbFalse ); + } +} +/************************************************************************/ +//! @brief operator >= +/*! + \param s String to compare + \returns xbTrue - Left string is greater than or equal to the right string.
+ zbFalse - Left string is not greater than or equal to the right string.
+*/ +xbBool xbString::operator>=( const xbString &s ) const { + if( data == NULL || data[0] == 0 ) { + if( s.data == NULL || s.data[0] == 0 ) + return true; + return false; + } else { + if( s.data == NULL || s.data[0] == 0 ) + return true; + return( strcmp(data, s.data) >= 0 ? xbTrue : xbFalse ); + } +} + +/************************************************************************/ +//! @brief Add a prefixing back slash to specified characters in the string. +/*! + \param c Character to prefix with a backslash. + \returns Reference to this string. +*/ +xbString &xbString::AddBackSlash( char c ) { + + xbUInt32 lCnt = CountChar( c ); + if( lCnt == 0 ) + return *this; + char *p; + if(( p = (char *)calloc( 1, size + lCnt )) == NULL ) + return *this; + + char *p2 = p; + for( xbUInt32 lS = 0; lS < size; lS++ ){ + if( data[lS] == c ) + *p2++ = '\\'; + *p2++ = data[lS]; + } + if( data ) + free( data ); + data = p; + + // size += lCnt; + size == 0 ? size += (lCnt+1) : size += lCnt; + + return *this; +} +/************************************************************************/ +//! @brief Append data to string. +/*! + \param s String data to append. + \returns Reference to this string. +*/ +xbString &xbString::Append( const xbString &s ) { + *this += s; + return *this; +} + +/************************************************************************/ +//! @brief Append data to string. +/*! + \param s String data to append. + \returns Reference to this string. +*/ +xbString &xbString::Append( const char *s ) { + *this += s; + return *this; +} +/************************************************************************/ +//! @brief Append data to string. +/*! + \param c String data to append. + \returns Reference to this string. +*/ +xbString &xbString::Append( char c ) { + *this += c; + return *this; +} +/************************************************************************/ +//! @brief Append data to string. +/*! + \param s String data to append. + \param ulByteCount Maximum number of bytes to append. + \returns Reference to this string. +*/ +xbString &xbString::Append( const char *s, xbUInt32 ulByteCount ) { + + if ( s == NULL || !*s || ulByteCount == 0) + return (*this); + + xbUInt32 ulOrigLen = this->Len(); + + // s might not be null byte at the end, can't use strlen + // xbUInt32 ulAddLen = strlen( s ); + xbUInt32 ulAddLen = 0; + const char *p = s; + + while( ulAddLen < ulByteCount && *p ){ + p++; + ulAddLen++; + } + + if( ulAddLen > ulByteCount ) + ulAddLen = ulByteCount; + + size = ulOrigLen + ulAddLen + 1; + data = (char *) realloc( data, size ); + + for( xbUInt32 i = 0; i < ulAddLen; i++ ) + data[i+ulOrigLen] = s[i]; + + data[size-1] = 0x00; + return (*this); +} + +/************************************************************************/ +//! @brief Assign portion of string. +/*! + \param sStr - Source string for copy operation. sStr needs to be a Null terminated string. + \param ulStartPos - Starting position within source string. + \param ulCopyLen - Length of data to copy. + \returns Reference to this string. +*/ +xbString &xbString::Assign(const char * sStr, xbUInt32 ulStartPos, xbUInt32 ulCopyLen){ + if(data){ + free(data); + data = 0; + size = 0; + } + xbUInt32 lLen = (xbUInt32) strlen( sStr ); + if( ulStartPos > lLen ){ + size = 0; + return( *this ); + } + if((( ulCopyLen - 1) + ulStartPos ) > lLen ) + ulCopyLen = lLen - ulStartPos + 1; + data = (char *)calloc(1, ulCopyLen + 1); + + //size = ulCopyLen + 1; + size == 0 ? size += (ulCopyLen+1) : size += ulCopyLen; + + for( xbUInt32 i = 0; i < ulCopyLen; i++ ) + data[i] = sStr[i + ulStartPos - ((xbUInt32) 1)]; + data[ulCopyLen] = '\0'; + return (*this); +} +/************************************************************************/ +//! @brief Assign portion of string. +/*! + \param sStr - Source string for copy operation. sStr needs to be a Null terminated string. + \param ulStartPos - Starting position within source string. + \returns Reference to this string. +*/ +xbString &xbString::Assign(const char * sStr, xbUInt32 ulStartPos){ + if(data){ + free(data); + data = 0; + size = 0; + } + xbUInt32 ulSrcLen = (xbUInt32) strlen( sStr ); + if( ulStartPos > ulSrcLen ){ + size = 0; + return( *this ); + } + xbUInt32 ulCopyLen; + ulCopyLen = ulSrcLen - ulStartPos + 1; + data = (char *)calloc(1, ulCopyLen + 1); + + size = ulCopyLen + 1; + + for( xbUInt32 i = 0; i < ulCopyLen; i++ ) + data[i] = sStr[i + ulStartPos - ((xbUInt32) 1)]; + data[ulCopyLen] = '\0'; + return (*this); +} + +/************************************************************************/ +//! @brief Assign portion of string. +/*! + \param sStr - Source string for copy operation. sStr needs to be a Null terminated string. + \param ulStartPos - Starting position within source string. + \param ulCopyLen - Length of data to copy. + \returns Reference to this string. +*/ +xbString &xbString::Assign(const xbString& sStr, xbUInt32 ulStartPos, xbUInt32 ulCopyLen){ + if(data){ + free(data); + data = 0; + size = 0; + } + xbUInt32 ulSrcLen = sStr.Len(); + if( ulStartPos > ulSrcLen ){ + size = 0; + return( *this ); + } + if((( ulCopyLen - 1) + ulStartPos ) > ulSrcLen ) + ulCopyLen = ulSrcLen - ulStartPos + 1; + data = (char *)calloc(1, ulCopyLen + 1); + size = ulCopyLen + 1; + for( xbUInt32 i = 0; i < ulCopyLen; i++ ) + data[i] = sStr[i + ulStartPos]; + data[ulCopyLen] = '\0'; + return (*this); +} +/************************************************************************/ +//! @brief Assign portion of string. +/*! + \param sStr - Source string for copy operation. sStr needs to be a Null terminated string. + \param ulStartPos - Starting position within source string. + \returns Reference to this string. +*/ +xbString &xbString::Assign(const xbString& sStr, xbUInt32 ulStartPos){ + if(data){ + free(data); + data = 0; + size = 0; + } + xbUInt32 ulSrcLen = sStr.Len(); + if( ulStartPos > ulSrcLen ){ + size = 0; + return( *this ); + } + xbUInt32 ulCopyLen; + ulCopyLen = ulSrcLen - ulStartPos + 1; + data = (char *)calloc(1, ulCopyLen + 1); + size = ulCopyLen; + for( xbUInt32 i = 0; i < ulCopyLen; i++ ) + data[i] = sStr[i + ulStartPos]; + data[ulCopyLen] = '\0'; + size++; + return (*this); +} +/************************************************************************/ +//! @brief Copy a string +/*! + \returns xbString. +*/ +xbString xbString::Copy() const { + return( *this ); +} +/************************************************************************/ +//! @brief Count the number of characters in the string. +/*! + \param c Character to count. + \param iOpt 0 - Count the number of characters.
+ 1 - Count the number of characters not between single or double quotes. + \returns The number of characters. +*/ +xbUInt32 xbString::CountChar( char c, xbInt16 iOpt ) const { + if( iOpt == 0 ) + return CountChar( c ); + else{ + xbBool bSingleQuote = xbFalse; + xbBool bDoubleQuote = xbFalse; + char cPrevChar = 0x00; + xbUInt32 i,j; + for( i = 0, j = 0; i < size; i++ ){ + if( bSingleQuote && data[i] == '\'' && cPrevChar != '\\' ){ + bSingleQuote = xbFalse; + } + else if( bDoubleQuote && data[i] == '"' && cPrevChar != '\\' ){ + bDoubleQuote = xbFalse; + } + else if( data[i] == '\'' && cPrevChar != '\\' && !bDoubleQuote ){ + bSingleQuote = xbTrue; + } + else if( data[i] == '"' && cPrevChar != '\\' && !bSingleQuote ){ + bDoubleQuote = xbTrue; + } + else if( !bDoubleQuote && !bSingleQuote && data[i] == c ){ + j++; + } + cPrevChar = data[i]; + } + return j; + } +} +/************************************************************************/ +//! @brief Count the number of characters in the string. +/*! + \param c Character to count. + \returns The number of characters. +*/ +xbUInt32 xbString::CountChar( char c ) const { + xbUInt32 i,j; + for( i = 0,j = 0; i < size; i++ ) + if( data[i] == c ) + j++; + return j; +} +/************************************************************************/ +void xbString::ctor( const char *s ) { + + // this routine assumes it was called by one of the constructors. + + if (s == NULL) { + data = NULL; + size = 0; + return; + } + + size = (xbUInt32) (strlen(s) + 1); + data = (char *) calloc( 1, size); + + xb_strcpy(data, s); +} +/************************************************************************/ +//! @brief Convert hex character to string. +/*! + This routine converts a four byte string in the format of 0x00 to a one byte char value. + The first four bytes of the string must be in the format 0x00. + Anything past the first four bytes is disregarded. + + \param cOut Output character. + \returns XB_INVALID_PARM on error
+ XB_NO_ERROR on success. +*/ +xbInt16 xbString::CvtHexChar( char &cOut ){ + + int j, k; + char c; + + if( Len() < 4 || data[0] != '0' || (data[1]!='X' && data[1]!='x' )) + return XB_INVALID_PARM; + + c = (char) toupper( data[2] ); + j = ( c > '9' ? c - 'A' + 10 : c - '0' ); + c = (char)toupper( data[3] ); + k = ( c > '9' ? c - 'A' + 10 : c - '0' ); + j = ( j << 4 ) + k; + + cOut = ( char ) j; + return XB_NO_ERROR; +} +/************************************************************************/ +//! @brief Convert string of hex characters to string. +/*! + + This routine converts a string of four byte format of 0x00 to a string of one byte chars. + + \param sOut Output string of converted characters. + \returns XB_INVALID_PARM on error
+ XB_NO_ERROR on success. +*/ +xbInt16 xbString::CvtHexString( xbString &sOut ){ + char c; + xbString ws; + ws = data; + sOut = ""; + xbInt16 iRc; + while( ws.Len()){ + if(( iRc= ws.CvtHexChar( c )) != XB_NO_ERROR ) + return iRc; + sOut += c; + ws.Ltrunc( 4 ); + } + return XB_NO_ERROR; +} +/************************************************************************/ +//! @brief Convert string to xbUInt64 number +/*! + \param ullOut - output unsigned long long. + \returns XB_NO_ERROR +*/ +xbInt16 xbString::CvtULongLong( xbUInt64 &ullOut ){ + + // version 1 - fast, but no data checking + ullOut = 0; + char *s = data; + int i = 0; + while( *s ){ + ullOut *= 10; + ullOut += (xbUInt64) *s - '0'; + s++; + i++; + } + return XB_NO_ERROR; +} +/************************************************************************/ +//! @brief Convert string to xbInt64 number +/*! + \param llOut - output long long. + \returns XB_NO_ERROR +*/ +xbInt16 xbString::CvtLongLong( xbInt64 &llOut ){ + + // version 1 - fast, but no data checking + llOut = 0; + char *s = data; + int i = 0; + while( *s ){ + llOut *= 10; + llOut += (xbInt64) *s - '0'; + s++; + i++; + } + return XB_NO_ERROR; +} + +/************************************************************************/ +#ifdef XB_DEBUG_SUPPORT +void xbString::Dump( const char * title, xbInt16 iHexOption ) const { + fprintf(stdout, "%s StringSize[%d] DataLen=[%d] data=[%s]\n", title, size, Len(), data ); + if( iHexOption ){ + std::cout << "Hex values" << std::endl; + for( xbUInt32 i = 0; i < strlen( data ); i++ ) + printf( " %x", data[i] ); + std::cout << std::endl; + } +} +void xbString::Dump( const char * title ) const { + Dump( title, 0 ); +} + +void xbString::DumpHex( const char * title ) const { + Dump( title, 1 ); +} +#endif + +/************************************************************************/ +//! @brief Extract an element out of a delimited string. +/*! + \param sSrc Source string. + \param cDelim Delimiter. + \param lSkipCnt Number of delimiters to skip. + \param iOpt 0 - ignore single and double quotes.
+ 1 - ignore delimiters between single or double quotes. + \returns Reference to string extracted from element. +*/ +xbString &xbString::ExtractElement( xbString &sSrc, char cDelim, xbUInt32 lSkipCnt, xbInt16 iOpt ) +{ + return ExtractElement( sSrc.Str(), cDelim, lSkipCnt, iOpt ); +} + +/************************************************************************/ +//! @brief Extract an element out of a delimited string. +/*! + \param pSrc Source string. + \param cDelim Delimiter. + \param lSkipCnt Number of delimiters to skip. + \param iOpt 0 - ignore single and double quotes.
+ 1 - ignore delimiters between single or double quotes. + \returns Reference to string extracted from element. +*/ +xbString &xbString::ExtractElement( const char *pSrc, char cDelim, xbUInt32 lSkipCnt, xbInt16 iOpt ) +{ + /* opt values + 0 - ignore single and double quotes + 1 - ignore delimiters between single or double quotes + */ + + xbUInt32 lLen; + xbUInt32 lCurCnt = 0; + xbBool bInSingleQuotes = xbFalse; + xbBool bInDoubleQuotes = xbFalse; + char cPrevChar = 0x00; + const char *s = pSrc; + const char *pAnchor; + + /* skip past skipcnt delimiters */ + while( *s && lCurCnt < (lSkipCnt-1) ){ + if( iOpt == 0 ){ + if( *s == cDelim ) + lCurCnt++; + } else { + if( *s == cDelim && !bInSingleQuotes && !bInDoubleQuotes ){ + lCurCnt++; + } else if( *s == '\'' && !bInDoubleQuotes && cPrevChar != '\\' ){ + if( bInSingleQuotes == xbTrue ) + bInSingleQuotes = xbFalse; + else + bInSingleQuotes = xbTrue; + } else if( *s == '"' && !bInSingleQuotes && cPrevChar != '\\' ){ + if( bInDoubleQuotes == xbTrue ) + bInDoubleQuotes = xbFalse; + else + bInDoubleQuotes = xbTrue; + } + } + cPrevChar = *s; + s++; + } + + /* at the beginning of the field */ + pAnchor = s; + xbBool bDone = xbFalse; + while( *s && !bDone ){ + if( iOpt == 0 ){ + if( *s == cDelim ) + bDone = xbTrue; + } else { + if( *s == cDelim && !bInSingleQuotes && !bInDoubleQuotes ){ + bDone = xbTrue; + } else if( *s == '\'' && !bInDoubleQuotes && cPrevChar != '\\' ){ + if( bInSingleQuotes == xbTrue ) + bInSingleQuotes = xbFalse; + else + bInSingleQuotes = xbTrue; + } else if( *s == '"' && !bInSingleQuotes && cPrevChar != '\\' ){ + if( bInDoubleQuotes == xbTrue ) + bInDoubleQuotes = xbFalse; + else + bInDoubleQuotes = xbTrue; + } + } + cPrevChar = *s; + s++; + } + + // if at end of string, go back one and drop the delimiter + if( *s ) s--; + + lLen = (xbUInt32)(s - pAnchor); + + /* copy data */ + data = (char *) realloc( data, lLen+1 ); + memcpy( data, pAnchor, lLen ); + data[lLen] = 0; + this->size = lLen+1; + return *this; +} + +/************************************************************************/ +//! @brief Get a character by position +/*! + \param n - Position in string to extract. First position is 1 (not 0). + \returns Character from position n, or null. +*/ +char xbString::GetCharacter( xbUInt32 n ) const { + if( n > 0 && n <= size ) + return data[n-1]; + else + return 0x00; +} +/************************************************************************/ +//! @brief Get the position of the last occurrence of a given character. +/*! + \param c - Character to search for. + \returns Last position of character in the string. +*/ +xbUInt32 xbString::GetLastPos(char c) const { + + if (data == NULL) + return 0; + + char *p = data; + xbUInt32 iPos = 0; + xbUInt32 hPos = 0; + while( *p && iPos++ < ( size - 1 )){ + if( *p == c ) + hPos = iPos; + p++; + } + if( hPos ) + return hPos; + else + return 0; +} +/************************************************************************/ +//! @brief Get the position of the last occurrence of a given string. +/*! + \param s - String to search for. + \returns Last position of character in the string. +*/ +xbUInt32 xbString::GetLastPos(const char* s) const{ + + if (data == NULL) + return 0; + + char *p = data; + char *saveP = NULL; + while( p ){ + p = strstr( p, s); + if( p ){ + saveP = p; + p++; + } + } + if (saveP == NULL) + return 0; + return (xbUInt32)(saveP - data) + 1; +} +/************************************************************************/ +//! @brief Get the path separator out of the string. +/*! + This method assumes the string is a valid path name. + If it is, it returns either / or \. + \returns Char value containing either / or \ depending on OS. +*/ +char xbString::GetPathSeparator() const { + + if (data == NULL) + return 0x00; + char *p = data; + while( *p ){ + if( *p == '\\' || *p == '/' ) + return *p; + else + p++; + } + return 0x00; +} + +/************************************************************************/ +//! @brief Retrieve the size of the string buffer. +/*! + \returns Size of string buffer including the null terminating byte. +*/ +xbUInt32 xbString::GetSize() const { + return size; +} + +/************************************************************************/ +//! @brief Determine if the string has any alpha characters +/*! + \returns xbTrue - String contains one or more aloha characters.
+ xbFalse - String contains no alpha characters. +*/ +xbBool xbString::HasAlphaChars() const { + for( xbUInt32 i = 0; i < size; i++ ) + if( isalpha( data[i] )) + return xbTrue; + return xbFalse; +} + + +/************************************************************************/ +//! @brief Determine if string is empty +/*! + \returns xbTrue if string is empty.
+ xbFalse if string is not empty. +*/ +xbBool xbString::IsEmpty() const { + if( data == NULL ) + return true; + if( data[0] == 0 ) + return xbTrue; + return xbFalse; +} + +/************************************************************************/ +//! @brief Determine if string is NULL +/*! + \returns xbTrue if string is NULL.
+ xbFalse if string is not NULL. +*/ +xbBool xbString::IsNull() const { + return( data == NULL ); +} + + +/************************************************************************/ +//! @brief Retain left part of string, drop rightmost characters. +/*! + \param ulLen New string length, truncate rightmost excess. + \returns Reference to string. +*/ +xbString &xbString::Left( xbUInt32 ulLen ) { + return Mid( 1, ulLen ); +} + +/************************************************************************/ +//! @brief Retrieve length of current string. +/*! + \returns String length, excluding the terminating null byte. +*/ +// return length of string +xbUInt32 xbString::Len() const { + return( data ? (xbUInt32) strlen(data) : 0 ); +} + +/************************************************************************/ +//! @brief Left trim white space from string. +/*! + \returns Reference to this string. +*/ +xbString &xbString::Ltrim(){ + + if( !data ) + return *this; + + char *p = data; + if( !*p || (*p && *p != ' ') ) + return *this; /* nothing to do */ + + xbUInt32 s = 0; + while( *p && *p == ' ' ){ + p++; + s++; + size--; + } + + xbUInt32 i; + for( i = 0; i < size; i++ ) + data[i] = data[i+s]; + data[i] = 0x00; + data = (char *) realloc( data, size ); + + return *this; + +} + +/************************************************************************/ +//! @brief Left truncate string +/*! + \param ulCnt Number of bytes to remove from the left. + \returns Reference to this string. +*/ +xbString &xbString::Ltrunc( xbUInt32 ulCnt ){ + if( ulCnt >= size ){ + if( size > 0 ){ + free( data ); + data = NULL; + size = 0; + } + return *this; + } + + char * ndata; + char * p; + ndata = (char *) calloc( 1, size - ulCnt ); + p = data; + p += ulCnt; + xb_strcpy( ndata, p ); + free( data ); + data = ndata; + size = size - ulCnt; + return *this; +} + +/************************************************************************/ +//! @brief Extract portion of data from string +/*! + \param ulStartPos Starting position + \param ulTargLen Length + \returns Reference to string +*/ +xbString &xbString::Mid( xbUInt32 ulStartPos, xbUInt32 ulTargLen ){ + + // this is a 1 based routine + if( ulStartPos == 0 ) + return *this; + + if( data == NULL ) + return( *this ); + if( data[0] == 0 ) + return( *this ); + if( ulStartPos > Len() ) + return( *this ); +/* + // Resize( ulTargLen + 1 ); + char *pTarg = data; + char *pSrc = data + ulStartPos - 1; + for( xbUInt32 l = 0; l < ulTargLen; l++ ) + *pTarg++ = *pSrc++; + *pTarg = 0x00; + // Resize( ulTargLen + 1 ); + */ + + char * newData = (char *) calloc( 1, ulTargLen + 1 ); + char *pTarg = newData; + char *pSrc = data + ulStartPos - 1; + for( xbUInt32 l = 0; l < ulTargLen; l++ ) + *pTarg++ = *pSrc++; + *pTarg = 0x00; + + free( data ); + data = newData; + size = ulTargLen + 1; + + return *this; +} + +/************************************************************************/ +//! @brief Left pad string +/*! + \param c Padding character. + \param ulNewLen New string length. + \returns Reference to this string. +*/ +xbString &xbString::PadLeft( char c, xbUInt32 ulNewLen ){ + + xbUInt32 srcLen; + if( data ) + srcLen = (xbUInt32) strlen( data ); + else + srcLen = 0; + + if( srcLen >= ulNewLen ) + return *this; + + char * newData = (char *) calloc( 1, ulNewLen + 1 ); + xbUInt32 i; + for( i = 0; i < ulNewLen - srcLen; i++ ) + newData[i] = c; + + char *targ = &newData[i]; + xb_strcpy( targ, data ); + free( data ); + data = newData; + size = ulNewLen + 1; + return *this; +} + +/************************************************************************/ +//! @brief Right pad string +/*! + \param c Padding character. + \param ulNewLen New string length. + \returns Reference to this string. +*/ +xbString &xbString::PadRight( char c, xbUInt32 ulNewLen ){ + xbUInt32 srcLen = (xbUInt32) strlen( data ); + if( srcLen >= ulNewLen ) + return *this; + data = (char *) realloc( data, ulNewLen + 1 ); + xbUInt32 i; + for( i = srcLen; i < ulNewLen; i++ ) + data[i] = c; + data[i] = 0x00; + size = ulNewLen + 1; + return *this; +} + +/************************************************************************/ +//! @brief Determine position of a given character +/*! + \param c Seek character + \param ulStartPos starting position for search, first position is 1 + \returns Position within string. Returns 0 if not found. +*/ +xbUInt32 xbString::Pos(char c, xbUInt32 ulStartPos ) const { + + if (data == NULL) + return 0; + char *p = data; + + if( ulStartPos >= size ) + return 0; + + xbUInt32 iPos = 0; + while( (iPos+1) < ulStartPos ){ + p++; + iPos++; + } + xbBool bFound = 0; + while( *p && !bFound && iPos < ( size - 1 )){ + if( *p == c ) + bFound = 1; + else { + iPos++; + p++; + } + } + + if( bFound ) + return iPos + 1; + else + return 0; +} + +/************************************************************************/ +//! @brief Determine position of a given character +/*! + \param c Seek character + \returns Position within string. Returns 0 if not found. +*/ +xbUInt32 xbString::Pos(char c) const { + + if (data == NULL) + return 0; + char *p = data; + xbUInt32 iPos = 0; + int iFound = 0; + while( *p && !iFound && iPos < ( size - 1 )){ + if( *p == c ) + iFound = 1; + else { + iPos++; + p++; + } + } + if( iFound ) + return iPos + 1; + else + return 0; +} + +/************************************************************************/ +//! @brief Determine position of a given substring +/*! + \param s Substring + \returns Position within string. Returns 0 if not found. +*/ +xbUInt32 xbString::Pos(const char* s) const{ + + if (data == NULL) + return 0; + + char *p = strstr(data, s); + if ( p == NULL) + return 0; + + return (xbUInt32)(p - data + 1); +} + +/************************************************************************/ +//! @brief Insert character into string +/*! + \param ulPos Insertion position. + \param c Character to insert. + \returns Reference to this string. +*/ +xbString &xbString::PutAt(xbUInt32 ulPos, char c){ + if((ulPos-1) > Len() ) + return *this; + data[ulPos-1] = c; + return *this; +} + +/************************************************************************/ +//! @brief Remove portion of string. +/*! + \param ulStartPos Starting position for removal operation. + \param ulDelSize Size of deletion. + \returns Reference to string. +*/ +xbString &xbString::Remove(xbUInt32 ulStartPos, xbUInt32 ulDelSize ) { + if( data == NULL ) + return( *this ); + if( data[0] == 0 ) + return( *this ); + xbUInt32 srcLen = Len(); + + if( ulStartPos > srcLen || ulStartPos < 1 || ulDelSize < 1 ) + return( *this ); + + if(( ulStartPos + ulDelSize - 1) >= size ){ + data[ulStartPos-1] = 0x00; + size = ulStartPos; + return( *this ); + } + + char *t; + char *s; + t = data + (ulStartPos - 1); + s = t + ulDelSize; + size -= ulDelSize; + while( *s ) + *t++ = *s++; + *t = 0x00; + return( *this ); +} + +/************************************************************************/ +//! @brief Replace a value within a string with another value +/*! + \param sReplace - Character string to replace. + \param sReplaceWith - Character string to replace with + \param iOption - 0 = All occurrences, 1 = first occurrence + \returns Reference to this string. +*/ + +//the new size includes the null termination byte +xbString &xbString::Replace( const char *sReplace, const char *sReplaceWith, xbInt16 iOption ){ + + xbBool bDone = xbFalse; + xbUInt32 ulPos; + xbUInt32 ulNewLen; + xbUInt32 ulReplaceWithLen; + xbUInt32 ulRsLen; // size of right side of string after replaced data + xbUInt32 ulSp2; + char *sBuf2; + + const char *s; // source ptr + char *t; // target ptr + + while( !bDone ){ + ulPos = Pos( sReplace ); + if( ulPos == 0 ){ + bDone = xbTrue; + } else { + + ulReplaceWithLen = (xbUInt32) strlen( sReplaceWith ); + ulNewLen = this->size + ulReplaceWithLen - (xbUInt32) strlen( sReplace ); + sBuf2 = (char *) calloc( 1, ulNewLen ); + + // copy part1 + t = sBuf2; + s = data; + for( xbUInt32 ul = 0; ul < ulPos-1; ul++ ) + *t++ = *s++; + + // copy part2 + s = sReplaceWith; + for( xbUInt32 ul = 0; ul < ulReplaceWithLen; ul++ ) + *t++ = *s++; + + // copy part3 + ulSp2 = ulPos + (xbUInt32) strlen( sReplace ); + s = data; + s+= (ulSp2 - 1); + ulRsLen = (xbUInt32) strlen( s ); + for( xbUInt32 ul = 0; ul < ulRsLen; ul++ ) + *t++ = *s++; + + if( iOption ) + bDone = xbTrue; + + free(data); + data = sBuf2; + size = ulNewLen; + } + } + return *this; +} + +/************************************************************************/ +//! @brief Resize a string +/*! + \param ulSize - New string size, including null termination byte. + \returns Reference to this string. +*/ +//the new size includes the null termination byte +xbString &xbString::Resize(xbUInt32 ulSize) { + +// data = (char *) realloc( data, ulSize ); + + +// original + data = (char *) realloc( data, ulSize ); + + if( ulSize > 0 ) + data[ulSize-1] = 0; + this->size = ulSize; + return *this; +} +/************************************************************************/ +//! @brief Right trim the string. +/*! + This routine removes any trailing white space on the string. + + \returns Reference to string. +*/ +xbString &xbString::Rtrim(){ + + xbUInt32 l = Len(); + if( l == 0 ) + return *this; + + xbUInt32 ulOrigSize = size; + l--; + + for(;;) { + if( data[l] != ' ' ) + break; + data[l] = 0; + size--; + if( l == 0 ) + break; + l--; + } + + if( ulOrigSize != size ) + data = (char * ) realloc( data, size ); + return *this; +} + +/************************************************************************/ +//! @brief Set the value of the string. +/*! + + Note: This routine fails if you try to set the string to itself or some part of itself. + + \param s Value to set the string. + \returns Reference to string. + +*/ +xbString &xbString::Set( const char *s ) { + + if(data != NULL){ + free(data); + data = NULL; + } + if( s == NULL || !*s ) { + if( data ) + free( data ); + data = NULL; + size = 0; + } else { + //data = (char *)calloc(1, strlen(s) + 1 ); + data = (char *) realloc( data, strlen(s) + 1 ); + xb_strcpy(data, s); + size = (xbUInt32) (strlen(data) + 1); + } + return (*this); +} +/************************************************************************/ +//! @brief Set the value of the string. +/*! + \param s Value to set the string. + \returns Reference to string. +*/ +xbString &xbString::Set( const xbString &s ) { + +// if( s.Str() == NULL || s.Len() == 0 ){ + + if( s.Str() == NULL ){ + if( data ) free( data ); + data = NULL; + size = 0; + } else { + xbUInt32 ulLen = s.Len(); + char *p = (char *) calloc( 1, ulLen + 1 ); + xb_strcpy( p, s.Str()); + size = ulLen + 1; + if( data ) free( data ); + data = p; + } + return (*this ); +} + +/************************************************************************/ +//! @brief Set the value of the string. +/*! + + Note: This routine fails if you try to set the string to itself or some part of itself. + + \param s Value to set the string. + \param ulSize Maximum size of resultant string. + \returns Reference to string. +*/ + +xbString &xbString::Set(const char *s, xbUInt32 ulSize) { + + if( data != NULL ) + free( data ); + + if(s == NULL) { + data = NULL; + size = 0; + return (*this); + } + + data = (char *) calloc( 1, ulSize+1 ); + char *pTarget = data; + for( xbUInt32 i = 0; i < ulSize; i++ ){ + *pTarget = *s; + pTarget++; + s++; + } + this->size = ulSize + 1; + return *this; +} + + +/************************************************************************/ +//! @brief Set the string to long integer numeric value. +/*! + \param lNum Value to set the string + \returns Reference to this string. +*/ +xbString &xbString::SetNum(xbInt32 lNum) { + Sprintf("%ld", lNum); + return *this; +} + +/************************************************************************/ +//! @brief Printf routine for formatting a string. +/*! + See documentation on the standard C printf function for how to use this. + + MyString.Sprintf( "a number %d some text %s", 100, "test text data" ); + + \param sFormat A format specifier + \returns Reference to a formatted string +*/ +xbString &xbString::Sprintf( const char *sFormat, ...) { + + xbInt32 iRc; + va_list ap; + char *t; + +#ifdef HAVE__VSNPRINTF_S_F + + va_start( ap, sFormat ); + size = (xbUInt32) _vsnprintf_s( NULL, 0, sFormat, ap ) + 1; + va_end( ap ); + + t = (char *) malloc( size ); + if( !t ){ + size = 0; + return( *this ); + } + + va_start( ap, sFormat ); + iRc = _vsnprintf_s( t, size, sFormat, ap ); + va_end( ap ); + +#else +#ifdef HAVE_VSPRINTF_S_F + + va_start( ap, sFormat ); + // size = (xbUInt32) vsprintf_s( NULL, 0, sFormat, ap ) + 1; + size = (xbUInt32) _vscprintf( sFormat, ap ) + 1; + va_end( ap ); + + t = (char *) malloc( size ); + if( !t ){ + size = 0; + return( *this ); + } + + va_start( ap, sFormat ); + iRc = vsprintf_s( t, size, sFormat, ap ); + va_end( ap ); + +#else +#ifdef HAVE_VSNPRINTF_F + + va_start( ap, sFormat ); + size = (xbUInt32) vsnprintf( NULL, 0, sFormat, ap) + 1; + va_end( ap ); + + t = (char *) calloc( 1, size ); + if( !t ){ + size = 0; + return( *this ); + } + va_start( ap, sFormat ); + iRc = vsnprintf( t, size, sFormat, ap ); + va_end( ap ); + +# else +# error "Fatal error building [xbstring.cpp] - You have neither _vsnprintf_s nor vsnprintf_s." +# endif +#endif +#endif + + if( iRc < 0 ){ + if( data ) + free( data ); + data = NULL; + size = 0; + } else { + if( data ) + free( data ); + data = t; + } + return( *this ); +} + +/************************************************************************/ +//! @brief Return string data +/*! + \returns char * to string data or NULL if string is empty +*/ +const char *xbString::Str() const { + return data ? data : NullString; +} + +/************************************************************************/ +//! @brief Copy all or part of string to character array +/*! + \param cDest pointer to destination buffer. + \param n Number of bytest to copy. It is the responsibility of the application + to verify the buffer is large enough to hold the string contents. + \returns char * to result + +*/ +char *xbString::strncpy( char * cDest, xbUInt32 n ) const { + xbUInt32 i; + xbUInt32 ulLen; + n > (size-1) ? ulLen = size-1 : ulLen = n; + memset( cDest, 0x00, ulLen ); + for( i = 0; i < ulLen; i++ ) + cDest[i] = data[i]; +// cDest[i] = 0x00; + return cDest; +} +/************************************************************************/ +//! @brief Swap characters +/*! + \param cFrom character to replace. + \param cTo character to replace with. + \returns Reference to this string. +*/ +xbString &xbString::SwapChars( char cFrom, char cTo ){ + xbUInt32 i; + for( i = 0; i < size; i++ ) + if( data[i] == cFrom ) + data[i] = cTo; + return *this; +} + + +/************************************************************************/ +//! @brief Replace all upper case charaters with lower case characters +/*! + \returns Reference to this string. +*/ +xbString &xbString::ToLowerCase(){ + xbUInt32 Len = this->Len(); + for (xbUInt32 i=0; iLen(); + for (xbUInt32 i=0;i + + \returns xbTrue if the data is valid logical data.
+ xbFalse if not valid logical data. +*/ + +xbBool xbString::ValidLogicalValue() const { + if( Len() == 1 ) + if( data[0] == 'T' || data[0] == 'F' || data[0] == 'Y' || data[0] == 'N' || data[0] == '?' ) + return xbTrue; + return xbFalse; +} +/************************************************************************/ +//! @brief This function returns true if the data is valid numeric data +/*! + \returns xbTrue if valid numeric data.
+ xbFalse if not valid numeric daata. +*/ + +xbBool xbString::ValidNumericValue() const { + const char *p; + p = data; + while( *p ){ + if( *p != '+' && *p != '-' && *p != '.' && *p != '0' && *p != '1' && + *p != '2' && *p != '3' && *p != '4' && *p != '5' && *p != '6' && + *p != '7' && *p != '8' && *p != '9' ) + return xbFalse; + else + p++; + } + return xbTrue; +} + + +/************************************************************************/ +//! @brief Remove every instance of a character from a string. +/*! + \param c character to remove from string. + \returns Reference to this stirng.void +*/ + +xbString &xbString::ZapChar( char c ){ + + if( data == NULL ) + return *this; + if( data[0] == 0 ) + return *this; + + char *s; + char *t; + + s = data; + t = data; + while( *s ){ + if( *s == c ){ + s++; + size--; + } else { + *t++ = *s++; + } + } + *t = 0x00; + + data = (char *) realloc( data, size ); + return *this; +} + +/************************************************************************/ +//! @brief Remove leading character from a string. +/*! + \param c character to remove from beginning of string. + \returns Reference to this string. +*/ +xbString &xbString::ZapLeadingChar( char c ){ + /* left truncate all of character c */ + xbUInt32 iLen = 0; + char *p; + p = data; + while( *p && *p == c ){ + iLen++; + p++; + } + if( iLen ) + Ltrunc( iLen ); + return *this; +} + +/************************************************************************/ +//! @brief Remove trailing character from a string. +/*! + \param c character to remove from ending of string. + \returns Reference to this string. +*/ +xbString &xbString::ZapTrailingChar( char c ){ + + xbUInt32 l = Len(); + if( l == 0 ) + return *this; + xbUInt32 ulOrigSize = size; + l--; + for(;;) { + if( data[l] != c ) + break; + data[l] = 0; + size--; + if( l == 0 ) + break; + l--; + } + if( ulOrigSize != size ) + data = (char *) realloc( data, size ); + return *this; +} + +} /* namespace */ \ No newline at end of file diff --git a/src/core/xbtag.cpp b/src/core/xbtag.cpp new file mode 100755 index 0000000..621d44b --- /dev/null +++ b/src/core/xbtag.cpp @@ -0,0 +1,121 @@ +/* xbtag.cpp + + +XBase64 Software Library + +Copyright (c) 1997,2003,2014,2022,2023 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" + +#ifdef XB_INDEX_SUPPORT + + +namespace xb{ + +/************************************************************************/ +//! @brief Constructor +/*! + \param pIx Pointer to index file instance. + \param vpTag Pointer to tag structure within file instance. + \param sType NDX or MDX + \param sTagName Name of tag. + \param sExpression Tag key definition. + \param sFilter MDX only - tag qualification expression. + \param bUnique xbTrue - Index is unique.
xbFalse - Index is not unique. + \param bSort MDX only
xbTrue - Descending.
xbFalse - Ascending. +*/ + +xbTag::xbTag( xbIx *pIx, void *vpTag, xbString &sType, xbString &sTagName, xbString &sExpression, xbString &sFilter, +xbBool bUnique, xbBool bSort ) +{ + this->pIx = pIx; + this->vpTag = vpTag; + this->sType = sType; + this->sTagName = sTagName; + this->sExpression = sExpression; + this->sFilter = sFilter; + this->bUnique = bUnique; + this->bSort = bSort; +} + + +/************************************************************************/ +//! @brief Get tag key expression. +/*! + \returns Tag key expression. +*/ +const xbString &xbTag::GetExpression() const { + return sExpression; +} +/************************************************************************/ +//! @brief Get tag filter expression. +/*! + \returns Tag filter expression (mdx only). +*/ + +const xbString &xbTag::GetFilter() const { + return sFilter; +} +/************************************************************************/ +//! @brief Get index file pointer. +/*! + \returns Pointer to index file instance. +*/ +xbIx *xbTag::GetIx() const { + return pIx; +} +/************************************************************************/ +//! @brief Get tag ascending setting. +/*! + \returns Tag sort setting - MDX only.
xbTrue - Descending.
xbFalse - Ascending. +*/ +xbBool xbTag::GetSort() const { + return bSort; +} +/************************************************************************/ +//! @brief Get tag name. +/*! + \returns Tag name. +*/ + +const xbString &xbTag::GetTagName() const { + return sTagName; +} +/************************************************************************/ +//! @brief Get tag type. +/*! + \returns Tag type. +*/ +const xbString &xbTag::GetType() const { + return sType; +} + +/************************************************************************/ +//! @brief Get tag unique setting. +/*! + \returns Tag unique setting.
xbTrue - Unique.
xbFalse - Not unique. +*/ + +xbBool xbTag::GetUnique() const { + return bUnique; +} +/************************************************************************/ +//! @brief Get tag pointer for tag within index file. +/*! + \returns Pointer to tag within index file instance. +*/ +void *xbTag::GetVpTag() const { + return vpTag; +} +/************************************************************************/ +} /* namespace */ +#endif /* XB_INDEX_SUPPORT */ \ No newline at end of file diff --git a/src/core/xbtblmgr.cpp b/src/core/xbtblmgr.cpp new file mode 100755 index 0000000..53b6dd9 --- /dev/null +++ b/src/core/xbtblmgr.cpp @@ -0,0 +1,312 @@ +/* xbtblmgr.cpp + +XBase64 Software Library + +Copyright (c) 1997,2003,2014,2022,2023 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" + +//#ifdef HAVE_STRING_H +//#include +//#endif + +namespace xb{ + +/*************************************************************************/ +xbTblMgr::xbTblMgr(){ + TblList = NULL; + iOpenTableCount = 0; +} + +/*************************************************************************/ +xbTblMgr::~xbTblMgr(){ + xbTblList *l; + if( TblList ){ + while( TblList ){ + l = TblList; + TblList = TblList->pNext; + delete l->psFqTblName; + delete l->psTblName; + delete l->psTblAlias; + free( l ); + } + } +} + +/*************************************************************************/ +xbInt16 xbTblMgr::AddTblToTblList( xbDbf *d, const xbString & sFqTblName ){ + return AddTblToTblList( d, sFqTblName, "" ); +} + +/*************************************************************************/ +xbInt16 xbTblMgr::AddTblToTblList( xbDbf *d, const xbString & sFqTblName, const xbString & sTblAlias ) { + + // Set the Fq (fully qualified name) + // Pull the table name from the FQ name + // Set the Alias to the table name if the alias name is not provided + + + xbTblList *i, *s, *t; + xbInt16 iRc = 0; + xbInt16 iErrorStop = 0; + xbString sTblName; + xbString sAlias; + xbString sTemp; + xbString sFqTemp; + xbUInt32 iSlashPos; + + // std::cout << "AddTblToTblList fq in = [" << sFqTblName.Str() << "] alias in =[" << sTblAlias.Str() << "]\n"; + + try{ + + if( sFqTblName.Len() == 0 ){ + iErrorStop = 100; + iRc = XB_INVALID_TABLE_NAME; + throw iRc; + } + + sTblName = sFqTblName; + sTblName.SwapChars( '\\', '/' ); + iSlashPos = sTblName.GetLastPos( '/' ); + + // std::cout << "slashpos = " << iSlashPos << "\n"; + + + if( iSlashPos > 0 ){ + sTblName.Ltrunc( iSlashPos ); // remove the directory part from the table name + sFqTemp = sFqTblName; + } else{ + sFqTemp.Sprintf( "%s%s", GetDataDirectory().Str(), sFqTblName.Str()); // add the dir part to the FQ name + } + + xbUInt32 iDbfPos = sFqTemp.Pos( ".DBF" ); + if( iDbfPos == 0 ) + sFqTemp += ".DBF"; + else + sTblName.Resize( sTblName.Len() - 3 ); + + if( sTblAlias.Len() == 0 ) + sAlias = sTblName; + else + sAlias = sTblAlias; + + //std::cout << "fq=[" << sFqTemp.Str() << "] tblname = [" << sTblName.Str() << "] alias = [" << sAlias.Str() << "]\n"; + + if((i = (xbTblList *) calloc(1, sizeof(xbTblList))) == NULL){ + iErrorStop = 110; + iRc = XB_NO_MEMORY; + throw iRc; + } + i->psFqTblName = new xbString( sFqTemp ); + i->psTblName = new xbString( sTblName ); + i->psTblAlias = new xbString( sAlias ); + i->pDbf = d; + i->pNext = NULL; + + // insert new table into the list of open tables, sorted by table name + s = NULL; + t = TblList; + + while(t && (strcmp( t->psTblAlias->Str(), sAlias.Str()) < 0 )){ + s = t; + t = t->pNext; + } + + if( t && (strcmp( t->psTblAlias->Str(), sAlias.Str()) == 0 )){ + iErrorStop = 120; + delete i->psFqTblName; + delete i->psTblName; + delete i->psTblAlias; + free( i ); + iRc = XB_DUP_TABLE_OR_ALIAS; + throw iRc; + } + i->pNext = t; + if (s == NULL) + TblList = i; + else + s->pNext = i; + } + catch (xbInt16 iRc ){ + if( iErrorStop != 120 ){ + xbString sMsg; + sMsg.Sprintf( "xbTblMgr::AddTblToTblList() Exception Caught. Error Stop = [%d] iRc = [%d] Tbl Name = [%s] Alias = [%s]", iErrorStop, iRc, sTblName.Str(), sTblAlias.Str() ); + std::cout << sMsg << std::endl; + } + } + if( iRc == XB_NO_ERROR ) + iOpenTableCount++; + return iRc; +} + +/*************************************************************************/ +xbInt16 xbTblMgr::DisplayTableList() const { + xbInt16 iTblCnt = 0; + xbTblList * l = TblList; + std::cout << "-- Open Table List --" << std::endl; + if( l == NULL ) + std::cout << "Table list is empty" << std::endl; + else{ + while( l ){ + iTblCnt++; + std::cout << iTblCnt << " FqFileName=[" << l->psFqTblName->Str() << "] TableName=[" << l->psTblName->Str() << "] Alias=[" << l->psTblAlias->Str() << "]" << std::endl; + l = l->pNext; + } + } + return iTblCnt; +} +/*************************************************************************/ +/* Get pointer to named dbf. + Looks up an open DBF file by Name. + + returns A pointer to the xbDbf class instance if found or NULL if not found. + + // looks for a match as an alias first, if not found as an alias, looks at the name + +*/ + +xbDbf *xbTblMgr::GetDbfPtr(const xbString& sTblAlias) const { + + + xbTblList *t; + t = TblList; + xbString s; + xbUInt32 ui = sTblAlias.Pos( ".DBF" ); + if( ui > 0 ) + s.Assign( sTblAlias.Str(), 1, ui - 1 ); + else + s.Set( sTblAlias ); + + while( t ){ + if( s == t->psTblAlias->Str()){ + return t->pDbf; + } + t = t->pNext; + } + + t = TblList; + while( t ){ + // std::cout << "s = [" << s.Str() << "] tbl name = [" << t->psTblName->Str() << "]\n"; + if( s == t->psTblName->Str()){ + std::cout << "found\n"; + return t->pDbf; + } + t = t->pNext; + } + + + t = TblList; + while( t ){ + if( sTblAlias == t->psFqTblName->Str()) + return t->pDbf; + t = t->pNext; + } + return NULL; +} +/*************************************************************************/ +/* Get pointer to named dbf. + Looks up an open DBF file by Name. + + returns pointer to the xbDbf class instance if found or NULL if not found. +*/ + +xbDbf *xbTblMgr::GetDbfPtr(xbInt16 iItemNo ) const { + + xbTblList *t; + t = TblList; + xbInt16 iCnt = 1; + + if( iItemNo < 1 || iItemNo > iOpenTableCount ) + return NULL; + + while( t && iCnt < iItemNo ){ + t = t->pNext; + iCnt++; + } + if( t ) + return t->pDbf; + else + return NULL; +} +/*************************************************************************/ +xbInt16 xbTblMgr::GetOpenTableCount() const { + return iOpenTableCount; +} +/*************************************************************************/ +xbTblList * xbTblMgr::GetTblListEntry( xbDbf *pTbl ){ + + xbTblList * i = TblList; + while( i ){ + if( i->pDbf == pTbl ) + return i; + i = i->pNext; + } + return NULL; +} +/*************************************************************************/ +xbInt16 xbTblMgr::RemoveTblFromTblList( const xbString & sTblAlias ) { + xbTblList *i, *s; + + i = TblList; + s = NULL; + + while( i ){ + + if( strcmp( i->psTblAlias->Str(), sTblAlias.Str()) == 0 ) { + if(s) + s->pNext = i->pNext; + else + TblList = i->pNext; + + delete i->psFqTblName; + delete i->psTblName; + delete i->psTblAlias; + free( i ); + iOpenTableCount--; + return XB_NO_ERROR; + } else { + s = i; + i = i->pNext; + } + } + return XB_NOT_FOUND; +} +/*************************************************************************/ +xbInt16 xbTblMgr::RemoveTblFromTblList( xbDbf *pTbl ) { + xbTblList *i, *s; + + i = TblList; + s = NULL; + + while( i ){ + + if( i->pDbf == pTbl ) { + if(s) + s->pNext = i->pNext; + else + TblList = i->pNext; + + delete i->psFqTblName; + delete i->psTblName; + delete i->psTblAlias; + free( i ); + iOpenTableCount--; + return XB_NO_ERROR; + } else { + s = i; + i = i->pNext; + } + } + return XB_NOT_FOUND; +} +/*************************************************************************/ +} /* namespace */ diff --git a/src/core/xbuda.cpp b/src/core/xbuda.cpp new file mode 100755 index 0000000..5db7aec --- /dev/null +++ b/src/core/xbuda.cpp @@ -0,0 +1,78 @@ +/* xbuda.cpp + +XBase64 Software Library + +Copyright (c) 1997,2003,2014,2022,2023 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 + +This module handles uda (user data area) methods + +*/ + +#include "xbase.h" + + +// might need to change thisto XB_EXPRESSION_SUPPORT +#ifdef XB_EXPRESSION_SUPPORT + + +namespace xb{ + +/************************************************************************/ +xbUda::xbUda() { + llOrd. SetDupKeys( xbFalse ); +} +/************************************************************************/ +xbUda::~xbUda() {} +/************************************************************************/ +void xbUda::Clear() { + llOrd.Clear(); +} +/************************************************************************/ +xbInt16 xbUda::GetTokenCnt() const { + return llOrd.GetNodeCnt(); +} +/************************************************************************/ + +xbInt16 xbUda::GetTokenForKey( const xbString &sKey, xbString &sToken ) { + return llOrd.GetDataForKey( sKey, sToken ); +} + +/************************************************************************/ +xbInt16 xbUda::AddTokenForKey( const xbString &sKey, const xbString &sToken ) { + return llOrd.InsertKey( sKey, sToken ); +} +/************************************************************************/ + +xbInt16 xbUda::UpdTokenForKey( const xbString &sKey, const xbString &sData ) { + return llOrd.UpdateForKey( sKey, sData ); +} + +/************************************************************************/ +xbInt16 xbUda::DelTokenForKey( const xbString &sKey ) { + return llOrd.RemoveKey( sKey ); //, sToken ); +} + + +/************************************************************************/ + +void xbUda::DumpUda() const{ + + xbLinkListNode *lln = llOrd.GetHeadNode(); + + xbInt32 l = 0; + while( lln ){ + std::cout << ++l << " Key=[" << lln->GetKey() << "] Data=[" << lln->GetData() << "]" << std::endl; + lln = lln->GetNextNode(); + } +} + +/************************************************************************/ +} /* namespace */ +#endif /* XB_EXPRESSION_SUPPORT */ \ No newline at end of file diff --git a/src/core/xbxbase.cpp b/src/core/xbxbase.cpp new file mode 100755 index 0000000..7267f98 --- /dev/null +++ b/src/core/xbxbase.cpp @@ -0,0 +1,803 @@ +/* xbxbase.cpp + +XBase64 Software Library + +Copyright (c) 1997,2003,2014,2022,2023 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 Class Constructor. +xbXBase::xbXBase() { + SetEndianType(); + + xbFile f( this ); + f.SetHomeFolders(); + xbDate d( (xbUInt16) 1); // initialize xbDate static variables + #ifdef XB_LOGGING_SUPPORT + xLog = new xbLog(); + #endif +} +/*************************************************************************/ +//! @brief Class Deconstructor. +xbXBase::~xbXBase(){ + CloseAllTables(); + #ifdef XB_LOGGING_SUPPORT + delete xLog; + #endif +} +/*************************************************************************/ +//! @brief Close all tables / files. +/*! + This closes everything and deletes references to the associated xbDbf objects. + \returns Return Codes +*/ +xbInt16 xbXBase::CloseAllTables(){ + + xbInt16 iRc = 0; + xbInt16 iErrorStop = 0; + xbInt16 iOpenTableCnt = GetOpenTableCount(); + try{ + xbDbf *d; + for( xbInt16 i = 0; i < iOpenTableCnt; i++ ){ + d = (xbDbf *) GetDbfPtr( 1 ); + if( d ){ + if(( iRc = d->Close()) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + delete d; + } else { + iRc = XB_INVALID_OBJECT; + iErrorStop = 110; + throw iRc; + } + } + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbxbase::CloseAllTables() Exception Caught. Error Stop = [%d] rc = [%d]", iErrorStop, iRc ); + WriteLogMessage( sMsg.Str() ); + WriteLogMessage( GetErrorMessage( iRc )); + } + return iRc; +} + +/************************************************************************/ +//! @brief Fully qualified file name from a directory, filename and extension. +/*! + Given a directory, file name and file extension as inputs, create a fully qualified file name. + + \param sDirIn Directory + \param sFileIn File Name + \param sExtIn File Extension + \param sFqnOut A fully qualifed unique file name as output + \returns XB_INVALIED_PARAMETER or XB_NO_ERROR +*/ +xbInt16 xbXBase::CreateFqn( const xbString &sDirIn, const xbString &sNameIn, const xbString &sExtIn, xbString &sFqnOut ){ + + if( sNameIn == "" || sExtIn == "" ) + return XB_INVALID_PARAMETER; + + sFqnOut = sDirIn; + #ifdef WIN32 + sFqnOut.SwapChars( '/', '\\' ); + if( sFqnOut[sFqnOut.Len()] != '\\' ) + sFqnOut += '\\'; + #else + sFqnOut.SwapChars( '\\', '/' ); + if( sFqnOut[sFqnOut.Len()] != '/' ) + sFqnOut += '/'; + #endif + sFqnOut += sNameIn; + if( sExtIn != "" ){ + sFqnOut += '.'; + sFqnOut += sExtIn; + } + return XB_NO_ERROR; +} + +/*************************************************************************/ +//! @brief Parse commmand line options for a given parm request +/*! + \param lArgc Value passed from main( argc, argv[] ) + \param sArgv Valued passed from main + \param sOptRqst Option to search for in the arguments list + \param sParmOut String token immediately to the right of the the option request, if found + \returns 0 - paramater request not found
1 - Parameter found +*/ + +xbInt16 xbXBase::GetCmdLineOpt( xbInt32 lArgc, char **sArgv, const char *sOptRqst, xbString &sParmOut ){ + xbString sOpt( sOptRqst ); + return GetCmdLineOpt( lArgc, sArgv, sOpt, sParmOut ); +} + +/*************************************************************************/ +//! @brief Parse commmand line options for a given parm request +/*! + \param lArgc Value passed from main( argc, argv[] ) + \param sArgv Valued passed from main + \param sOptRqst Option to search for in the arguments list + \param sParmOut String token immediately to the right of the the option request, if found + \returns 0 - paramater request not found
1 - Parameter found +*/ + +xbInt16 xbXBase::GetCmdLineOpt( xbInt32 lArgc, char **sArgv, xbString &sOptRqst, xbString &sParmOut ){ + + xbInt16 iFound = 0; + sParmOut = ""; + if( lArgc < 2 ) // first string is the program name + return iFound; + + xbInt32 i = 1; + while( iFound == 0 && i < lArgc ){ + if( sOptRqst == sArgv[i] ){ + iFound = 1; + if( i < (lArgc-1)) + sParmOut = sArgv[i+1]; + } + i++; + } + return iFound; +} + +/*************************************************************************/ +#ifdef XB_LOGGING_SUPPORT +//! @brief Get fully qualified log file name. +/*! + \returns Returns the fully qualified log file name. +*/ +const xbString & xbXBase::GetLogFqFileName() const { + return xLog->GetFqFileName(); +} + +//! @brief Get the log file name. +/*! + \returns Returns the log file name. +*/ +/* +const xbString & xbXBase::GetLogFileName() const { + return xLog->GetFileName(); +} +*/ + +//! @brief Get the log directory. +/*! + \returns Returns the log directory. +*/ +/* +const xbString & xbXBase::GetLogDirectory() const { + return GetLogDirectory(); +} +*/ +//! @brief Get the log directory. +/*! + \returns xbTrue - Logging enabled.
xbFalse - Logging disables. +*/ +xbBool xbXBase::GetLogStatus() const { + return xLog->LogGetStatus(); +} + +//! @brief Set the log file name. +/*! + \param sLogFileName - Log File Name. + \return void +*/ +/* +void xbXBase::SetLogFileName( const xbString & sLogFileName ){ + + xLog->SetFileName( sLogFileName ); +} +*/ +//! @brief Set the log directory. +/*! + \param sLogDirectory - Log File Directory. + \return void +*/ +/* +void xbXBase::SetLogDirectory( const xbString & sLogDirectory ){ + xLog->SetDirectory( sLogDirectory ); +} +*/ + + +//! @brief Set the logfile size. +/*! + \param lSize - Log File Size. + \return void +*/ +void xbXBase::SetLogSize( size_t lSize ) { + xLog->LogSetLogSize( lSize ); +} + +//! @brief Get the logfile size. +/*! + \return log file size +*/ +size_t xbXBase::GetLogSize() const { + return xLog->LogGetLogSize(); +} + + +//! @brief Write message to logfile. +/*! + \param sLogMessage - Message to write. + \param iOpt 0 = stdout
+ 1 = Syslog
+ 2 = Both
+ \returns Return Codes +*/ + +xbInt16 xbXBase::WriteLogMessage( const xbString & sLogMessage, xbInt16 iOpt ){ + return xLog->LogWrite( sLogMessage, iOpt ); +} + +//! @brief Write message to logfile. +/*! + \param lCnt - Number of bytes to write. + \param p - Pointer to bytes to write to log file. + \returns Return Codes +*/ +xbInt16 xbXBase::WriteLogBytes( xbUInt32 lCnt, const char *p ){ + return xLog->LogWriteBytes( lCnt, p ); +} + +//! @brief Enable message logging. +void xbXBase::EnableMsgLogging() { + xLog->LogSetStatus( xbTrue ); +} + +//! @brief Disable message logging. +void xbXBase::DisableMsgLogging() { + xLog->LogSetStatus( xbFalse ); +} + +//! @brief Flush log file updates to disk. +xbInt16 xbXBase::FlushLog() { + return xLog->xbFflush(); +} +#else + +// if logging not compiled in, these stubs are called with no effect +const xbString & xbXBase::GetLogFqFileName() const { + return sNullString; +} +/* +const xbString & xbXBase::GetLogFileName() const { + return sNullString; +} +const xbString & xbXBase::GetLogDirectory() const { + return sNullString; +} +*/ +/* +void xbXBase::SetLogFileName( const xbString & sLogFileName ){ + return; +} +void xbXBase::SetLogDirectory( const xbString & sLogDirectory ){ + return; +} +*/ +xbBool xbXBase::GetLogStatus() const { + return xbFalse; +} +xbInt16 xbXBase::WriteLogMessage( const xbString & sLogMessage, xbInt16 ){ + return XB_NO_ERROR; +} +xbInt16 xbXBase::WriteLogBytes( xbUInt32 lCnt, const char *p ){ + return XB_NO_ERROR; +} +void xbXBase::EnableMsgLogging() { + return; +} +void xbXBase::DisableMsgLogging() { + return; +} +xbInt16 xbXBase::FlushLog() { + return XB_NO_ERROR; +} +void xbXBase::SetLogSize( size_t lSize ) { + return; +} + +#endif // XB_LOGGING_SUPPORT + +/*************************************************************************/ +#ifdef XB_FUNCTION_SUPPORT + +//! @brief Get information regarding expression functions. +/*! + \param sExpLine An expression beginning with function name. + \param cReturnType Output - return type of function. + \param iCalc Used to calculate the function return value is
+ 1 = use value specified in lReturnLenVal
+ 2 = use length of operand specified in col 4
+ 3 = use valued of numeric operand specified in col 4
+ 4 = length of parm 1 * numeric value parm
+ 5 = larger length of parm 2 or length of parm 3
+ 6 = if two or more parms, use numeric value from second parm, otherwise use col4 value + \param lReturnLenVal Used in combination with iReturnLenCalc. + \returns Return Codes +*/ + +xbInt16 xbXBase::GetFunctionInfo( const xbString &sExpLine, char &cReturnType, xbInt16 &iCalc, xbInt32 &lReturnLenVal ) const{ + + xbUInt32 iLen; + const char *s; + if( sExpLine.Len() == 0 ) + return XB_INVALID_FUNCTION; + + s = sExpLine; + iLen = 0; + while( *s && *s != '(' && *s != ' ' ) { s++; iLen++; } + xbString sFunction( sExpLine, iLen ); + cReturnType = 0x00; + char cFunc1 = sFunction[1]; + + if( cFunc1 < 'L' ){ + // std::cout << "less than L\n"; + if( cFunc1 < 'D' ){ + // std::cout << "less than D\n"; + if( sFunction == "ABS" ){ + // { "ABS", 'N', 1, 4 }, + cReturnType = 'N'; + iCalc = 1; + lReturnLenVal = 4; + } else if( sFunction == "ALLTRIM" ){ + // { "ALLTRIM", 'C', 2, 1 }, + cReturnType = 'C'; + iCalc = 2; + lReturnLenVal = 1; + } else if( sFunction == "ASC" ){ + // { "ASC", 'N', 1, 4 }, + cReturnType = 'N'; + iCalc = 1; + lReturnLenVal = 4; + } else if( sFunction == "AT" ){ + // { "AT", 'N', 1, 4 }, + cReturnType = 'N'; + iCalc = 1; + lReturnLenVal = 4; + } else if( sFunction == "CDOW" ){ + // { "CDOW", 'C', 1, 9 }, + cReturnType = 'C'; + iCalc = 1; + lReturnLenVal = 9; + } else if( sFunction == "CHR" ){ + // { "CHR", 'C', 1, 1 }, + cReturnType = 'C'; + iCalc = 1; + lReturnLenVal = 1; + } else if( sFunction == "CMONTH" ){ + // { "CMONTH", 'C', 1, 9 }, + cReturnType = 'C'; + iCalc = 1; + lReturnLenVal = 9; + } else if( sFunction == "CTOD" ){ + // { "CTOD", 'D', 1, 8 }, + cReturnType = 'D'; + iCalc = 1; + lReturnLenVal = 8; + } + } else { + // std::cout << ">= D\n"; + if( sFunction == "DATE" ){ + // { "DATE", 'D', 1, 8 }, + cReturnType = 'D'; + iCalc = 1; + lReturnLenVal = 8; + } else if( sFunction == "DAY" ){ + // { "DAY", 'N', 1, 4 }, + cReturnType = 'N'; + iCalc = 1; + lReturnLenVal = 4; + } else if( sFunction == "DEL" ){ + // { "DEL", 'C', 1, 1 }, + cReturnType = 'C'; + iCalc = 1; + lReturnLenVal = 1; + } else if( sFunction == "DELETED" ){ + // { "DELETED", 'L', 1, 1 }, + cReturnType = 'L'; + iCalc = 1; + lReturnLenVal = 1; + } else if( sFunction == "DESCEND" ){ + // { "DESCEND", '1', 2, 1 }, + cReturnType = '1'; + iCalc = 2; + lReturnLenVal = 1; + } else if( sFunction == "DOW" ){ + // { "DOW", 'N', 1, 4 }, + cReturnType = 'N'; + iCalc = 1; + lReturnLenVal = 4; + } else if( sFunction == "DTOC" ){ + // { "DTOC", 'C', 1, 8 }, + cReturnType = 'C'; + iCalc = 1; + lReturnLenVal = 8; + } else if( sFunction == "DTOS" ){ + // { "DTOS", 'C', 1, 8 }, + cReturnType = 'C'; + iCalc = 1; + lReturnLenVal = 8; + } else if( sFunction == "EXP" ){ + // { "EXP", 'N', 1, 4 }, + cReturnType = 'N'; + iCalc = 1; + lReturnLenVal = 4; + } else if( sFunction == "IIF" ){ + // { "IIF", 'C', 5, 0 }, + cReturnType = 'C'; + iCalc = 5; + lReturnLenVal = 0; + } else if( sFunction == "INT" ){ + // { "INT", 'N', 1, 4 }, + cReturnType = 'N'; + iCalc = 1; + lReturnLenVal = 4; + } else if( sFunction == "ISALPHA" ){ + // { "ISALPHA", 'L', 1, 1 }, + cReturnType = 'L'; + iCalc = 1; + lReturnLenVal = 1; + } else if( sFunction == "ISLOWER" ){ + // { "ISLOWER", 'L', 1, 1 }, + cReturnType = 'L'; + iCalc = 1; + lReturnLenVal = 1; + } else if( sFunction == "ISUPPER" ){ + // { "ISUPPER", 'L', 1, 1 }, + cReturnType = 'L'; + iCalc = 1; + lReturnLenVal = 1; + } + } + } else { + // std::cout << ">= L\n"; + if( cFunc1 < 'R' ) { + // std::cout << " < R\n"; + if( sFunction == "LEFT" ){ + // { "LEFT", 'C', 3, 2 }, + cReturnType = 'C'; + iCalc = 3; + lReturnLenVal = 2; + } else if( sFunction == "LEN" ){ + // { "LEN", 'N', 1, 4 }, + cReturnType = 'N'; + iCalc = 1; + lReturnLenVal = 3; + } else if( sFunction == "LOG" ){ + // { "LOG", 'N', 1, 4 }, + cReturnType = 'N'; + iCalc = 1; + lReturnLenVal = 4; + } else if( sFunction == "LOWER" ){ + // { "LOWER", 'C', 2, 1 }, + cReturnType = 'C'; + iCalc = 2; + lReturnLenVal = 1; + } else if( sFunction == "LTRIM" ){ + // { "LTRIM", 'C', 2, 1 }, + cReturnType = 'C'; + iCalc = 2; + lReturnLenVal = 1; + } else if( sFunction == "MAX" ){ + // { "MAX", 'N', 1, 4 }, + cReturnType = 'N'; + iCalc = 1; + lReturnLenVal = 4; + } else if( sFunction == "MIN" ){ + // { "MIN", 'N', 1, 4 }, + cReturnType = 'N'; + iCalc = 1; + lReturnLenVal = 4; + } else if( sFunction == "MONTH" ){ + // { "MONTH", 'N', 1, 4 }, + cReturnType = 'N'; + iCalc = 1; + lReturnLenVal = 4; + } + } else if( cFunc1 == 'R' ){ + // std::cout << "==R\n"; + if( sFunction == "RECNO" ){ + // { "RECNO", 'N', 1, 4 }, + cReturnType = 'N'; + iCalc = 1; + lReturnLenVal = 4; + } else if( sFunction == "RECCOUNT" ){ + // { "RECCOUNT", 'N', 1, 4 }, + cReturnType = 'N'; + iCalc = 1; + lReturnLenVal = 4; + } else if( sFunction == "REPLICATE" ){ + // { "REPLICATE", 'C', 4, 0 }, + cReturnType = 'C'; + iCalc = 4; + lReturnLenVal = 0; + } else if( sFunction == "RIGHT" ){ + // { "RIGHT", 'C', 3, 2 }, + cReturnType = 'C'; + iCalc = 3; + lReturnLenVal = 2; + } else if( sFunction == "RTRIM" ){ + // { "RTRIM", 'C', 2, 1 }, + cReturnType = 'C'; + iCalc = 2; + lReturnLenVal = 1; + } + } else if( cFunc1 == 'S' ){ + // std::cout << "==S\n"; + if( sFunction == "SPACE" ){ + // { "SPACE", 'C', 3, 1 }, + cReturnType = 'C'; + iCalc = 3; + lReturnLenVal = 1; + } else if( sFunction == "SQRT" ){ + // { "SQRT", 'N', 1, 4 }, + cReturnType = 'N'; + iCalc = 1; + lReturnLenVal = 4; + } else if( sFunction == "STOD" ){ + // { "STOD", 'D', 1, 8 }, + cReturnType = 'D'; + iCalc = 1; + lReturnLenVal = 8; + } else if( sFunction == "STR" ){ + // { "STR", 'C', 6, 10 }, + cReturnType = 'C'; + iCalc = 6; + lReturnLenVal = 10; + } else if( sFunction == "STRZERO" ){ + // { "STRZERO", 'C', 3, 2 }, + cReturnType = 'C'; + iCalc = 3; + lReturnLenVal = 2; + } else if( sFunction == "SUBSTR" ){ + // { "SUBSTR", 'C', 3, 3 }, + cReturnType = 'C'; + iCalc = 3; + lReturnLenVal = 3; + } + } else { + // std::cout << ">S\n"; + if( sFunction == "TRIM" ){ + // { "TRIM", 'C', 2, 1 }, + cReturnType = 'C'; + iCalc = 2; + lReturnLenVal = 1; + } else if( sFunction == "UPPER" ){ + // { "UPPER", 'C', 2, 1 }, + cReturnType = 'C'; + iCalc = 2; + lReturnLenVal = 1; + } else if( sFunction == "VAL" ){ + // { "VAL", 'N', 1, 4 }, + cReturnType = 'N'; + iCalc = 1; + lReturnLenVal = 3; + } else if( sFunction == "YEAR" ){ + // { "YEAR", 'N', 1, 4 }, + cReturnType = 'N'; + iCalc = 1; + lReturnLenVal = 4; + } + } + } + if( cReturnType == 0x00 ) + return XB_INVALID_FUNCTION; + else + return XB_NO_ERROR; +} +#endif +/*************************************************************************/ +//! @brief Cross platform sleep function. +/*! + \param lMillisecs Milliseconds to sleep. +*/ +void xbXBase::xbSleep( xbInt32 lMillisecs ){ + #ifdef WIN32 + Sleep( (xbUInt32) lMillisecs ); + #else + usleep( (xbInt64) lMillisecs * 1000 ); + #endif + +} +/***********************************************************************/ +//! @brief Cross memcmp function. +/*! + \param s1 Left operand to compare. + \param s2 Right operand to compare. + \param n Number of bytes to compare. + \returns 1 s1 > s2
+ 0 s1 == s2
+ -1 s1 < s2 +*/ +xbInt16 xbXBase::xbMemcmp( const unsigned char *s1, const unsigned char *s2, size_t n ){ + // The standard memcmp function was found not to behave the same across all platforms + for( size_t i = 0; i < n; i++ ){ + if( s1[i] > s2[i] ) + return 1; + else if( s1[i] < s2[i] ) + return -1; + } + return 0; +} + +/***********************************************************************/ +//! @brief Open highest qualified class available for dbf file. +/*! + This routine opens the highest available version of the dbf file. + Defaults to XB_READ_WRITE and XB_MULTI_USER mode. + \returns param dbf - Output pointer to dbf file opened or null if error +*/ +xbDbf* xbXBase::Open( const xbString &sTableName, xbInt16 &iRc ){ + return Open( sTableName, "", XB_READ_WRITE, XB_MULTI_USER, 0, iRc ); +} +/***********************************************************************/ +//! @brief Open highest qualified class available for dbf file. +/*! + This routine can open various versions of the dbf file dependent on the iVersion field + + \param sTableName - Table name to open. + \param sAlias - Optional alias name. + \param iOpenMode - XB_READ_WRITE or XB_READ + \param iShareMode - XB_SINGLE_USER or XB_MULTI_USER + \param iRequestVersion 0 - Highest available + 4 - Version four dbf + 3 - Version three dbf + \param iRc - Return code from open request + \returns param dbf - Output pointer to dbf file opened or null if error +*/ + + +xbDbf* xbXBase::Open( const xbString &sTableName, const xbString &sAlias, xbInt16 iOpenMode, + xbInt16 iShareMode, xbInt16 iRequestVersion, xbInt16 &iRc ){ + + xbInt16 iErrorStop = 0; + xbDbf * pDbf = 0; + iRc = 0; + xbString sFqFileName; + + try{ + + if( sTableName.Len() == 0 ){ + iErrorStop = 100; + iRc = XB_FILE_NOT_FOUND; + throw iRc; + } + xbFile *f = new xbFile(this); + f->SetFileName( sTableName ); + if(( iRc = f->FileExists( f->GetFqFileName())) != xbTrue ){ + iErrorStop = 110; + iRc = XB_FILE_NOT_FOUND; + throw iRc; + } + unsigned char cFileTypeByte; + if(( iRc = f->GetXbaseFileTypeByte( f->GetFqFileName(), cFileTypeByte )) != XB_NO_ERROR ){ + iErrorStop = 120; + throw iRc; + } + xbInt16 iTblVsn = f->DetermineXbaseTableVersion( cFileTypeByte ); + f->xbFclose(); + sFqFileName.Set( f->GetFqFileName() ); + delete f; + + if( iTblVsn == 4 && ( iRequestVersion == 0 || iRequestVersion == 4 )){ + #ifdef XB_DBF4_SUPPORT + pDbf = new xbDbf4( this ); + iRc = pDbf->Open( sFqFileName, sAlias, iOpenMode, iShareMode ); + #else + // std::cout << "Dbase IV file support not build into library. See XB_DBF4_SUPPORT" << std::endl; + iErrorStop = 130; + iRc = XB_FILE_TYPE_NOT_SUPPORTED; + throw iRc; + #endif + } + else if( iTblVsn == 3 && ( iRequestVersion == 0 || iRequestVersion == 3 )){ + + #ifdef XB_DBF3_SUPPORT + pDbf = new xbDbf3( this ); + iRc = pDbf->Open( sFqFileName, sAlias, iOpenMode, iShareMode ); + #else + //std::cout << "Dbase III file support not build into library. See XB_DBF3_SUPPORT" << std::endl; + iErrorStop = 140; + iRc = XB_FILE_TYPE_NOT_SUPPORTED; + throw iRc; + #endif + + } else { + iErrorStop = 150; + iRc = XB_FILE_TYPE_NOT_SUPPORTED; + throw iRc; + } + + if( iRc != XB_NO_ERROR ){ + iErrorStop = 160; + throw iRc; + } + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbxbase::Open() Exception Caught. Error Stop = [%d] rc = [%d]", iErrorStop, iRc ); + WriteLogMessage( sMsg.Str() ); + WriteLogMessage( GetErrorMessage( iRc )); + } + return pDbf; +} + +xbInt16 xbXBase::OpenHighestVersion( const xbString &sTableName, const xbString &sAlias, + xbDbf **dbf ) +{ + xbInt16 iRc = 0; + xbInt16 iErrorStop = 0; + + try{ + xbFile f(this); + if( sTableName.Len() == 0 ){ + iErrorStop = 100; + iRc = XB_FILE_NOT_FOUND; + throw iRc; + } + f.SetFileName( sTableName ); + if(( iRc = f.FileExists( f.GetFqFileName() )) != xbTrue ){ + iErrorStop = 110; + iRc = XB_FILE_NOT_FOUND; + throw iRc; + } + + unsigned char cFileTypeByte; + if(( iRc = f.GetXbaseFileTypeByte( f.GetFqFileName(), cFileTypeByte )) != XB_NO_ERROR ){ + iErrorStop = 120; + return iRc; + } + if( f.DetermineXbaseTableVersion( cFileTypeByte ) == 4 ){ + #ifdef XB_DBF4_SUPPORT + xbDbf *pwDbf = new xbDbf4( this ); + pwDbf->Open( f.GetFqFileName(), sAlias ); + *dbf = pwDbf; + pwDbf = 0; + #else + // std::cout << "Dbase IV file support not build into library. See XB_DBF4_SUPPORT" << std::endl; + iErrorStop = 130; + iRc = XB_FILE_TYPE_NOT_SUPPORTED; + throw iRc; + #endif + + } else if( f.DetermineXbaseTableVersion( cFileTypeByte ) == 3 ){ + #ifdef XB_DBF3_SUPPORT + *dbf = new xbDbf3( this ); + #else + //std::cout << "Dbase III file support not build into library. See XB_DBF3_SUPPORT" << std::endl; + iErrorStop = 140; + iRc = XB_FILE_TYPE_NOT_SUPPORTED; + throw iRc; + #endif + + } else { + iErrorStop = 150; + iRc = XB_FILE_TYPE_NOT_SUPPORTED; + throw iRc; + } + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbxbase::OpenHighestVersion() Exception Caught. Error Stop = [%d] rc = [%d]", iErrorStop, iRc ); + WriteLogMessage( sMsg.Str() ); + WriteLogMessage( GetErrorMessage( iRc )); + } + return iRc; +} +/***********************************************************************/ +} /* namespace */ -- cgit v1.2.3