From bfa452a375ea0a0a3f95304a69186936567e5263 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Frings-F=C3=BCrst?= Date: Mon, 14 Aug 2023 19:45:36 +0200 Subject: New upstream version 4.1.4 --- src/core/xbbcd.cpp | 301 --- src/core/xbblockread.cpp | 271 --- src/core/xbdate.cpp | 802 -------- src/core/xbdbf.cpp | 4338 --------------------------------------- src/core/xbdbf3.cpp | 767 ------- src/core/xbdbf4.cpp | 884 -------- src/core/xbexp.cpp | 2661 ------------------------ src/core/xbexpnode.cpp | 562 ----- src/core/xbfields.cpp | 1130 ----------- src/core/xbfile.cpp | 2193 -------------------- src/core/xbfilter.cpp | 544 ----- src/core/xbfuncs.cpp | 850 -------- src/core/xbixbase.cpp | 789 ------- src/core/xbixmdx.cpp | 5073 ---------------------------------------------- src/core/xbixndx.cpp | 2820 -------------------------- src/core/xblog.cpp | 208 -- src/core/xbmemo.cpp | 219 -- src/core/xbmemo3.cpp | 575 ------ src/core/xbmemo4.cpp | 1335 ------------ src/core/xbssv.cpp | 573 ------ src/core/xbstring.cpp | 1943 ------------------ src/core/xbtag.cpp | 121 -- src/core/xbtblmgr.cpp | 311 --- src/core/xbuda.cpp | 78 - src/core/xbxbase.cpp | 749 ------- 25 files changed, 30097 deletions(-) delete mode 100755 src/core/xbbcd.cpp delete mode 100755 src/core/xbblockread.cpp delete mode 100755 src/core/xbdate.cpp delete mode 100755 src/core/xbdbf.cpp delete mode 100755 src/core/xbdbf3.cpp delete mode 100755 src/core/xbdbf4.cpp delete mode 100755 src/core/xbexp.cpp delete mode 100755 src/core/xbexpnode.cpp delete mode 100755 src/core/xbfields.cpp delete mode 100755 src/core/xbfile.cpp delete mode 100755 src/core/xbfilter.cpp delete mode 100755 src/core/xbfuncs.cpp delete mode 100755 src/core/xbixbase.cpp delete mode 100755 src/core/xbixmdx.cpp delete mode 100755 src/core/xbixndx.cpp delete mode 100755 src/core/xblog.cpp delete mode 100755 src/core/xbmemo.cpp delete mode 100755 src/core/xbmemo3.cpp delete mode 100755 src/core/xbmemo4.cpp delete mode 100755 src/core/xbssv.cpp delete mode 100755 src/core/xbstring.cpp delete mode 100755 src/core/xbtag.cpp delete mode 100755 src/core/xbtblmgr.cpp delete mode 100755 src/core/xbuda.cpp delete mode 100755 src/core/xbxbase.cpp (limited to 'src/core') diff --git a/src/core/xbbcd.cpp b/src/core/xbbcd.cpp deleted file mode 100755 index f86e74f..0000000 --- a/src/core/xbbcd.cpp +++ /dev/null @@ -1,301 +0,0 @@ -/* 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 deleted file mode 100755 index 09076b1..0000000 --- a/src/core/xbblockread.cpp +++ /dev/null @@ -1,271 +0,0 @@ -/* 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(){ - std::cout << "------- DumpBlockInternals ---------" << std::endl; - std::cout << "Dflt Blk Size = [" << dbf->GetXbasePtr()->GetDefaultBlockReadSize() - << "]" << std::endl; - std::cout << "Dbf Record Count = [" << dbf->GetRecordCount() << "]" << 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 = dbf->GetRecordCount(); - - // 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 deleted file mode 100755 index ba0fdf1..0000000 --- a/src/core/xbdate.cpp +++ /dev/null @@ -1,802 +0,0 @@ -/* xbdate.cpp - -XBase64 Software Library - -Copyright (c) 1997,2003,2014,2022 Gary A Kunkel - -The xb64 software library is covered under the terms of the GPL Version 3, 2007 license. - -Email Contact: - - XDB-devel@lists.sourceforge.net - XDB-users@lists.sourceforge.net - -*/ - -#include "xbase.h" -#include - -namespace xb{ - -int xbDate::iDaysInMonths[2][13]; -int xbDate::iAggregatedDaysInMonths[2][13]; - -/*************************************************************************/ -//! @brief Constructor. - -xbDate::xbDate() { - Sysdate(); - SetDateTables(); -} - -/*************************************************************************/ -//! @brief Constructor. -/*! - \param sDate8In - Input date. -*/ - -xbDate::xbDate( const xbString & sDate8In ) { - if( DateIsValid( sDate8In )) - sDate8.Set( sDate8In ); - else - Sysdate(); - SetDateTables(); -} - -/*************************************************************************/ -//! @brief Constructor. -/*! - \param sDate8In - Input date. -*/ -xbDate::xbDate( const char * sDate8In ) { - - if( DateIsValid( sDate8In )) - sDate8.Set( sDate8In ); - else - Sysdate(); - 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 - \param lDays - Number of days to add to the date. -*/ -void xbDate::operator+=( xbInt32 lDays ){ - JulToDate8( JulianDays() + lDays ); -} -/*************************************************************************/ -//! @brief operator -= -/*! - This routine subtracts lDays from the date. - \param lDays - Number of days to subtract from the date. -*/ -void xbDate::operator-=( xbInt32 lDays ){ - JulToDate8( JulianDays() - lDays ); -} -/*************************************************************************/ -//! @brief operator ++ -/*! - This routine adds one day to the date field. -*/ -void xbDate::operator++(xbInt32){ - *this+=1; -} -/*************************************************************************/ -//! @brief operator -- -/*! - This routine subtracts one day from the date field. -*/ -void xbDate::operator--(xbInt32){ - *this-=1; - return; -} -/*************************************************************************/ -//! @brief operator - -/*! - This routine subtracts one date from another date returning the difference. - \param dt - Date to subtract. - \returns Number of days -*/ -xbInt32 xbDate::operator-( const xbDate &dt ) const{ - return JulianDays() - dt.JulianDays(); -} -/*************************************************************************/ -//! @brief operator + -/*! - This routine adds additional days to a date field. - \param lCount - Number of days to add. - \returns New date in CCYYMMDD format. -*/ -const char *xbDate::operator+( xbInt32 lCount ){ - JulToDate8( JulianDays() + lCount ); - return sDate8.Str(); -} -/*************************************************************************/ -//! @brief operator - -/*! - This routine subtracts days from a date field. - \param lCount - Number of days to subtract. - \returns New date in CCYYMMDD format. -*/ -const char *xbDate::operator-( xbInt32 lCount ){ - 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; - 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 This routine returns the century from the date. -*/ -xbInt16 xbDate::CenturyOf() const { - char Century[3]; - Century[0] = sDate8[1]; - Century[1] = sDate8[2]; - Century[2] = 0x00; - return( atoi( Century )); -} -/*************************************************************************/ -//! @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 ) { - - 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 ) { - 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 { - - 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; -} - -/*************************************************************************/ -#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 = ""; - - /* 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 { - 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 Calculate julian days for a given date. -/*! - \returns The number of days since 01/01/0001 + JUL_OFFSET. -*/ -xbInt32 xbDate::JulianDays() const{ - xbInt32 ly = YearOf() - 1; - xbInt32 lDays = ly * 365L + ly / 4L - ly / 100L + ly / 400L; - lDays += DayOf( XB_FMT_YEAR ); - return lDays + JUL_OFFSET; -} -/*************************************************************************/ -//! @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(){ - 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 { - xbInt16 iOutMonth; - char month[3]; - month[0] = sDate8[5]; - month[1] = sDate8[6]; - month[2] = 0x00; - iOutMonth = atoi( month ); - return iOutMonth; -} - -/*************************************************************************/ -//! @brief Set the date. -/*! - \param sDateIn - Input date. - \returns XB_NO_ERROR
XB_INVALID_DATE -*/ -xbInt16 xbDate::Set( const xbString & sDateIn ){ - if( DateIsValid( sDateIn )){ - sDate8 = sDateIn; - return XB_NO_ERROR; - } - return XB_INVALID_DATE; -} -/*************************************************************************/ -//! @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 { - 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; -}; -} /* namespace */ \ No newline at end of file diff --git a/src/core/xbdbf.cpp b/src/core/xbdbf.cpp deleted file mode 100755 index 4033ef9..0000000 --- a/src/core/xbdbf.cpp +++ /dev/null @@ -1,4338 +0,0 @@ -/* 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. - - \param ixIn Pointer to index object for a given index file. - \param sFmt NDX or MDX. - \returns Return Codes - -*/ - -xbInt16 xbDbf::AddIndex( xbIx * ixIn, const xbString &sFmt ){ - - xbIxList *ixt; // this - if(( ixt = (xbIxList *) malloc( sizeof( xbIxList ))) == NULL ) - return XB_NO_ERROR; - - ixt->ix = ixIn; - ixt->next = NULL; - ixt->sFmt = new xbString( sFmt ); - ixt->sFmt->ToUpperCase(); - - if( ixList ){ - xbIxList *ixn = ixList; // next - while( ixn->next ){ - ixn = ixn->next; - } - ixn->next = ixt; - } else { - ixList = ixt; - } - return XB_NO_ERROR; -} -#endif // XB_INDEX_SUPPORT - - -/************************************************************************/ -//! @brief Append the current record to the data file. -/*! - This method attempts to append the contents of the current record buffer - to the end of the DBF file, updates the file date, number of records in the file - and updates any open indices associated with this data file.
- - To add a record, an application would typically blank the record buffer, - update various fields in the record buffer, then append the record.
- - The append method performs the following tasks:
- 1) Create new index key values
- 2) Lock the table
- 3) Lock append bytes
- 4) Lock indices
- 5) Read the dbf header
- 6) Check for dup keys
- 7) Calc last update date, no of recs
- 8) Add keys
- 9) Unlock indices
- 10) Update file header
- 11) Unlock file header
- 12) Append record
- 13) Unlock append bytes
- -Note: Locking memo files is not needed as the memo file updates are handled outside of the append method.
- - \returns Return Codes -*/ - -xbInt16 xbDbf::AppendRecord(){ - xbInt16 iErrorStop = 0; - xbInt16 iRc = XB_NO_ERROR; - xbUInt32 ulSaveCurRec = 0; - - try{ - #ifdef XB_INDEX_SUPPORT - xbIxList *ixList = GetIxList(); - // do this step first before anything is locked, reduce lock time as much as possible - while( ixList ){ - - // std::cout << "xbDbf::CreateKeys(x)\n"; - if(( iRc = ixList->ix->CreateKeys( 1 )) != XB_NO_ERROR ){ - iErrorStop = 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; - 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 there - - \returns Return Codes - -*/ - -xbInt16 xbDbf::AssociateIndex( const xbString &sIxType, const xbString &sIxName, xbInt16 iOpt ){ - - xbInt16 iRc = 0; - xbInt16 iErrorStop = 0; - - try{ - xbString sIxTypeIn = sIxType; - sIxTypeIn.Trim(); - xbString sIxNameIn = sIxName; - sIxNameIn.Trim(); - - if( sIxTypeIn != "NDX" || sIxName == "" ) - return XB_INVALID_INDEX; - - if(( iRc = 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 tag
- - \param iOutputOpt Output message destination
- 0 = stdout
- 1 = Syslog
- 2 = Both
- - \returns Return Codes -*/ - -xbInt16 xbDbf::CheckTagIntegrity( xbInt16 iTagOpt, xbInt16 iOutputOpt ){ - xbInt16 iRc = XB_NO_ERROR; - xbInt16 iErrorStop = 0; - - try{ - 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
- - \returns Return Codes -*/ - -xbInt16 xbDbf::Reindex( xbInt16 iTagOpt ){ - xbInt16 iRc = XB_NO_ERROR; - xbInt16 iErrorStop = 0; - void *vp; - - try{ - if( iTagOpt == 0 ){ - if( pCurIx ){ - - iRc = pCurIx->Reindex( &vpCurIxTag ); - if( iRc != XB_NO_ERROR ){ - iErrorStop = 100; - throw iRc; - } - return iRc; - } else { - return XB_INVALID_TAG; - } - - } else { - - xbLinkListNode *llN = GetTagList(); - xbTag *pTag; - - while( llN ){ - pTag = llN->GetKey(); - vp = pTag->GetVpTag(); - if(( iRc = pTag->GetIx()->Reindex( &vp )) != XB_NO_ERROR ){ - iErrorStop = 110; - throw iRc; - } - llN = llN->GetNextNode(); - } - } - } - catch (xbInt16 iRc ){ - xbString sMsg; - sMsg.Sprintf( "xbdbf::Reindex() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); - xbase->WriteLogMessage( sMsg.Str() ); - xbase->WriteLogMessage( GetErrorMessage( iRc )); - } - return iRc; -} - -/************************************************************************/ -// @brief Clear the index tag list. -/* - Protected method. Clears the list inf index tags. - \returns void. -*/ -void xbDbf::ClearTagList(){ - - xbTag *pTag; - xbBool bDone = xbFalse; - while( llTags.GetNodeCnt() > 0 && !bDone ){ - if( llTags.RemoveFromFront( pTag ) != XB_NO_ERROR ){ - bDone = xbTrue; - } else { - if( pTag ) - delete pTag; - } - } -} -#endif // XB_INDEX_SUPPORT - -/************************************************************************/ -//! @brief Close DBF file/table. -/*! - This routine flushes any remaining updates to disk, closes the DBF file and - any associated memo and index files. - - \returns Return Codes -*/ - -xbInt16 xbDbf::Close(){ - - xbInt16 iRc = XB_NO_ERROR; - xbInt16 iErrorStop = 0; - - try{ - - if(iDbfStatus == XB_CLOSED) - return XB_NO_ERROR; - - else if( iDbfStatus == XB_UPDATED ){ - - if( GetAutoCommit() == 1 ){ - if(( iRc = Commit()) != XB_NO_ERROR ){ - iErrorStop = 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 and MDX - indices. If you don't have a specific need for an NDX file, use MDX. - - \param sIxType "MDX" or "NDX". - \param sName Index or tag name. - \param sKey Index key expression, - \param sFilter Filter expression. Not applicable for NDX indices. - \param iDescending xbTrue for descending. Not available for NDX indices.
- xbFalse - ascending - \param iUnique xbTrue - Unique index
xbFalse - Not unique index. - \param iOverLay xbTrue - Overlay if exists
- xbFalse - Don't overlay if it exists. - \param pIxOut Pointer to pointer of output index object. - \param vpTagOut Pointer to pointer of newly created tag, - \returns Return Codes -*/ - -xbInt16 xbDbf::CreateTag( const xbString &sIxType, const xbString &sName, const xbString &sKey, const xbString &sFilter, - xbInt16 iDescending, xbInt16 iUnique, xbInt16 iOverLay, xbIx **pIxOut, void **vpTagOut ){ - - // this routine is used to open indices and link to files - - xbInt16 iErrorStop = 0; - xbInt16 iRc = XB_NO_ERROR; - - #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; - } - } - - 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 - - } 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 ){ - ixList->ix->Close(); - ixList->ix->xbRemove(); - #ifdef XB_INF_SUPPORT - // if XB_INF_SUPPORT is enabled, all open non prod indices should be in here - if( *ixList->sFmt != "MDX" ){ // production indices not stored in .INF dataset - if(( iRc = ixList->ix->GetFileNamePart( sIxName )) != XB_NO_ERROR ){ - iErrorStop = 110; - throw iRc; - } - if(( iRc = AssociateIndex( *ixList->sFmt, sIxName, 1 )) != XB_NO_ERROR ){ - iErrorStop = 120; - 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" or "MDX".
- \param sName Tag name to delete.
- \returns Return Codes -*/ - -xbInt16 xbDbf::DeleteTag( const xbString &sIxType, const xbString &sName ){ - - xbInt16 iErrorStop = 0; - xbInt16 iRc = XB_NO_ERROR; - xbIx *pIx = NULL; - - #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( ixl->ix == pCurIx ) - SetCurTag( "", NULL, NULL ); - - } - ixl = ixl->next; - } - #endif - - #ifdef XB_MDX_SUPPORT - } else if( sIxType == "MDX" ){ - xbIxList *ixl = ixList; - xbIxList *ixlNext; - xbIxList *ixlPrev = NULL; - xbBool bDone = xbFalse; - xbIxMdx *pMdx; - xbMdxTag *pMdxTag; - xbInt16 iTagCnt = 0; - - while( ixl && !bDone ){ - ixlNext = ixl->next; - pMdx = (xbIxMdx *) ixl->ix; - iTagCnt = pMdx->GetTagCount(); - for( xbInt16 i = 0; i < iTagCnt && !bDone; i++ ){ - pMdxTag = (xbMdxTag *) pMdx->GetTag( i ); - if( pMdx->GetTagName( pMdxTag ) == sName ){ - bDone = xbTrue; - iRc = pMdx->DeleteTag( pMdxTag ); - if( iRc > 0 ){ - // Successful delete of only tag in production mdx file - need to remove it from the list, update the dbf header - cIndexFlag = 0x00; - if(( iRc = WriteHeader( 1, 0 )) != XB_NO_ERROR ){ - iErrorStop = 150; - throw iRc; - } - if(( iRc = RemoveIndex( ixl->ix )) != XB_NO_ERROR ){ - iErrorStop = 160; - throw iRc; - } - if( ixlPrev == NULL ){ - // std::cout << "setting ixList to null or should be\n"; - ixList = ixlNext; - } else { - ixlPrev = ixlNext; - } - } else if( iRc < 0 ){ - iErrorStop = 170; - throw iRc; - } - if( ixl->ix == pCurIx ) - SetCurTag( "", NULL, NULL ); - } - } - ixlPrev = ixl; - ixl = ixlNext; - } - - if( !bDone ) - return XB_INVALID_TAG; - - #endif - - } else { - iErrorStop = 180; - iRc = XB_INVALID_OPTION; - throw iRc; - } - - if(( iRc = UpdateTagList()) != XB_NO_ERROR ){ - iErrorStop = 190; - throw iRc; - } - - - } - 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 ) const { - int i; - int iMemoCtr = 0; - - if( iOption < 1 || iOption > 4 ) - return XB_INVALID_OPTION; - - if( iDbfStatus == XB_CLOSED ) - return XB_NOT_OPEN; - - std::cout << "\nDatabase file " << GetFqFileName() << std::endl << std::endl; - - if( iOption != 2 ){ - std::cout << "File header data:" << std::endl; - - xbInt16 sVer = DetermineXbaseTableVersion( cVersion ); - - if( sVer == 3 ) - std::cout << "Dbase III file" << std::endl; - else if ( sVer == 4 ) - std::cout << "Dbase IV file" << std::endl << std::endl; - else if ( sVer == 5 ) - std::cout << "Dbase V file" << std::endl << std::endl; - else if ( sVer == 7 ) - std::cout << "Dbase VII file" << std::endl << std::endl; - else - std::cout << "Unknown Version" << std::endl; - - /* display the bit stream */ - unsigned char c, tfv, displayMask = 1 << 7; - tfv = cVersion; - std::cout << "File descriptor bits = "; - for( c = 1; c<= 8; c++ ){ - std::cout << (tfv & displayMask ? '1' : '0'); - tfv <<= 1; - } - std::cout << std::endl; - - std::cout << "Descriptor bits legend:" << std::endl; - std::cout << " 0-2 = version number" << std::endl; - std::cout << " 3 = presence of dBASE IV memo" << std::endl; - std::cout << " 4-6 = SQL table presence" << std::endl; - std::cout << " 7 = Presence of any memo file (dBASE III PLUS or dBASE IV)" << std::endl << std::endl; - - std::cout << "Last update date = " - << (int) cUpdateMM << "/" << (int) cUpdateDD << "/" << (int) cUpdateYY % 100 << std::endl; - - std::cout << "Header length = " << uiHeaderLen << std::endl; - std::cout << "Record length = " << uiRecordLen << std::endl; - std::cout << "Records in file = " << ulNoOfRecs << std::endl << std::endl << std::endl; - - std::cout << "Transaction Flag = "; - xbase->BitDump( cTransactionFlag ); - std::cout << std::endl; - - std::cout << "Encryption Flag = "; - xbase->BitDump( cEncryptionFlag ); - std::cout << std::endl; - - std::cout << "Index Flag = "; - xbase->BitDump( cIndexFlag ); - std::cout << std::endl; - - std::cout << "Lang Driver = " << (int) cIndexFlag << " - "; - xbase->BitDump( cIndexFlag ); - std::cout << std::endl; - #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. An NDX index has one tag. - An MDX file can have up to 47 tags. - - \returns Pointer to current tag. -*/ -void *xbDbf::GetCurTag() const { - return vpCurIxTag; -} -/************************************************************************/ -//! @brief Get the current index type. -/*! - \returns NDX for single tag index.
- MDX for production multi tag index. -*/ -const xbString &xbDbf::GetCurIxType() const { - return sCurIxType; -} - -/************************************************************************/ -//! @brief Get the current tag name. -/*! - \returns Current Tag Name.
-*/ - -const xbString &xbDbf::GetCurTagName() const { - - if( pCurIx ) - return pCurIx->GetTagName( vpCurIxTag ); - else - return sNullString; -} - -/************************************************************************/ -//! @brief GetFirstKey for tag. -/*! - - Position to the first key for the current tag - \returns Return Codes - -*/ -xbInt16 xbDbf::GetFirstKey(){ - if( pCurIx ) - return pCurIx->GetFirstKey( vpCurIxTag, 1 ); - else - return XB_INVALID_TAG; -} - -/************************************************************************/ -//! @brief 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 dbf file is empty or positioned on the last record. -*/ -xbBool xbDbf::GetEof() { - xbUInt32 ulRecCnt = GetRecordCount(); - - if( 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), 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 - - } 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 - - } - 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; - 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; - 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" or MDX", - \param pIx - Pointer to index object. - \param vpTag - Pointer to tag object. - \returns Return Codes -*/ - -void xbDbf::SetCurTag( const xbString &sIxType, xbIx *pIx, void *vpTag ){ - pCurIx = pIx; - vpCurIxTag = vpTag; - sCurIxType.Set( sIxType ); -} -#endif // XB_INDEX_SUPPORT - -/************************************************************************/ -//! @brief Set the header locked status. -/*! - \param bHeaderLocked xbTrue - Locked
xbFalse - Not locked. - \returns void -*/ -#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; - 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 deleted file mode 100755 index d57be3c..0000000 --- a/src/core/xbdbf3.cpp +++ /dev/null @@ -1,767 +0,0 @@ -/* xbdbf3.cpp - -XBase64 Software Library - -Copyright (c) 1997,2003,2014,2022 Gary A Kunkel - -The xb64 software library is covered under the terms of the GPL Version 3, 2007 license. - -Email Contact: - - XDB-devel@lists.sourceforge.net - XDB-users@lists.sourceforge.net - -*/ - -#include "xbase.h" - - -#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; - 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 deleted file mode 100755 index 59ba39a..0000000 --- a/src/core/xbdbf4.cpp +++ /dev/null @@ -1,884 +0,0 @@ -/* xbdbf4.cpp - -XBase64 Software Library - -Copyright (c) 1997,2003,2014,2022 Gary A Kunkel - -The xb64 software library is covered under the terms of the GPL Version 3, 2007 license. - -Email Contact: - - XDB-devel@lists.sourceforge.net - XDB-users@lists.sourceforge.net - -*/ - -#include "xbase.h" - - -#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; - 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 give table, associated memo 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 deleted file mode 100755 index deea53d..0000000 --- a/src/core/xbexp.cpp +++ /dev/null @@ -1,2661 +0,0 @@ -/* xbexp.cpp - -XBase64 Software Library - -Copyright (c) 1997,2003,2014,2017,2021,2022 Gary A Kunkel - -The xb64 software library is covered under -the terms of the GPL Version 3, 2007 license. - -Email Contact: - - XDB-devel@lists.sourceforge.net - XDB-users@lists.sourceforge.net - - -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 seperator -/*! This method determines if the next token is a seperator. - - \param sExpression - String expression to be evaluated. - \returns xbTrue - Is a token seperator.
- xbFalse - Is not a token seperator. -*/ -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; - xbExpNode *nLastNode = NULL; // pointer to the last node processed - xbExpToken t; - xbInt16 iRc = XB_NO_ERROR; - xbInt16 iErrorStop = 0; - xbString s; - - 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 ); - if(( iRc = ParseExpressionConstant( t, n )) != XB_NO_ERROR ){ - iErrorStop = 120; - throw iRc; - } - break; - - case XB_EXP_FUNCTION: - n = new xbExpNode( t.cNodeType ); - if(( iRc = ParseExpressionFunction( t, n, iWeight )) != XB_NO_ERROR ){ - iErrorStop = 130; - throw iRc; - } - break; - - case XB_EXP_FIELD: - n = new xbExpNode( t.cNodeType ); - 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 ); - 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 ){ - 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 == " " ){ - nWork->SetResult( (xbDouble) 21474835648 ); // dbase sets a date value in ndx to this if spaces on dbf record - } 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 || nChild1->GetReturnType() == XB_EXP_DATE ) - 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 || nChild1->GetReturnType() == XB_EXP_DATE ) - 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 || nChild1->GetReturnType() == XB_EXP_DATE ) - n->SetResult((xbBool)( nChild1->GetNumericResult() < nChild2->GetNumericResult())); - - 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 || nChild1->GetReturnType() == XB_EXP_DATE ) - 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 || nChild1->GetReturnType() == XB_EXP_DATE ) - 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 || nChild1->GetReturnType() == XB_EXP_DATE ) - 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 deleted file mode 100755 index 77a8c33..0000000 --- a/src/core/xbexpnode.cpp +++ /dev/null @@ -1,562 +0,0 @@ -/* xbexpnode.cpp - -XBase64 Software Library - -Copyright (c) 1997,2003,2014,2017,2022 Gary A Kunkel - -The xb64 software library is covered under -the terms of the GPL Version 3, 2007 license. - -Email Contact: - - XDB-devel@lists.sourceforge.net - XDB-users@lists.sourceforge.net - -*/ - -#include "xbase.h" - -#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 deleted file mode 100755 index e683d9c..0000000 --- a/src/core/xbfields.cpp +++ /dev/null @@ -1,1130 +0,0 @@ -/* xbfields.cpp - -XBase64 Software Library - -Copyright (c) 1997,2003,2014,2022 Gary A Kunkel - -The xb64 software library is covered under the terms of the GPL Version 3, 2007 license. - -Email Contact: - - XDB-devel@lists.sourceforge.net - XDB-users@lists.sourceforge.net - -*/ - -#include "xbase.h" - -namespace xb{ - -/************************************************************************/ -//! @brief 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; - 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; - 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 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 deleted file mode 100755 index 3b798a2..0000000 --- a/src/core/xbfile.cpp +++ /dev/null @@ -1,2193 +0,0 @@ -/* 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 Home Folders. -/*! - Create xbase64 log and data 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 cPathSeperator; - 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 - cPathSeperator = '\\'; - #else - cPathSeperator = '/'; - #endif - sDflt.Sprintf( ".%c", cPathSeperator ); - // 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()] != cPathSeperator ) - sHomeDir += cPathSeperator; - - 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(), cPathSeperator ); - // 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(), cPathSeperator ); - // 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 - } - xbase->SetDataDirectory( 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 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; - - - while( !bUniqueFileNameFound ){ - sFqnOut.Sprintf( "%sxbTmp%03d.%s", sDirIn.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 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 = 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(); - 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(); - 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 deleted file mode 100755 index 0fb643d..0000000 --- a/src/core/xbfilter.cpp +++ /dev/null @@ -1,544 +0,0 @@ -/* xbfilter.cpp - -XBase64 Software Library - -Copyright (c) 1997,2003,2014,2022 Gary A Kunkel - -The xb64 software library is covered under the terms of the GPL Version 3, 2007 license. - -Email Contact: - - XDB-devel@lists.sourceforge.net - XDB-users@lists.sourceforge.net - - -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 deleted file mode 100755 index a038611..0000000 --- a/src/core/xbfuncs.cpp +++ /dev/null @@ -1,850 +0,0 @@ -/* xbfuncs.cpp - -XBase64 Software Library - -Copyright (c) 1997,2003,2014,2017,2022 Gary A Kunkel - -The xb64 software library is covered under the terms of the GPL Version 3, 2007 license. - -Email Contact: - - XDB-devel@lists.sourceforge.net - XDB-users@lists.sourceforge.net - -*/ - -#include "xbase.h" - -#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. - \returns XB_NO_ERROR. -*/ -xbInt16 xbXBase::SPACE( xbInt32 lCnt, xbString &sOut ){ - sOut = ""; - sOut.PadLeft( ' ', (xbUInt32) lCnt ); - 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 deleted file mode 100755 index 8b64fd6..0000000 --- a/src/core/xbixbase.cpp +++ /dev/null @@ -1,789 +0,0 @@ -/* xbixbase.cpp - -XBase64 Software Library - -Copyright (c) 1997,2003,2014,2022 Gary A Kunkel - -The xb64 software library is covered under the terms of the GPL Version 3, 2007 license. - -Email Contact: - - XDB-devel@lists.sourceforge.net - XDB-users@lists.sourceforge.net - - 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 deleted file mode 100755 index 135cde8..0000000 --- a/src/core/xbixmdx.cpp +++ /dev/null @@ -1,5073 +0,0 @@ -/* 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 ); - - // std::cout << "xbIxMdx::AddKeys() lKeyCnt = " << lKeyCnt << " KeysPerBlock = " << npTag->iKeysPerBlock << " npBlockNo = " << npTag->npCurNode->ulBlockNo << "\n"; - - 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; - } - //std::cout << "AddKey Section A end\n"; - } else { - - // land here with a full leaf node - // std::cout << "Section B begin split leaf node\n"; - - 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; - } - - // std::cout << "ulNewRightChild = " << ulNewRightChild << "\n"; - - 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; - - - //std::cout << "ulTempBlockNo = " << ulTempBlockNo << "\n"; - //std::cout << "key count left block " << GetKeyCount( npTag->npCurNode ) << "\n"; - //std::cout << "key count right block " << GetKeyCount( npRightNode ) << "\n"; - - - //std::cout << "Section B end\n"; - - // 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; - } - - // std::cout << "Past Section C Cur Node Block No = " << npTag->npCurNode->ulBlockNo << " root page = " << npTag->ulRootPage << "\n"; - // section D - if cur node is split root, create new root - - if(( npTag->npCurNode->ulBlockNo * (xbUInt32) iBlockFactor ) == npTag->ulRootPage ){ - - // std::cout << "Section D begin right node = " << npRightNode << "\n"; - - if(( iRc = AddKeyNewRoot( npTag, npTag->npCurNode, npRightNode )) != XB_NO_ERROR ){ - iErrorStop = 160; - throw iRc; - } - - if( npRightNode ) - npRightNode = FreeNodeChain( npRightNode ); - - // std::cout << "Section D end\n"; - - } 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 ){ - -// std::cout << "ulRightChild was = " << npTag->ulRightChild << " changed to " << ulNewRightChild << "\n"; - - 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; - } -// std::cout << "setting right child\n"; - 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; - - // std::cout << "AddKeyNewRoot - RootBlock = " << npRoot->ulBlockNo << "\n"; - // std::cout << "AddKeyNewRoot - LeftBlock = " << npLeft->ulBlockNo << "\n"; - // std::cout << "AddKeyNewRoot - RightBlock = " << npRight->ulBlockNo << "\n"; - - - 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; - // std::cout << "AddKeyNewRoot - key [" << pLastKey << "] len = [" << strlen( pLastKey) << "]\n"; - memcpy( pTrg, pLastKey, (size_t) npTag->iKeyLen ); - - // set the right node number - //pTrg+= (npTag->iKeyLen); - // pTrg+= (npTag->iKeyItemLen)-4; - - pTrg+= npTag->iKeyItemLen - 4; - - //pTrg-=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; - - // std::cout << "ulPagePos = " << ulPagePos << " root block no = " << npRoot->ulBlockNo << " \n"; - - // 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{ - - // std::cout << "xbIxMdx::AllocateIxNode()\n"; - - if(( n = xbIx::AllocateIxNode( ulBufSize )) == NULL ){ - iRc = XB_NO_MEMORY; - iErrorStop = 100; - throw iRc; - } - char *p = n->cpBlockData; - p += 4; - - if( ulFirstFreePage > 0 && bReuseEmptyNodes ){ - // we have an empty node we can reuse - - // std::cout << "Reusing node " << ulFirstFreePage << "\n"; - - 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 ); - } - - // std::cout << "AllocateIxNode incremented the block to " << ulPageCnt << "\n"; - // std::cout << "AllocateIxNode new block number = " << n->ulBlockNo << "\n"; - } - 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++ ){ -// s.Sprintf( "tag = [%d] parent = [%d] left = [%d] right = [%d]\n", i, iaParent[i], iaLeftChild[i], iaRightChild[i]); -// std::cout << s; - 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{ -// xbase->WriteLogMessage( "xbIxMdx::CheckTagIntegrity()", iOpt ); - - #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 ); - // memset( npTag->cpKeyBuf2, 0x00, (size_t) npTag->iKeyItemLen ); - 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; - - // char *p0 = dbf->GetRecordBuf(0); - // char *p1 = dbf->GetRecordBuf(1); - - // 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; - } - // std::cout << "cp1 f0 = " << bFilter0 << "\n"; - // printf( "del byte 0 [%x] 1 [%x]\n", *p0, *p1 ); - } 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; - } - } - -// std::cout << "xbIxMdx::CreateKey key sts = " << npTag->iKeySts << " iOpt = " << iOpt << " type = " << npTag->exp->GetReturnType() << " name = " << npTag->cTagName; -// std::cout << " f0 = " << bFilter0 << " f1 = " << bFilter1 << "\n"; - - } - 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; - - - // 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; - - 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 ); - -// 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 = 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; - - //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 = 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 ); - -// xbIx::SetCurTag( ( void *) tte ); - - } - - 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; - } - - // do any empty node processing here -// if( bReuseEmptyNodes ){ -// if( bLeaf && lKeyCnt == 1 ){ -// std::cout << "Empty node ready for reuse\n"; -// } -// } - - } - 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; - - // std::cout << "xbIxMdx::DeleteKey()\n"; - - 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 Dump a given block for a 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; - -// std::cout << "xbIxMdx::DumpHeader options - " << iDestOpt << " fmtopt = " << iFmtOpt << "\n"; - - 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 ); - - -// std::cout << std::endl; -// std::cout << - 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; - } - } - // else { - // 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; - - // std::cout << "GetKeyFilter() "; - if( mpTag->sFiltExp ) - return *mpTag->sFiltExp; -// std::cout << " not null\n"; - else - return sNullString; - // std::cout << " null\n"; - -// next line causes seg faults -// return *mpTag->sFiltExp; - -} -/**************************************************************************************************/ -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{ - -// std::cout << "xbIxMdx::HarvestEmptyNode() page= " << BlockToPage( npNode->ulBlockNo ); -// std::cout << " block = " << npNode->ulBlockNo << "\n"; - - if( mpTag->ulRootPage == BlockToPage( npNode->ulBlockNo ) && !bHarvestRoot ){ - bRootPage = xbTrue; - -// std::cout << "special root page processing *****************\n"; - } - - 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 ); - -// std::cout << "InsertNodeL Keycount = " << lKeyCnt << "\n"; -// next line is correct, this aligns with db7 -// "4" is the four byte record number stored to the left of the key -// xbInt16 iKeyPos = 4 + iSlotNo * npTag->iKeyItemLen; - - 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) (iKeyPos + (npTag->iKeyItemLen * 2) + iCopyLen + 8); - xbUInt32 ulRqdBufSize = (xbUInt32) ((lKeyCnt + 1) * npTag->iKeyItemLen) + 8; - -/* - std::cout << "InsertNodeL CopyLen = " << iCopyLen << "\n"; - std::cout << "InsertNodeL iNewKeyPos = " << iNewKeyPos << "\n"; - std::cout << "InsertNodeL SlotNo = " << iSlotNo << "\n"; - std::cout << "InsertNodeL lKeyCnt = " << lKeyCnt << "\n"; - std::cout << "InsertNodeL node buf size = " << npNode->ulBufSize << "\n"; - std::cout << "InsertNodeL key item len = " << npTag->iKeyItemLen << "\n"; - std::cout << "InsertNodeL key len = " << npTag->iKeyLen << "\n"; - std::cout << "required buf size = " << ulRqdBufSize << "\n"; - std::cout << "InsertNodeL key buf strlen = " << strlen( npTag->cpKeyBuf ) << "\n"; -*/ - - 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 end, move things right - - pNewKeyPos = npNode->cpBlockData; - pNewKeyPos += iNewKeyPos; - - if( iSlotNo < lKeyCnt ) { - // pKeyPos = npNode->cpBlockData; - // pKeyPos += iKeyPos; - // pKeyPos += iNewKeyPos; - // pTrg = pKeyPos; - 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 ); -// std::cout << "lKeyCntA = " << GetKeyCount( npNode ) << "\n"; - - - // determine length of node, zap everything to the right of it - xbUInt32 iStartPos = 8 + ((xbUInt32) lKeyCnt * (xbUInt32) npTag->iKeyItemLen ); - xbUInt32 iClearLen = npNode->ulBufSize - iStartPos; - -// std::cout << "InsertNodeL SP = " << iStartPos << " clear len = " << iClearLen << " ulBufsize = " << npNode->ulBufSize << "\n"; - 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; - } - -// std::cout << "lKeyCntB = " << GetKeyCount( npNode ) << "\n"; - - - } - 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; - - // printf( "IsLeaf p = [%d] b1 = [%x] keylen = [%d]\n", eGetUInt32( p ), *p, mTag->iKeyItemLen ); - - 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 ); - - // iRc == 0 ? return 1 : return 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 - - // std::cout << "LoadTagDetail() iOption = " << iOption << "\n"; - - 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; - -//for( int i = 0; i < 5; i++ ) -// printf( "%d [%x]\n", i, *p++ ); -// p+=2; - - 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; - // tte->sKeyExp = new xbString(); - - 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; - -// std::cout << "ReadHeadBlock() option = " << iOpt << "\n"; - - 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 ){ - - //std::cout << "Reindex() linked list extract\n"; - //std::cout << "Tag Name = [" << p->sTagName << "]\n"; - //std::cout << "Key Exp = [" << p->sKeyExp->Str() << "]\n"; - //std::cout << "Filt Exp = [" << p->sFiltExp->Str() << "]\n"; - //std::cout << "bDesc = [" << p->bDesc << "]\n"; - //std::cout << "bUnique = [" << p->bUnique << "]\n"; - - 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 )); - } - - #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 Set Tag Pointer. -/*! - Set binary tree pointer value. The MDX tags are stored with binary - tree positions. This routine sets the value in memory. - \param cPtr L - Left child.
- R - Right child.
- P - Parent. - \param iWhich - Which tag to update - \param cVal - Value to set. - \returns void -*/ - -/* -void xbIxMdx::SetTagPtr( char cPtr, xbInt16 iWhich, char cVal ){ - - xbMdxTag *mpTag = (xbMdxTag *) GetTag( iWhich ); - if( mpTag ){ - switch( cPtr ){ - case 'L': - mpTag->cLeftChild = cVal; - break; - case 'R': - mpTag->cRightChild = cVal; - break; - case 'P': - mpTag->cParent = cVal; - break; - } - } -} -*/ - -/***********************************************************************/ -//! @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; - -// std::cout << "UpdateSerialNo offset = " << lPos << " option = " << iOption << "\n"; - - 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; - // std::cout << "old size = " << ulTagSizeSave << " new size = " << npTag->ulTagSize << "\n"; - //std::cout << "UpdateTagKey - tag size was updated need to do something here - test \n"; - - - if( cAction == 'D' || cAction == 'R' ){ - // std::cout << "UpdateTagKey-delete going to DeleteKey \n"; - if(( iRc = DeleteKey( vpTag )) != XB_NO_ERROR ){ - iErrorStop = 100; - throw iRc; - } - //std::cout << "UpdateTagKey-delete back from DeleteKey \n"; - } - - 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; // default is 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 deleted file mode 100755 index 9d946dd..0000000 --- a/src/core/xbixndx.cpp +++ /dev/null @@ -1,2820 +0,0 @@ -/* xbixndx.cpp - -XBase64 Software Library - -Copyright (c) 1997,2003,2014,2022 Gary A Kunkel - -The xb64 software library is covered under the terms of the GPL Version 3, 2007 license. - -Email Contact: - - XDB-devel@lists.sourceforge.net - XDB-users@lists.sourceforge.net - -*/ - -#include "xbase.h" - -#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 = stdout
- 1 = Syslog
- 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->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; - //vpTag ? npTag = (xbNdxTag *) vpTag : npTag = ndxTag; - - try{ - xbString sFileName = GetFqFileName(); - xbString sKey; // = GetKeyExpression( vpTag ); - sKey.Set( GetKeyExpression( *vpTag )); - xbInt16 iUnique = GetUnique( *vpTag ); - xbString sFilter = ""; - - void *vpTag2; - if(( iRc = CreateTag( sFileName, sKey, sFilter, 0, iUnique, xbTrue, &vpTag2 )) != XB_NO_ERROR ){ - iErrorStop = 100; - throw iRc; - } - - xbUInt32 ulRecCnt = 0; - if(( iRc = dbf->GetRecordCnt( ulRecCnt )) != XB_NO_ERROR ){ - iErrorStop = 110; - throw iRc; - } - - for( xbUInt32 l = 1; l <= ulRecCnt; l++ ){ - if(( iRc = dbf->GetRecord( l )) != XB_NO_ERROR ){ - iErrorStop = 120; - throw iRc; - } - - if(( iRc = CreateKey( vpTag2, 1 )) != XB_NO_ERROR ){ - iErrorStop = 130; - throw iRc; - } - - if( iUnique ){ - iRc = CheckForDupKey( vpTag2 ); - if( iRc != 0 ){ - if( iRc < 0 ){ - iErrorStop = 140; - throw iRc; - } - return XB_KEY_NOT_UNIQUE; - } - } - - if(( iRc = AddKey( vpTag2, l )) != XB_NO_ERROR ){ - iErrorStop = 150; - throw iRc; - } - } - *vpTag = vpTag2; - } - 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 )); - } - 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/xblog.cpp b/src/core/xblog.cpp deleted file mode 100755 index 6031c9e..0000000 --- a/src/core/xblog.cpp +++ /dev/null @@ -1,208 +0,0 @@ -/* xblog.cpp - -XBase64 Software Library - -Copyright (c) 1997,2003,2014,2022 Gary A Kunkel - -The xb64 software library is covered under the terms of the GPL Version 3, 2007 license. - -Email Contact: - - XDB-devel@lists.sourceforge.net - XDB-users@lists.sourceforge.net - -*/ - - -#include "xbase.h" -//#include - -#ifdef XB_LOGGING_SUPPORT - -namespace xb{ - -/******************************************************************************/ -//! @brief Constructor. -xbLog::xbLog() : xbFile( NULL ){ - - SetDirectory( GetLogDirectory()); - SetFileName ( GetLogFileName()); - - bLoggingStatus = xbFalse; - lLogSize = 50000; - - #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 = 50000; - - #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 ){ - bLoggingStatus = bStatus; -} -/******************************************************************************/ -//! @brief Open the logfile. -/*! - \returns Return Codes -*/ -xbInt16 xbLog::LogOpen(){ - xbInt16 rc; - 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 deleted file mode 100755 index 2bde853..0000000 --- a/src/core/xbmemo.cpp +++ /dev/null @@ -1,219 +0,0 @@ -/* xbmemo.cpp - -XBase64 Software Library - -Copyright (c) 1997,2003,2014,2022 Gary A Kunkel - -The xb64 software library is covered under the terms of the GPL Version 3, 2007 license. - -Email Contact: - - XDB-devel@lists.sourceforge.net - XDB-users@lists.sourceforge.net - - 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 deleted file mode 100755 index 60c1d53..0000000 --- a/src/core/xbmemo3.cpp +++ /dev/null @@ -1,575 +0,0 @@ -/* xbmemo3.cpp - -XBase64 Software Library - -Copyright (c) 1997,2003,2014,2022 Gary A Kunkel - -The xb64 software library is covered under the terms of the GPL Version 3, 2007 license. - -Email Contact: - - XDB-devel@lists.sourceforge.net - XDB-users@lists.sourceforge.net - - 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 ){ - 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 deleted file mode 100755 index d02df99..0000000 --- a/src/core/xbmemo4.cpp +++ /dev/null @@ -1,1335 +0,0 @@ -/* xbmemo4.cpp - -XBase64 Software Library - -Copyright (c) 1997,2003,2014,2022 Gary A Kunkel - -The xb64 software library is covered under the terms of the GPL Version 3, 2007 license. - -Email Contact: - - XDB-devel@lists.sourceforge.net - XDB-users@lists.sourceforge.net - - 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 ){ - 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 deleted file mode 100755 index d3e1070..0000000 --- a/src/core/xbssv.cpp +++ /dev/null @@ -1,573 +0,0 @@ -/* 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_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; - - -#ifdef WIN32 -xbString xbSsv::sDataDirectory = PROJECT_DATA_DIR; -#else -xbString xbSsv::sDataDirectory = PROJECT_DATA_DIR; -#endif // - -#ifdef XB_LOGGING_SUPPORT -xbString xbSsv::sLogDirectory = PROJECT_LOG_DIR; -//xbString xbSsv::sLogDirectory = ""; -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. - \returns void -*/ - -void xbSsv::GetHomeDir( xbString &sHomeDirOut ){ - - #ifdef 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 direcroty. -*/ - -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; -} - -/*************************************************************************/ -#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 - -} - -#else - -xbString & xbSsv::GetLogFileName() const { - return sNullString; -} - -xbString & xbSsv::GetLogDirectory() const { - return sNullString; -} - -void xbSsv::SetLogDirectory( const xbString &sLogDirectory ){ - 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 deleted file mode 100755 index 701e50e..0000000 --- a/src/core/xbstring.cpp +++ /dev/null @@ -1,1943 +0,0 @@ -/* xbstring.cpp - -XBase64 Software Library - -Copyright (c) 1997,2003,2014,2021,2022 Gary A Kunkel - -The xb64 software library is covered under the terms of the GPL Version 3, 2007 license. - -Email Contact: - - XDB-devel@lists.sourceforge.net - XDB-users@lists.sourceforge.net - -*/ - -//#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 += 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; - 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++; - 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; - 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; - 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; - 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; - 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) return (*this); - xbUInt32 oldLen = this->Len(); - xbUInt32 newLen = ulByteCount + oldLen; - - data = (char *)realloc(data, newLen+1); - - if(oldLen == 0) - data[0] = 0; - - for( xbUInt32 i = 0; i < ulByteCount; i++ ) - data[i+oldLen] = s[i]; - - data[newLen] = '\0'; - size += ulByteCount; - 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; - 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; - 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; - 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'; - 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 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. -/*! - \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 ){ - // left truncate cnt bytes - - char * ndata; - char * p; - if( ulCnt >= size ){ - if( size > 0 ){ - free( data ); - data = NULL; - size = 0; - } - return *this; - } - 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 ); - char *pTarg = data; - char *pSrc = data + ulStartPos - 1; - for( xbUInt32 l = 0; l < ulTargLen; l++ ) - *pTarg++ = *pSrc++; - *pTarg = 0x00; - Resize( 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; - } - } - 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((void *) 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; - l--; - - for(;;) { - if( data[l] != ' ' ) - break; - data[l] = 0; - size--; - if( l == 0 ) - break; - l--; - } - 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 ) { - 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 ){ - if( data ) free( data ); - data = NULL; - size = 0; - } else { - char *p = (char *) calloc( 1, s.Len() + 1 ); - xb_strcpy( p, s.Str()); - size = s.Len() + 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; - -// if( data ) -// free( data ); - -#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 = _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; - Resize( 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; - - l--; - for(;;) { - if( data[l] != c ) - break; - data[l] = 0; - size--; - if( l == 0 ) - break; - l--; - } - return *this; -} - -} /* namespace */ \ No newline at end of file diff --git a/src/core/xbtag.cpp b/src/core/xbtag.cpp deleted file mode 100755 index a71880b..0000000 --- a/src/core/xbtag.cpp +++ /dev/null @@ -1,121 +0,0 @@ -/* xbtag.cpp - - -XBase64 Software Library - -Copyright (c) 1997,2003,2014,2022 Gary A Kunkel - -The xb64 software library is covered under the terms of the GPL Version 3, 2007 license. - -Email Contact: - - XDB-devel@lists.sourceforge.net - XDB-users@lists.sourceforge.net - -*/ - -#include "xbase.h" - -#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 deleted file mode 100755 index 2fe6a8c..0000000 --- a/src/core/xbtblmgr.cpp +++ /dev/null @@ -1,311 +0,0 @@ -/* xbtblmgr.cpp - -XBase64 Software Library - -Copyright (c) 1997,2003,2014,2022 Gary A Kunkel - -The xb64 software library is covered under the terms of the GPL Version 3, 2007 license. - -Email Contact: - - XDB-devel@lists.sourceforge.net - XDB-users@lists.sourceforge.net - -*/ - -#include "xbase.h" - -//#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->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 deleted file mode 100755 index 0a9c2e7..0000000 --- a/src/core/xbuda.cpp +++ /dev/null @@ -1,78 +0,0 @@ -/* xbuda.cpp - -XBase64 Software Library - -Copyright (c) 1997,2003,2014,2022 Gary A Kunkel - -The xb64 software library is covered under the terms of the GPL Version 3, 2007 license. - -Email Contact: - - XDB-devel@lists.sourceforge.net - XDB-users@lists.sourceforge.net - -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 deleted file mode 100755 index 20b7fcc..0000000 --- a/src/core/xbxbase.cpp +++ /dev/null @@ -1,749 +0,0 @@ -/* xbxbase.cpp - -XBase64 Software Library - -Copyright (c) 1997,2003,2014,2022 Gary A Kunkel - -The xb64 software library is covered under the terms of the GPL Version 3, 2007 license. - -Email Contact: - - XDB-devel@lists.sourceforge.net - XDB-users@lists.sourceforge.net - -*/ - -#include "xbase.h" - -namespace xb{ - -/*************************************************************************/ -//! @brief Class Constructor. -xbXBase::xbXBase() { - SetEndianType(); - #ifdef XB_LOGGING_SUPPORT - xLog = new xbLog(); - #endif - - xbFile f( this ); - f.SetHomeFolders(); - -} -/*************************************************************************/ -//! @brief Class Deconstructor. -xbXBase::~xbXBase(){ - CloseAllTables(); - #ifdef XB_LOGGING_SUPPORT - delete xLog; - #endif -} - -/*************************************************************************/ -//! @brief Close all tables / files. -/*! - This closes everything. - \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; - } - } 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 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 xLog->GetDirectory(); -} - -//! @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 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( 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 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