summaryrefslogtreecommitdiff
path: root/src/core
diff options
context:
space:
mode:
Diffstat (limited to 'src/core')
-rwxr-xr-xsrc/core/xbbcd.cpp333
-rwxr-xr-xsrc/core/xbdate.cpp802
-rwxr-xr-xsrc/core/xbdbf.cpp4220
-rwxr-xr-xsrc/core/xbdbf3.cpp577
-rwxr-xr-xsrc/core/xbdbf4.cpp590
-rwxr-xr-xsrc/core/xbexp.cpp2643
-rwxr-xr-xsrc/core/xbexpnode.cpp562
-rwxr-xr-xsrc/core/xbfields.cpp1122
-rwxr-xr-xsrc/core/xbfile.cpp2052
-rwxr-xr-xsrc/core/xbfilter.cpp569
-rwxr-xr-xsrc/core/xbfuncs.cpp850
-rwxr-xr-xsrc/core/xbixbase.cpp792
-rwxr-xr-xsrc/core/xbixmdx.cpp5067
-rwxr-xr-xsrc/core/xbixndx.cpp2840
-rwxr-xr-xsrc/core/xblog.cpp208
-rwxr-xr-xsrc/core/xbmemo.cpp219
-rwxr-xr-xsrc/core/xbmemo3.cpp590
-rwxr-xr-xsrc/core/xbmemo4.cpp1372
-rwxr-xr-xsrc/core/xbssv.cpp550
-rwxr-xr-xsrc/core/xbstring.cpp1839
-rwxr-xr-xsrc/core/xbtag.cpp121
-rwxr-xr-xsrc/core/xbtblmgr.cpp257
-rwxr-xr-xsrc/core/xbuda.cpp78
-rwxr-xr-xsrc/core/xbxbase.cpp712
24 files changed, 28965 insertions, 0 deletions
diff --git a/src/core/xbbcd.cpp b/src/core/xbbcd.cpp
new file mode 100755
index 0000000..4855e80
--- /dev/null
+++ b/src/core/xbbcd.cpp
@@ -0,0 +1,333 @@
+/* xbbcd.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
+
+ 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;
+
+
+/*
+ std::cout << "num in = " << sIn.Str() << "\n";
+ unsigned char bW[12];
+ memcpy( bW, &bcd, 12 );
+ std::cout << "bx ";
+ for( int i = 0; i < 12; i++ ){
+ bcdBitDump( bW[i] );
+ std::cout << " ";
+ }
+ std::cout << "\n";
+
+ unsigned char cSign = bcd.cEncDigits >> 7;
+ std::cout << "bs ";
+ bcdBitDump( cSign );
+ std::cout << "\n";
+
+ std::cout << "test = ";
+ bcdBitDump( cEdc );
+ std::cout << " ";
+ bcdBitDump( GetEncDigitsNoSign() );
+ std::cout << "\n";
+ bcdBitDump( bcd.cEncDigits );
+
+ unsigned char c = bcd.cEncDigits;
+ c = c << 1;
+ c = c >> 3;
+ std::cout << " ";
+ bcdBitDump( c );
+ std::cout << "\n";
+*/
+}
+/***********************************************************************/
+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( (bcd.cEncDigits >> 7 ) != bcdIn.GetSign() ){
+ (bcd.cEncDigits >> 7 ) > 0 ? iRc = -1 : iRc = 1;
+ return iRc;
+ }
+
+ if( bcd.cSigDigits != bcdIn.GetSigDigits()){
+ // if( !bcd.Sign ){ // positive numbers
+ if( !(bcd.cEncDigits >> 7 )){ // positive numbers
+ if( bcd.cSigDigits > bcdIn.GetSigDigits())
+ return 1;
+ else
+ return -1;
+ } else { // negative numbers
+ if( 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 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/xbdate.cpp b/src/core/xbdate.cpp
new file mode 100755
index 0000000..ba0fdf1
--- /dev/null
+++ b/src/core/xbdate.cpp
@@ -0,0 +1,802 @@
+/* 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 <time.h>
+
+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.<br>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.<br>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.<br>
+ 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.<br>
+ 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.<br>
+ 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.<br>
+ 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<br>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<br>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.<br>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 )<br>
+ XB_FMT_MONTH Number of day in MONTH 1-31<br>
+ XB_FMT_YEAR Number of day in YEAR 1-366
+ \returns XB_INVALID_OPTION<br>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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+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<br>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:<br>
+
+ 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
+ <br><br>
+ Format Examples:<br>
+ MM/DD/YY<br>
+ YYYY-MM-DD<br>
+ 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.<br> 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.<br> 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<br>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
new file mode 100755
index 0000000..8e020fb
--- /dev/null
+++ b/src/core/xbdbf.cpp
@@ -0,0 +1,4220 @@
+/* xbdbf.cpp
+
+XBase64 Software Library
+
+Copyright (c) 1997,2003,2014,2022 Gary A Kunkel
+
+The xb64 software library is covered under the terms of the GPL Version 3, 2007 license.
+
+Email Contact:
+
+ XDB-devel@lists.sourceforge.net
+ XDB-users@lists.sourceforge.net
+
+*/
+
+#include "xbase.h"
+
+
+namespace xb{
+
+/************************************************************************/
+//! @brief Constructor
+/*!
+ \param x Pointer to xbXbase
+*/
+xbDbf::xbDbf( xbXBase * x ) : xbFile( x ){
+ xbase = x;
+ SchemaPtr = NULL;
+ RecBuf = NULL;
+ RecBuf2 = NULL;
+ InitVars();
+}
+/************************************************************************/
+void xbDbf::InitVars()
+{
+ iNoOfFields = 0;
+ iDbfStatus = XB_CLOSED;
+ ulCurRec = 0L;
+ cVersion = 0x00;
+ cUpdateYY = 0x00;
+ cUpdateMM = 0x00;
+ cUpdateDD = 0x00;
+ ulNoOfRecs = 0L;
+ uiHeaderLen = 0x00;
+ uiRecordLen = 0x00;
+ cTransactionFlag = 0x00;
+ cEncryptionFlag = 0x00;
+ cIndexFlag = 0x00;
+ cLangDriver = 0x00;
+ iFileVersion = 0; /* Xbase64 file version */
+ iAutoCommit = -1;
+
+ SetFileName ( "" );
+ sAlias.Set ( "" );
+ SetDirectory ( GetDataDirectory());
+
+ #ifdef XB_LOCKING_SUPPORT
+ iLockFlavor = -1;
+ bTableLocked = xbFalse;
+ bHeaderLocked = xbFalse;
+ ulAppendLocked = 0;
+ SetAutoLock( -1 );
+ lloRecLocks.SetDupKeys( xbFalse );
+ #endif
+
+ #ifdef XB_INDEX_SUPPORT
+ ixList = NULL;
+ pCurIx = NULL;
+ vpCurIxTag = NULL;
+ sCurIxType = "";
+ ClearTagList();
+ #endif
+ #ifdef XB_NDXINF_SUPPORT
+ llNdxInfData.Clear();
+ #endif
+}
+
+/************************************************************************/
+//! @brief Destructor
+xbDbf::~xbDbf(){
+
+ // is there is an uncommited update, discard it.
+ // as we don't know if it is an append or an update
+ if( iDbfStatus == XB_UPDATED )
+ Abort();
+
+ if( iDbfStatus != XB_CLOSED )
+ Close();
+
+ if( SchemaPtr ){
+ free( SchemaPtr );
+ SchemaPtr = NULL;
+ }
+ if( RecBuf ){
+ free( RecBuf );
+ RecBuf = NULL;
+ }
+ if( RecBuf2){
+ free( RecBuf2 );
+ RecBuf2 = NULL;
+ }
+ Close();
+}
+/************************************************************************/
+//! @brief Abort any uncommited changes for the current record buffer.
+/*!
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbDbf::Abort(){
+
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+
+ try{
+ if( iDbfStatus == XB_UPDATED ){
+ #ifdef XB_MEMO_SUPPORT
+ if( MemoFieldsExist()){
+ if(( iRc = Memo->Abort()) != XB_NO_ERROR ){
+ iErrorStop = 10;
+ throw iRc;
+ }
+ }
+ #endif
+ memcpy( RecBuf, RecBuf2, uiRecordLen );
+ iDbfStatus = XB_OPEN;
+ }
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbdbf::Abort() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+/************************************************************************/
+#ifdef XB_INDEX_SUPPORT
+//! @brief Add an index to the internal list of indices for this table.
+/*!
+ The index list is used during any table update process to update any open
+ index file. Index files can contain one or more tags.
+
+ \param ixIn Pointer to index object for a given index file.
+ \param sFmt NDX or MDX.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+
+*/
+
+xbInt16 xbDbf::AddIndex( xbIx * ixIn, const xbString &sFmt ){
+
+ xbIxList *ixt; // this
+ if(( ixt = (xbIxList *) malloc( sizeof( xbIxList ))) == NULL )
+ return XB_NO_ERROR;
+
+ ixt->ix = ixIn;
+ ixt->next = NULL;
+ ixt->sFmt = new xbString( sFmt );
+ ixt->sFmt->ToUpperCase();
+
+ if( ixList ){
+ xbIxList *ixn = ixList; // next
+ while( ixn->next ){
+ ixn = ixn->next;
+ }
+ ixn->next = ixt;
+ } else {
+ ixList = ixt;
+ }
+ return XB_NO_ERROR;
+}
+#endif // XB_INDEX_SUPPORT
+
+
+/************************************************************************/
+//! @brief Append the current record to the data file.
+/*!
+ This method attempts to append the contents of the current record buffer
+ to the end of the DBF file, updates the file date, number of records in the file
+ and updates any open indices associated with this data file.<br>
+
+ To add a record, an application would typically blank the record buffer,
+ update various fields in the record buffer, then append the record.<br>
+
+ The append method performs the following tasks:<br>
+ 1) Create new index key values<br>
+ 2) Lock the table<br>
+ 3) Lock append bytes<br>
+ 4) Lock indices<br>
+ 5) Read the dbf header<br>
+ 6) Check for dup keys<br>
+ 7) Calc last update date, no of recs<br>
+ 8) Add keys<br>
+ 9) Unlock indices<br>
+ 10) Update file header<br>
+ 11) Unlock file header<br>
+ 12) Append record<br>
+ 13) Unlock append bytes<br>
+
+Note: Locking memo files is not needed as the memo file updates are handled outside of the append method.<br>
+
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbDbf::AppendRecord(){
+ xbInt16 iErrorStop = 0;
+ xbInt16 iRc = XB_NO_ERROR;
+ xbUInt32 ulSaveCurRec = 0;
+
+ try{
+ #ifdef XB_INDEX_SUPPORT
+ xbIxList *ixList = GetIxList();
+ // do this step first before anything is locked, reduce lock time as much as possible
+ while( ixList ){
+
+ // std::cout << "xbDbf::CreateKeys(x)\n";
+ if(( iRc = ixList->ix->CreateKeys( 1 )) != XB_NO_ERROR ){
+ iErrorStop = 10;
+ throw iRc;
+ }
+
+ ixList = ixList->next;
+ }
+ #endif // XB_INDEX_SUPPORT
+
+ // lock everything up for an update
+ #ifdef XB_LOCKING_SUPPORT
+ // xbInt16 iAutoLock = GetAutoLock();
+ if( iAutoLock ){
+
+ if(( iRc = LockHeader( XB_LOCK )) != XB_NO_ERROR ){
+ if( iRc == XB_LOCK_FAILED ) {
+ return iRc;
+ } else {
+ iErrorStop = 20;
+ throw iRc;
+ }
+ }
+ if(( iRc = LockAppend( XB_LOCK )) != XB_NO_ERROR ){
+ if( iRc == XB_LOCK_FAILED ){
+ LockHeader( XB_UNLOCK );
+ return iRc;
+ } else {
+ iErrorStop = 30;
+ throw iRc;
+ }
+ }
+
+ #ifdef XB_INDEX_SUPPORT
+ if(( iRc = LockIndices( XB_LOCK )) != XB_NO_ERROR ){
+ iErrorStop = 40;
+ throw iRc;
+ }
+ #endif // XB_INDEX_SUPPORT
+
+ }
+ #endif // XB_LOCKING_SUPPORT
+ if(( iRc = ReadHeader( 1, 1 )) != XB_NO_ERROR ){
+ iErrorStop = 50;
+ throw iRc;
+ }
+ #ifdef XB_INDEX_SUPPORT
+ ixList = GetIxList();
+
+ while( ixList ){
+ if(( iRc = ixList->ix->CheckForDupKeys()) != 0 ){
+ if( iRc < 0 ){
+ iErrorStop = 60;
+ throw iRc;
+ }
+ return XB_KEY_NOT_UNIQUE;
+ }
+ ixList = ixList->next;
+ }
+
+ #endif // XB_INDEX_SUPPORT
+
+ // calculate the latest header information
+ xbDate d;
+ cUpdateYY = (char) d.YearOf() - 1900;
+ cUpdateMM = (char) d.MonthOf();
+ cUpdateDD = (char) d.DayOf( XB_FMT_MONTH );
+ ulSaveCurRec = ulCurRec;
+ ulNoOfRecs++;
+ ulCurRec = ulNoOfRecs;
+
+ #ifdef XB_INDEX_SUPPORT
+
+
+ ixList = GetIxList();
+ while( ixList ){
+ if(( iRc = ixList->ix->AddKeys( ulCurRec )) != XB_NO_ERROR ){
+ iErrorStop = 50;
+ throw iRc;
+ }
+ ixList = ixList->next;
+ }
+
+ #ifdef XB_LOCKING_SUPPORT
+ if( iAutoLock ){
+ if(( iRc = LockIndices( XB_UNLOCK )) != XB_NO_ERROR ){
+ iErrorStop = 60;
+ throw iRc;
+ }
+ }
+ #endif // XB_LOCKING_SUPPORT
+ #endif // XB_INDEX_SUPPORT
+
+ // rewrite the header record
+ if(( iRc = WriteHeader( 1, 1 )) != XB_NO_ERROR ){
+ iErrorStop = 60;
+ throw iRc;
+ }
+
+ #ifdef XB_LOCKING_SUPPORT
+ if( iAutoLock ){
+ if(( iRc = LockHeader( XB_UNLOCK )) != XB_NO_ERROR ){
+ iErrorStop = 70;
+ throw iRc;
+ }
+ }
+ #endif
+
+ // write the last record
+ if(( iRc = xbFseek( (uiHeaderLen+((xbInt64)(ulNoOfRecs-1)*uiRecordLen)), 0 )) != XB_NO_ERROR ){
+ iErrorStop = 80;
+ throw iRc;
+ }
+
+ if(( iRc = xbFwrite( RecBuf, uiRecordLen, 1 )) != XB_NO_ERROR ){
+ iErrorStop = 90;
+ throw iRc;
+ }
+
+ // write the end of file marker
+ if(( iRc = xbFputc( XB_CHAREOF )) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+
+ #ifdef XB_LOCKING_SUPPORT
+ if( iAutoLock ){
+ if(( iRc = LockAppend( XB_UNLOCK )) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ throw( iRc );
+ }
+ }
+ #endif // XB_LOCKING_SUPPORT
+
+ }
+ catch (xbInt16 iRc ){
+ if( ulSaveCurRec != 0 ){
+ ulCurRec = ulSaveCurRec;
+ ulNoOfRecs--;
+ }
+ #ifdef XB_LOCKING_SUPPORT
+ if( iAutoLock ){
+ #ifdef XB_INDEX_SUPPORT
+ LockIndices( XB_UNLOCK );
+ #endif // XB_INDEX_SUPPORT
+ LockAppend( XB_UNLOCK );
+ LockHeader( XB_UNLOCK );
+ }
+ #endif // XB_LOCKING_SUPPORT
+
+ if( iRc != XB_LOCK_FAILED && iRc != XB_KEY_NOT_UNIQUE ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbdbf::Append() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ }
+
+ if( iRc == XB_NO_ERROR )
+ iDbfStatus = XB_OPEN;
+ return iRc;
+}
+
+/************************************************************************/
+#ifdef XB_NDXINF_SUPPORT
+//! @brief Asscoiate a non production index to a DBF file.
+/*!
+
+ The original Dbase (TM) software supported non production indices (NDX) and production indices (MDX).
+ The production indices are opened automatically when the DBF file is opened but the non-production
+ indices are not. This method is specific to the Xbas64 library and providex a means to link non production
+ NDX index files to the DBF file so they will be opened automatically when the DBF file is opened.<br>
+
+ This routine requires NDXINF support be enabled when building the library.<br>
+ This routine creates a file with the same name as the DBF file, but with an extension of INF.<br>
+
+
+ \param sIxType Currently only NDX. Future versions will support additional non prod index types.
+ \param sIxName The index name.
+ \param iOpt 0 - Add index to .INF if not already there<br>
+ 1 - Remove index from .INF if there
+
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+
+*/
+
+xbInt16 xbDbf::AssociateIndex( const xbString &sIxType, const xbString &sIxName, xbInt16 iOpt ){
+
+ xbInt16 iRc = 0;
+ xbInt16 iErrorStop = 0;
+
+ try{
+ xbString sIxTypeIn = sIxType;
+ sIxTypeIn.Trim();
+ xbString sIxNameIn = sIxName;
+ sIxNameIn.Trim();
+
+ if( sIxTypeIn != "NDX" || sIxName == "" )
+ return XB_INVALID_INDEX;
+
+ if(( iRc = LoadNdxInfData()) != XB_NO_ERROR ){
+ iErrorStop = 10;
+ throw iRc;
+ }
+
+ // check if entry exists
+ xbLinkListNode<xbString> * llN = llNdxInfData.GetHeadNode();
+ xbBool bFound = xbFalse;
+ xbString s;
+
+ while( llN && !bFound ){
+ s = llN->GetKey();
+ if( s.Len() > 0 ){
+ if( sIxNameIn == s )
+ bFound = xbTrue;
+ }
+ llN = llN->GetNextNode();
+ }
+
+ xbBool bUpdated = xbFalse;
+ if( iOpt == 0 && !bFound ){
+ s.Sprintf( "%s%c%c", sIxName.Str(), 0x0d, 0x0a );
+ llNdxInfData.InsertAtEnd( s );
+ bUpdated = xbTrue;
+
+ } else if( iOpt == 1 && bFound ){
+ llNdxInfData.RemoveByVal( s );
+ bUpdated = xbTrue;
+ }
+
+ if( bUpdated ){
+ if(( iRc = SaveNdxInfData()) != XB_NO_ERROR ){
+ iErrorStop = 20;
+ throw iRc;
+ }
+ }
+
+ } catch( xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbdbf::AssociateIndex() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+#endif // XB_NDXINF_SUPPORT
+
+/************************************************************************/
+//! @brief Blank the record buffer.
+/*!
+
+ This method would typically be called to initialize the record buffer before
+ updates are applied to append a new record.
+
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbDbf::BlankRecord()
+{
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+
+ try{
+
+ if( iDbfStatus == XB_CLOSED ){
+ iErrorStop = 10;
+ iRc = XB_NOT_OPEN;
+ throw iRc;
+ }
+
+ if( iDbfStatus == XB_UPDATED ){
+ if( GetAutoCommit() == 1 ){
+ if(( iRc = Commit()) != XB_NO_ERROR ){
+ iErrorStop = 20;
+ throw iRc;
+ }
+ } else {
+ if(( iRc = Abort()) != XB_NO_ERROR ){
+ iErrorStop = 30;
+ throw iRc;
+ }
+ }
+ }
+ ulCurRec = 0;
+ memset( RecBuf, 0x20, uiRecordLen );
+ memset( RecBuf2, 0x20, uiRecordLen );
+ }
+
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbdbf::BlankRecord() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+
+ return iRc;
+}
+/************************************************************************/
+#ifdef XB_INDEX_SUPPORT
+
+#ifdef XB_DEBUG_SUPPORT
+
+/*!
+ This method is used to check an index tag's intgerity.
+
+ \param iTagOpt 0 - Check current tag<br>
+ 1 - Check all tag<br>
+
+ \param iOutputOpt Output message destination<br>
+ 0 = stdout<br>
+ 1 = Syslog<br>
+ 2 = Both<br>
+
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbDbf::CheckTagIntegrity( xbInt16 iTagOpt, xbInt16 iOutputOpt ){
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+
+ try{
+
+// std::cout << "CheckTagIntegrity()\n";
+
+ if( iTagOpt == 0 ){
+ if( pCurIx )
+ return pCurIx->CheckTagIntegrity( vpCurIxTag, iOutputOpt );
+ else
+ return XB_INVALID_TAG;
+
+ } else {
+
+ xbLinkListNode<xbTag *> *llN = GetTagList();
+ xbTag *pTag;
+
+ while( llN ){
+ pTag = llN->GetKey();
+ if(( iRc = pTag->GetIx()->CheckTagIntegrity( pTag->GetVpTag(), iOutputOpt )) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+ llN = llN->GetNextNode();
+ }
+ }
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbdbf::CheckTagIntegrity() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+
+ return iRc;
+}
+
+#endif // XB_DEBUG_SUPPORT
+
+/************************************************************************/
+/*!
+ This method is used to reindex / rebuild index tag.
+
+ \param iTagOpt 0 - Reindex current tag<br>
+ 1 - Reindex all tags<br>
+
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbDbf::Reindex( xbInt16 iTagOpt ){
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+ void *vp;
+
+ try{
+ if( iTagOpt == 0 ){
+ if( pCurIx ){
+
+ //
+ // xbString = save current tag name
+ //
+
+ iRc = pCurIx->Reindex( &vpCurIxTag );
+ if( iRc != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+ //
+ // restore current tag name
+ //
+ return iRc;
+
+ } else {
+
+ return XB_INVALID_TAG;
+
+ }
+
+ } else {
+
+ xbLinkListNode<xbTag *> *llN = GetTagList();
+ xbTag *pTag;
+
+ while( llN ){
+ pTag = llN->GetKey();
+ vp = pTag->GetVpTag();
+ if(( iRc = pTag->GetIx()->Reindex( &vp )) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+ llN = llN->GetNextNode();
+ }
+
+
+ }
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbdbf::Reindex() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+
+ return iRc;
+}
+
+/************************************************************************/
+// @brief Clear the index tag list.
+/*
+ Protected method. Clears the list inf index tags.
+ \returns void.
+*/
+void xbDbf::ClearTagList(){
+
+ xbTag *pTag;
+ xbBool bDone = xbFalse;
+ while( llTags.GetNodeCnt() > 0 && !bDone ){
+ if( llTags.RemoveFromFront( pTag ) != XB_NO_ERROR ){
+ bDone = xbTrue;
+ } else {
+ if( pTag )
+ delete pTag;
+ }
+ }
+}
+#endif // XB_INDEX_SUPPORT
+
+/************************************************************************/
+//! @brief Close DBF file/table.
+/*!
+ This routine flushes any remaining updates to disk, closes the DBF file and
+ any associated memo and index files.
+
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbDbf::Close(){
+
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+
+ try{
+
+ if(iDbfStatus == XB_CLOSED)
+ return XB_NO_ERROR;
+
+ else if( iDbfStatus == XB_UPDATED ){
+
+ if( GetAutoCommit() == 1 ){
+ if(( iRc = Commit()) != XB_NO_ERROR ){
+ iErrorStop = 10;
+ throw iRc;
+ }
+ } else {
+ if(( iRc = Abort()) != XB_NO_ERROR ){
+ iErrorStop = 20;
+ throw iRc;
+ }
+ }
+ }
+
+ if(SchemaPtr){
+ free( SchemaPtr );
+ SchemaPtr = NULL;
+ }
+ if(RecBuf){
+ free( RecBuf );
+ RecBuf = NULL;
+ }
+ if(RecBuf2){
+ free( RecBuf2 );
+ RecBuf2 = NULL;
+ }
+
+
+ #ifdef XB_MEMO_SUPPORT
+ if( iMemoFieldCnt > 0 ){
+ Memo->CloseMemoFile();
+ delete Memo;
+ Memo = NULL;
+ }
+ #endif
+
+ // close any open index files, remove from the ix list
+ #ifdef XB_INDEX_SUPPORT
+ while( ixList ){
+ ixList->ix->Close();
+ RemoveIndex( ixList->ix );
+ }
+ #endif
+
+ #ifdef XB_LOCKING_ON
+ if( xblfh ){
+ fclose( xblfh );
+ xblfh = NULL;
+ }
+ #endif
+
+ if(( iRc = xbase->RemoveTblFromTblList( this )) != XB_NO_ERROR ){
+ xbString sMsg;
+ sMsg.Sprintf( "Alias = [%s]", sAlias.Str());
+ xbase->WriteLogMessage( sMsg.Str() );
+ iErrorStop = 30;
+ throw iRc;
+ }
+
+ xbFclose();
+ InitVars();
+
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbdbf::Close() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+
+
+/************************************************************************/
+#ifdef XB_INDEX_SUPPORT
+//! @brief Close an open index file
+/*!
+ All index files are automatically closed when the DBF file is closed.
+ Under normal conditions, it is not necessary to explicitly close an index file
+ with this routine. Any updates posted to a DBF file while an index is closed
+ will not be reflected in the closed index file.
+
+ \param pIx Pointer to index object to close.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbDbf::CloseIndexFile( xbIx *pIx ){
+
+ xbInt16 iErrorStop = 0;
+ xbInt16 iRc = XB_NO_ERROR;
+
+ try{
+
+ // verify index is open and in the list
+ xbBool bFound = xbFalse;
+ xbIxList *p = GetIxList();
+ while( p && !bFound ){
+ if( pIx == p->ix )
+ bFound = xbTrue;
+ p = p->next;
+ }
+ if( !bFound ){
+ iErrorStop = 10;
+ iRc = XB_NOT_OPEN;
+ throw iRc;
+ }
+
+ // close it
+ if(( iRc = pIx->Close()) != XB_NO_ERROR ){
+ iErrorStop = 20;
+ throw iRc;
+ }
+
+ // remove it from the list
+ if(( iRc = RemoveIndex( pIx )) != XB_NO_ERROR ){
+ iErrorStop = 30;
+ throw iRc;
+ }
+
+ // refresh the tag list
+ if(( iRc = UpdateTagList()) != XB_NO_ERROR ){
+ iErrorStop = 40;
+ throw iRc;
+ }
+
+ if( pIx == pCurIx ){
+ pCurIx = NULL;
+ vpCurIxTag = NULL;
+ sCurIxType = "";
+ }
+
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbdbf::CloseIndexFile() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+#endif // XB_INDEX_SUPPORT
+
+/************************************************************************/
+//! @brief Commit updates to disk
+/*!
+
+ This routine commits any pending updates to disk.
+
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbDbf::Commit(){
+
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+ try{
+
+ if( iDbfStatus == XB_UPDATED ){
+ if( ulCurRec == 0 ){
+ if(( iRc = AppendRecord()) != XB_NO_ERROR ){
+ iErrorStop = 20;
+ throw iRc;
+ }
+ } else {
+ if(( iRc = PutRecord( ulCurRec )) != XB_NO_ERROR ){
+ iErrorStop = 30;
+ throw iRc;
+ }
+ }
+ }
+ }
+ catch (xbInt16 iRc ){
+ if( iRc != XB_LOCK_FAILED ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbdbf::Commit() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ }
+ return iRc;
+}
+
+
+/************************************************************************/
+//! @brief Copy table (dbf) file structure.
+/*!
+
+ This routine will copy the structure of a dbf file and if successful
+ return a pointer to the new table in an open state.
+
+ \param dNewTable Reference to new table object.
+ \param sNewTableName New table (dbf) name.
+ \param sNewTableAlias Alias name of new table.
+ \param iOverlay xbTrue - Overlay existing file.<br>
+ xbFalse - Don't overlay existing file.
+ \param iShareMode XB_SINGLE_USER<br>
+ XB_MULTI_USER
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+//! Copy DBF structure
+/*!
+*/
+xbInt16 xbDbf::CopyDbfStructure( xbDbf * dNewTable, const xbString &sNewTableName,
+ const xbString & sNewTableAlias, xbInt16 iOverlay, xbInt16 iShareMode ) {
+
+// If successful, the table is returned in an open state after executing this method
+
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+ xbSchema *newTableSchema = NULL;
+
+ try{
+
+ if( iDbfStatus == XB_CLOSED ){
+ iErrorStop = 10;
+ iRc = XB_DBF_FILE_NOT_OPEN;
+ throw iRc;
+ }
+
+ if( !dNewTable ){
+ iErrorStop = 20;
+ iRc = XB_INVALID_OBJECT;
+ throw iRc;
+ }
+
+ // Get the number of schema entries for this table
+ xbInt32 lSchemaRecCnt = GetFieldCnt() + 1;
+
+ // Allocate a Schema = No Of Fields + 1
+ if((newTableSchema=(xbSchema *)malloc( (size_t) lSchemaRecCnt * sizeof(xbSchema)))==NULL){
+ iErrorStop = 30;
+ iRc = XB_NO_MEMORY;
+ throw iRc;
+ }
+
+ // Populate the Schema
+ xbInt32 l;
+ for( l = 0; l < lSchemaRecCnt-1; l++ ){
+ memset( newTableSchema[l].cFieldName, 0x00, 11 );
+ for( int x = 0; x < 10 && SchemaPtr[l].cFieldName[x]; x++ )
+ newTableSchema[l].cFieldName[x] = SchemaPtr[l].cFieldName[x];
+ newTableSchema[l].cType = SchemaPtr[l].cType;
+ newTableSchema[l].iFieldLen = SchemaPtr[l].cFieldLen;
+ newTableSchema[l].iNoOfDecs = SchemaPtr[l].cNoOfDecs;
+ }
+
+ // set the last one to zeroes
+ memset( newTableSchema[l].cFieldName, 0x00, 11 );
+ newTableSchema[l].cType = 0;
+ newTableSchema[l].iFieldLen = 0;
+ newTableSchema[l].iNoOfDecs = 0;
+
+ dNewTable->SetVersion();
+ #ifdef XB_MEMO_SUPPORT
+ if( MemoFieldsExist())
+ dNewTable->SetCreateMemoBlockSize( Memo->GetBlockSize() );
+ #endif
+
+ // Call the create a table function
+ if(( iRc = dNewTable->CreateTable( sNewTableName, sNewTableAlias, newTableSchema, iOverlay, iShareMode )) != XB_NO_ERROR ){
+ iErrorStop = 40;
+ throw iRc;
+ }
+ }
+
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbDbf::CopyDbfStructure() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+
+ if( newTableSchema )
+ free( newTableSchema );
+
+ return iRc;
+}
+
+
+/************************************************************************/
+#ifdef XB_INDEX_SUPPORT
+//! @brief Create a new tag (index) for this dbf file (table).
+/*!
+ This routine creates a new tag (index) on a dbf file. The library currently supports NDX and MDX
+ indices. If you don't have a specific need for an NDX file, use MDX.
+
+ \param sIxType "MDX" or "NDX".
+ \param sName Index or tag name.
+ \param sKey Index key expression,
+ \param sFilter Filter expression. Not applicable for NDX indices.
+ \param iDescending xbTrue for descending. Not available for NDX indices.<br>
+ xbFalse - ascending
+ \param iUnique xbTrue - Unique index<br>xbFalse - Not unique index.
+ \param iOverLay xbTrue - Overlay if exists<br>
+ xbFalse - Don't overlay if it exists.
+ \param pIxOut Pointer to pointer of output index object.
+ \param vpTagOut Pointer to pointer of newly created tag,
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbDbf::CreateTag( const xbString &sIxType, const xbString &sName, const xbString &sKey, const xbString &sFilter,
+ xbInt16 iDescending, xbInt16 iUnique, xbInt16 iOverLay, xbIx **pIxOut, void **vpTagOut ){
+
+ // this routine is used to open indices and link to files
+
+ xbInt16 iErrorStop = 0;
+ xbInt16 iRc = XB_NO_ERROR;
+ xbBool bLocked = xbFalse;
+
+ try{
+ xbString sType = sIxType;
+ sType.ToUpperCase();
+
+ #ifdef XB_LOCKING_SUPPORT
+ if( !bTableLocked ){
+ if(( iRc = LockTable( XB_LOCK )) != XB_NO_ERROR ){
+ iErrorStop = 10;
+ throw( iRc );
+ }
+ bLocked = xbTrue;
+ }
+ #endif // XB_LOCKING_SUPPORT
+
+ if( sIxType == "" ){
+ iErrorStop = 100;
+ iRc = XB_INVALID_OPTION;
+ throw iRc;
+
+ #ifdef XB_NDX_SUPPORT
+ } else if( sIxType == "NDX" ){
+ xbIxNdx *ixNdx = new xbIxNdx( this );
+
+ if(( iRc = ixNdx->CreateTag( sName, sKey, sFilter, iDescending, iUnique, iOverLay, vpTagOut )) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ throw iRc;
+ }
+
+ if(( iRc = AddIndex( ixNdx, sIxType )) != XB_NO_ERROR ){
+ iErrorStop = 120;
+ throw iRc;
+ }
+ *pIxOut = ixNdx;
+
+ // Set the current tag if one not already set
+ if( sCurIxType == "" ){
+ sCurIxType = "NDX";
+ pCurIx = ixNdx;
+ vpCurIxTag = ixNdx->GetTag(0);
+ }
+
+ #endif
+
+ #ifdef XB_MDX_SUPPORT
+ } else if( sIxType == "MDX" ){
+
+ if( GetVersion() == 3 ){ // MDX indexes were version 4 and higher
+ iErrorStop = 200;
+ iRc = XB_INVALID_INDEX;
+ throw iRc;
+ }
+
+ xbIxMdx *ixMdx;
+ xbString s;
+ // look through the index list and see if there is an mdx pointer we can grab
+ xbBool bMdxFound = xbFalse;
+ xbIxList *ixList = GetIxList();
+ while( ixList && !bMdxFound ){
+ s = ixList->sFmt->Str();
+ if( s == "MDX" ){
+ ixMdx = (xbIxMdx *) ixList->ix;
+ bMdxFound = xbTrue;
+ }
+ }
+
+ if( !bMdxFound )
+ ixMdx = new xbIxMdx( this );
+
+ if(( iRc = ixMdx->CreateTag( sName, sKey, sFilter, iDescending, iUnique, iOverLay, vpTagOut )) != XB_NO_ERROR ){
+ iErrorStop = 210;
+ throw iRc;
+ }
+
+ if( !bMdxFound ){
+ if(( iRc = AddIndex( ixMdx, "MDX" )) != XB_NO_ERROR ){
+ iErrorStop = 220;
+ throw iRc;
+ }
+
+ cIndexFlag = 0x01;
+ if(( iRc = WriteHeader( 1, 0 )) != XB_NO_ERROR ){
+ iErrorStop = 230;
+ throw iRc;
+ }
+ }
+ *pIxOut = ixMdx;
+
+ // set the current tag if one not already set
+ if( sCurIxType == "" ){
+ sCurIxType = "MDX";
+ pCurIx = ixMdx;
+ vpCurIxTag = ixMdx->GetTag(0);
+ }
+
+ #endif
+
+ } else {
+ iErrorStop = 200;
+ iRc = XB_INVALID_OPTION;
+ throw iRc;
+ }
+
+ if(( iRc = UpdateTagList()) != XB_NO_ERROR ){
+ iErrorStop = 300;
+ throw iRc;
+ }
+
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbdbf::CreateTag() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+
+ #ifdef XB_LOCKING_SUPPORT
+ if( bLocked )
+ LockTable( XB_UNLOCK );
+ #endif // XB_LOCKING_SUPPORT
+
+
+ return iRc;
+}
+#endif // XB_INDEX_SUPPORT
+
+/************************************************************************/
+//! @brief Delete or undelete all records in a dbf file (table).
+/*!
+ This routine deletes or un-deletes all records. The xbase file format contains
+ a leading one byte character used for flagging a record as deleted. When a record
+ is deleted, it's not physically removed from the file, the first byte is flagged as deleted.
+
+ \param iOption 0 - Delete all records.<br>
+ 1 - Un-delete all deleted records.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbDbf::DeleteAll( xbInt16 iOption )
+{
+ xbInt16 iErrorStop = 0;
+ xbInt16 iRc = XB_NO_ERROR;
+ xbUInt32 ulRecCnt;
+
+ try{
+ if(( iRc = GetRecordCnt( ulRecCnt )) != XB_NO_ERROR ){
+ iErrorStop = 10;
+ throw iRc;
+ }
+ if( ulRecCnt == 0 )
+ return XB_NO_ERROR;
+ for( xbUInt32 ul = 0; ul < ulRecCnt; ul++ ){
+ if(( iRc = GetRecord( ul+1 )) != XB_NO_ERROR ){
+ iErrorStop = 20;
+ throw iRc;
+ }
+ if( iOption == 0 ){ /* delete all option */
+ if( !RecordDeleted()){
+ if(( iRc = DeleteRecord()) != XB_NO_ERROR ){
+ iErrorStop = 30;
+ throw iRc;
+ }
+ }
+ } else { /* undelete all option */
+ if( RecordDeleted()){
+ if(( iRc = UndeleteRecord()) != XB_NO_ERROR ){
+ iErrorStop = 40;
+ throw iRc;
+ }
+ }
+ }
+ }
+ }
+ catch (xbInt16 iRc ){
+ if( iRc != XB_LOCK_FAILED ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbDbf::DeleteAll() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ }
+ return iRc;
+}
+
+/************************************************************************/
+#ifdef XB_INDEX_SUPPORT
+//! @brief
+/*!
+ This routine deletes all indices associated with the dbf file.
+
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbDbf::DeleteAllIndexFiles(){
+
+ xbInt16 iRc = 0;
+ xbInt16 iErrorStop = 0;
+ xbBool bLocked = xbFalse;
+
+ #ifdef XB_NDXINF_SUPPORT
+ xbString sIxName;
+ #endif // XB_NDXINF_SUPPORT
+
+ try{
+ #ifdef XB_LOCKING_SUPPORT
+ if( !bTableLocked ){
+ if(( iRc = LockTable( XB_LOCK )) != XB_NO_ERROR ){
+ iErrorStop = 10;
+ throw( iRc );
+ }
+ bLocked = xbTrue;
+ }
+ #endif // XB_LOCKING_SUPPORT
+
+ // close any open index files, delete it, remove from the ix list
+ while( ixList ){
+ ixList->ix->Close();
+ ixList->ix->xbRemove();
+ #ifdef XB_NDXINF_SUPPORT
+ // if XB_NDXINF_SUPPORT is enabled, all open non prod indices should be in here
+ if( *ixList->sFmt != "MDX" ){ // production indices not stored in .INF dataset
+ if(( iRc = ixList->ix->GetFileNamePart( sIxName )) != XB_NO_ERROR ){
+ iErrorStop = 20;
+ throw iRc;
+ }
+ if(( iRc = AssociateIndex( *ixList->sFmt, sIxName, 1 )) != XB_NO_ERROR ){
+ iErrorStop = 30;
+ throw iRc;
+ }
+ }
+ #endif
+ RemoveIndex( ixList->ix );
+ }
+ } catch( xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbdbf::DeleteAllIndexFiles() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ #ifdef XB_LOCKING_SUPPORT
+ if( bLocked )
+ LockTable( XB_UNLOCK );
+ #endif // XB_LOCKING_SUPPORT
+ return iRc;
+}
+#endif // XB_INDEX_SUPPORT
+
+/************************************************************************/
+//! @brief Delete all records.
+/*!
+ This routine deletes all the records in a table / dbf file.
+
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbDbf::DeleteAllRecords(){
+ return DeleteAll( 0 );
+}
+
+/************************************************************************/
+#ifdef XB_NDXINF_SUPPORT
+//! @brief Delete .INF File
+/*!
+ The original Dbase (TM) software supported non production indices (NDX) and production indices (MDX).
+ The production indices are opened automatically when the DBF file is opened but the non-production
+ indices are not. This method is specific to the Xbas64 library and providex a means to link non production
+ NDX index files to the DBF file so they will be opened automatically when the DBF file is opened.<br>
+
+ This routine requires NDXINF support be enabled when building the library.<br>
+ This routine deletes the .INF file.<br>
+
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbDbf::DeleteNdxInfData(){
+ xbInt16 iRc = 0;
+ xbInt16 iErrorStop = 0;
+
+ try{
+ xbString sNdxInfFileName;
+ if(( iRc = GetNdxInfFileName( sNdxInfFileName )) != XB_NO_ERROR ){
+ iErrorStop = 10;
+ throw iRc;
+ }
+ xbFile f( xbase );
+ f.SetFileName( sNdxInfFileName );
+ if( f.FileExists()){
+ if(( iRc = f.xbRemove()) != XB_NO_ERROR ){
+ iErrorStop = 20;
+ throw iRc;
+ }
+ }
+ } catch( xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbdbf::DeleteNdxInfData() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+#endif // XB_NDXINF_SUPPORT
+
+/************************************************************************/
+//! @brief Delete the current record.
+/*!
+ This routine flags the current record for deletion if it's not already flagged.
+
+
+ \returns XB_NO_ERROR<br>
+ XB_INVALID_RECORD
+*/
+
+xbInt16 xbDbf::DeleteRecord(){
+ if( RecBuf && ulCurRec > 0 ){
+ if( RecBuf[0] != 0x2a){
+ if( iDbfStatus != XB_UPDATED ){
+ iDbfStatus = XB_UPDATED;
+ memcpy( RecBuf2, RecBuf, uiRecordLen ); // save off original before making any updates
+ }
+ RecBuf[0] = 0x2a;
+ }
+ return XB_NO_ERROR;
+ }
+ else
+ return XB_INVALID_RECORD;
+}
+/************************************************************************/
+//! @brief Delete a table.
+/*!
+ This routine deletes a given table, associated index files if any, the
+ memo file if any and the .INF file if any.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbDbf::DeleteTable(){
+
+ xbInt16 iRc = 0;
+ xbInt16 iErrorStop = 0;
+
+ try{
+
+ #ifdef XB_LOCKING_SUPPORT
+ if(( iRc = LockTable( XB_LOCK )) != XB_NO_ERROR ){
+ iErrorStop = 10;
+ throw iRc;
+ }
+ #endif // XB_LOCKING_SUPPORT
+
+ #ifdef XB_INDEX_SUPPORT
+ if(( iRc = DeleteAllIndexFiles()) != XB_NO_ERROR ){
+ iErrorStop = 20;
+ throw iRc;
+ }
+
+ #ifdef XB_NDXINF_SUPPORT
+ if(( iRc = DeleteNdxInfData()) != XB_NO_ERROR ){
+ iErrorStop = 30;
+ throw iRc;
+ }
+ #endif // XB_NDXINF_SUPPORT
+ #endif // XB_INDEX_SUPPORT
+
+ #ifdef XB_MEMO_SUPPORT
+ xbInt16 iMemoFldCnt = GetMemoFieldCnt();
+ xbString sMemoFileName;
+ if(iMemoFldCnt > 0 ){
+ sMemoFileName = Memo->GetFqFileName();
+ }
+ #endif // XB_MEMO_SUPPORT
+
+ if(( iRc = Close()) != XB_NO_ERROR ){
+ iErrorStop = 40;
+ throw iRc;
+ }
+
+ if(( iRc = xbRemove()) != XB_NO_ERROR ){
+ iErrorStop = 50;
+ throw iRc;
+ }
+
+ #ifdef XB_MEMO_SUPPORT
+ if( iMemoFieldCnt > 0 ){
+ xbFile f( xbase );
+ if(( iRc = f.xbRemove( sMemoFileName )) != XB_NO_ERROR ){
+ iErrorStop = 60;
+ throw iRc;
+ }
+ }
+ #endif // XB_MEMO_SUPPORT
+
+ } catch( xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbdbf::DeleteTable() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ #ifdef XB_LOCKING_SUPPORT
+ LockTable( XB_UNLOCK );
+ #endif // XB_LOCKING_SUPPORT
+ return iRc;
+}
+
+/************************************************************************/
+//! @brief Delete an index tag.
+
+/*!
+ This routine deletes an index tag
+ \param sIxType Either "NDX" or "MDX".<br>
+ \param sName Tag name to delete.<br>
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbDbf::DeleteTag( const xbString &sIxType, const xbString &sName ){
+
+ xbInt16 iErrorStop = 0;
+ xbInt16 iRc = XB_NO_ERROR;
+ xbIx *pIx = NULL;
+
+ try{
+
+ #ifdef XB_LOCKING_SUPPORT
+ // xbInt16 iAutoLock = GetAutoLock();
+ if( iAutoLock && !GetTableLocked() ){
+ if(( iRc = LockTable( XB_LOCK )) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+ }
+ #endif
+
+ if( sIxType == "" ){
+ iErrorStop = 110;
+ iRc = XB_INVALID_OPTION;
+ throw iRc;
+
+ #ifdef XB_NDX_SUPPORT
+ } else if( sIxType == "NDX" ){
+
+ xbIxList *ixl = ixList;
+ xbBool bDone = xbFalse;
+ while( ixl && !bDone ){
+
+ if( ixl->ix->GetTagName( NULL ) == sName ){
+ bDone = xbTrue;
+
+ // remove from .INF if it's there
+ #ifdef XB_NDXINF_SUPPORT
+ if(( iRc = AssociateIndex( "NDX", sName, 1 )) != XB_NO_ERROR ){
+ iErrorStop = 120;
+ throw iRc;
+ }
+ #endif // XB_NDXINF_SUPPORT
+
+ if(( iRc = ixl->ix->DeleteTag( ixl->ix->GetTag( 0 ))) != XB_NO_ERROR ){
+ iErrorStop = 130;
+ throw iRc;
+ }
+
+ if(( iRc = RemoveIndex( ixl->ix )) != XB_NO_ERROR ){
+ iErrorStop = 140;
+ throw iRc;
+ }
+
+ if( ixl->ix == pCurIx )
+ SetCurTag( "", NULL, NULL );
+
+ }
+ ixl = ixl->next;
+ }
+ #endif
+
+ #ifdef XB_MDX_SUPPORT
+ } else if( sIxType == "MDX" ){
+ xbIxList *ixl = ixList;
+ xbIxList *ixlNext;
+ xbIxList *ixlPrev = NULL;
+ xbBool bDone = xbFalse;
+ xbIxMdx *pMdx;
+ xbMdxTag *pMdxTag;
+ xbInt16 iTagCnt = 0;
+
+ while( ixl && !bDone ){
+ ixlNext = ixl->next;
+ pMdx = (xbIxMdx *) ixl->ix;
+ iTagCnt = pMdx->GetTagCount();
+ for( xbInt16 i = 0; i < iTagCnt && !bDone; i++ ){
+ pMdxTag = (xbMdxTag *) pMdx->GetTag( i );
+ if( pMdx->GetTagName( pMdxTag ) == sName ){
+ bDone = xbTrue;
+ iRc = pMdx->DeleteTag( pMdxTag );
+ if( iRc > 0 ){
+ // Successful delete of only tag in production mdx file - need to remove it from the list, update the dbf header
+ cIndexFlag = 0x00;
+ if(( iRc = WriteHeader( 1, 0 )) != XB_NO_ERROR ){
+ iErrorStop = 150;
+ throw iRc;
+ }
+ if(( iRc = RemoveIndex( ixl->ix )) != XB_NO_ERROR ){
+ iErrorStop = 160;
+ throw iRc;
+ }
+ if( ixlPrev == NULL ){
+ // std::cout << "setting ixList to null or should be\n";
+ ixList = ixlNext;
+ } else {
+ ixlPrev = ixlNext;
+ }
+ } else if( iRc < 0 ){
+ iErrorStop = 170;
+ throw iRc;
+ }
+ if( ixl->ix == pCurIx )
+ SetCurTag( "", NULL, NULL );
+ }
+ }
+ ixlPrev = ixl;
+ ixl = ixlNext;
+ }
+
+ if( !bDone )
+ return XB_INVALID_TAG;
+
+ #endif
+
+ } else {
+ iErrorStop = 180;
+ iRc = XB_INVALID_OPTION;
+ throw iRc;
+ }
+
+ if(( iRc = UpdateTagList()) != XB_NO_ERROR ){
+ iErrorStop = 190;
+ throw iRc;
+ }
+
+ // unlock as necessary
+ #ifdef XB_LOCKING_SUPPORT
+ if( iAutoLock && GetTableLocked() ){
+ if(( iRc = LockTable( XB_UNLOCK )) != XB_NO_ERROR ){
+ iErrorStop = 200;
+ throw iRc;
+ }
+ }
+ #endif
+
+ }
+ catch (xbInt16 iRc ){
+ if( pIx ) delete pIx;
+ xbString sMsg;
+ sMsg.Sprintf( "xbdbf::DeleteTag() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+
+ #ifdef XB_LOCKING_SUPPORT
+ if( iAutoLock && GetTableLocked() ){
+ LockTable( XB_UNLOCK );
+ }
+ #endif
+
+ }
+ return iRc;
+}
+
+/************************************************************************/
+#ifdef XB_DEBUG_SUPPORT
+//! @brief Dump dbf file header.
+/*!
+ This routine dumps dbf header information to the console.
+
+ \param iOption 1 = Print header only<br>
+ 2 = Field data only<br>
+ 3 = Header and Field data<br>
+ 4 = Header, Field and Memo header data if applicable
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbDbf::DumpHeader( xbInt16 iOption ) const {
+ int i;
+ int iMemoCtr = 0;
+
+ if( iOption < 1 || iOption > 4 )
+ return XB_INVALID_OPTION;
+
+ if( iDbfStatus == XB_CLOSED )
+ return XB_NOT_OPEN;
+
+ std::cout << "\nDatabase file " << GetFqFileName() << std::endl << std::endl;
+
+ if( iOption != 2 ){
+ std::cout << "File header data:" << std::endl;
+
+ xbInt16 sVer = DetermineXbaseTableVersion( cVersion );
+
+ if( sVer == 3 )
+ std::cout << "Dbase III file" << std::endl;
+ else if ( sVer == 4 )
+ std::cout << "Dbase IV file" << std::endl << std::endl;
+ else if ( sVer == 5 )
+ std::cout << "Dbase V file" << std::endl << std::endl;
+ else if ( sVer == 7 )
+ std::cout << "Dbase VII file" << std::endl << std::endl;
+ else
+ std::cout << "Unknown Version" << std::endl;
+
+ /* display the bit stream */
+ unsigned char c, tfv, displayMask = 1 << 7;
+ tfv = cVersion;
+ std::cout << "File descriptor bits = ";
+ for( c = 1; c<= 8; c++ ){
+ std::cout << (tfv & displayMask ? '1' : '0');
+ tfv <<= 1;
+ }
+ std::cout << std::endl;
+
+ std::cout << "Descriptor bits legend:" << std::endl;
+ std::cout << " 0-2 = version number" << std::endl;
+ std::cout << " 3 = presence of dBASE IV memo" << std::endl;
+ std::cout << " 4-6 = SQL table presence" << std::endl;
+ std::cout << " 7 = Presence of any memo file (dBASE III PLUS or dBASE IV)" << std::endl << std::endl;
+
+ std::cout << "Last update date = "
+ << (int) cUpdateMM << "/" << (int) cUpdateDD << "/" << (int) cUpdateYY % 100 << std::endl;
+
+ std::cout << "Header length = " << uiHeaderLen << std::endl;
+ std::cout << "Record length = " << uiRecordLen << std::endl;
+ std::cout << "Records in file = " << ulNoOfRecs << std::endl << std::endl << std::endl;
+
+ std::cout << "Transaction Flag = ";
+ xbase->BitDump( cTransactionFlag );
+ std::cout << std::endl;
+
+ std::cout << "Encryption Flag = ";
+ xbase->BitDump( cEncryptionFlag );
+ std::cout << std::endl;
+
+ std::cout << "Index Flag = ";
+ xbase->BitDump( cIndexFlag );
+ std::cout << std::endl;
+
+ std::cout << "Lang Driver = " << (int) cIndexFlag << " - ";
+ xbase->BitDump( cIndexFlag );
+ std::cout << std::endl;
+
+ std::cout << "Open Index Files = " << GetPhysicalIxCnt() << std::endl;
+ }
+
+ if( iOption != 1 ){
+ char c;
+ std::cout << "Field Name Type Length Decimals IxFlag" << std::endl;
+ std::cout << "---------- ---- ------ -------- ------" << std::endl;
+ for( i = 0; i < iNoOfFields; i++ ){
+
+ SchemaPtr[i].cIxFlag ? c = 'Y' : c = ' ';
+
+ if( SchemaPtr[i].cType == 'C' && SchemaPtr[i].cNoOfDecs > 0 )
+ printf( "%10s %1c %4d %4d %c\n", SchemaPtr[i].cFieldName,
+ SchemaPtr[i].cType, SchemaPtr[i].cFieldLen, 0, c );
+ else
+ printf( "%10s %1c %4d %4d %c\n", SchemaPtr[i].cFieldName,
+ SchemaPtr[i].cType, SchemaPtr[i].cFieldLen, SchemaPtr[i].cNoOfDecs, c );
+
+ if( SchemaPtr[i].cType == 'M' )
+ iMemoCtr++;
+ }
+ }
+ std::cout << std::endl;
+
+#ifdef XB_MEMO_SUPPORT
+ if( iOption > 3 && iMemoCtr > 0 )
+ Memo->DumpMemoHeader();
+#endif
+
+ return XB_NO_ERROR;
+}
+
+/************************************************************************/
+//! Dump record
+/*!
+ Dump the contents of the specified record
+
+
+ \param ulRecNo Record number of record to be dumped.
+ \param iOutputDest 0 = stdout<br>
+ 1 = Syslog<br>
+ 2 = Both<br>
+
+ \param iOutputFmt 0 = with field names<br>
+ 1 = 1 line per rec, no field names<br>
+ 2 = 1 line per rec, first line is a list of field names.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbDbf::DumpRecord( xbUInt32 ulRecNo, xbInt16 iOutputDest, xbInt16 iOutputFmt ) {
+ int i, iRc = XB_NO_ERROR;
+
+ xbString sTemp;
+ xbString s2;
+ if( ulRecNo == 0 || ulRecNo > ulNoOfRecs )
+ return XB_INVALID_RECORD;
+
+ if( ulCurRec != ulRecNo ){
+ iRc = GetRecord( ulRecNo );
+ if( iRc != XB_NO_ERROR )
+ return iRc;
+ }
+
+ if( iOutputFmt >= 1 ){
+ if( iOutputFmt == 2 ){
+ sTemp = "RecNo";
+ for( i = 0; i < iNoOfFields; i++ ){
+ s2 = SchemaPtr[i].cFieldName;
+ s2.Trim();
+ sTemp += ",";
+ sTemp += s2;
+ }
+ xbase->WriteLogMessage( sTemp.Str(), iOutputDest );
+ }
+
+ if( RecordDeleted() )
+ s2.Sprintf( "%ld Deleted", ulRecNo );
+
+ else{
+ s2.Sprintf( "%ld ", ulRecNo );
+ //xbString s3;
+ for( i = 0; i < iNoOfFields; i++ ){
+ GetField( i, sTemp );
+ sTemp.Trim();
+ s2.Sprintf( "%s,'%s'", s2.Str(), sTemp.Str());
+ }
+ xbase->WriteLogMessage( s2.Str(),iOutputDest );
+ }
+
+ return XB_NO_ERROR;
+ }
+
+ sTemp.Sprintf( "\nRec Number: %ld", ulRecNo );
+ xbase->WriteLogMessage( sTemp.Str(),iOutputDest);
+
+ if( RecordDeleted())
+ xbase->WriteLogMessage( "Record flagged as deleted", iOutputDest );
+
+
+ #ifdef XB_MEMO_SUPPORT
+ xbString sMemo;
+ #endif
+
+ for( i = 0; i < iNoOfFields; i++ ){
+
+ #ifdef XB_MEMO_SUPPORT
+ GetField( i, sTemp );
+ sTemp.Trim();
+
+ if(SchemaPtr[i].cType == 'M'){
+ GetMemoField( i, sMemo );
+ if( sMemo.Len() > 70 )
+ sMemo.Resize( 70 );
+ s2.Sprintf ( "%c %s = '%s'", SchemaPtr[i].cType, SchemaPtr[i].cFieldName, sMemo.Str());
+ xbase->WriteLogMessage( s2.Str(), iOutputDest);
+
+ /*
+ xbUInt32 ulMlen;
+ if( MemoFieldExists( i )){
+ Memo->GetMemoFieldLen( i, ulMlen );
+ s2.Sprintf( " Len = %d", ulMlen );
+ }
+ xbase->WriteLogMessage( s2.Str(), iOutputDest);
+ */
+
+ }
+ else{
+ s2.Sprintf ( "%c %s = '%s'", SchemaPtr[i].cType, SchemaPtr[i].cFieldName, sTemp.Str());
+ xbase->WriteLogMessage( s2.Str(), iOutputDest);
+ }
+ #else
+ GetField( i, sTemp );
+ sTemp.Trim();
+ s2.Sprintf( "%s = '%s'", SchemaPtr[i].cFieldName, sTemp.Str());
+ xbase->WriteLogMessage( s2.Str(), iOutputDest);
+ #endif
+
+ }
+ return XB_NO_ERROR;
+}
+
+
+/************************************************************************/
+#ifdef XB_LOCKING_SUPPORT
+//! @brief Dump the table lock status
+/*!
+ Debugging routine. Dumps the table lock status to the console.
+ \returns void
+*/
+
+void xbDbf::DumpTableLockStatus() const {
+
+ std::cout << "File Lock Retry Count = [" << GetLockRetryCount() << "]" << std::endl;
+ std::cout << "File Lock Flavor = [";
+ switch (GetLockFlavor()){
+ case 1:
+ std::cout << "Dbase]" << std::endl;
+ break;
+ case 2:
+ std::cout << "Clipper]" << std::endl;
+ break;
+ case 3:
+ std::cout << "Fox]" << std::endl;
+ break;
+ case 9:
+ std::cout << "Xbase64]" << std::endl;
+ break;
+ default:
+ std::cout << "Unknown]" << std::endl;
+ break;
+ }
+ std::cout << "File Auto Lock = [";
+
+ if( GetAutoLock())
+ std::cout << "ON]" << std::endl;
+ else
+ std::cout << "OFF]" << std::endl;
+ if( GetHeaderLocked())
+ std::cout << "Header Locked = [TRUE]\n";
+ else
+ std::cout << "Header Locked = [FALSE]\n";
+
+ if( GetTableLocked())
+ std::cout << "Table Locked = [TRUE]\n";
+ else
+ std::cout << "Table Locked = [FALSE]\n";
+
+ if( GetAppendLocked() > 0 )
+ std::cout << "Append Locked = [" << GetAppendLocked() << "]\n";
+ else
+ std::cout << "Append Locked = [FALSE]\n";
+
+ #ifdef XB_MEMO_SUPPORT
+ if( GetMemoLocked())
+ std::cout << "Memo Locked = [TRUE]\n";
+ else
+ std::cout << "Memo Locked = [FALSE]\n";
+ #endif // XB_MEMO_SUPPORT
+
+ xbLinkListNode<xbUInt32> * llN = GetFirstRecLock();
+ if( llN ){
+ while( llN ){
+ std::cout << "Record Locked = [" << llN->GetKey() << "]\n";
+ llN = llN->GetNextNode();
+ }
+ } else {
+ std::cout << "Record Locked = [None]\n";
+ }
+}
+#endif // XB_LOCKING_SUPPORT
+#endif // XB_DEBUG_SUPPORT
+
+/************************************************************************/
+#ifdef XB_LOCKING_SUPPORT
+//! @brief Get the append locked bytes status
+/*!
+ \returns The record number of the new record for the append lock operation.
+*/
+
+xbUInt32 xbDbf::GetAppendLocked() const {
+ return this->ulAppendLocked;
+}
+
+#endif // XB_LOCKING_SUPPORT
+
+/************************************************************************/
+//! @brief Get auto commit setting.
+/*!
+
+ This routine returns the table setting if set, otherwise returns the system
+ level setting.
+
+ \returns Not 0 - Auto commit on for this table.<br>
+ 0 - Auto commit off for this table.
+*/
+
+xbInt16 xbDbf::GetAutoCommit() const {
+ return GetAutoCommit( 1 );
+}
+
+/************************************************************************/
+//! @brief Get auto commit setting.
+/*!
+
+ \param iOption 0 - Specific setting for this table<br>
+ 1 - If this table should be auto updated (takes DBMS setting into account)
+ \returns Not 0 - Auto commit on for this table.<br>
+ 0 - Auto commit off for this table.
+*/
+
+xbInt16 xbDbf::GetAutoCommit( xbInt16 iOption ) const {
+ if( iOption == 1 && iAutoCommit == -1 )
+ return xbase->GetDefaultAutoCommit();
+ else
+ return iAutoCommit;
+}
+
+
+/************************************************************************/
+//! @brief Get Auto Lock setting.
+/*!
+ \returns Auto lock setting.
+*/
+#ifdef XB_LOCKING_SUPPORT
+xbInt16 xbDbf::GetAutoLock() const{
+ return iAutoLock;
+}
+#endif // XB_LOCKING_SUPPORT
+
+/************************************************************************/
+#ifdef XB_MEMO_SUPPORT
+//! @brief Get the memo file block size used when creating a memo file.
+/*!
+ \returns Memo block size.
+*/
+xbUInt32 xbDbf::GetCreateMemoBlockSize() const {
+ return ulCreateMemoBlockSize;
+}
+#endif
+
+/************************************************************************/
+//! @brief Get a pointer to the current index object.
+/*!
+ \returns Pointer to current index.
+*/
+#ifdef XB_INDEX_SUPPORT
+xbIx *xbDbf::GetCurIx() const {
+ return pCurIx;
+}
+/************************************************************************/
+//! @brief Get pointer to current tag for the current index.
+/*!
+ An index file can have one or more tags. An NDX index has one tag.
+ An MDX file can have up to 47 tags.
+
+ \returns Pointer to current tag.
+*/
+void *xbDbf::GetCurTag() const {
+ return vpCurIxTag;
+}
+/************************************************************************/
+//! @brief Get the current index type.
+/*!
+ \returns NDX for single tag index.<br>
+ MDX for production multi tag index.
+*/
+const xbString &xbDbf::GetCurIxType() const {
+ return sCurIxType;
+}
+
+/************************************************************************/
+//! @brief Get the current tag name.
+/*!
+ \returns Current Tag Name.<br>
+*/
+
+const xbString &xbDbf::GetCurTagName() const {
+
+ if( pCurIx )
+ return pCurIx->GetTagName( vpCurIxTag );
+ else
+ return sNullString;
+}
+
+/************************************************************************/
+//! @brief GetFirstKey for tag.
+/*!
+
+ Position to the first key for the current tag
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+
+*/
+xbInt16 xbDbf::GetFirstKey(){
+ if( pCurIx )
+ return pCurIx->GetFirstKey( vpCurIxTag, 1 );
+ else
+ return XB_INVALID_TAG;
+}
+
+/************************************************************************/
+//! @brief GetLastKey for tag.
+/*!
+
+ Position to the last key for the current tag
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+
+*/
+xbInt16 xbDbf::GetLastKey(){
+ if( pCurIx )
+ return pCurIx->GetLastKey( vpCurIxTag, 1 );
+ else
+ return XB_INVALID_TAG;
+}
+
+/************************************************************************/
+//! @brief GetNextKey for tag.
+/*!
+
+ Position to the next key for the current tag
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+
+*/
+xbInt16 xbDbf::GetNextKey(){
+ if( pCurIx )
+ return pCurIx->GetNextKey( vpCurIxTag, 1 );
+ else
+ return XB_INVALID_TAG;
+}
+
+/************************************************************************/
+//! @brief GetPrevKey for tag.
+/*!
+
+ Position to the previous key for the current tag
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+
+*/
+xbInt16 xbDbf::GetPrevKey(){
+ if( pCurIx )
+ return pCurIx->GetPrevKey( vpCurIxTag, 1 );
+ else
+ return XB_INVALID_TAG;
+}
+
+/************************************************************************/
+//! @brief Find record for key.
+/*!
+
+ Find a key and position to record if key found
+
+ \param sKey String key to find
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+
+*/
+xbInt16 xbDbf::Find( xbString &sKey ){
+ if( pCurIx )
+ return pCurIx->FindKey( vpCurIxTag, sKey.Str(), (xbInt32) sKey.Len(), 1 );
+ else
+ return XB_INVALID_TAG;
+}
+
+/************************************************************************/
+//! @brief Find record for key.
+/*!
+
+ Find a key and position to record if key found
+
+ \param dtKey Date key to find
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+
+*/
+xbInt16 xbDbf::Find( xbDate &dtKey ){
+
+ if( pCurIx )
+ return pCurIx->FindKey( vpCurIxTag, dtKey, 1 );
+ else
+ return XB_INVALID_TAG;
+
+}
+/************************************************************************/
+//! @brief Find record for key.
+/*!
+
+ Find a key and position to record if key found
+
+ \param dtKey Date key to find
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+
+*/
+xbInt16 xbDbf::Find( xbDouble &dKey ){
+
+ if( pCurIx )
+ return pCurIx->FindKey( vpCurIxTag, dKey, 1 );
+ else
+ return XB_INVALID_TAG;
+
+}
+
+
+#endif // XB_INDEX_SUPPORT
+
+/************************************************************************/
+//! @brief Return the current record number.
+/*!
+ \returns Returns the current record number.
+*/
+xbUInt32 xbDbf::GetCurRecNo() const {
+ return ulCurRec;
+}
+
+/************************************************************************/
+//! @brief Return the current dbf status.
+/*!
+ \returns 0 = closed<br>
+ 1 = open<br>
+ 2 = updates pending<br>
+*/
+xbInt16 xbDbf::GetDbfStatus() const {
+ return iDbfStatus;
+}
+/************************************************************************/
+//! @brief Return the number of fields in the table.
+/*!
+ \returns The number of fields in the table.
+*/
+xbInt32 xbDbf::GetFieldCnt() const {
+ return iNoOfFields;
+}
+
+/************************************************************************/
+#ifdef XB_LOCKING_SUPPORT
+//! @brief Get the first first record lock.
+/*!
+ Get the first record lock from a linked list of record locks.
+
+ \returns First record lock.
+*/
+xbLinkListNode<xbUInt32> * xbDbf::GetFirstRecLock() const {
+ return lloRecLocks.GetHeadNode();
+}
+#endif // XB_LOCKING_SUPPORT
+/************************************************************************/
+//! @brief Get the first record.
+/*!
+ Get the first not deleted record. This routines skips over any deleted records.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbDbf::GetFirstRecord()
+{
+ return GetFirstRecord( XB_ACTIVE_RECS );
+}
+
+/************************************************************************/
+//! @brief Get the first record.
+/*!
+
+ \param iOption XB_ALL_RECS - Get the first record, deleted or not.<br>
+ XB_ACTIVE_RECS - Get the first active record.<br>
+ XB_DELETED_RECS - Get the first deleted record.<br>
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbDbf::GetFirstRecord( xbInt16 iOption )
+{
+ if( ulNoOfRecs == 0 )
+ return XB_EMPTY;
+
+ xbInt16 iRc = GetRecord( 1L );
+ while( iRc == XB_NO_ERROR &&
+ ((RecordDeleted() && iOption == XB_ACTIVE_RECS) ||
+ (!RecordDeleted() && iOption == XB_DELETED_RECS)))
+ if( ulCurRec < ulNoOfRecs )
+ iRc = GetRecord( ulCurRec + 1 );
+ else
+ return XB_EOF;
+
+ return iRc;
+}
+
+/************************************************************************/
+#ifdef XB_LOCKING_SUPPORT
+//! @brief Return lock status of the table header
+/*! \returns DBF header lock status
+*/
+
+xbBool xbDbf::GetHeaderLocked() const {
+ return this->bHeaderLocked;
+}
+#endif
+
+#ifdef XB_INDEX_SUPPORT
+//! @brief Return pointer to list of index files for the table.
+/*!
+ \returns Returns an xbIxList * pointer to list of open index files.
+*/
+
+xbIxList *xbDbf::GetIxList() const{
+ return ixList;
+}
+#endif // XB_INDEX_SUPPORT
+
+
+/************************************************************************/
+//! @brief Get the last record.
+/*!
+ Get the last not deleted record. This routines skips over any deleted records.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbDbf::GetLastRecord()
+{
+ return GetLastRecord( XB_ACTIVE_RECS );
+}
+/************************************************************************/
+//! @brief Get the last record.
+/*!
+
+ \param iOption XB_ALL_RECS - Get the last record, deleted or not.<br>
+ XB_ACTIVE_RECS - Get the last active record.<br>
+ XB_DELETED_RECS - Get the last deleted record.<br>
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbDbf::GetLastRecord( xbInt16 iOption )
+{
+ if( ulNoOfRecs == 0 )
+ return XB_EMPTY;
+
+ xbInt16 iRc = GetRecord( ulNoOfRecs );
+ while( iRc == XB_NO_ERROR &&
+ ((RecordDeleted() && iOption == XB_ACTIVE_RECS) ||
+ (!RecordDeleted() && iOption == XB_DELETED_RECS)))
+ if( ulCurRec > 1 )
+ iRc = GetRecord( ulCurRec - 1 );
+ else
+ return XB_EOF;
+
+ return iRc;
+}
+
+
+/************************************************************************/
+//! @brief Get lock flavor.
+/*!
+ This routine is currently in place to provide structure for future locking
+ schemes that may differ from the legacy DBase (TM) locking scheme.
+ \returns Always 1.
+*/
+
+xbInt16 xbDbf::GetLockFlavor() const{
+ if( iLockFlavor == -1 )
+ return xbase->GetDefaultLockFlavor();
+ else
+ return iLockFlavor;
+}
+
+/************************************************************************/
+#ifdef XB_MEMO_SUPPORT
+/************************************************************************/
+//! @brief Get the lock status of the memo file.
+/*!
+ \returns Lock status of memo file.
+*/
+xbBool xbDbf::GetMemoLocked() const {
+ if( MemoFieldsExist())
+ return Memo->GetMemoLocked();
+ else
+ return xbFalse;
+}
+/************************************************************************/
+//! @brief Get pointer to Memo object.
+/*!
+ \returns This routine returns the pointer to the memo object.
+*/
+
+xbMemo * xbDbf::GetMemoPtr(){
+ return Memo;
+}
+#endif
+
+
+/************************************************************************/
+#ifdef XB_NDXINF_SUPPORT
+//! @brief Return the .INF file name
+/*!
+ If NDXIDX support is enabled in the library, and a non production (ndx)
+ has been associated with the dbf file, the .INF file name can be retrieved
+ with this routine.
+
+ \param sNdxInfFileName Output string containing .INF file name.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbDbf::GetNdxInfFileName( xbString &sNdxInfFileName ){
+
+ sNdxInfFileName = GetFqFileName();
+ xbUInt32 lLen = sNdxInfFileName.Len();
+ if( lLen < 5 )
+ return XB_FILE_NOT_FOUND;
+ sNdxInfFileName.PutAt(lLen-2, 'I');
+ sNdxInfFileName.PutAt(lLen-1, 'N');
+ sNdxInfFileName.PutAt(lLen, 'F');
+ return XB_NO_ERROR;
+}
+
+/************************************************************************/
+//! @brief Return first node of linked list of .INF items.
+/*!
+ \returns List of .INF entries.
+*/
+
+xbLinkListNode<xbString> * xbDbf::GetNdxInfList() const{
+ return llNdxInfData.GetHeadNode();
+}
+#endif // XB_NDXINF_SUPPORT
+
+
+/************************************************************************/
+//! @brief Get the next record.
+/*!
+ Get the next not deleted record. This routines skips over any deleted records.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbDbf::GetNextRecord(){
+ return GetNextRecord( XB_ACTIVE_RECS );
+}
+
+/************************************************************************/
+//! @brief Get the next record.
+/*!
+ \param iOption XB_ALL_RECS - Get the next record, deleted or not.<br>
+ XB_ACTIVE_RECS - Get the next active record.<br>
+ XB_DELETED_RECS - Get the next deleted record.<br>
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbDbf::GetNextRecord( xbInt16 iOption ){
+ if( ulNoOfRecs == 0 )
+ return XB_EMPTY;
+ else if( ulCurRec >= ulNoOfRecs )
+ return XB_EOF;
+ xbInt16 iRc = GetRecord( ulCurRec + 1 );
+ while( iRc == XB_NO_ERROR &&
+ ((RecordDeleted() && iOption == XB_ACTIVE_RECS) ||
+ (!RecordDeleted() && iOption == XB_DELETED_RECS)))
+ if( ulCurRec < ulNoOfRecs )
+ iRc = GetRecord( ulCurRec + 1 );
+ else
+ return XB_EOF;
+ return iRc;
+}
+/************************************************************************/
+//! @brief Get the next record.
+/*!
+
+ \param iOption XB_ALL_RECS - Get the next record, deleted or not.<br>
+ XB_ACTIVE_RECS - Get the next active record.<br>
+ XB_DELETED_RECS - Get the next deleted record.<br>
+ \param ulStartRec Get next record, starting from ulStartRec.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbDbf::GetNextRecord( xbInt16 iOption , xbUInt32 ulStartRec ){
+
+ if( iOption == 0 )
+ return GetNextRecord();
+ else if( iOption == 1 ){
+ if( ulStartRec > 0 )
+ ulCurRec = ulStartRec;
+ xbInt16 iRc = GetNextRecord();
+ while( iRc == XB_NO_ERROR && RecordDeleted())
+ iRc = GetNextRecord();
+ return iRc;
+ }
+ else
+ return XB_INVALID_OPTION;
+}
+
+
+/************************************************************************/
+#ifdef XB_INDEX_SUPPORT
+//! @brief Physical count of open index files.
+/*!
+
+ Returns a physical count of open index files for the dbf file. An index file
+ can contain one or more tags.
+ \returns Count of open index files.
+*/
+
+xbInt32 xbDbf::GetPhysicalIxCnt() const {
+
+ xbInt32 lCnt = 0;
+ #ifdef XB_INDEX_SUPPORT
+ xbIxList *p = ixList;
+ while( p ){
+ lCnt++;
+ p = p->next;
+ }
+ #endif
+ return lCnt;
+}
+#endif
+
+
+/************************************************************************/
+//! @brief Get the previous record.
+/*!
+ Get the previous not deleted record. This routine skips over any deleted records.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbDbf::GetPrevRecord()
+{
+ return GetPrevRecord( XB_ACTIVE_RECS );
+}
+
+/************************************************************************/
+//! @brief Get the previous record.
+/*!
+
+ \param iOption XB_ALL_RECS - Get the previous record, deleted or not.<br>
+ XB_ACTIVE_RECS - Get the previous active record.<br>
+ XB_DELETED_RECS - Get the previous deleted record.<br>
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbDbf::GetPrevRecord( xbInt16 iOption ){
+ if( ulNoOfRecs == 0 )
+ return XB_EMPTY;
+ else if( ulCurRec <= 1L )
+ return XB_BOF;
+ xbInt16 iRc = GetRecord( ulCurRec - 1 );
+ while( iRc == XB_NO_ERROR &&
+ ((RecordDeleted() && iOption == XB_ACTIVE_RECS) ||
+ (!RecordDeleted() && iOption == XB_DELETED_RECS)))
+ if( ulCurRec > 1 )
+ iRc = GetRecord( ulCurRec - 1 );
+ else
+ return XB_BOF;
+
+ return iRc;
+}
+
+
+/************************************************************************/
+//! @brief Get record for specified record number.
+/*!
+ Retrieve a record from disk and load it into the record buffer. If auto commit
+ is enabled and there are pending updates, this routine will flush the updates
+ to disk before proceeding to ulRecNo.
+
+ \param ulRecNo - Record number to retrieve.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbDbf::GetRecord( xbUInt32 ulRecNo ){
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+
+ try{
+ /* verify the file is open */
+ if( iDbfStatus == XB_CLOSED ){
+ iErrorStop = 10;
+ iRc = XB_NOT_OPEN;
+ throw iRc;
+ }
+ if( iDbfStatus == XB_UPDATED ){
+ if( GetAutoCommit() == 1 ){
+ if(( iRc = Commit()) != XB_NO_ERROR ){
+ iErrorStop = 20;
+ throw iRc;
+ }
+ } else {
+ if(( iRc = Abort()) != XB_NO_ERROR ){
+ iErrorStop = 30;
+ throw iRc;
+ }
+ }
+ }
+
+ if( ulRecNo > ulNoOfRecs || ulRecNo == 0L ){
+ iErrorStop = 40;
+ iRc = XB_INVALID_RECORD;
+ throw iRc;
+ }
+
+ if(( xbFseek( (uiHeaderLen+(( (xbInt64) ulRecNo-1L ) * uiRecordLen )), SEEK_SET )) != XB_NO_ERROR ){
+ iErrorStop = 50;
+ iRc = XB_SEEK_ERROR;
+ throw iRc;
+ }
+
+ if( xbFread( RecBuf, uiRecordLen, 1 ) != XB_NO_ERROR ){
+ iErrorStop = 60;
+ iRc = XB_READ_ERROR;
+ throw iRc;
+ }
+ ulCurRec = ulRecNo;
+ }
+
+ catch (xbInt16 iRc ){
+ if( iRc != XB_LOCK_FAILED ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbDbf::GetRecord() Exception Caught. Error Stop = [%d] iRc = [%d] record = [%d]", iErrorStop, iRc, ulRecNo );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ }
+ return iRc;
+}
+/************************************************************************/
+//! @brief Get pointer to record buffer
+/*!
+ \param iOpt 0 for RecBuf (current) or 1 for RecBuf2 (original contents)
+
+ \returns Pointer to record buffer.
+*/
+char * xbDbf::GetRecordBuf( xbInt16 iOpt ) const {
+ if( iOpt )
+ return RecBuf2;
+ else
+ return RecBuf;
+}
+
+/************************************************************************/
+//! @brief Get the current number of records in the dbf data file.
+/*!
+ \param ulRecCnt Output number of records in file.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbDbf::GetRecordCnt( xbUInt32 & ulRecCnt )
+{
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+ xbBool bIlocked = xbFalse;
+ try{
+ #ifdef XB_LOCKING_SUPPORT
+ if( iAutoLock && !bTableLocked ){
+ if(( iRc = LockHeader( XB_LOCK )) != XB_NO_ERROR ){
+ iErrorStop = 10;
+ throw iRc;
+ } else
+ bIlocked = xbTrue;
+ }
+
+ if((iRc = ReadHeader(1,1)) != XB_NO_ERROR){
+ LockHeader( XB_UNLOCK );
+ iErrorStop = 20;
+ throw iRc;
+ }
+
+ if( iAutoLock && bIlocked ){
+ if(( iRc = LockHeader( XB_UNLOCK )) != XB_NO_ERROR ){
+ iErrorStop = 30;
+ throw iRc;
+ }
+ }
+ #endif
+ }
+ catch( xbInt16 iRc ){
+ if( iRc != XB_LOCK_FAILED ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbDbf::GetRecordCnt() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ }
+ ulRecCnt = ulNoOfRecs;
+ return iRc;
+}
+/************************************************************************/
+//! @brief Get the dbf record length.
+/*!
+ \returns Record length.
+*/
+xbUInt16 xbDbf::GetRecordLen() const {
+ return uiRecordLen;
+}
+/************************************************************************/
+//! @brief Get table locked status
+/*!
+ \returns Table lock status.
+*/
+
+xbBool xbDbf::GetTableLocked() const {
+ return this->bTableLocked;
+}
+/************************************************************************/
+//! @brief Get tag list for dbf file.
+/*!
+ This routine returns a list of tags for the file.<br>
+
+ The library is structured to support one or more files of the same or differing
+ index types (NDX/MDX), with each file supporting one or more index tags.<br>
+
+ \returns Tag list for the file/table.
+*/
+xbLinkListNode<xbTag *> *xbDbf::GetTagList() const {
+ return llTags.GetHeadNode();
+}
+/************************************************************************/
+//! @brief Get the table alias.
+/*!
+ This routine returns the table alias.
+ \returns Table alias
+*/
+const xbString & xbDbf::GetTblAlias() const {
+ return this->sAlias;
+}
+
+/************************************************************************/
+//! @brief Get the pointer to the xbXbase structure,
+/*!
+ \returns Pointer to xbXbase structure.
+*/
+xbXBase * xbDbf::GetXbasePtr() const {
+ return xbase;
+}
+/************************************************************************/
+#ifdef XB_NDXINF_SUPPORT
+//! @brief Load .INF data file,
+/*!
+ Protected method. This routine loads the ndx inf file.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbDbf::LoadNdxInfData(){
+
+ xbInt16 iRc = 0;
+ xbInt16 iErrorStop = 0;
+
+ try{
+ // create file name
+ xbString sNdxInfFileName;
+ if(( iRc = GetNdxInfFileName( sNdxInfFileName )) != XB_NO_ERROR ){
+ iErrorStop = 10;
+ throw iRc;
+ }
+ // if file does not exist, return no error
+ xbFile fMd( xbase );
+ if( !fMd.FileExists( sNdxInfFileName ))
+ return XB_NO_ERROR;
+
+ // open file file in read only mode
+ if(( iRc = fMd.xbFopen( "r", sNdxInfFileName, GetShareMode())) != XB_NO_ERROR ){
+ iErrorStop = 20;
+ throw iRc;
+ }
+ // clear the linked list
+ llNdxInfData.Clear();
+
+ // for each entry in the file, add a linked list item
+ xbString sRec;
+ xbString sLeft3;
+ xbString sFn;
+
+ while( iRc == XB_NO_ERROR ){
+ sRec = "";
+ if(( iRc = fMd.xbFgets( 132, sRec )) == XB_NO_ERROR ){
+ sLeft3 = sRec;
+ sLeft3.Left( 3 );
+ sLeft3.ToUpperCase();
+ if( sLeft3 == "NDX"){
+ sFn.ExtractElement(sRec.Str(), '=', 2 );
+ sFn.ZapChar( 0x0d );
+ sFn.ZapChar( 0x0a );
+ llNdxInfData.InsertAtEnd( sFn );
+ }
+ }
+ }
+ // close the file
+ if(( iRc = fMd.xbFclose()) != XB_NO_ERROR ){
+ iErrorStop = 40;
+ throw iRc;
+ }
+
+ } catch( xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbdbf::LoadNdxInfData() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+#endif // XB_NDXINF_SUPPORT
+
+/************************************************************************/
+#ifdef XB_LOCKING_SUPPORT
+//! @brief Lock append bytes.
+/*!
+ This routine locks the append bytes and is used by the AppendRecord function.
+
+ \param iLockFunction XB_LOCK<br>XB_UNLOCK
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbDbf::LockAppend( xbInt16 iLockFunction )
+{
+ xbInt16 iRc = 0;
+ xbInt16 iErrorStop = 0;
+ xbUInt32 ulAppendRec;
+
+ try{
+ if( iLockFunction == XB_LOCK ){
+ iErrorStop = 100;
+ if( ulAppendLocked > 0 ) /* already have an append lock */
+ return XB_NO_ERROR;
+
+ ulAppendRec = ulNoOfRecs + 1; /* record number needing to be locked */
+ if( GetLockFlavor() == LK_DBASE ){
+ iRc = xbLock( XB_LOCK, LK4026531839, 1 );
+ if( iRc != XB_NO_ERROR ){
+ if( iRc == XB_LOCK_FAILED )
+ return iRc;
+ else{
+ iErrorStop = 120;
+ throw iRc;
+ }
+ }
+
+ xbInt64 llAppendRecLockByte = (xbInt64) LK4026531838 - ulAppendRec;
+ iRc = xbLock( XB_LOCK, llAppendRecLockByte, 1 );
+ if( iRc != XB_NO_ERROR ){
+ xbLock( XB_UNLOCK, LK4026531839, 1 );
+ if( iRc == XB_LOCK_FAILED ){
+ return iRc;
+ } else {
+ iErrorStop = 130;
+ throw iRc;
+ }
+ }
+ ulAppendLocked = ulAppendRec; /* set the append lock switch */
+
+
+ // } else { - other lock-table flavor options go here Clipper, Fox, etc - }
+
+ } else {
+ iErrorStop = 190;
+ iRc = XB_INVALID_OPTION;
+ throw iRc;
+ }
+
+ } else if( iLockFunction == XB_UNLOCK ){
+
+ iErrorStop = 200;
+
+ if( ulAppendLocked == 0 ) /* verify we have an active append lock */
+ return XB_NO_ERROR;
+
+ if( GetLockFlavor() == LK_DBASE ){
+ xbInt64 llAppendRecLockByte =(xbInt64) LK4026531838 - ulAppendLocked;
+ iRc = xbLock( XB_UNLOCK, llAppendRecLockByte, 1 );
+ if( iRc != XB_NO_ERROR ){
+ xbLock( XB_UNLOCK, LK4026531839, 1 );
+ iErrorStop = 220;
+ throw iRc;
+ }
+ iRc = xbLock( XB_UNLOCK, LK4026531839, 1 );
+ if( iRc != XB_NO_ERROR ){
+ iErrorStop = 230;
+ throw iRc;
+ }
+
+ ulAppendLocked = 0; /* release the append lock switch */
+
+ // } else { - other unlock-table flavor options go here Clipper, Fox, etc - }
+
+ } else {
+ iErrorStop = 290;
+ iRc = XB_INVALID_OPTION;
+ throw iRc;
+ }
+ } else {
+ iErrorStop = 300;
+ iRc = XB_INVALID_OPTION;
+ throw iRc;
+ }
+ } catch( xbInt16 iRc ){
+ if( iRc != XB_LOCK_FAILED ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbdbf::LockAppendBytes() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ }
+
+ return iRc;
+}
+
+
+/************************************************************************/
+//! @brief Lock Header
+/*!
+ This routine locks the file header.
+ \param iLockFunction XB_LOCK<br>XB_UNLOCK
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbDbf::LockHeader( xbInt16 iLockFunction )
+{
+ xbInt16 iRc = 0;
+ xbInt16 iErrorStop = 0;
+ try{
+
+ if( iLockFunction == XB_LOCK ){
+ iErrorStop = 100;
+ if( GetHeaderLocked())
+ return XB_NO_ERROR;
+
+ iErrorStop = 110;
+ if( GetLockFlavor() == LK_DBASE ){
+
+ iRc = xbLock( XB_LOCK, LK4026531838, 1 );
+
+ if( iRc != XB_NO_ERROR ){
+ if( iRc == XB_LOCK_FAILED )
+ return iRc;
+ else{
+ iErrorStop = 120;
+ throw iRc;
+ }
+ }
+
+ } else {
+ iErrorStop = 190;
+ iRc = XB_INVALID_OPTION;
+ throw iRc;
+ }
+
+ SetHeaderLocked( xbTrue );
+
+ } else if( iLockFunction == XB_UNLOCK ){
+
+ iErrorStop = 200;
+ if( !GetHeaderLocked())
+ return XB_NO_ERROR;
+
+ iErrorStop = 210;
+ if( GetLockFlavor() == LK_DBASE ){
+ iRc = xbLock( XB_UNLOCK, LK4026531838, 1 );
+ if( iRc != XB_NO_ERROR ){
+ iErrorStop = 220;
+ throw iRc;
+ }
+ } else {
+ iErrorStop = 290;
+ iRc = XB_INVALID_OPTION;
+ throw iRc;
+ }
+ SetHeaderLocked( xbFalse );
+ } else {
+ iErrorStop = 300;
+ iRc = XB_INVALID_OPTION;
+ throw iRc;
+ }
+
+ } catch (xbInt16 iRc ){
+ if( iRc != XB_LOCK_FAILED ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbdbf::LockHeader() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ }
+ return iRc;
+}
+/************************************************************************/
+#ifdef XB_INDEX_SUPPORT
+//! @brief Lock Index files.
+/*!
+ This routine locks all the index files.
+ \param iLockFunction XB_LOCK<br>XB_UNLOCK
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbDbf::LockIndices( xbInt16 iLockFunction )
+{
+ // this function doesn't take into account any Lack Flavors other than DBASE,
+ // would need updated to supprot other lock flavors - Clipper, FoxPro etc
+
+ xbInt16 iRc = 0;
+ xbInt16 iErrorStop = 0;
+
+ try{
+ xbIxList *ixLI = GetIxList(); // index list item
+
+ while( ixLI ){
+ if( iLockFunction == XB_LOCK ){
+
+ #ifdef XB_NDX_SUPPORT
+ if( *ixLI->sFmt == "NDX" ){
+ if( !ixLI->ix->GetLocked()){
+ if(( iRc = ixLI->ix->xbLock( XB_LOCK, LK4026531838, 1 )) != XB_NO_ERROR ){
+ ixLI->ix->xbLock( XB_UNLOCK, LK4026531838, 1 );
+ iErrorStop = 100;
+ throw iRc;
+ }
+ ixLI->ix->SetLocked( xbTrue );
+ }
+ }
+ #endif
+
+ } else if( iLockFunction == XB_UNLOCK ){
+
+ #ifdef XB_NDX_SUPPORT
+ if( *ixLI->sFmt == "NDX" ){
+ if( ixLI->ix->GetLocked()){
+ if(( iRc = ixLI->ix->xbLock( XB_UNLOCK, LK4026531838, 1 )) != XB_NO_ERROR ){
+ ixLI->ix->xbLock( XB_UNLOCK, LK4026531838, 1 );
+ iErrorStop = 100;
+ throw iRc;
+ }
+ ixLI->ix->SetLocked( xbFalse );
+ }
+ }
+ #endif
+ }
+ ixLI = ixLI->next;
+ }
+
+ } catch( xbInt16 iRc ){
+ if( iRc != XB_LOCK_FAILED ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbdbf::LockIndices() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ }
+ return iRc;
+}
+#endif // XB_INDEX_SUPPORT
+/************************************************************************/
+#ifdef XB_MEMO_SUPPORT
+//! @brief Lock Memo file.
+/*!
+ This routine locks the memo file for updates.
+ \param iLockFunction XB_LOCK<br>XB_UNLOCK
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbDbf::LockMemo( xbInt16 iLockFunction ){
+ if( MemoFieldsExist())
+ return Memo->LockMemo( iLockFunction );
+ else
+ return XB_NO_ERROR;
+}
+#endif // XB_MEMO_SUPPORT
+
+
+
+/************************************************************************/
+//! @brief Loc Record
+/*!
+ This routine locks a record for update.
+ \param iLockFunction XB_LOCK<br>XB_UNLOCK
+ \param ulRecNo Record number to lock
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbDbf::LockRecord( xbInt16 iLockFunction, xbUInt32 ulRecNo )
+{
+ xbInt16 iRc = 0;
+ xbInt16 iErrorStop = 0;
+
+ try{
+
+ if( ulRecNo > ulNoOfRecs )
+ return XB_INVALID_RECORD;
+
+ if( iLockFunction == XB_LOCK ){
+ iErrorStop = 100;
+
+ if( lloRecLocks.KeyExists( ulRecNo ))
+ return XB_NO_ERROR;
+
+ if( GetLockFlavor() == LK_DBASE ){
+
+ iRc = xbLock( XB_LOCK, LK4026531838 - ulRecNo, 1 );
+ if( iRc != XB_NO_ERROR ){
+ if( iRc == XB_LOCK_FAILED ){
+ return iRc;
+ } else {
+ iErrorStop = 130;
+ throw iRc;
+ }
+ }
+ // other lock-table flavor options go here Clipper, Fox, etc
+
+ } else {
+ iErrorStop = 190;
+ iRc = XB_INVALID_OPTION;
+ throw iRc;
+ }
+
+ // add the record lock info to the linked list chain of record locks
+ iRc = lloRecLocks.InsertKey( ulRecNo );
+ if( iRc != XB_NO_ERROR ){
+ xbLock( XB_UNLOCK, LK4026531838 - ulRecNo, 1 );
+ iErrorStop = 140;
+ throw iRc;
+ }
+
+ } else if( iLockFunction == XB_UNLOCK ){
+
+ iErrorStop = 200;
+
+ if( !lloRecLocks.KeyExists( ulRecNo ) )
+ return XB_NO_ERROR;
+
+ if( GetLockFlavor() == LK_DBASE ){
+ iRc = xbLock( XB_UNLOCK, LK4026531838 - ulRecNo, 1 );
+ if( iRc != XB_NO_ERROR ){
+ iErrorStop = 220;
+ throw iRc;
+ }
+ } else {
+ iErrorStop = 290;
+ iRc = XB_INVALID_OPTION;
+ throw iRc;
+ }
+
+ // remove the record lock info to the linked list chain of record locks
+ // next line is crashing
+ iRc = lloRecLocks.RemoveKey( ulRecNo );
+ if( iRc != XB_NO_ERROR ){
+ iErrorStop = 230;
+ throw iRc;
+ }
+
+ } else {
+ iErrorStop = 300;
+ iRc = XB_INVALID_OPTION;
+ throw iRc;
+ }
+
+ } catch( xbInt16 iRc ){
+ if( iRc != XB_LOCK_FAILED ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbdbf::LockRecord() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ }
+
+ return iRc;
+}
+
+
+/************************************************************************/
+//! @brief Lock table.
+/*!
+ This routine locks the table for updates.
+
+ \param iLockFunction XB_LOCK<br>XB_UNLOCK
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbDbf::LockTable( xbInt16 iLockFunction )
+{
+ xbInt16 iRc = 0;
+ xbInt16 iErrorStop = 0;
+
+ try{
+ if( iLockFunction == XB_LOCK ){
+ iErrorStop = 100;
+ if( GetTableLocked())
+ return XB_NO_ERROR; // table already locked
+
+ iErrorStop = 110;
+ if( GetLockFlavor() == LK_DBASE ){
+
+ // lOffset = LK4026531838;
+ // iLen = 2;
+ iRc = xbLock( XB_LOCK, LK4026531838, 2 );
+ if( iRc != XB_NO_ERROR ){
+ if( iRc == XB_LOCK_FAILED )
+ return iRc;
+ else{
+ iErrorStop = 120;
+ throw iRc;
+ }
+ }
+
+ // lOffset = LK3026531838;
+ // iLen = LK1000000000;
+ iRc = xbLock( XB_LOCK, LK3026531838, LK1000000000);
+ if( iRc != XB_NO_ERROR ){
+
+ // lOffset = LK4026531838;
+ // iLen = 2;
+ xbLock( XB_UNLOCK, LK4026531838, 2 );
+ if( iRc == XB_LOCK_FAILED ){
+ return iRc;
+ } else {
+ iErrorStop = 130;
+ throw iRc;
+ }
+ }
+
+ // iRc = xbLock( XB_UNLOCK, lOffset, iLen );
+ iRc = xbLock( XB_UNLOCK, LK3026531838, LK1000000000);
+ if( iRc != XB_NO_ERROR ){
+ // lOffset = LK4026531838;
+ // iLen = 2;
+ xbLock( XB_UNLOCK, LK4026531838, 2 );
+ iErrorStop = 140;
+ throw iRc;
+ }
+
+ // other lock-table flavor options go here Clipper, Fox, etc
+
+ } else {
+ iErrorStop = 190;
+ iRc = XB_INVALID_OPTION;
+ throw iRc;
+ }
+ SetTableLocked( xbTrue );
+
+ } else if( iLockFunction == XB_UNLOCK ){
+
+ iErrorStop = 200;
+ if( !GetTableLocked())
+ return XB_NO_ERROR; // table already unlocked
+
+ iErrorStop = 210;
+ if( GetLockFlavor() == LK_DBASE ){
+
+ // lOffset = LK4026531838;
+ // iLen = 2;
+ iRc = xbLock( XB_UNLOCK, LK4026531838, 2 );
+ if( iRc != XB_NO_ERROR ){
+ iErrorStop = 220;
+ throw iRc;
+ }
+ } else {
+ iErrorStop = 290;
+ iRc = XB_INVALID_OPTION;
+ throw iRc;
+ }
+ SetTableLocked( xbFalse );
+
+ } else {
+ iErrorStop = 300;
+ iRc = XB_INVALID_OPTION;
+ throw iRc;
+ }
+
+ } catch (xbInt16 iRc ){
+ if( iRc != XB_LOCK_FAILED ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbdbf::LockFile() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ }
+ return iRc;
+
+}
+
+
+/************************************************************************/
+//! @brief Check for existence of any memo fields.
+/*!
+ \returns xbTrue - Memo fields exist.<br>xbFalse - Memo fields don't exist.
+*/
+xbBool xbDbf::MemoFieldsExist() const {
+#ifdef XB_MEMO_SUPPORT
+ if( iMemoFieldCnt > 0 )
+ return xbTrue;
+#endif
+ return xbFalse;
+}
+
+/************************************************************************/
+//! @brief Open a table/dbf file.
+/*!
+ This routine sets the alias name to the same as the table name.
+
+ \param sTableName Table name to open, Include the .dbf or .DBF extension.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbDbf::Open( const xbString & sTableName ) {
+ return Open( sTableName, sTableName );
+}
+
+/************************************************************************/
+//! @brief Open a table/dbf file.
+/*!
+ \param sTableName Table name to open, Include the .dbf or .DBF extension.
+ \param sAlias Alias name to assign to this entry.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbDbf::Open( const xbString & sTableName, const xbString & sAlias ){
+
+ xbInt16 iRc = 0;
+ xbInt16 iErrorStop = 0;
+
+
+ try{
+
+ if(( iRc = Open( sTableName, sAlias, XB_READ_WRITE, XB_MULTI_USER )) != XB_NO_ERROR ){
+ iErrorStop = 10;
+ throw iRc;
+ }
+
+ // do any .INF data things on the file, like open indices
+ #ifdef XB_NDXINF_SUPPORT
+ if(( iRc = LoadNdxInfData()) != XB_NO_ERROR ){
+ iErrorStop = 20;
+ throw iRc;
+ }
+ xbUInt32 llNodeCnt = llNdxInfData.GetNodeCnt();
+ if( llNodeCnt > 0 ){
+ xbString s2;
+ xbLinkListNode<xbString> * llN = llNdxInfData.GetHeadNode();
+ for( xbUInt32 i = 0; i < llNodeCnt; i++ ){
+ s2 = llN->GetKey();
+ if(( iRc = OpenIndex( "NDX", s2 )) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc ;
+ }
+ llN = llN->GetNextNode();
+ }
+ }
+ #endif // XB_NDXINF_SUPPORT
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbdbf::Open() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+#endif // XB_LOCKING_SUPPORT
+
+/************************************************************************/
+#ifdef XB_INDEX_SUPPORT
+//! @brief Open an index.
+/*!
+ Open an index file for the dbf file.
+
+ \param sIxType - "NDX" or "MDX"
+ \param sFileName - File name of index,
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbDbf::OpenIndex( const xbString &sIxType, const xbString &sFileName ){
+
+ // this routine is used to open indices and set up linkages
+
+ xbInt16 iErrorStop = 0;
+ xbInt16 iRc = XB_NO_ERROR;
+ xbIx *pIx = NULL;
+
+ try{
+ xbString sType = sIxType;
+ sType.ToUpperCase();
+
+ if( sType == "" ){
+ iErrorStop = 10;
+ iRc = XB_INVALID_OPTION;
+ throw iRc;
+
+ #ifdef XB_NDX_SUPPORT
+ } else if( sType == "NDX" ){
+ pIx = new xbIxNdx( this );
+ if(( iRc = pIx->Open( sFileName )) != XB_NO_ERROR ){
+ iErrorStop = 20;
+ throw iRc;
+ }
+ #endif
+
+ #ifdef XB_MDX_SUPPORT
+ } else if( sType == "MDX" ){
+
+ pIx = new xbIxMdx( this );
+ if(( iRc = pIx->Open( sFileName )) != XB_NO_ERROR ){
+ iErrorStop = 30;
+ throw iRc;
+ }
+
+ #endif
+
+ } else {
+ iErrorStop = 40;
+ iRc = XB_INVALID_OPTION;
+ throw iRc;
+ }
+
+ if(( iRc = AddIndex( pIx, sIxType )) != XB_NO_ERROR ){
+ iErrorStop = 50;
+ throw iRc;
+ }
+ if(( iRc = UpdateTagList()) != XB_NO_ERROR ){
+ iErrorStop = 60;
+ throw iRc;
+ }
+ pCurIx = pIx;
+ sCurIxType = sIxType;
+ vpCurIxTag = pIx->GetTag( 0 );
+ }
+ catch (xbInt16 iRc ){
+ if( pIx ) delete pIx;
+ xbString sMsg;
+ sMsg.Sprintf( "xbdbf::OpenIndex() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+#endif // XB_INDEX_SUPPORT
+
+
+
+/************************************************************************/
+//! @brief Pack dbf file.
+/*!
+ This routine eliminates all deleted records from the file.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbDbf::Pack()
+{
+ xbUInt32 ulDeletedRecCnt;
+ return Pack( ulDeletedRecCnt );
+}
+
+
+/************************************************************************/
+//! @brief Pack dbf file.
+/*!
+ This routine eliminates all deleted records from the file and clears
+ out any unused blocks in the memo file if one exists.
+ \param ulDeletedRecCnt - Output - number of recrods removed from the file.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbDbf::Pack( xbUInt32 &ulDeletedRecCnt )
+{
+ xbInt16 iRc = 0;
+ xbInt16 iErrorStop = 0;
+ xbUInt32 ulLastMovedRec = 0;
+ xbUInt32 ulStartPos = 0;
+ xbUInt32 ulLastPackedRec = 0;
+ xbUInt32 ulMoveRec = 0;
+ xbUInt32 ulRecCnt = 0;
+ ulDeletedRecCnt = 0;
+
+ try{
+ if( !FileIsOpen() ){
+ iErrorStop = 10;
+ iRc = XB_DBF_FILE_NOT_OPEN;
+ throw iRc;
+ }
+
+ #ifdef XB_LOCKING_SUPPORT
+ if( iAutoLock ){
+ if(( iRc = LockTable( XB_LOCK )) != XB_NO_ERROR ){
+ iErrorStop = 20;
+ throw iRc;
+ }
+ }
+ #endif
+
+ if(( iRc = GetRecordCnt( ulRecCnt )) != XB_NO_ERROR ){
+ iErrorStop = 30;
+ throw iRc;
+ }
+
+ xbBool bDone = xbFalse;
+ for( xbUInt32 ulI = 1; ulI <= ulRecCnt && !bDone; ulI++ ){
+
+
+ if(( iRc = GetRecord( ulI )) != XB_NO_ERROR ){
+ iErrorStop = 40;
+ throw iRc;
+ }
+
+ if( RecordDeleted()){
+
+ ulDeletedRecCnt++;
+ if( ulI > ulLastMovedRec )
+ ulStartPos = ulI;
+ else
+ ulStartPos = ulLastMovedRec;
+
+ iRc = GetNextRecord( 1, ulStartPos );
+
+ if( iRc == XB_NO_ERROR ){
+ ulMoveRec = ulCurRec;
+ }
+ else if( iRc == XB_EOF ){
+ ulMoveRec = 0;
+ bDone = xbTrue;
+ }
+ else{
+ iErrorStop = 50;
+ throw iRc;
+ }
+ if( ulMoveRec > 0 ){
+ if(( iRc = DeleteRecord()) != XB_NO_ERROR ){
+ iErrorStop = 60;
+ throw iRc;
+ }
+ if(( iRc = PutRecord( ulMoveRec )) != XB_NO_ERROR ){
+ iErrorStop = 70;
+ throw iRc;
+ }
+ if(( iRc = UndeleteRecord()) != XB_NO_ERROR ){
+ iErrorStop = 80;
+ throw iRc;
+ }
+ if(( iRc = PutRecord( ulI )) != XB_NO_ERROR ){
+ iErrorStop = 90;
+ throw iRc;
+ }
+ ulLastPackedRec = ulI;
+ }
+
+ } else {
+ ulLastPackedRec = ulI;
+ }
+ }
+
+ if( ulLastPackedRec < ulRecCnt ){
+ // update header record count
+
+ xbDate d;
+ cUpdateYY = (char) d.YearOf() - 1900;
+ cUpdateMM = (char) d.MonthOf();
+ cUpdateDD = (char) d.DayOf( XB_FMT_MONTH );
+ ulNoOfRecs = ulLastPackedRec;
+
+ // rewrite the header record
+ if(( iRc = WriteHeader( 1, 1 )) != XB_NO_ERROR ){
+ iErrorStop = 200;
+ throw iRc;
+ }
+
+ // truncate the file to the new size
+ if(( iRc = xbTruncate( uiHeaderLen + uiRecordLen * ulLastPackedRec )) != XB_NO_ERROR ){
+ iErrorStop = 210;
+ throw iRc;
+ }
+ }
+
+ if( ulNoOfRecs > 0 ){
+ if(( iRc = GetRecord( 1 )) != XB_NO_ERROR ){
+ iErrorStop = 220;
+ throw iRc;
+ }
+ } else {
+ BlankRecord();
+ ulCurRec = 0;
+ }
+
+ #ifdef XB_MEMO_SUPPORT
+ if( iMemoFieldCnt > 0 ){
+ if(( iRc = Memo->PackMemo( 0 )) != XB_NO_ERROR ){
+ iErrorStop = 230;
+ throw iRc;
+ }
+ }
+ #endif // XB_MEMO_SUPPORT
+
+ #ifdef XB_LOCKING_SUPPORT
+ if( iAutoLock ){
+ if(( iRc = LockTable( XB_UNLOCK )) != XB_NO_ERROR ){
+ iErrorStop = 240;
+ throw iRc;
+ }
+ }
+
+ #endif
+ }
+ catch (xbInt16 iRc ){
+ if( iRc != XB_LOCK_FAILED ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbdbf::Pack() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ }
+ return iRc;
+}
+
+/************************************************************************/
+//! @brief Write the current record to disk.
+/*!
+ This routine is used to write any updates to the current record buffer to disk.
+
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbDbf::PutRecord() {
+ return PutRecord(ulCurRec);
+}
+
+/************************************************************************/
+//! @brief Write record to disk.
+/*!
+ This routine is used to write a copy of the current record buffer to disk
+ for a given record number.
+
+ \param ulRecNo Record number to update.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbDbf::PutRecord(xbUInt32 ulRecNo)
+{
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+ try{
+
+ if( ulRecNo < 1 ){
+ iErrorStop = 100;
+ throw XB_INVALID_RECORD;
+ }
+
+ xbUInt32 ulRecCnt;
+ if(( iRc = GetRecordCnt( ulRecCnt )) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ throw iRc;
+ }
+
+ if( ulRecNo > ulRecCnt ){
+ iErrorStop = 120;
+ throw XB_INVALID_RECORD;
+ }
+
+ if( iDbfStatus == XB_CLOSED ){
+ iErrorStop = 130;
+ iRc = XB_NOT_OPEN;
+ throw iRc;
+ }
+ /* lock the database */
+ #ifdef XB_LOCKING_SUPPORT
+ // xbInt16 iAutoLock = GetAutoLock();
+ if( iAutoLock && !bTableLocked ){
+ if(( iRc = LockHeader( XB_LOCK )) != XB_NO_ERROR ){
+ if( iRc == XB_LOCK_FAILED )
+ return iRc;
+ else{
+ iErrorStop = 140;
+ throw iRc;
+ }
+ }
+
+ if(( iRc = ReadHeader( 1, 1 )) != XB_NO_ERROR ){
+ iErrorStop = 150;
+ LockHeader( XB_UNLOCK );
+ throw iRc;
+ }
+ }
+ #endif
+
+ // verify valid record number request
+ if( ulRecNo > ulNoOfRecs || ulRecNo == 0L ){
+ iErrorStop = 160;
+ iRc = XB_INVALID_RECORD;
+ throw iRc;
+ }
+
+ #ifdef XB_LOCKING_SUPPORT
+ if( iAutoLock ){
+ if( !bTableLocked ){
+ if(( iRc = LockRecord( XB_LOCK, ulRecNo )) != XB_NO_ERROR ){
+ if( iRc == XB_LOCK_FAILED )
+ return iRc;
+ else{
+ iErrorStop = 170;
+ throw iRc;
+ }
+ }
+ }
+
+ #ifdef XB_INDEX_SUPPORT
+ if(( iRc = LockIndices( XB_LOCK )) != XB_NO_ERROR ){
+ if( iRc == XB_LOCK_FAILED )
+ return iRc;
+ else{
+ iErrorStop = 180;
+ throw iRc;
+ }
+ }
+ #endif
+ }
+ #endif
+
+ // build keys, check for duplicate keys, add keys
+ #ifdef XB_INDEX_SUPPORT
+ xbIxList *ixList = GetIxList();
+
+ while( ixList ){
+ if(( iRc = ixList->ix->CreateKeys( 2 )) != XB_NO_ERROR ){
+ iErrorStop = 190;
+ throw iRc;
+ }
+ iRc = ixList->ix->CheckForDupKeys();
+ if( iRc != 0 ){
+ if( iRc < 0 ){
+ iErrorStop = 200;
+ throw iRc;
+ }
+ return XB_KEY_NOT_UNIQUE;
+ }
+ ixList = ixList->next;
+ }
+
+ ixList = GetIxList();
+ while( ixList ){
+
+ if(( iRc = ixList->ix->AddKeys( ulCurRec )) != XB_NO_ERROR ){
+ iErrorStop = 210;
+ throw iRc;
+ }
+ ixList = ixList->next;
+ }
+
+ ixList = GetIxList();
+ while( ixList ){
+
+// if(( iRc = ixList->ix->DeleteKeys( ulCurRec )) != XB_NO_ERROR ){
+
+ if(( iRc = ixList->ix->DeleteKeys()) != XB_NO_ERROR ){
+ iErrorStop = 220;
+ throw iRc;
+ }
+ ixList = ixList->next;
+ }
+ #endif // XB_INDEX_SUPPORT
+
+ // update latest header date if changed
+ xbDate d;
+ if( (cUpdateYY != (char)(d.YearOf() - 1900)) || (cUpdateMM != (char) d.MonthOf()) || (cUpdateDD != (char)d.DayOf( XB_FMT_MONTH))){
+ cUpdateYY = (char) d.YearOf() - 1900;
+ cUpdateMM = (char) d.MonthOf();
+ cUpdateDD = (char) d.DayOf( XB_FMT_MONTH );
+ // rewrite the header record - first 8 bytes
+ if(( iRc = WriteHeader( 1, 1 )) != XB_NO_ERROR ){
+ iErrorStop = 70;
+ throw iRc;
+ }
+ }
+
+ #ifdef XB_LOCKING_SUPPORT
+ if( iAutoLock && !bTableLocked ){
+ if(( iRc = LockHeader( XB_UNLOCK )) != XB_NO_ERROR ){
+ if( iRc == XB_LOCK_FAILED )
+ return iRc;
+ else{
+ iErrorStop = 230;
+ throw iRc;
+ }
+ }
+ }
+ #endif
+
+ // update record
+ iRc = xbFseek( (uiHeaderLen+(( (xbInt64) ulRecNo-1L ) * uiRecordLen )),0 );
+ if( iRc != XB_NO_ERROR ){
+ iErrorStop = 240;
+ throw iRc;
+ }
+
+ if( xbFwrite( RecBuf, uiRecordLen, 1 ) != XB_NO_ERROR ){
+ iErrorStop = 250;
+ iRc = XB_WRITE_ERROR;
+ throw iRc;
+ }
+
+ #ifdef XB_MEMO_SUPPORT
+ if( MemoFieldsExist() ){
+ if(( iRc = Memo->Commit()) != XB_NO_ERROR ){
+ iErrorStop = 260;
+ throw iRc;
+ }
+ }
+ #endif
+
+ // unlock record
+ #ifdef XB_LOCKING_SUPPORT
+ if( iAutoLock ){
+ #ifdef XB_INDEX_SUPPORT
+ LockIndices( XB_UNLOCK );
+ #endif // XB_INDEX_SUPPORT
+ if( !bTableLocked ){
+ if(( iRc = LockRecord( XB_UNLOCK, ulRecNo )) != XB_NO_ERROR ){
+ if( iRc == XB_LOCK_FAILED )
+ return iRc;
+ else{
+ iErrorStop = 270;
+ throw iRc;
+ }
+ }
+ }
+ }
+
+ #endif
+ ulCurRec = ulRecNo;
+ iDbfStatus = XB_OPEN;
+ }
+
+ catch (xbInt16 iRc ){
+
+ #ifdef XB_LOCKING_SUPPORT
+ if( iAutoLock ){
+ LockHeader( XB_UNLOCK );
+ LockAppend( XB_UNLOCK );
+ LockRecord( XB_UNLOCK, ulRecNo );
+ LockIndices( XB_UNLOCK );
+ }
+ #endif // XB_LOCKING_SUPPORT
+
+ if( iRc != XB_LOCK_FAILED ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbdbf::PutRecord() Exception Caught. Error Stop = [%d] iRc = [%d] record = [%d]", iErrorStop, iRc, ulRecNo );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ }
+ return iRc;
+}
+
+/************************************************************************/
+//! @brief Read dbf file header information.
+/*!
+ This method assumes the header has been locked appropriately
+ in a multi user environment
+
+
+ \param iPositionOption 0 - Don't fseek to beginning of file before read.<br>
+ 1 - Start from beginning of file.
+
+ \param iReadOption 0 - Read entire 32 byte header<br>
+ 1 - Read first eight bytes which includes the last update date and number of records.
+
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbDbf::ReadHeader( xbInt16 iPositionOption, xbInt16 iReadOption ){
+
+ char buf[32];
+ size_t iReadSize;
+
+ if(iPositionOption)
+ xbRewind();
+ if( iReadOption == 1 )
+ iReadSize = 8;
+ else
+ iReadSize = 32;
+
+ if(xbFread(buf, iReadSize, 1) != XB_NO_ERROR)
+ return XB_READ_ERROR;
+ memcpy(&cVersion, buf, 4);
+ ulNoOfRecs = eGetUInt32(&buf[4]);
+ if( iReadOption == 1 )
+ return XB_NO_ERROR;
+
+ uiHeaderLen = eGetUInt16(&buf[8]);
+ uiRecordLen = eGetUInt16(&buf[10]);
+ cTransactionFlag = buf[14];
+ cEncryptionFlag = buf[15];
+ cIndexFlag = buf[28];
+ cLangDriver = buf[29];
+ return XB_NO_ERROR;
+}
+
+/************************************************************************/
+//! @brief Return record deletion status.
+/*!
+ This routine returns the record deletion status.
+ \param iOpt 0 = Current record buffer, 1 = Original record buffer
+ \returns xbTrue - Record deleted.<br>xbFalse - Record not deleted.
+*/
+xbInt16 xbDbf::RecordDeleted( xbInt16 iOpt ) const {
+ if( !iOpt && RecBuf && RecBuf[0] == 0x2a )
+ return xbTrue;
+ else if( iOpt && RecBuf2 && RecBuf2[0] == 0x2a )
+ return xbTrue;
+ else
+ return xbFalse;
+}
+
+/************************************************************************/
+#ifdef XB_INDEX_SUPPORT
+//! @brief Remove an index from the internal list of indices for this table
+/*
+ The index list is used during any table update process to update any open
+ index file. Index files can contain one or more tags.
+
+ \param ixIn Pointer to index object for a given index file.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbDbf::RemoveIndex( xbIx * ixIn ){
+
+ xbIxList *p = ixList;
+ // if index is the first entry in the list
+ if( ixList->ix == ixIn ){
+ ixList = ixList->next;
+ delete p->sFmt;
+ delete p->ix;
+ free( p );
+ return XB_NO_ERROR;
+ }
+
+ // spin down to the correct ix
+ xbIxList *p2 = NULL;
+ while( p && p->ix != ixIn ){
+ p2 = p;
+ p = p->next;
+ }
+ if( p ){
+ p2->next = p->next;
+ delete p->sFmt;
+ delete p->ix;
+ free( p );
+ }
+ return XB_NO_ERROR;
+}
+#endif // XB_INDEX_SUPPORT
+
+/************************************************************************/
+// @brief Reset number of records.
+/*
+ Protected method. Resets number of records to 0.
+ \returns void
+*/
+void xbDbf::ResetNoOfRecords() {
+ ulNoOfRecs = 0UL;
+}
+
+/************************************************************************/
+#ifdef XB_NDXINF_SUPPORT
+// @brief Update .INF data file.
+/*
+ Protected method.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbDbf::SaveNdxInfData(){
+
+ xbInt16 iRc = 0;
+ xbInt16 iErrorStop = 0;
+ xbFile fMd( xbase );
+
+ try{
+
+ xbUInt32 llNodeCnt = llNdxInfData.GetNodeCnt();
+
+ xbString sNdxInfFileName;
+ if(( iRc = GetNdxInfFileName( sNdxInfFileName )) != XB_NO_ERROR ){
+ iErrorStop = 10;
+ throw iRc;
+ }
+
+ // open the file
+ if(( iRc = fMd.xbFopen( "w", sNdxInfFileName, GetShareMode())) != XB_NO_ERROR ){
+ iErrorStop = 20;
+ throw iRc;
+ }
+
+ xbString s1;
+ xbString s2;
+ s2.Sprintf( "[dbase]%c%c", 0x0d, 0x0a );
+ if(( iRc = fMd.xbFputs( s2 )) != XB_NO_ERROR ){
+ iErrorStop = 30;
+ throw iRc;
+ }
+
+
+ // for each entry in the linked list, write a line
+ xbLinkListNode<xbString> * llN = llNdxInfData.GetHeadNode();
+ for( xbUInt32 i = 0; i < llNodeCnt; i++ ){
+ s2 = llN->GetKey();
+ if( i > 0 )
+ s1.Sprintf( "NDX%d=%s%c%c", i, s2.Str(), 0x0d, 0x0a );
+ else
+ s1.Sprintf( "NDX=%s%c%c", s2.Str(), 0x0d, 0x0a );
+
+ if(( iRc = fMd.xbFputs( s1 )) != XB_NO_ERROR ){
+ iErrorStop = 40;
+ throw iRc;
+ }
+ llN = llN->GetNextNode();
+ }
+
+ // close the file
+ if(( iRc = fMd.xbFclose()) != XB_NO_ERROR ){
+ iErrorStop = 50;
+ throw iRc;
+ }
+
+ } catch( xbInt16 iRc ){
+ if( fMd.FileIsOpen())
+ fMd.xbFclose();
+ xbString sMsg;
+ sMsg.Sprintf( "xbdbf::SaveNdxInfData() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+#endif // XB_NDXINF_SUPPORT
+/************************************************************************/
+//! @brief Set auto commit.
+/*!
+ This routine sets the auto commit setting for this table.
+ \returns XB_NO_ERROR;
+*/
+xbInt16 xbDbf::SetAutoCommit( xbBool iAutoCommit ) {
+ this->iAutoCommit = iAutoCommit;
+ return XB_NO_ERROR;
+}
+
+
+/************************************************************************/
+//! @brief Set auto lock.
+/*!
+ This routine sets the auto lock setting for this table.
+ There is an overall system level auto lock default setting and each table
+ can have it's own autolock setting. This method controls the table level
+ auto lock setting.
+
+ \param iAutoLock 1 - Use auto lock for this table.<br>
+ 0 - Don't use auto lock for this table.<br>
+ -1 - (minus one) Use system default.<br>
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+void xbDbf::SetAutoLock( xbInt16 iAutoLock ){
+ if( iAutoLock == -1 )
+ this->iAutoLock = xbase->GetDefaultAutoLock();
+ else
+ this->iAutoLock = iAutoLock;
+}
+
+
+
+/************************************************************************/
+#ifdef XB_INDEX_SUPPORT
+//! @brief Set the current tag for the dbf file.
+/*!
+ \param sTagName - Tag Name
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbDbf::SetCurTag( const xbString &sTagName ){
+
+ if( sTagName == "" ){
+ SetCurTag( "", 0, 0 );
+ return XB_NO_ERROR;
+
+ } else {
+ xbLinkListNode<xbTag *> *llN = GetTagList();
+ xbTag *pTag;
+ while( llN ){
+ pTag = llN->GetKey();
+ if( pTag->GetTagName() == sTagName ){
+ SetCurTag( pTag->GetType(), pTag->GetIx(), pTag->GetVpTag());
+ return XB_NO_ERROR;
+ }
+ llN = llN->GetNextNode();
+ }
+ }
+
+ return XB_INVALID_TAG;
+}
+
+/************************************************************************/
+//! @brief Set the current tag for the dbf file.
+/*!
+
+ \param sIxType - One of "NDX" or MDX",
+ \param pIx - Pointer to index object.
+ \param vpTag - Pointer to tag object.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+void xbDbf::SetCurTag( const xbString &sIxType, xbIx *pIx, void *vpTag ){
+ pCurIx = pIx;
+ vpCurIxTag = vpTag;
+ sCurIxType.Set( sIxType );
+}
+#endif // XB_INDEX_SUPPORT
+
+/************************************************************************/
+//! @brief Set the header locked status.
+/*!
+ \param bHeaderLocked xbTrue - Locked<br>xbFalse - Not locked.
+ \returns void
+*/
+void xbDbf::SetHeaderLocked( xbBool bHeaderLocked ){
+ this->bHeaderLocked = bHeaderLocked;
+}
+
+/************************************************************************/
+//! @brief Set lock flavor.
+/*!
+ This routine is for future expansion.
+ \param iLockFlavor 1 - Use Dbase (tm) style locking.
+ \returns void
+*/
+
+void xbDbf::SetLockFlavor( xbInt16 iLockFlavor ){
+ this->iLockFlavor = iLockFlavor;
+}
+
+/************************************************************************/
+//! @brief Set table locked status.
+/*!
+ \param bTableLocked - xbTrue Table locked.<br>xbFalse Table unlocked.
+ \returns void
+*/
+
+void xbDbf::SetTableLocked( xbBool bTableLocked ){
+ this->bTableLocked = bTableLocked;
+}
+/************************************************************************/
+//! @brief Undelete all records.
+/*!
+ This routine will remove the deletion flag on any deleted records in the table.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbDbf::UndeleteAllRecords(){
+ return DeleteAll( 1 );
+}
+
+/************************************************************************/
+//! @brief Undelete one record.
+/*!
+ This routine will undelete the current record, if it is deleted.
+ \returns XB_NO_ERROR<br>XB_INVALID_RECORD
+*/
+xbInt16 xbDbf::UndeleteRecord()
+{
+ if( RecBuf && ulCurRec > 0 ){
+ if( RecBuf[0] != 0x20 ){
+ if( iDbfStatus != XB_UPDATED ){
+ iDbfStatus = XB_UPDATED;
+ memcpy( RecBuf2, RecBuf, uiRecordLen ); // save off original before making updates
+ }
+ RecBuf[0] = 0x20;
+ }
+ return XB_NO_ERROR;
+ }
+ else
+ return XB_INVALID_RECORD;
+}
+
+/************************************************************************/
+#ifdef XB_MEMO_SUPPORT
+//! @brief Update memo field
+/*!
+ This routine updates a memo field.
+ \param iFieldNo - Memo field number to update.
+ \param sMemoData - Memo data for update.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbDbf::UpdateMemoField( xbInt16 iFieldNo, const xbString &sMemoData ){
+ return Memo->UpdateMemoField( iFieldNo, sMemoData );
+}
+/************************************************************************/
+//! @brief Update memo field
+/*!
+ This routine updates a memo field.
+ \param sFieldName - Memo field name to update.
+ \param sMemoData - Memo data for update.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbDbf::UpdateMemoField( const xbString & sFieldName, const xbString & sMemoData ){
+ return Memo->UpdateMemoField( GetFieldNo( sFieldName ), sMemoData );
+}
+#endif
+
+
+/************************************************************************/
+#ifdef XB_INDEX_SUPPORT
+//! @brief Update SchemaIxFlag
+/*!
+ This routine can be called from the DeleteTag routine if a tag has been deleted and the flag needs reset
+ \param iFldNo - Which field the index flag needs changed on
+ \param cVal - Value to change it to
+*/
+
+void xbDbf::UpdateSchemaIxFlag( xbInt16 iFldNo, unsigned char cVal ){
+ if( cVal != 0x00 || cVal != 0x01 )
+ SchemaPtr[iFldNo].cIxFlag = cVal;
+}
+
+#endif // XB_INDEX_SUPPORT
+/************************************************************************/
+//! @brief Update tag list.
+/*!
+ This routine updates the internal tag list of open index tags.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbDbf::UpdateTagList(){
+
+ xbInt16 iErrorStop = 0;
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt32 lTagCnt;
+
+ try{
+ ClearTagList();
+
+ // For each active index
+ xbIxList *p = GetIxList();
+ xbIx *ixp;
+ while( p ){
+ ixp = p->ix;
+ // for each tag within the file
+ lTagCnt = ixp->GetTagCount();
+ for( xbInt32 l = 0; l < lTagCnt; l++ ){
+ xbTag *pTag = new xbTag( ixp, ixp->GetTag( l ), *p->sFmt, ixp->GetTagName( ixp->GetTag( l )),
+ ixp->GetKeyExpression( ixp->GetTag( l )), ixp->GetKeyFilter( ixp->GetTag( l )),
+ ixp->GetUnique( ixp->GetTag( l )), ixp->GetSortOrder( ixp->GetTag( l )));
+
+ // append it to the llTags list
+ llTags.InsertAtEnd( pTag );
+ }
+ p = p->next;
+ }
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbdbf::UpdateTagList() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+
+/************************************************************************/
+// @brief Write Header
+/*
+ Protected method.
+
+ \param iPositionOption 0 - Don't fseek to beginning of file before read.<br>
+ 1 - Go to beginning of file before read.
+ \param iWriteOption 0 - Write entire 32 byte header.<br>
+ 1 - Write first eight bytes needed for table updates - last update date and number of records.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbDbf::WriteHeader( xbInt16 iPositionOption, xbInt16 iWriteOption )
+{
+ char buf[32];
+ xbInt32 lWriteLen;
+
+ if(iPositionOption)
+ xbRewind();
+
+ memset(buf, 0, 32);
+ if( iWriteOption == 1 )
+ lWriteLen = 8;
+ else{
+ lWriteLen = 32;
+ ePutUInt16( &buf[8], uiHeaderLen );
+ ePutUInt16( &buf[10], uiRecordLen );
+ buf[14] = cTransactionFlag;
+ buf[15] = cEncryptionFlag;
+ buf[28] = cIndexFlag;
+ buf[29] = cLangDriver;
+ }
+ memcpy(&buf[0], &cVersion, 4);
+ ePutUInt32( &buf[4], ulNoOfRecs);
+ if(xbFwrite(buf, (size_t) lWriteLen, 1) != XB_NO_ERROR)
+ return XB_WRITE_ERROR;
+
+ return XB_NO_ERROR;
+}
+
+/************************************************************************/
+//! @brief Zap (remove) everything from the file,
+/*!
+ This routine eliminates everything from the dbf file and dbt memo file.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbDbf::Zap(){
+
+ xbInt16 iRc = 0;
+ xbInt16 iErrorStop = 0;
+
+ try{
+ if( iDbfStatus != XB_OPEN ){
+ iErrorStop = 10;
+ iRc = XB_DBF_FILE_NOT_OPEN;
+ throw iRc;
+ }
+ #ifdef XB_LOCKING_SUPPORT
+ if( iAutoLock ){
+ if(( iRc = LockTable( XB_LOCK )) != XB_NO_ERROR ){
+ iErrorStop = 20;
+ throw iRc;
+ }
+ }
+ #endif
+
+ xbDate d;
+ cUpdateYY = (char) d.YearOf() - 1900;
+ cUpdateMM = (char) d.MonthOf();
+ cUpdateDD = (char) d.DayOf( XB_FMT_MONTH );
+ ulNoOfRecs = 0;
+
+ // rewrite the header record
+ if(( iRc = WriteHeader( 1, 1 )) != XB_NO_ERROR ){
+ iErrorStop = 30;
+ throw iRc;
+ }
+
+ // truncate the file to the new size
+ if(( iRc = xbTruncate( uiHeaderLen )) != XB_NO_ERROR ){
+ iErrorStop = 40;
+ throw iRc;
+ }
+ BlankRecord();
+ ulCurRec = 0;
+
+ #ifdef XB_MEMO_SUPPORT
+ if( iMemoFieldCnt ){
+ if(( iRc = Memo->Zap()) != XB_NO_ERROR ){
+ iErrorStop = 50;
+ throw iRc;
+ }
+ }
+ #endif
+ #ifdef XB_INDEX_SUPPORT
+ xbLinkListNode<xbTag *> *llN = GetTagList();
+ xbTag *pTag;
+ xbIx *pIx;
+ void *vpTag;
+ while( llN ){
+ pTag = llN->GetKey();
+ pIx = pTag->GetIx();
+ vpTag = pTag->GetVpTag();
+ if(( iRc = pIx->Reindex( &vpTag )) != XB_NO_ERROR ){
+ iErrorStop = 60;
+ throw iRc;
+ }
+ llN = llN->GetNextNode();
+ }
+
+ #endif // XB_INDEX_SUPPORT
+ #ifdef XB_LOCKING_SUPPORT
+ if( iAutoLock ){
+ if(( iRc = LockTable( XB_UNLOCK )) != XB_NO_ERROR ){
+ iErrorStop = 70;
+ throw iRc;
+ }
+ }
+ #endif // XB_LOCKING_SUPPORT
+ }
+ catch (xbInt16 iRc ){
+ if( iRc != XB_LOCK_FAILED ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbdbf::Zap() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ }
+ return iRc;
+}
+/************************************************************************/
+} /* namespace */ \ No newline at end of file
diff --git a/src/core/xbdbf3.cpp b/src/core/xbdbf3.cpp
new file mode 100755
index 0000000..cbbff1f
--- /dev/null
+++ b/src/core/xbdbf3.cpp
@@ -0,0 +1,577 @@
+/* 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.<br> xbFalse - Don't overlay.
+ \param iShareMode XB_SINGLE_USER<br>XB_MULTI_USER
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+
+*/
+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 = 10;
+ rc = XB_FILE_EXISTS;
+ throw rc;
+ }
+ #ifdef XB_NDXINF_SUPPORT
+ // remove the metadata file if there is one
+ xbString sMname = sNfn;
+ xbUInt32 iMnameLen = sMname.Len();
+ sMname.PutAt( iMnameLen-2, 'X' );
+ sMname.PutAt( iMnameLen-1, 'B' );
+ sMname.PutAt( iMnameLen, 'M' );
+ xbFile fTemp( xbase );
+ fTemp.SetFileName( sMname );
+ if( fTemp.FileExists() )
+ fTemp.xbRemove();
+ #endif // XB_NDXINF_SUPPORT
+ }
+
+ /* check if we already have a file with this alias */
+ if(( rc = xbase->AddTblToTblList( this, GetFqFileName(), sAlias )) != XB_NO_ERROR ){
+ iErrorStop = 20;
+ throw rc;
+ }
+
+ rc = ValidateSchema( pSchema );
+ if( rc < 0 ){
+ iErrorStop = 30;
+ 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 = 40;
+ rc = XB_FILE_EXISTS;
+ throw rc;
+ }
+
+ Memo = new xbMemoDbt3( this, fTemp.GetFqFileName());
+
+ if(( rc = Memo->CreateMemoFile()) != XB_NO_ERROR ){
+ iErrorStop = 50;
+ 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 = 60;
+ throw rc;
+ }
+ uiRecordLen++; /* add one byte for 0x0D */
+
+ if(( RecBuf = (char *) malloc( uiRecordLen )) == NULL ){
+ iErrorStop = 70;
+ throw rc;
+ }
+
+ if(( RecBuf2 = (char *) malloc( uiRecordLen )) == NULL ){
+ iErrorStop = 80;
+ 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 = 90;
+ rc = XB_WRITE_ERROR;
+ throw rc;
+ }
+ if((SchemaPtr = (xbSchemaRec *) malloc( (size_t) iNoOfFields * sizeof( xbSchemaRec ))) == NULL){
+ iErrorStop = 100;
+ 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 = 110;
+ rc = XB_WRITE_ERROR;
+ throw rc;
+ }
+
+ if(( xbFwrite( &SchemaPtr[i].cType, 1, 1 )) != XB_NO_ERROR ) {
+ iErrorStop = 120;
+ 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 = 130;
+ rc = XB_WRITE_ERROR;
+ throw rc;
+ }
+
+ if(( xbFwrite( &SchemaPtr[i].cNoOfDecs, 1, 1 )) != XB_NO_ERROR ) {
+ iErrorStop = 140;
+ 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 = 150;
+ 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<br>XB_READ_WRITE
+ \param iShareMode XB_SINGLE_USER<br>XB_MULTI_USER
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+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 = 10;
+ iRc = XB_ALREADY_OPEN;
+ throw iRc;
+ }
+ /* copy the file name to the class variable */
+ SetFileName( sTableName );
+ this->sAlias = sAlias;
+
+ if( !FileExists()){
+ iErrorStop = 20;
+ iRc = XB_FILE_NOT_FOUND;
+ throw iRc;
+ }
+
+ if(( iRc = xbase->AddTblToTblList( this, GetFqFileName(), sAlias )) != XB_NO_ERROR ){
+ iErrorStop = 30;
+ throw iRc;
+ }
+
+ /* open the file */
+ if(( iRc = xbFopen( iOpenMode, iShareMode )) != XB_NO_ERROR ){
+ iErrorStop = 40;
+ throw iRc;
+ }
+
+ /* copy the header into memory */
+ if(( iRc = ReadHeader( 1, 0 )) != XB_NO_ERROR ){
+ iErrorStop = 50;
+ throw iRc;
+ }
+
+ /* check the version */
+ if(( cVersion & 0x07 ) != 3 ){
+ // if( xFileVersion != 3 && xFileVersion != 4 ){
+ iErrorStop = 60;
+ 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 = 70;
+ iRc = XB_NO_MEMORY;
+ throw iRc;
+ }
+
+ if(( RecBuf2 = (char *) malloc( uiRecordLen )) == NULL ) {
+ iErrorStop = 80;
+ iRc = XB_NO_MEMORY;
+ throw iRc;
+ }
+
+ if((SchemaPtr=(xbSchemaRec *)malloc( (size_t) iNoOfFields * sizeof( xbSchemaRec ))) == NULL){
+ iErrorStop = 90;
+ 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 = 100;
+ 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 = 110;
+ throw iRc;
+ }
+ }
+ #endif
+
+ }
+ 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
+ }
+
+ if( iRc == XB_NO_ERROR )
+ iDbfStatus = XB_OPEN;
+
+ 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<br>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;
+ }
+
+#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..c421add
--- /dev/null
+++ b/src/core/xbdbf4.cpp
@@ -0,0 +1,590 @@
+/* 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.<br> xbFalse - Don't overlay.
+ \param iShareMode XB_SINGLE_USER<br>XB_MULTI_USER
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+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 = 10;
+ rc = XB_FILE_EXISTS;
+ throw rc;
+ }
+ #ifdef XB_NDXINF_SUPPORT
+ // remove the metadata file if there is one
+ xbString sMname = sNfn;
+ xbUInt32 iMnameLen = sMname.Len();
+ sMname.PutAt( iMnameLen-2, 'X' );
+ sMname.PutAt( iMnameLen-1, 'B' );
+ sMname.PutAt( iMnameLen, 'M' );
+ xbFile fTemp( xbase );
+ fTemp.SetFileName( sMname );
+ if( fTemp.FileExists() )
+ fTemp.xbRemove();
+ #endif // XB_NDXINF_SUPPORT
+ }
+
+ /* check if we already have a file with this alias */
+ if(( rc = xbase->AddTblToTblList( this, GetFqFileName(), sAlias )) != XB_NO_ERROR ){
+ iErrorStop = 20;
+ throw rc;
+ }
+
+ rc = ValidateSchema( pSchema );
+ if( rc < 0 ){
+ iErrorStop = 30;
+ 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 = 40;
+ rc = XB_FILE_EXISTS;
+ throw rc;
+ }
+
+ Memo = new xbMemoDbt4( this, fTemp.GetFqFileName());
+
+ if(( rc = Memo->CreateMemoFile()) != XB_NO_ERROR ){
+ iErrorStop = 50;
+ 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 = 60;
+ throw rc;
+ }
+ uiRecordLen++; /* add one byte for 0x0D */
+
+ if(( RecBuf = (char *) malloc( uiRecordLen )) == NULL ){
+ iErrorStop = 70;
+ throw rc;
+ }
+
+ if(( RecBuf2 = (char *) malloc( uiRecordLen )) == NULL ){
+ iErrorStop = 80;
+ 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 );
+
+ // Default language driver to 0x1b
+ cLangDriver = 0x1b;
+
+ /* write the header prolog */
+ if(( rc = WriteHeader( 0, 0 )) != XB_NO_ERROR ){
+ iErrorStop = 90;
+ rc = XB_WRITE_ERROR;
+ throw rc;
+ }
+ if((SchemaPtr=(xbSchemaRec *)malloc( (size_t) iNoOfFields * sizeof( xbSchemaRec ))) == NULL){
+ iErrorStop = 100;
+ 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 = 120;
+ rc = XB_WRITE_ERROR;
+ throw rc;
+ }
+
+ if(( xbFwrite( &SchemaPtr[i].cType, 1, 1 )) != XB_NO_ERROR ) {
+ iErrorStop = 130;
+ 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 = 140;
+ rc = XB_WRITE_ERROR;
+ throw rc;
+ }
+
+ if(( xbFwrite( &SchemaPtr[i].cNoOfDecs, 1, 1 )) != XB_NO_ERROR ) {
+ iErrorStop = 150;
+ 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 = 160;
+ 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<br>XB_READ_WRITE
+ \param iShareMode XB_SINGLE_USER<br>XB_MULTI_USER
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+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 = 10;
+ iRc = XB_ALREADY_OPEN;
+ throw iRc;
+ }
+ /* copy the file name to the class variable */
+ SetFileName( sTableName );
+ this->sAlias = sAlias;
+
+ if( !FileExists()){
+ iErrorStop = 20;
+ iRc = XB_FILE_NOT_FOUND;
+ throw iRc;
+ }
+
+ if(( iRc = xbase->AddTblToTblList( this, GetFqFileName(), sAlias )) != XB_NO_ERROR ){
+ iErrorStop = 30;
+ throw iRc;
+ }
+
+ /* open the file */
+ if(( iRc = xbFopen( iOpenMode, iShareMode )) != XB_NO_ERROR ){
+ iErrorStop = 40;
+ throw iRc;
+ }
+
+ /* copy the header into memory */
+ if(( iRc = ReadHeader( 1, 0 )) != XB_NO_ERROR ){
+ iErrorStop = 50;
+ throw iRc;
+ }
+
+ /* check the version */
+ //if(( xFileVersion = DetermineXbaseTableVersion( cVersion )) != 4 ){
+ if(( cVersion & 0x07 ) != 3 ){
+ iErrorStop = 60;
+ 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 = 70;
+ iRc = XB_NO_MEMORY;
+ throw iRc;
+ }
+
+ if(( RecBuf2 = (char *) malloc( uiRecordLen )) == NULL ) {
+ iErrorStop = 80;
+ iRc = XB_NO_MEMORY;
+ throw iRc;
+ }
+
+ if((SchemaPtr=(xbSchemaRec *)malloc((size_t) iNoOfFields * sizeof( xbSchemaRec ))) == NULL){
+ iErrorStop = 90;
+ 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 = 100;
+ 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 = 110;
+ throw iRc;
+ }
+ }
+ #endif
+
+ #ifdef XB_MDX_SUPPORT
+ 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 = 120;
+ 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;
+}
+
+
+/************************************************************************/
+#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<br>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;
+ }
+
+ #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..d3e8ca8
--- /dev/null
+++ b/src/core/xbexp.cpp
@@ -0,0 +1,2643 @@
+/* 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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+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 = 10;
+ 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 = 20;
+ 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 = 30;
+ 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 = 40;
+ 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 = 50;
+ 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 = 100;
+ 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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+
+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 = 10;
+ iRc = XB_UNBALANCED_PARENS;
+ throw iRc;
+ }
+ if( bInQuotes ){
+ iErrorStop = 20;
+ 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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+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 = 10;
+ throw iRc;
+ }
+ }
+ // Check for parens
+ else if( t.sExpression[1] == '(' || t.sExpression[1] == '{' ){
+ if(( iRc = GetTokenParen( t )) != XB_NO_ERROR ){
+ iErrorStop = 20;
+ throw iRc;
+ }
+ }
+ // Check for a char constant
+ else if( t.sExpression[1] == '"' || t.sExpression[1] == '\'' ){
+ if(( iRc = GetTokenCharConstant( t )) != XB_NO_ERROR ){
+ iErrorStop = 30;
+ iRc = XB_PARSE_ERROR;
+ throw iRc;
+ }
+ }
+ // Check for logical constant
+ else if( IsLogicalConstant( t.sExpression )){
+ if(( iRc = GetTokenLogicalConstant( t )) != XB_NO_ERROR ){
+ iErrorStop = 40;
+ 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 = 50;
+ iRc = XB_PARSE_ERROR;
+ throw iRc;
+ }
+ }
+ // check for operator
+ else if( IsOperator( t.sExpression )){
+ if(( iRc = GetTokenOperator( t )) != XB_NO_ERROR ){
+ iErrorStop = 60;
+ iRc = XB_PARSE_ERROR;
+ throw iRc;
+ }
+ }
+ // check for function
+ else if( IsFunction( t.sExpression, t.cReturnType )){
+ if(( iRc = GetTokenFunction( t )) != XB_NO_ERROR ){
+ iErrorStop = 70;
+ iRc = XB_PARSE_ERROR;
+ throw iRc;
+ }
+ }
+ else if(( iRc = GetTokenDatabaseField( t )) != XB_NO_ERROR ){
+ iErrorStop = 80;
+ iRc = XB_PARSE_ERROR;
+ throw iRc;
+ }
+ }
+
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+
+ //t.sExpression;
+ // std::cout << ">>>>>>>>>" << sMsg.Str() << "\n";
+ // sMsg.Sprintf( "[%d][%s]", iErrorStop, t.sExpression.Str() );
+ // sMsg.Sprintf( "xbexp::GetNextToken() Exception Caught. Error Stop = [%d] %s", iErrorStop, 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() );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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+
+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 <a href="xbretcod_8h.html">Return Codes</a>
+
+*/
+
+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 = 10;
+ 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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+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 = 10;
+ 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 = 20;
+ 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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+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 field name from before the ->
+ if( strncmp( s, "->", 2 ) == 0 ){
+ 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 <a href="xbretcod_8h.html">Return Codes</a>
+
+*/
+
+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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+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 <a href="xbretcod_8h.html">Return Codes</a>
+
+*/
+
+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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+
+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 = 10;
+ 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.<br>
+ 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.<br>
+ 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.<br>
+ 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.<br>
+ 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.<br>
+ 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.<br>
+ 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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+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 = 10;
+ 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 = 20;
+ 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 = 30;
+ throw iRc;
+ }
+ break;
+
+ case XB_EXP_FUNCTION:
+ n = new xbExpNode( t.cNodeType );
+ if(( iRc = ParseExpressionFunction( t, n, iWeight )) != XB_NO_ERROR ){
+ iErrorStop = 40;
+ throw iRc;
+ }
+ break;
+
+ case XB_EXP_FIELD:
+ n = new xbExpNode( t.cNodeType );
+ if(( iRc = ParseExpressionField( t, n )) != XB_NO_ERROR ){
+ iErrorStop = 50;
+ 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 = 60;
+ throw iRc;
+ }
+ break;
+
+ default:
+ iErrorStop = 70;
+ 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 = 80;
+ 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 = 90;
+ 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 = 100;
+ iRc = XB_PARSE_ERROR;
+ throw iRc;
+ }
+ if( nWork->GetChildCnt() < 2 ){
+ iErrorStop = 110;
+ 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 = 120;
+ iRc = XB_PARSE_ERROR;
+ throw iRc;
+ // break;
+ }
+ }
+ if( nWork->IsUnaryOperator() ){
+ if( nWork->GetChildCnt() != 1 ){
+ iErrorStop = 130;
+ iRc = XB_PARSE_ERROR;
+ throw iRc;
+ }
+ } else if( nWork->IsOperator() && nWork->GetChildCnt() != 2 ){
+ iErrorStop = 140;
+ 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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+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 );
+ } else {
+ // table name is not part of the token
+ pDbf = dbf;
+ sFieldName = t.sToken;
+ }
+ if( !pDbf ){
+ iErrorStop = 10;
+ iRc = XB_INVALID_FIELD;
+ throw iRc;
+ }
+ xbInt16 iFieldNo = 0;
+
+ if(( iRc = pDbf->GetFieldNo( sFieldName, iFieldNo )) != XB_NO_ERROR ){
+ iErrorStop = 20;
+ throw iRc;
+ }
+ char cFieldType;
+ if(( iRc = pDbf->GetFieldType( iFieldNo, cFieldType )) != XB_NO_ERROR ){
+ iErrorStop = 20;
+ 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 = 30;
+ iRc = XB_PARSE_ERROR;
+ throw iRc;
+ // break;
+ }
+ n->SetNodeText( sFieldName );
+ xbInt16 iResultLen = 0;
+ if(( iRc = pDbf->GetFieldLen( iFieldNo, iResultLen )) != XB_NO_ERROR ){
+ iErrorStop = 30;
+ 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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+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 = 10;
+ 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 = 20;
+ 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 = 30;
+ 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<xbString> llParms;
+ if(( iRc = ParseExpressionFunctionParms( sParms, llParms )) != XB_NO_ERROR ){
+ iErrorStop = 40;
+ throw iRc;
+ }
+
+ // for each function parm, recursively process it
+ xbLinkListNode<xbString> * llN = llParms.GetHeadNode();
+ xbString sParm;
+ while( llN ){
+ sParm = llN->GetKey();
+ if(( iRc = enr.ParseExpression( sParm, iWeight + 10 )) != XB_NO_ERROR ){
+ iErrorStop = 40;
+ 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 = 50;
+ 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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbExp::ParseExpressionFunctionParms( const xbString &sParms, xbLinkList<xbString> & 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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+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.<br>
+ 0 - Current record buffer.<br>
+ 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 = 10;
+ 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 = 20;
+ 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 = 30;
+ 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 = 40;
+ throw iRc;
+ }
+ nWork->SetResult( sWork1 );
+ break;
+
+ case XB_EXP_DATE:
+
+ if(( iRc = dbf->GetField( nWork->GetFieldNo(), sWork1, iRecBufSw )) < XB_NO_ERROR ){
+ iErrorStop = 50;
+ 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 = 60;
+ throw iRc;
+ }
+ nWork->SetResult( bWork );
+ break;
+
+ case XB_EXP_NUMERIC:
+ if(( iRc = dbf->GetDoubleField( nWork->GetFieldNo(), dWork, iRecBufSw )) != XB_NO_ERROR ){
+ iErrorStop = 70;
+ throw iRc;
+ }
+ nWork->SetResult( dWork );
+ break;
+
+ default:
+ iErrorStop = 80;
+ iRc = XB_PARSE_ERROR;
+ throw iRc;
+ // break;
+ }
+ break;
+
+
+ case XB_EXP_OPERATOR:
+ if(( iRc = ProcessExpressionOperator( nWork )) != XB_NO_ERROR ){
+ iErrorStop = 300;
+ throw iRc;
+ }
+
+ break;
+
+ case XB_EXP_FUNCTION:
+ if(( iRc = ProcessExpressionFunction( nWork, iRecBufSw )) != XB_NO_ERROR ){
+ iErrorStop = 400;
+ throw iRc;
+ }
+ break;
+
+ default:
+ iErrorStop = 500;
+ 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
new file mode 100755
index 0000000..77a8c33
--- /dev/null
+++ b/src/core/xbexpnode.cpp
@@ -0,0 +1,562 @@
+/* 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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+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.<bt>
+ 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<xbExpNode *> *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<xbExpNode *> *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.<br>
+ 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.<br>
+ 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.<br>
+ 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..8cfb4e6
--- /dev/null
+++ b/src/core/xbfields.cpp
@@ -0,0 +1,1122 @@
+/* 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:<br>
+ XB_NO_ERROR<br>XB_INVALID_DATA<br>XB_INVALID_FIELD_NO<br>XB_INVALID_FIELD_NAME<br>
+ XB_INVALID_FIELD_TYPE<br>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:<br>
+ XB_NO_ERROR<br>XB_INVALID_DATA<br>XB_INVALID_FIELD_NO<br>XB_INVALID_FIELD_NAME<br>
+ XB_INVALID_FIELD_TYPE<br>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.<br>1 - Record buffer with original data.
+ \returns The field routines return one of:<br>
+ XB_NO_ERROR<br>XB_INVALID_DATA<br>XB_INVALID_FIELD_NO<br>XB_INVALID_FIELD_NAME<br>
+ XB_INVALID_FIELD_TYPE<br>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:<br>
+ XB_NO_ERROR<br>XB_INVALID_DATA<br>XB_INVALID_FIELD_NO<br>XB_INVALID_FIELD_NAME<br>
+ XB_INVALID_FIELD_TYPE<br>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:<br>
+ XB_NO_ERROR<br>XB_INVALID_DATA<br>XB_INVALID_FIELD_NO<br>XB_INVALID_FIELD_NAME<br>
+ XB_INVALID_FIELD_TYPE<br>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:<br>
+ XB_NO_ERROR<br>XB_INVALID_DATA<br>XB_INVALID_FIELD_NO<br>XB_INVALID_FIELD_NAME<br>
+ XB_INVALID_FIELD_TYPE<br>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:<br>
+ XB_NO_ERROR<br>XB_INVALID_DATA<br>XB_INVALID_FIELD_NO<br>XB_INVALID_FIELD_NAME<br>
+ XB_INVALID_FIELD_TYPE<br>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.<br>1 - Record buffer with original data.
+ \returns The field routines return one of:<br>
+ XB_NO_ERROR<br>XB_INVALID_DATA<br>XB_INVALID_FIELD_NO<br>XB_INVALID_FIELD_NAME<br>
+ XB_INVALID_FIELD_TYPE<br>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:<br>
+ XB_NO_ERROR<br>XB_INVALID_DATA<br>XB_INVALID_FIELD_NO<br>XB_INVALID_FIELD_NAME<br>
+ XB_INVALID_FIELD_TYPE<br>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:<br>
+ XB_NO_ERROR<br>XB_INVALID_DATA<br>XB_INVALID_FIELD_NO<br>XB_INVALID_FIELD_NAME<br>
+ XB_INVALID_FIELD_TYPE<br>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:<br>
+ XB_NO_ERROR<br>XB_INVALID_DATA<br>XB_INVALID_FIELD_NO<br>XB_INVALID_FIELD_NAME<br>
+ XB_INVALID_FIELD_TYPE<br>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:<br>
+ XB_NO_ERROR<br>XB_INVALID_DATA<br>XB_INVALID_FIELD_NO<br>XB_INVALID_FIELD_NAME<br>
+ XB_INVALID_FIELD_TYPE<br>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:<br>
+ XB_NO_ERROR<br>XB_INVALID_DATA<br>XB_INVALID_FIELD_NO<br>XB_INVALID_FIELD_NAME<br>
+ XB_INVALID_FIELD_TYPE<br>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:<br>
+ XB_NO_ERROR<br>XB_INVALID_DATA<br>XB_INVALID_FIELD_NO<br>XB_INVALID_FIELD_NAME<br>
+ XB_INVALID_FIELD_TYPE<br>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:<br>
+ XB_NO_ERROR<br>XB_INVALID_DATA<br>XB_INVALID_FIELD_NO<br>XB_INVALID_FIELD_NAME<br>
+ XB_INVALID_FIELD_TYPE<br>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:<br>
+ XB_NO_ERROR<br>XB_INVALID_DATA<br>XB_INVALID_FIELD_NO<br>XB_INVALID_FIELD_NAME<br>
+ XB_INVALID_FIELD_TYPE<br>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:<br>
+ XB_NO_ERROR<br>XB_INVALID_DATA<br>XB_INVALID_FIELD_NO<br>XB_INVALID_FIELD_NAME<br>
+ XB_INVALID_FIELD_TYPE<br>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:<br>
+ XB_NO_ERROR<br>XB_INVALID_DATA<br>XB_INVALID_FIELD_NO<br>XB_INVALID_FIELD_NAME<br>
+ XB_INVALID_FIELD_TYPE<br>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:<br>
+ XB_NO_ERROR<br>XB_INVALID_DATA<br>XB_INVALID_FIELD_NO<br>XB_INVALID_FIELD_NAME<br>
+ XB_INVALID_FIELD_TYPE<br>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:<br>
+ XB_NO_ERROR<br>XB_INVALID_DATA<br>XB_INVALID_FIELD_NO<br>XB_INVALID_FIELD_NAME<br>
+ XB_INVALID_FIELD_TYPE<br>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.<br>1 - Record buffer with original data.
+ \returns The field routines return one of:<br>
+ XB_NO_ERROR<br>XB_INVALID_DATA<br>XB_INVALID_FIELD_NO<br>XB_INVALID_FIELD_NAME<br>
+ XB_INVALID_FIELD_TYPE<br>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:<br>
+ XB_NO_ERROR<br>XB_INVALID_DATA<br>XB_INVALID_FIELD_NO<br>XB_INVALID_FIELD_NAME<br>
+ XB_INVALID_FIELD_TYPE<br>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:<br>
+ XB_NO_ERROR<br>XB_INVALID_DATA<br>XB_INVALID_FIELD_NO<br>XB_INVALID_FIELD_NAME<br>
+ XB_INVALID_FIELD_TYPE<br>XB_INVALID_DATA
+*/
+xbInt16 xbDbf::GetLongField( xbInt16 iFieldNo, xbInt32 & lFieldValue ) const {
+
+ xbInt16 rc = 0;
+ xbInt16 iErrorStop = 0;
+ char cFieldType;
+
+ try{
+
+ if(( rc = GetFieldType( iFieldNo, cFieldType )) != XB_NO_ERROR ){
+ iErrorStop = 10;
+ throw rc;
+ }
+
+ if( cFieldType != 'N' && cFieldType != 'F' && cFieldType != 'M' ){
+ iErrorStop = 20;
+ rc = XB_INVALID_FIELD_TYPE;
+ throw rc;
+ }
+
+ xbString sTemp;
+ if(( rc = GetField( iFieldNo, sTemp, 0 )) < 0 ){
+ iErrorStop = 30;
+ throw rc;
+ }
+
+ if( !sTemp.ValidNumericValue() || ((int) sTemp.Pos( '.' ) > 0)){
+ iErrorStop = 40;
+ rc = XB_INVALID_DATA;
+ throw rc;
+ }
+
+ lFieldValue = atol( sTemp.Str());
+ }
+
+ catch (xbInt16 rc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbDbf::GetLongField() Exception Caught. Error Stop = [%d] rc = [%d]", iErrorStop, rc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( rc ));
+ }
+
+ 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:<br>
+ XB_NO_ERROR<br>XB_INVALID_DATA<br>XB_INVALID_FIELD_NO<br>XB_INVALID_FIELD_NAME<br>
+ XB_INVALID_FIELD_TYPE<br>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:<br>
+ XB_NO_ERROR<br>XB_INVALID_DATA<br>XB_INVALID_FIELD_NO<br>XB_INVALID_FIELD_NAME<br>
+ XB_INVALID_FIELD_TYPE<br>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:<br>
+ XB_NO_ERROR<br>XB_INVALID_DATA<br>XB_INVALID_FIELD_NO<br>XB_INVALID_FIELD_NAME<br>
+ XB_INVALID_FIELD_TYPE<br>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:<br>
+ XB_NO_ERROR<br>XB_INVALID_DATA<br>XB_INVALID_FIELD_NO<br>XB_INVALID_FIELD_NAME<br>
+ XB_INVALID_FIELD_TYPE<br>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:<br>
+ XB_NO_ERROR<br>XB_INVALID_DATA<br>XB_INVALID_FIELD_NO<br>XB_INVALID_FIELD_NAME<br>
+ XB_INVALID_FIELD_TYPE<br>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 = 10;
+ throw rc;
+ }
+
+ if( cFieldType != 'N' && cFieldType != 'F' && cFieldType != 'M' ){
+ iErrorStop = 20;
+ rc = XB_INVALID_FIELD_TYPE;
+ throw rc;
+ }
+
+ xbString sTemp;
+ if(( rc = GetField( iFieldNo, sTemp, 0 )) != XB_NO_ERROR ){
+ iErrorStop = 30;
+ throw rc;
+ }
+ sTemp.Trim();
+ if( !sTemp.ValidNumericValue() || ((int) sTemp.Pos( '.' ) > 0)){
+ iErrorStop = 40;
+ 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:<br>
+ XB_NO_ERROR<br>XB_INVALID_DATA<br>XB_INVALID_FIELD_NO<br>XB_INVALID_FIELD_NAME<br>
+ XB_INVALID_FIELD_TYPE<br>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:<br>
+ XB_NO_ERROR<br>XB_INVALID_DATA<br>XB_INVALID_FIELD_NO<br>XB_INVALID_FIELD_NAME<br>
+ XB_INVALID_FIELD_TYPE<br>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.<br>1 - Record buffer with original data.
+ \returns The field routines return one of:<br>
+ XB_NO_ERROR<br>XB_INVALID_DATA<br>XB_INVALID_FIELD_NO<br>XB_INVALID_FIELD_NAME<br>
+ XB_INVALID_FIELD_TYPE<br>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:<br>
+ XB_NO_ERROR<br>XB_INVALID_DATA<br>XB_INVALID_FIELD_NO<br>XB_INVALID_FIELD_NAME<br>
+ XB_INVALID_FIELD_TYPE<br>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:<br>
+ XB_NO_ERROR<br>XB_INVALID_DATA<br>XB_INVALID_FIELD_NO<br>XB_INVALID_FIELD_NAME<br>
+ XB_INVALID_FIELD_TYPE<br>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:<br>
+ XB_NO_ERROR<br>XB_INVALID_DATA<br>XB_INVALID_FIELD_NO<br>XB_INVALID_FIELD_NAME<br>
+ XB_INVALID_FIELD_TYPE<br>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:<br>
+ XB_NO_ERROR<br>XB_INVALID_DATA<br>XB_INVALID_FIELD_NO<br>XB_INVALID_FIELD_NAME<br>
+ XB_INVALID_FIELD_TYPE<br>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:<br>
+ XB_NO_ERROR<br>XB_INVALID_DATA<br>XB_INVALID_FIELD_NO<br>XB_INVALID_FIELD_NAME<br>
+ XB_INVALID_FIELD_TYPE<br>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:<br>
+ XB_NO_ERROR<br>XB_INVALID_DATA<br>XB_INVALID_FIELD_NO<br>XB_INVALID_FIELD_NAME<br>
+ XB_INVALID_FIELD_TYPE<br>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:<br>
+ XB_NO_ERROR<br>XB_INVALID_DATA<br>XB_INVALID_FIELD_NO<br>XB_INVALID_FIELD_NAME<br>
+ XB_INVALID_FIELD_TYPE<br>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:<br>
+ XB_NO_ERROR<br>XB_INVALID_DATA<br>XB_INVALID_FIELD_NO<br>XB_INVALID_FIELD_NAME<br>
+ XB_INVALID_FIELD_TYPE<br>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:<br>
+ XB_NO_ERROR<br>XB_INVALID_DATA<br>XB_INVALID_FIELD_NO<br>XB_INVALID_FIELD_NAME<br>
+ XB_INVALID_FIELD_TYPE<br>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:<br>
+ XB_NO_ERROR<br>XB_INVALID_DATA<br>XB_INVALID_FIELD_NO<br>XB_INVALID_FIELD_NAME<br>
+ XB_INVALID_FIELD_TYPE<br>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:<br>
+ XB_NO_ERROR<br>XB_INVALID_DATA<br>XB_INVALID_FIELD_NO<br>XB_INVALID_FIELD_NAME<br>
+ XB_INVALID_FIELD_TYPE<br>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:<br>
+ XB_NO_ERROR<br>XB_INVALID_DATA<br>XB_INVALID_FIELD_NO<br>XB_INVALID_FIELD_NAME<br>
+ XB_INVALID_FIELD_TYPE<br>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:<br>
+ XB_NO_ERROR<br>XB_INVALID_DATA<br>XB_INVALID_FIELD_NO<br>XB_INVALID_FIELD_NAME<br>
+ XB_INVALID_FIELD_TYPE<br>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:<br>
+ XB_NO_ERROR<br>XB_INVALID_DATA<br>XB_INVALID_FIELD_NO<br>XB_INVALID_FIELD_NAME<br>
+ XB_INVALID_FIELD_TYPE<br>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:<br>
+ XB_NO_ERROR<br>XB_INVALID_DATA<br>XB_INVALID_FIELD_NO<br>XB_INVALID_FIELD_NAME<br>
+ XB_INVALID_FIELD_TYPE<br>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:<br>
+ XB_NO_ERROR<br>XB_INVALID_DATA<br>XB_INVALID_FIELD_NO<br>XB_INVALID_FIELD_NAME<br>
+ XB_INVALID_FIELD_TYPE<br>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.<br> 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.<br> 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..a7d5257
--- /dev/null
+++ b/src/core/xbfile.cpp
@@ -0,0 +1,2052 @@
+/* xbfile.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 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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+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<br>
+ 1 - if file name extension is "dbf" or "DBF", verify unique dbt or DBT (memo) file is also available<br>
+ \param sFqnOut A fully qualifed unique file name as output
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+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:<br>
+ <br>
+ 0 - none<br>
+ 3 - Dbase III+<br>
+ 4 - Dbase IV<br>
+
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+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.<br>
+
+ It attempts to use the highest version compiled into the library.<br>
+
+ References:<br>
+ This routine uses the first byte from the dbf file.<br>
+ Per DBase documentation:<br>
+ Valid dBASE for Windows table file, bits 0-2 indicate version number: 3 for dBASE Level 5, 4 for dBASE Level 7.<br>
+ Bit 3 and bit 7 indicate presence of a dBASE IV or dBASE for Windows memo file;<br>
+ Bits 4-6 indicate the presence of a dBASE IV SQL table;<br>
+ 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).<br>
+<br>
+ Bachmann spec (used extensively in library build), page 7 - does not match DBase documentation<br>
+<br>
+ returns<br>
+ 0 - unknown<br>
+ 3 - Dbase level 3<br>
+ 4 - Dbase level 4<br>
+ 5 - Dbase Level 5 (future)<br>
+ 7 - Dbase Level 7 (future)<br>
+<br>
+ 1x - Clipper files (future)<br>
+ 2x - Foxbase files (future)<br>
+<br>
+*/
+
+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<br>
+ 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<br>
+ 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<br>
+ 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<br>
+ 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<br>
+ 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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+
+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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+
+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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+
+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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+
+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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+//*********** 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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbFile::GetFileSize( xbUInt64 &ullFileSize ){
+
+ xbInt16 iRc = 0;
+ xbInt16 iErrorStop = 0;
+
+ try{
+
+ if(( iRc = xbFseek( 0, SEEK_END )) != XB_NO_ERROR ){
+ iErrorStop = 10;
+ 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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+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<br>
+ XB_READ_WRITE<br>
+ XB_WRITE<BR>
+*/
+
+xbInt16 xbFile::GetOpenMode() const {
+ return iOpenMode;
+}
+
+/************************************************************************/
+//! @brief Get the share mode of the file.
+/*!
+ \returns XB_SINGLE_USER - (file buffering on><br>
+ XB_MULTI_USER - (file buffering off)<br>
+*/
+
+
+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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+
+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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+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 = 20;
+ iRc = XB_OPEN_ERROR;
+ throw iRc;
+ }
+ #else
+ if(( tfp = fopen( sFileName.Str(), "r" )) == NULL ){
+ iErrorStop = 20;
+ 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 = 30;
+ iRc = XB_SEEK_ERROR;
+ throw iRc;
+ }
+ stRc = fread( &cFileTypeByte, (size_t) 1, (size_t) 1, tfp );
+ if( stRc != (size_t) 1 ){
+ iErrorStop = 40;
+ 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<br>
+ 1 check for DBF<br>
+ 2 check for NDX<br>
+ 3 check for MDX<br>
+ 4 check for NTX<br>
+
+ \returns 0 if suffix found<br>
+ 1 if suffix not found, lower case<br>
+ 2 is suffix not found, upper case<br>
+
+*/
+
+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 <a href="xbretcod_8h.html">Return Codes</a>
+
+*/
+
+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 <a href="xbretcod_8h.html">Return Codes</a>
+
+*/
+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 = 10;
+ iRc = XB_INVALID_BLOCK_SIZE;
+ throw iRc;
+ }
+
+ if(( iRc = xbFseek(((xbInt64) ulBlockNo*ulBlockSize ), SEEK_SET )) != XB_NO_ERROR ){
+ iErrorStop = 20;
+ iRc = XB_SEEK_ERROR;
+ throw iRc;
+ }
+
+ if( lReadSize <= 0 )
+ lReadSize = ulBlockSize;
+
+ if(( iRc = xbFread( buf, lReadSize, 1 )) != XB_NO_ERROR ){
+ iErrorStop = 30;
+ 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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+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<br>
+ sFileName - the file name part<br>
+ sFqFileName - the fully qualified file name<br>
+
+
+ \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 <a href="xbretcod_8h.html">Return Codes</a>
+
+*/
+xbInt16 xbFile::WriteBlock( xbUInt32 ulBlockNo, size_t lWriteSize, void * buf ){
+
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+
+ try{
+
+ if( ulBlockSize == 0 ){
+ iErrorStop = 10;
+ iRc = XB_INVALID_BLOCK_SIZE;
+ throw iRc;
+ }
+ if( lWriteSize <= 0 )
+ lWriteSize = ulBlockSize;
+ if(( iRc = xbFseek(( (xbInt64) ulBlockNo*ulBlockSize), SEEK_SET )) != XB_NO_ERROR ){
+ iErrorStop = 20;
+ throw iRc;
+ }
+ if(( iRc = xbFwrite( buf, lWriteSize, 1 )) != XB_NO_ERROR ){
+ iErrorStop = 30;
+ 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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+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
+ <table>
+ <tr><th>OpenMode<th>Description
+ <tr><td>r<td>Reading
+ <tr><td>r+<td>Reading and Writing
+ <tr><td>w<td>Open for writing. Truncate to zero bytes if it exists
+ <tr><td>w+<td>Open for reading and writing, truncate to zero bytes if it exists
+ <tr><td>a<td>Open for append
+ <tr><td>a+<td>Open for reading and writing (at end).
+ </table>
+ 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<br>
+ XB_MULTI_USER<br>
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+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
+ <table>
+ <tr><th>OpenMode<th>Description
+ <tr><td>r<td>Reading
+ <tr><td>r+<td>Reading and Writing
+ <tr><td>w<td>Open for writing. Truncate to zero bytes if it exists
+ <tr><td>w+<td>Open for reading and writing, truncate to zero bytes if it exists
+ <tr><td>a<td>Open for append
+ <tr><td>a+<td>Open for reading and writing (at end).
+ </table>
+ 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<br>
+ XB_MULTI_USER<br>
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+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<br>
+ XB_READ_WRITE<br>
+ \param iShareMode
+ XB_SINGLE_USER<br>
+ XB_MULTI_USER<br>
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+
+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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+
+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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+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 write it
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+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<br>
+ SEEK_CUR - from current position<br>
+ SEEK_END - from end of file<br>
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+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 = 200;
+ 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 = 300;
+ 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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+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 = 10;
+ throw iRc;
+ }
+ sOut = c;
+ while( iRc == XB_NO_ERROR && c != cDelim ){
+ if(( iRc = xbFgetc( c )) != XB_NO_ERROR ){
+ iErrorStop = 10;
+ 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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbFile::xbRemove() {
+ return xbRemove( sFqFileName.Str(), 0 );
+}
+
+/************************************************************************/
+//! @brief Delete file.
+/*!
+ \param sFileNameIn Name of file to delete
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbFile::xbRename( const xbString & sOldName, const xbString & sNewName ){
+ return xbRename( sOldName, sNewName, 0 );
+}
+
+/************************************************************************/
+//! @brief Rename file.
+/*!
+ \param sOldName Original file name
+ \param sNewName New file name
+ \param iOption If Set to 1, assume this is a rename request for a dbf file, and should rename the dbt file also
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbFile::xbRename( const xbString & sOldName, const xbString & sNewName, xbInt16 iOption ){
+
+
+ if( rename( sOldName.Str(), sNewName.Str())){
+ return XB_RENAME_ERROR;
+ }
+ if( iOption == 1 ){
+ xbString sOldName2 = sOldName;
+ xbString sNewName2 = sNewName;
+
+ if( sOldName2[sOldName2.Len()] == 'F' )
+ sOldName2.PutAt( sOldName2.Len(), 'T' );
+ else
+ sOldName2.PutAt( sOldName2.Len(), 't' );
+
+ if( sNewName2[sNewName2.Len()] == 'F' )
+ sNewName2.PutAt( sNewName2.Len(), 'T' );
+ else
+ sNewName2.PutAt( sNewName2.Len(), 't' );
+
+ if( rename( sOldName2.Str(), sNewName2.Str()))
+ return XB_RENAME_ERROR;
+ }
+ 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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbFile::xbTruncate( xbInt64 llSize ) {
+
+ xbInt16 iRc = 0;
+ xbInt16 iErrorStop = 0;
+ try{
+ #ifdef HAVE_FTRUNCATE_F
+ if(( iRc = ftruncate( iFileNo, llSize )) != 0 ){
+ iErrorStop = 10;
+ iRc = XB_WRITE_ERROR;
+ throw iRc;
+ }
+ #elif defined(HAVE_SETENDOFFILE_F)
+ if(( iRc = xbFseek( llSize, SEEK_SET )) != XB_NO_ERROR ){
+ iErrorStop = 20;
+ throw iRc;
+ }
+ if(( iRc = SetEndOfFile( fHandle )) == 0 ){
+ iErrorStop = 30;
+ 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<br>
+ XB_UNLOCK<br>
+ \param lOffset Position in file to lock
+ \param stLen Length to lock
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+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 = 10;
+ 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 = 20;
+ 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 = 30;
+ 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 = 30;
+ 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 = 50;
+ iRc = XB_INVALID_LOCK_OPTION;
+ throw iRc;
+ }
+
+ if(( iRc = xbFseek( lOffset, SEEK_SET )) != XB_NO_ERROR ){
+ iErrorStop = 60;
+ 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 a block to the log file.
+/*!
+ This routine dumps a block to a file in the log file. This is
+ primarily used for debugging and analysis purposes.
+
+ \param ulBlockNo Block number to write
+ \param lBlxkSize Size of block
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+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{
+ iErrorStop = 10;
+
+ if( ulBlockNo == 0 ){
+ ulStartBlock = 0;
+ xbUInt64 ullFileSizeulBlockNo;
+ if(( iRc = GetFileSize( ullFileSizeulBlockNo )) != XB_NO_ERROR ){
+ iErrorStop = 20;
+ throw iRc;
+ }
+ ulEndBlock = (xbUInt32) (ullFileSizeulBlockNo / lBlkSize);
+ } else {
+ ulStartBlock = ulBlockNo;
+ ulEndBlock = ulBlockNo;
+ }
+
+ if(( buf = (char *) malloc( lBlkSize )) == NULL ){
+ iErrorStop = 30;
+ iRc = XB_NO_MEMORY;
+ throw iRc;
+ }
+
+ sDir = GetDefaultLogDirectory();
+ char cLastChar = sDir[sDir.Len()];
+
+ for( xbUInt32 l = ulStartBlock; l < ulEndBlock; l++ ){
+
+ if(( iRc = ReadBlock( l, lBlkSize, buf )) != XB_NO_ERROR ){
+ iErrorStop = 40;
+ 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 = 50;
+ 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 = 60;
+ 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;
+ if( iErrorStop == 60 )
+ sMsg.Sprintf( "xbFile::DumpBlockToDisk() Exception Caught. Error Stop = [%d] iRc = [%d] c=[%c][%x]", iErrorStop, iRc, *p, *p );
+ else
+ 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..e75f583
--- /dev/null
+++ b/src/core/xbfilter.cpp
@@ -0,0 +1,569 @@
+/* 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( xbXBase *xbase, xbDbf *dbf ) {
+ this->xbase = xbase;
+ 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( xbString &sFilter ) {
+
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+ try{
+
+ if( exp )
+ delete exp;
+
+ exp = new xbExp( xbase, dbf );
+ if(( iRc = exp->ParseExpression( sFilter.Str() )) != XB_NO_ERROR ){
+ iErrorStop = 10;
+ throw iRc;
+ }
+ if( exp->GetReturnType() != XB_EXP_LOGICAL ){
+ iErrorStop = 20;
+ 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 );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( xbase->GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+/************************************************************************/
+xbInt16 xbFilter::GetFirstRecord( xbInt16 iOption ) {
+
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+ try{
+
+ if( !exp ){
+ iErrorStop = 10;
+ throw iRc;
+ }
+
+ #ifdef XB_INDEX_SUPPORT
+ if( dbf->GetCurIx() && dbf->GetCurTag() )
+ return GetFirstRecordIx( iOption );
+ #endif
+
+ lCurQryCnt = 0;
+ if(( iRc = dbf->GetFirstRecord( iOption )) != XB_NO_ERROR ){
+ if( iRc == XB_EMPTY || iRc == XB_EOF )
+ return iRc;
+ else{
+ iErrorStop = 20;
+ throw iRc;
+ }
+ }
+
+ xbBool bFound = xbFalse;
+ while( !bFound && iRc == XB_NO_ERROR ){
+ if(( iRc = exp->ProcessExpression()) != XB_NO_ERROR ){
+ iErrorStop = 30;
+ throw iRc;
+ }
+ if(( iRc = exp->GetBoolResult( bFound )) != XB_NO_ERROR ){
+ iErrorStop = 40;
+ throw iRc;
+ }
+ if( !bFound ){
+ if(( iRc = dbf->GetNextRecord( iOption )) != XB_NO_ERROR ){
+ if( iRc == XB_EOF ){
+ return iRc;
+ } else {
+ iErrorStop = 50;
+ throw iRc;
+ }
+ }
+ }
+ }
+ lCurQryCnt++;
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbFilter::GetFirstRecord() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( xbase->GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+/************************************************************************/
+xbInt16 xbFilter::GetNextRecord( xbInt16 iOption ){
+
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+ try{
+
+ if( !exp ){
+ iErrorStop = 10;
+ throw iRc;
+ }
+
+ #ifdef XB_INDEX_SUPPORT
+ if( dbf->GetCurIx() && dbf->GetCurTag())
+ return GetNextRecordIx( iOption );
+ #endif
+
+ 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 = 20;
+ throw iRc;
+ }
+ }
+
+ xbBool bFound = xbFalse;
+ while( !bFound && iRc == XB_NO_ERROR ){
+ if(( iRc = exp->ProcessExpression()) != XB_NO_ERROR ){
+ iErrorStop = 30;
+ throw iRc;
+ }
+ if(( iRc = exp->GetBoolResult( bFound )) != XB_NO_ERROR ){
+ iErrorStop = 40;
+ throw iRc;
+ }
+ if( !bFound ){
+ if(( iRc = dbf->GetNextRecord( iOption )) != XB_NO_ERROR ){
+ if( iRc == XB_EOF ){
+ return iRc;
+ } else {
+ iErrorStop = 50;
+ throw iRc;
+ }
+ }
+ }
+ }
+ lCurQryCnt++;
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbFilter::GetNextRecord() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( xbase->GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+/************************************************************************/
+xbInt16 xbFilter::GetPrevRecord( xbInt16 iOption ){
+
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+ try{
+
+ if( !exp ){
+ iErrorStop = 10;
+ throw iRc;
+ }
+
+ #ifdef XB_INDEX_SUPPORT
+ //if( pIx && vpTag )
+ if( dbf->GetCurIx() && dbf->GetCurTag())
+ return GetPrevRecordIx( iOption );
+ #endif
+
+ 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 = 20;
+ throw iRc;
+ }
+ }
+
+ xbBool bFound = xbFalse;
+ while( !bFound && iRc == XB_NO_ERROR ){
+ if(( iRc = exp->ProcessExpression()) != XB_NO_ERROR ){
+ iErrorStop = 30;
+ throw iRc;
+ }
+ if(( iRc = exp->GetBoolResult( bFound )) != XB_NO_ERROR ){
+ iErrorStop = 40;
+ throw iRc;
+ }
+ if( !bFound ){
+ if(( iRc = dbf->GetPrevRecord( iOption )) != XB_NO_ERROR ){
+ if( iRc == XB_BOF ){
+ return iRc;
+ } else {
+ iErrorStop = 50;
+ throw iRc;
+ }
+ }
+ }
+ }
+ lCurQryCnt--;
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbFilter::GetPrevRecord() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( xbase->GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+/************************************************************************/
+xbInt16 xbFilter::GetLastRecord( xbInt16 iOption ){
+
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+ try{
+
+ if( !exp ){
+ iErrorStop = 10;
+ throw iRc;
+ }
+
+ #ifdef XB_INDEX_SUPPORT
+ if( dbf->GetCurIx() && dbf->GetCurTag())
+ return GetLastRecordIx( iOption );
+ #endif
+
+ lCurQryCnt = 0;
+ if(( iRc = dbf->GetLastRecord( iOption )) != XB_NO_ERROR ){
+ if( iRc == XB_EOF )
+ return iRc;
+ else{
+ iErrorStop = 20;
+ throw iRc;
+ }
+ }
+
+ xbBool bFound = xbFalse;
+ while( !bFound && iRc == XB_NO_ERROR ){
+ if(( iRc = exp->ProcessExpression()) != XB_NO_ERROR ){
+ iErrorStop = 30;
+ throw iRc;
+ }
+ if(( iRc = exp->GetBoolResult( bFound )) != XB_NO_ERROR ){
+ iErrorStop = 40;
+ throw iRc;
+ }
+ if( !bFound ){
+ if(( iRc = dbf->GetPrevRecord( iOption )) != XB_NO_ERROR ){
+ if( iRc == XB_BOF ){
+ return iRc;
+ } else {
+ iErrorStop = 50;
+ throw iRc;
+ }
+ }
+ }
+ }
+ lCurQryCnt--;
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbFilter::GetLastRecord() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( xbase->GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+
+/************************************************************************/
+
+#ifdef XB_INDEX_SUPPORT
+
+//void xbFilter::Set( xbIx *pIx, void *vpTag ) {
+// this->pIx = pIx;
+// this->vpTag = vpTag;
+//}
+
+/************************************************************************/
+xbInt16 xbFilter::GetFirstRecordIx( xbInt16 iOption ) {
+
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+ try{
+
+ if( !exp ){
+ iErrorStop = 10;
+ 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 = 20;
+ throw iRc;
+ }
+ }
+
+ xbBool bFound = xbFalse;
+ while( !bFound && iRc == XB_NO_ERROR ){
+ if(( iRc = exp->ProcessExpression()) != XB_NO_ERROR ){
+ iErrorStop = 30;
+ throw iRc;
+ }
+ if(( iRc = exp->GetBoolResult( bFound )) != XB_NO_ERROR ){
+ iErrorStop = 40;
+ 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 = 50;
+ throw iRc;
+ }
+ }
+ }
+ }
+ lCurQryCnt++;
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbFilter::GetFirstRecordIx() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( xbase->GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+/************************************************************************/
+xbInt16 xbFilter::GetNextRecordIx( xbInt16 iOption ){
+
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+ try{
+
+ if( !exp ){
+ iErrorStop = 10;
+ 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 = 20;
+ throw iRc;
+ }
+ }
+
+ xbBool bFound = xbFalse;
+ while( !bFound && iRc == XB_NO_ERROR ){
+ if(( iRc = exp->ProcessExpression()) != XB_NO_ERROR ){
+ iErrorStop = 30;
+ throw iRc;
+ }
+ if(( iRc = exp->GetBoolResult( bFound )) != XB_NO_ERROR ){
+ iErrorStop = 40;
+ 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 = 50;
+ throw iRc;
+ }
+ }
+ }
+ }
+ lCurQryCnt++;
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbFilter::GetNextRecordIx() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( xbase->GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+/************************************************************************/
+xbInt16 xbFilter::GetPrevRecordIx( xbInt16 iOption ){
+
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+ try{
+
+ if( !exp ){
+ iErrorStop = 10;
+ 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 = 20;
+ throw iRc;
+ }
+ }
+
+ xbBool bFound = xbFalse;
+ while( !bFound && iRc == XB_NO_ERROR ){
+ if(( iRc = exp->ProcessExpression()) != XB_NO_ERROR ){
+ iErrorStop = 30;
+ throw iRc;
+ }
+ if(( iRc = exp->GetBoolResult( bFound )) != XB_NO_ERROR ){
+ iErrorStop = 40;
+ 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 = 50;
+ throw iRc;
+ }
+ }
+ }
+ }
+ lCurQryCnt--;
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbFilter::GetPrevRecordIx() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( xbase->GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+/************************************************************************/
+xbInt16 xbFilter::GetLastRecordIx( xbInt16 iOption ){
+
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+ try{
+
+ if( !exp ){
+ iErrorStop = 10;
+ throw iRc;
+ }
+
+ lCurQryCnt = 0;
+ if(( iRc = dbf->GetCurIx()->GetLastKey( dbf->GetCurTag(), iOption )) != XB_NO_ERROR ){
+ if( iRc == XB_EOF )
+ return iRc;
+ else{
+ iErrorStop = 20;
+ throw iRc;
+ }
+ }
+
+ xbBool bFound = xbFalse;
+ while( !bFound && iRc == XB_NO_ERROR ){
+ if(( iRc = exp->ProcessExpression()) != XB_NO_ERROR ){
+ iErrorStop = 30;
+ throw iRc;
+ }
+ if(( iRc = exp->GetBoolResult( bFound )) != XB_NO_ERROR ){
+ iErrorStop = 40;
+ throw iRc;
+ }
+ if( !bFound ){
+ if(( iRc = dbf->GetCurIx()->GetPrevKey( dbf->GetCurTag(), iOption )) != XB_NO_ERROR ){
+ if( iRc == XB_BOF ){
+ return iRc;
+ } else {
+ iErrorStop = 50;
+ throw iRc;
+ }
+ }
+ }
+ }
+ lCurQryCnt--;
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbFilter::GetLastRecordIx() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( xbase->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..a038611
--- /dev/null
+++ b/src/core/xbfuncs.cpp
@@ -0,0 +1,850 @@
+/* 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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+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<br>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.<br>xbFalse if record is not deleted.
+ \returns XB_NO_ERROR<br>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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+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.<br>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.<br>
+ 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.<br>
+ 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.<br>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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+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.<br>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.<br>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.<br>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.<br>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..40c1563
--- /dev/null
+++ b/src/core/xbixbase.cpp
@@ -0,0 +1,792 @@
+/* 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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbIx::AddKeys( xbUInt32 ulRecNo ){
+
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+ xbInt16 i = 0;
+ xbInt16 iKeySts;
+// std::cout << "xbIx::AddKeys\n";
+
+ try{
+ void * vpTag;
+ xbInt16 iTagCount = GetTagCount();
+
+ for( i = 0; i < iTagCount; i++ ){
+ vpTag = GetTag( i );
+
+ iKeySts = GetKeySts( vpTag );
+// std::cout << "AddKeys() KeySts = " << iKeySts << "\n";
+
+ 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<br>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.<br>
+ 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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+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 = 10;
+ 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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbIx::Close(){
+
+ xbInt16 iRc = 0;
+ xbInt16 iErrorStop = 0;
+
+ try{
+ if(( iRc = xbFclose()) != XB_NO_ERROR ){
+ iErrorStop = 20;
+ 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.<br>
+ N - Numeric BCD compare.<br>
+ D - Numeric compare.<br>
+ F - Numeric compare.<br>
+ \param v1 Left compare.<br>v2 - Right Compare.
+ \param iSearchKeyLen Length of key compare.
+ \returns 1 - Left operand is greater then right operand.<br>
+ 0 - Left operand is equal to right operand.<br>
+ -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.<br>
+ 1 Append Mode, Create key for an append, only use rec buf 0, set updated switch.<br>
+ 2 Update Mode, Create old version and new version keys, check if different, set update switch appropriately.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+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 = 10;
+ 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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+//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 = 10;
+ 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<br>
+ 1 = Syslog<br>
+ 2 = Both<br>
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+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.<br>
+ xbFalse - Don't retrieve record, check for key existence only.
+ \returns XB_NO_ERROR - Key found.<br>
+ XB_NOT_FOUND - Key not found.<br>
+ <a href="xbretcod_8h.html">Return Codes</a>
+*/
+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.<br>
+ xbFalse - Don't retrieve record, check for key existence only.
+ \returns XB_NO_ERROR - Key found.<br>
+ XB_NOT_FOUND - Key not found.<br>
+ <a href="xbretcod_8h.html">Return Codes</a>
+*/
+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.<br>
+ xbFalse - Don't retrieve record, check for key existence only.
+ \returns XB_NO_ERROR - Key found.<br>
+ XB_NOT_FOUND - Key not found.<br>
+ <a href="xbretcod_8h.html">Return Codes</a>
+*/
+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.<br>
+ xbFalse - Don't retrieve record, check for key existence only.
+
+ \returns XB_NO_ERROR - Key found.<br>
+ XB_NOT_FOUND - Key not found.<br>
+ <a href="xbretcod_8h.html">Return Codes</a>
+*/
+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.<br>
+ xbFalse - Don't retrieve record, check for key existence only.
+ \returns XB_NO_ERROR - Key found.<br>
+ XB_NOT_FOUND - Key not found.<br>
+ <a href="xbretcod_8h.html">Return Codes</a>
+*/
+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<br>
+ 1 = Node is read into new xbIxNode, then added to the node chain, and sets CurNode with new node<br>
+ 2 = Node is read into new xbIxNode, not added to the node chain<br>
+ CurNode points to new node<br>
+ \param ulAddlBuf Additional buffer size added to memory
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+
+*/
+
+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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbIx::GetFirstKey( void *vpTag ){
+ return GetFirstKey( vpTag, 0 );
+}
+/***********************************************************************/
+//! @brief Get the last key for the current tag.
+/*!
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbIx::GetLastKey( void *vpTag ){
+ return GetLastKey( 0, vpTag, 0 );
+}
+
+/***********************************************************************/
+//! @brief Get the file lock status.
+/*!
+ \returns xbTrue - Index file is locked.<br>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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbIx::GetNextKey(){
+ return GetNextKey( vpCurTag, 0 );
+}
+/***********************************************************************/
+//! @brief Get the next key for the given tag.
+/*!
+ \param vpTag Tag for next key operation.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbIx::GetNextKey( void *vpTag ){
+ return GetNextKey( vpTag, 0 );
+}
+/***********************************************************************/
+//! @brief Get the prev key for the current tag.
+/*!
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbIx::GetPrevKey(){
+ return GetPrevKey( vpCurTag, 0 );
+}
+/***********************************************************************/
+//! @brief Get the previous key for the given tag.
+/*!
+ \param vpTag Tag for previous key operation.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+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 = 10;
+ iRc = XB_FILE_NOT_FOUND;
+ throw iRc;
+ }
+ /* open the file */
+ if(( iRc = xbFopen( dbf->GetOpenMode(), dbf->GetShareMode())) != XB_NO_ERROR ){
+ iErrorStop = 20;
+ throw iRc;
+ }
+ if(( iRc = ReadHeadBlock()) != XB_NO_ERROR ){
+ iErrorStop = 40;
+ 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.<br>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..53d7053
--- /dev/null
+++ b/src/core/xbixmdx.cpp
@@ -0,0 +1,5067 @@
+/* xbixmdx.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
+
+
+ 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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+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\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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+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;
+ memcpy( pTrg, pLastKey, (size_t) npTag->iKeyLen );
+
+ // set the right node number
+ pTrg+= (npTag->iKeyLen);
+ 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<br>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<br>
+ 0 = stdout<br>
+ 1 = Syslog<br>
+ 2 = Both<br>
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+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;
+ xbInt16 iAutoLock;
+ xbBool bDescending = npTag->cKeyFmt2 & 0x08;
+
+ try{
+// xbase->WriteLogMessage( "xbIxMdx::CheckTagIntegrity()", iOpt );
+
+ #ifdef XB_LOCKING_SUPPORT
+ iAutoLock = dbf->GetAutoLock();
+ if( iAutoLock && !dbf->GetTableLocked() ){
+ if(( iRc = dbf->LockTable( XB_LOCK )) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+ }
+ #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) 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 );
+ }
+
+ #ifdef XB_LOCKING_SUPPORT
+ if( iAutoLock && dbf->GetTableLocked() ){
+ if(( iRc = dbf->LockTable( XB_UNLOCK )) != XB_NO_ERROR ){
+ iErrorStop = 200;
+ throw iRc;
+ }
+ }
+ #endif
+
+ 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( "xbIxNdx::CheckTagIntegrity() Missing index entry for record [%d]", ulThisRecNo );
+ xbase->WriteLogMessage( sMsg, iOpt );
+ }
+ }
+ 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<br>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.<br>xbFalse - Not unique.
+ \param iOverLay xbTrue - Overlay if file already exists.<br>xbFalse - Don't overlay.
+ \param vpTag Output from method Pointer to vptag pointer.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+
+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;
+
+ tte->iKeysPerBlock = (xbInt16) (GetBlockSize() - 12) / tte->iKeyItemLen;
+ 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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+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<br>
+ \returns <a href="xbretcod_8h.html">Return Codes</a><br>
+ 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<br>
+ 0 = stdout<br>
+ 1 = Syslog<br>
+ 2 = Both<br>
+ \param ulBlockNo Block number to dump
+ \param mpTag Index tag pointer
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+
+*/
+
+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<br>
+ 0 = stdout<br>
+ 1 = Syslog<br>
+ 2 = Both<br>
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+
+*/
+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<br>
+ A block is one or more pages<br>
+ The default mdx block size is 2 pages, or 1024 bytes<br>
+ The first four pages or header pages<br>
+
+ \param iOpt Output message destination<br>
+ 0 = stdout<br>
+ 1 = Syslog<br>
+ 2 = Both<br>
+ \param vpTag Index tag pointer, defaults to all tags if null.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+
+*/
+
+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<xbUInt32> ll;
+ xbLinkListNode<xbUInt32> * 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<br>
+ 0 = stdout<br>
+ 1 = Syslog<br>
+ 2 = Both<br>
+ \param iFmtOpt Output Format<br>
+ 0, 1 = Header info only<br>
+ 2 = Tag info<br>
+ 3 = Header && Tag info<br>
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+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.<br>
+ xbFalse - Don't retrieve record.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+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.<br>
+ XB_DEL_KEY Key deleted.<br>
+ XB_ADD_KEY Key added.<br>
+ 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.<br>
+ xbFalse - Don't retrieve record.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+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.<br>
+ xbFalse - Don't retrieve record.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+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.<br>
+ xbFalse - Don't retrieve record.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+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.<br>
+ xbFalse - Don't retrieve record.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbIxMdx::HarvestTagNodes( xbMdxTag *mpTag, xbBool bHarvestRoot ){
+
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+
+ xbUInt32 ulBlkNo;
+ xbLinkListOrd<xbUInt32> ll;
+ xbLinkListNode<xbUInt32> * 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.<br>
+ 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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+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++;
+ 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.<br>
+ 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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+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.<br>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.<br>
+ 1 - Read in only dynamic section of block<br>
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+
+*/
+
+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 = 40;
+ throw XB_NO_MEMORY;
+ }
+
+ if(( iRc = xbIxMdx::LoadTagTable()) != XB_NO_ERROR ){
+ iErrorStop = 50;
+ 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.<br>
+ NULL - Reindex all tags<br>
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+
+ 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
+ xbInt16 iAutoLock = 0;
+ #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
+ iAutoLock = dbf->GetAutoLock();
+ if( iAutoLock && !dbf->GetTableLocked() ){
+ if(( iRc = dbf->LockTable( XB_LOCK )) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+ }
+ #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++ );
+ }
+ }
+
+ // unlock as necessary
+ #ifdef XB_LOCKING_SUPPORT
+ if( iAutoLock && dbf->GetTableLocked() ){
+ if(( iRc = dbf->LockTable( XB_UNLOCK )) != XB_NO_ERROR ){
+ iErrorStop = 190;
+ throw iRc;
+ }
+ }
+ #endif
+
+
+ }
+ 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 ));
+
+ // unlock as necessary
+ #ifdef XB_LOCKING_SUPPORT
+ if( iAutoLock && dbf->GetTableLocked() ){
+ 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.<br>
+ R - Right child.<br>
+ 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.<br>
+ This behaves differently than V7 Dbase. V7 does not balance the nodes.<br>
+ 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.<br><br>
+
+ Possible performance improvement options.<br>
+ Two modes when splitting:<br>
+ a) Split nodes in the middle - good for random access applications<br>
+ b) Split off right node with only one key - good for applications with
+ expectation of ascending keys added moving forward.<br>
+
+ 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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+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( "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.<br>
+
+ Possible performance improvement options.<br>
+ Two modes when splitting:<br>
+ a) Split nodes in the middle - good for random access applications<br>
+ b) Split off right node with only one key - good for applications with
+ expectation of ascending keys added moving forward.<br>
+
+ 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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+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<br>
+ 2 - Write serial number from memory to disk<br>
+ 3 - Read serial number off disk, increment, write updated number to disk<br>
+ mpTag - Pointer to tag for serial number update
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+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.<br>
+ D - Delete a key.<br>
+ R - Revise a key.<br>
+ \param vpTg - Pointer to tag.<br>
+ \param ulRecNo - Record number association with the action.<br>
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+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.<br>
+ 1 - Bytes 28 through 46, used when adding or deleting a tag.<br>
+ 2 - Bytes 32 through 46, used after updating keys in the file.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+
+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
new file mode 100755
index 0000000..097bd22
--- /dev/null
+++ b/src/core/xbixndx.cpp
@@ -0,0 +1,2840 @@
+/* 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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+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 = 200;
+ 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 = 100;
+ iRc = XB_NO_MEMORY;
+ throw iRc;
+ }
+ if(( iRc = SplitNodeL( npTag, npTag->npCurNode, npRightNode, npTag->npCurNode->iCurKeyNo, npTag->cpKeyBuf, ulRecNo )) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ 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 = 200;
+ iRc = XB_NO_MEMORY;
+ throw iRc;
+ }
+ if(( iRc = SplitNodeI( npTag, npParent, npRightNode, npParent->iCurKeyNo, ulTempBlockNo )) != XB_NO_ERROR ){
+ iErrorStop = 210;
+ 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 = 300;
+ 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 = 400;
+ throw iRc;
+ }
+ npRightNode = FreeNodeChain( npRightNode );
+ }
+ }
+
+ // update the header
+ if(( iRc = WriteHeadBlock( iHeadNodeUpdateOpt )) != XB_NO_ERROR ){
+ iErrorStop = 130;
+ 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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+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<br>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<br>
+ 0 = stdout<br>
+ 1 = Syslog<br>
+ 2 = Both<br>
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+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;
+ xbInt16 iAutoLock = xbFalse;
+
+ xbNdxTag * npTag;
+ vpTag ? npTag = (xbNdxTag *) vpTag : npTag = ndxTag;
+
+ try{
+// xbase->WriteLogMessage( "xbIxNdx::CheckTagIntegrity()", iOpt );
+
+ #ifdef XB_LOCKING_SUPPORT
+ iAutoLock = dbf->GetAutoLock();
+ if( iAutoLock && !dbf->GetTableLocked() ){
+ if(( iRc = dbf->LockTable( XB_LOCK )) != XB_NO_ERROR ){
+ iErrorStop = 10;
+ throw iRc;
+ }
+ }
+ #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 = 100;
+ iRc = XB_INVALID_INDEX;
+ throw iRc;
+ }
+
+ ulThisRecNo = 0;
+ if(( iRc3 = GetDbfPtr( vpTag, npTag->npCurNode->iCurKeyNo, npTag->npCurNode, ulThisRecNo )) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ throw iRc3;
+ }
+
+ if( iRc2 == 0 && (ulThisRecNo <= ulPrevRecNo )){
+ sMsg.Sprintf( "Dbf record number sequence error at key number [%ld].", iOpt );
+ xbase->WriteLogMessage( sMsg, iOpt );
+ iErrorStop = 120;
+ 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 = 130;
+ 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 = 140;
+ 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 = 150;
+ throw iRc;
+ }
+ if(( iRc = FindKeyForCurRec( vpTag )) != XB_NO_ERROR ){
+ ulThisRecNo = j;
+ iErrorStop = 160;
+ throw iRc;
+ }
+ }
+ sMsg.Sprintf( "CheckTagIntegrity() Index entry count [%ld] matches dbf record count [%ld]", ulIxCnt, ulDbfCnt );
+ xbase->WriteLogMessage( sMsg, iOpt );
+ }
+
+ #ifdef XB_LOCKING_SUPPORT
+ if( iAutoLock && !dbf->GetTableLocked() ){
+ if(( iRc = dbf->LockTable( XB_UNLOCK )) != XB_NO_ERROR ){
+ iErrorStop = 200;
+ throw iRc;
+ }
+ }
+ #endif
+
+
+ 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 == 160 ){
+ sMsg.Sprintf( "xbIxNdx::CheckTagIntegrity() Missing index entry for record [%d]", ulThisRecNo );
+ xbase->WriteLogMessage( sMsg, iOpt );
+ }
+ }
+ 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.<br>
+ 1 = Append Mode, Create key for an append, only use rec buf 0, set updated switch.<br>
+ 2 = Update Mode, Create old version and new version keys, check if different, set update switch appropriately.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+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 = 10;
+ 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 = 20;
+ 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.<br>xbFalse - Not unique.
+ \param iOverLay xbTrue - Overlay if file already exists.<br>xbFalse - Don't overlay.
+ \param vpTag Output from method Pointer to vptag pointer.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+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 = 10;
+ throw iRc;
+ }
+ if(( iRc = xbFclose()) != XB_NO_ERROR ){
+ iErrorStop = 20;
+ 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 = 30;
+ throw iRc;
+ }
+
+ //set up the key expression
+ npTag->exp = new xbExp( dbf->GetXbasePtr());
+ if(( iRc = npTag->exp->ParseExpression( dbf, sKey )) != XB_NO_ERROR ){
+ iErrorStop = 40;
+ 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 = 50;
+ 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 = 60;
+ 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 = 70;
+ throw iRc;
+ }
+
+ //write out block binary zeroes
+ char buf[512];
+ memset( buf, 0x00, 512 );
+ if(( iRc = xbFwrite( buf, 1, 512 )) != XB_NO_ERROR ){
+ iErrorStop = 80;
+ 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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+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<br>
+ 0 = stdout<br>
+ 1 = Syslog<br>
+ 2 = Both<br>
+ \param vpTag - Not required for single tag NDX files.
+ \returns void
+*/
+
+//xbInt16 xbIxNdx::DumpTagBlocks( xbInt16 iOpt, void *vpTag ){
+
+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 = 10;
+ 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 = 20;
+ 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<br>
+ 0 = stdout<br>
+ 1 = Syslog<br>
+ 2 = Both<br>
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+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<br>
+ 0 = stdout<br>
+ 1 = Syslog<br>
+ 2 = Both<br>
+ \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<br>
+ 0 = stdout<br>
+ 1 = Syslog<br>
+ 2 = Both<br>
+ \returns XB_NVALID_OBJECT<br>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.<br>
+ xbFalse - Don't retrieve record, check for key existence only.
+ \returns XB_NO_ERROR - Key found.<br>
+ XB_NOT_FOUND - Key not found.<br>
+ <a href="xbretcod_8h.html">Return Codes</a>
+*/
+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 = 10;
+ throw iRc;
+ }
+ } else {
+ if(( iRc = dbf->Abort()) != XB_NO_ERROR ){
+ iErrorStop = 20;
+ 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 = 30;
+ throw iRc;
+ }
+ if( npTag->tNodeChainTs < tFileTs ){
+ npTag->npNodeChain = FreeNodeChain( npTag->npNodeChain );
+ npTag->npCurNode = NULL;
+ if(( iRc = ReadHeadBlock( 1 )) != XB_NO_ERROR ){
+ iErrorStop = 40;
+ throw iRc;
+ }
+ if(( iRc = GetBlock( npTag, (npTag->ulRootBlock ), 1, (xbUInt32) npTag->iKeyItemLen )) != XB_NO_ERROR ){
+ iErrorStop = 50;
+ 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 = 60;
+ 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 = 80;
+ throw iRc;
+ }
+
+ if(( iRc = GetBlock( npTag, ulKeyPtr, 1, (xbUInt32) npTag->iKeyItemLen )) != XB_NO_ERROR ){
+ iErrorStop = 90;
+ 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 = 100;
+ throw iRc;
+ }
+ if(( iRc = dbf->GetRecord( ulKeyPtr )) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ 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.<br>
+ <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+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 = 10;
+ 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 = 20;
+ 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 = 30;
+ throw iRc;
+ }
+ if( ulIxRecNo == ulDbfRecNo )
+ bCurRecsMatch = true;
+
+ xbInt16 iCompRc;
+ while( !bCurRecsMatch && bKeysMatch ){
+
+ if(( iRc = GetNextKey( vpTag, 0 )) != XB_NO_ERROR ){
+ iErrorStop = 40;
+ 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 = 50;
+ 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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+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 = 10;
+ 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.<br>
+ xbFalse - Don't retrieve record.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+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 = 10;
+ throw iRc;
+ }
+ // lRootBlock is now available
+ if(( iRc = GetBlock( npTag, npTag->ulRootBlock, 1, (xbUInt32) npTag->iKeyItemLen )) != XB_NO_ERROR ){
+ iErrorStop = 20;
+ 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 = 30;
+ throw iRc;
+ }
+ if(( iRc = GetBlock( npTag, ulKeyPtr, 1, (xbUInt32) npTag->iKeyItemLen )) != XB_NO_ERROR ){
+ iErrorStop = 40;
+ throw iRc;
+ }
+ }
+ if( iRetrieveSw ){
+ if(( iRc = GetDbfPtr( npTag, 0, npTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){
+ iErrorStop = 50;
+ throw iRc;
+ }
+ if(( iRc = dbf->GetRecord( ulKeyPtr )) != XB_NO_ERROR ){
+ iErrorStop = 60;
+ 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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+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 = 10;
+ 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.<br>
+ XB_DEL_KEY Key deleted.<br>
+ XB_ADD_KEY Key added.<br>
+ 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.<br>
+ xbFalse - Don't retrieve record.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+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.<br>
+ xbFalse - Don't retrieve record.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+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 = 10;
+ throw iRc;
+ }
+ // lRootBlock is now available
+ if(( iRc = GetBlock( npTag, npTag->ulRootBlock, 1, (xbUInt32) npTag->iKeyItemLen )) != XB_NO_ERROR ){
+ iErrorStop = 20;
+ throw iRc;
+ }
+ } else {
+ if(( iRc = GetBlock( npTag, ulNodeNo, 1, (xbUInt32) npTag->iKeyItemLen )) != XB_NO_ERROR ){
+ iErrorStop = 20;
+ 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 = 40;
+ throw iRc;
+ }
+ if(( iRc = GetBlock( npTag, ulKeyPtr, 1, (xbUInt32) npTag->iKeyItemLen )) != XB_NO_ERROR ){
+ iErrorStop = 50;
+ 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 = 60;
+ throw iRc;
+ }
+ if(( iRc = dbf->GetRecord( ulKeyPtr )) != XB_NO_ERROR ){
+ iErrorStop = 70;
+ 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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+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.<br>
+ xbFalse - Don't retrieve record.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+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 = 20;
+ throw iRc;
+ }
+ if( iRetrieveSw ){
+ if(( iRc = dbf->GetRecord( ulKeyPtr )) != XB_NO_ERROR ){
+ iErrorStop = 30;
+ 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 = 40;
+ throw iRc;
+ }
+
+ if(( iRc = GetBlock( npTag, ulKeyPtr, 1, (xbUInt32) npTag->iKeyItemLen )) != XB_NO_ERROR ){
+ iErrorStop = 50;
+ 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 = 60;
+ throw iRc;
+ }
+ if(( iRc = GetBlock( npTag, ulKeyPtr, 1, (xbUInt32) npTag->iKeyItemLen )) != XB_NO_ERROR ){
+ iErrorStop = 70;
+ throw iRc;
+ }
+ }
+ if( iRetrieveSw ){
+ if(( iRc = GetDbfPtr( npTag, 0, npTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){
+ iErrorStop = 80;
+ throw iRc;
+ }
+ if(( iRc = dbf->GetRecord( ulKeyPtr )) != XB_NO_ERROR ){
+ iErrorStop = 90;
+ 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.<br>
+ xbFalse - Don't retrieve record.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+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 = 10;
+ throw iRc;
+ }
+ if( iRetrieveSw ){
+ if(( iRc = dbf->GetRecord( ulKeyPtr )) != XB_NO_ERROR ){
+ iErrorStop = 20;
+ 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 = 30;
+ throw iRc;
+ }
+
+ if(( iRc = GetBlock( npTag, ulKeyPtr, 1, (xbUInt32) npTag->iKeyItemLen )) != XB_NO_ERROR ){
+ iErrorStop = 40;
+ 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 = 60;
+ throw iRc;
+ }
+ if(( iRc = GetBlock( npTag, ulKeyPtr, 1, (xbUInt32) npTag->iKeyItemLen )) != XB_NO_ERROR ){
+ iErrorStop = 70;
+ 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 = 80;
+ throw iRc;
+ }
+ if(( iRc = dbf->GetRecord( ulKeyPtr )) != XB_NO_ERROR ){
+ iErrorStop = 90;
+ 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.<br> 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.<br>
+ 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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+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 = 10;
+ 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.<br>
+ 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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+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 = 10;
+ 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.<br> 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.<br> 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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+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 = 10;
+ 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 = 20;
+ 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 = 30;
+ 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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+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 = 20;
+ throw iRc;
+ }
+ xbUInt32 ulIxRecNo;
+ if(( iRc = GetDbfPtr( npTag, npTag->npCurNode->iCurKeyNo, npTag->npCurNode, ulIxRecNo )) != XB_NO_ERROR ){
+ iErrorStop = 30;
+ throw iRc;
+ }
+ if( ulIxRecNo == dbf->GetCurRecNo())
+ return XB_NO_ERROR;
+ if( GetUnique() == 1 ){
+ iErrorStop = 40;
+ 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 = 50;
+ 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 = 60;
+ 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.<br>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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbIxNdx::ReadHeadBlock( xbInt16 iOpt = 0 ) {
+
+ xbInt16 iRc = 0;
+ xbInt16 iErrorStop = 0;
+ try{
+ if( !FileIsOpen()){
+ iRc = XB_NOT_OPEN;
+ iErrorStop = 10;
+ throw iRc;
+ }
+ xbInt16 iLen;
+ iOpt == 0 ? iLen = 512 : iLen = 21;
+
+ if(( iRc = ReadBlock( (xbUInt32) 0, (size_t) iLen, cNodeBuf )) != XB_NO_ERROR ){
+ iErrorStop = 20;
+ 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 = 30;
+ 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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+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 = 10;
+ throw iRc;
+ }
+
+ xbUInt32 ulRecCnt = 0;
+ if(( iRc = dbf->GetRecordCnt( ulRecCnt )) != XB_NO_ERROR ){
+ iErrorStop = 20;
+ throw iRc;
+ }
+
+ for( xbUInt32 l = 1; l <= ulRecCnt; l++ ){
+ if(( iRc = dbf->GetRecord( l )) != XB_NO_ERROR ){
+ iErrorStop = 30;
+ throw iRc;
+ }
+
+ if(( iRc = CreateKey( vpTag2, 1 )) != XB_NO_ERROR ){
+ iErrorStop = 40;
+ throw iRc;
+ }
+
+ if( iUnique ){
+ iRc = CheckForDupKey( vpTag2 );
+ if( iRc != 0 ){
+ if( iRc < 0 ){
+ iErrorStop = 50;
+ throw iRc;
+ }
+ return XB_KEY_NOT_UNIQUE;
+ }
+ }
+
+ if(( iRc = AddKey( vpTag2, l )) != XB_NO_ERROR ){
+ iErrorStop = 60;
+ 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.<br>
+ This behaves differently than V7 Dbase. V7 does not balance the nodes.<br>
+ 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.<br><br>
+
+ Possible performance improvement options.<br>
+ Two modes when splitting:<br>
+ a) Split nodes in the middle - good for random access applications<br>
+ b) Split off right node with only one key - good for applications with
+ expectation of ascending keys added moving forward.<br>
+
+ 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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+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 = 100;
+ throw iRc;
+ }
+ // write out the block
+ if(( iRc = WriteBlock( npRight->ulBlockNo, GetBlockSize(), npRight->cpBlockData )) != XB_NO_ERROR ){
+ iErrorStop = 200;
+ 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.<br>
+ This behaves differently than V7 Dbase. V7 does not balance the nodes.<br>
+ 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.<br><br>
+
+ Possible performance improvement options.<br>
+ Two modes when splitting:<br>
+ a) Split nodes in the middle - good for random access applications<br>
+ b) Split off right node with only one key - good for applications with
+ expectation of ascending keys added moving forward.<br>
+
+ 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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+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 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::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.<br>
+ D - Delete a key.<br>
+ R - Revise a key.<br>
+ \param vpTg - Pointer to tag.<br>
+ \param ulRecNo - Record number association with the action.<br>
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+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' ){
+// std::cout << "UpdateTagKey add\n";
+ if(( iRc = AddKey( vpTag, ulRecNo )) != XB_NO_ERROR ){
+ iErrorStop = 200;
+ throw iRc;
+ }
+ }
+
+// if( ulTagSizeSave != mpTag->ulTagSize ){
+// std::cout << "UpdateTagKey - tag size was updated need to do something here\n";
+// }
+ }
+ 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.<br>
+ 1 - Update root block, number of blocks and seq number.<br>
+ 2 - Update sequence number only<br>
+ \returns <a href="xbretcod_8h.html">
+*/
+
+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 = 200;
+ 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 = 300;
+ 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
new file mode 100755
index 0000000..ea7c9d6
--- /dev/null
+++ b/src/core/xblog.cpp
@@ -0,0 +1,208 @@
+/* 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 <time.h>
+
+#ifdef XB_LOGGING_SUPPORT
+
+namespace xb{
+
+/******************************************************************************/
+//! @brief Constructor.
+xbLog::xbLog() : xbFile( NULL ){
+
+ SetDirectory( GetDefaultLogDirectory());
+ SetFileName ( GetDefaultLogFileName());
+
+ 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.<br>xbFalse - Logging turned off.
+*/
+xbBool xbLog::LogGetStatus(){
+ return bLoggingStatus;
+}
+/******************************************************************************/
+//! @brief Close the logfile.
+/*!
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+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.<br>xbFalse - Turn logging off.
+ \returns void
+*/
+void xbLog::LogSetStatus( xbBool bStatus ){
+ bLoggingStatus = bStatus;
+}
+/******************************************************************************/
+//! @brief Open the logfile.
+/*!
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+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.<br>
+ 1 - Write to stdout.<br>
+ 2 - Write to both logfile and stdout.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+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..02bdbbf
--- /dev/null
+++ b/src/core/xbmemo.cpp
@@ -0,0 +1,219 @@
+/* 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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbMemo::CloseMemoFile(){
+
+ if( mbb ){
+ free( mbb );
+ mbb = NULL;
+ }
+ return xbFclose();
+}
+
+
+/***********************************************************************/
+//! @brief Get memo file type.
+/*!
+ \returns 3 - Version 3 memo file.<br>
+ 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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbMemo::GetHdrNextBlock( xbUInt32 & ulBlockNo ){
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+ try{
+ if(( iRc = ReadDbtHeader( 0 )) != XB_NO_ERROR ){
+ iErrorStop = 10;
+ 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<br>XB_UNLOCK
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+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 = 120;
+ 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 = 140;
+ throw iRc;
+ }
+ } else {
+ bFileLocked = xbFalse;
+ }
+ }
+ } else {
+ iErrorStop = 300;
+ 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.<br>
+ xbFalse - Memo file is not locked.
+*/
+xbBool xbMemo::GetMemoLocked() const {
+ return bFileLocked;
+}
+#endif
+
+/***********************************************************************/
+//! @brief Update Next Node number in file header
+/*!
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+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 = 10;
+ throw iRc;
+ }
+ if(( iRc = xbFwrite( &buf, 4, 1 )) != XB_NO_ERROR ){
+ iErrorStop = 20;
+ 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..b5494fb
--- /dev/null
+++ b/src/core/xbmemo3.cpp
@@ -0,0 +1,590 @@
+/* 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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+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;
+}
+
+//! @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;
+}
+#endif // XB_DEBUG_SUPPORT
+/***********************************************************************/
+//! @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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+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 = 10;
+ throw rc;
+ }
+ if( ulBlockNo == 0L ){
+ sMemoData = "";
+ return XB_NO_ERROR;
+ }
+ spp = NULL;
+ while( !bDone ){
+ if(( rc = ReadBlock( ulBlockNo++, GetBlockSize(), mbb )) != XB_NO_ERROR ){
+ iErrorStop = 20;
+ 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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+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 = 10;
+ 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 = 1;
+ 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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbMemoDbt3::PackMemo( void (*memoStatusFunc ) ( xbUInt32 ulItemNum, xbUInt32 ulNumItems ))
+{
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+ char * cBlock = NULL;
+
+ #ifdef XB_LOCKING_SUPPORT
+ xbInt16 iAutoLock = dbf->GetAutoLock();
+ xbBool bTableLocked = xbFalse;
+ xbBool bMemoLocked = xbFalse;
+ #endif
+
+ try{
+ #ifdef XB_LOCKING_SUPPORT
+ if( iAutoLock && !dbf->GetTableLocked() ){
+ if(( iRc = dbf->LockTable( XB_LOCK )) != XB_NO_ERROR ){
+ iErrorStop = 10;
+ throw iRc;
+ } else {
+ bTableLocked = xbTrue;
+ }
+ if(( iRc = LockMemo( XB_LOCK )) != XB_NO_ERROR ){
+ iErrorStop = 20;
+ throw iRc;
+ } else {
+ bMemoLocked = xbTrue;
+ }
+ }
+ #endif
+
+ // create temp file
+ xbString sTempMemoName;
+ if(( iRc = CreateUniqueFileName( GetDirectory(), "dbt", sTempMemoName )) != XB_NO_ERROR ){
+ iErrorStop = 10;
+ throw iRc;
+ }
+
+ xbMemoDbt3 *pMemo = new xbMemoDbt3( dbf, sTempMemoName );
+ if(( iRc = pMemo->CreateMemoFile()) != XB_NO_ERROR ){
+ iErrorStop = 20;
+ 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 = 30;
+ 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 = 40;
+ 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 = 50;
+ throw iRc;
+ }
+ if( cFldType == 'M' ){
+ // copy it to work field
+ if(( iRc = dbf->GetMemoField( lFc, sMemoFldData )) != XB_NO_ERROR ){
+ iErrorStop = 60;
+ throw iRc;
+ }
+ // write it to new field
+ if(( iRc = pMemo->UpdateMemoField( lFc, sMemoFldData )) != XB_NO_ERROR ){
+ iErrorStop = 70;
+ throw iRc;
+ }
+ }
+ }
+ }
+
+ //copy target back to source
+ xbUInt32 ulBlkSize = GetBlockSize();
+ xbUInt64 ullFileSize;
+ if(( iRc = pMemo->GetFileSize( ullFileSize )) != XB_NO_ERROR ){
+ iErrorStop = 80;
+ throw iRc;
+ }
+ // file size should be evenly divisible by block size
+ xbUInt32 ulBlkCnt;
+
+ if( ullFileSize % ulBlkSize ){
+ iErrorStop = 90;
+ throw iRc;
+ } else {
+ ulBlkCnt = (xbUInt32) (ullFileSize / ulBlkSize);
+ }
+ if(( iRc = xbTruncate( 0 )) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+
+ if(( cBlock = (char *) malloc( (size_t) ulBlkSize )) == NULL ){
+ iErrorStop = 110;
+ 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 = 120;
+ throw iRc;
+ }
+ if(( iRc = WriteBlock( ulBc, ulBlkSize, cBlock )) != XB_NO_ERROR ){
+ iErrorStop = 130;
+ throw iRc;
+ }
+ }
+ //close and delete target
+ if(( iRc = pMemo->xbFclose()) != XB_NO_ERROR ){
+ iErrorStop = 140;
+ throw iRc;
+ }
+ if(( iRc = pMemo->xbRemove()) != XB_NO_ERROR ){
+ iErrorStop = 150;
+ throw iRc;
+ }
+ free( cBlock );
+ delete pMemo;
+
+ #ifdef XB_LOCKING_SUPPORT
+ if( iAutoLock && !dbf->GetTableLocked() ){
+ if(( iRc = dbf->LockTable( XB_UNLOCK )) != XB_NO_ERROR ){
+ iErrorStop = 200;
+ throw iRc;
+ }
+ if(( iRc = LockMemo( XB_UNLOCK )) != XB_NO_ERROR ){
+ iErrorStop = 210;
+ throw iRc;
+ }
+ }
+ #endif
+ }
+ catch (xbInt16 iRc ){
+ free( cBlock );
+
+ #ifdef XB_LOCKING_SUPPORT
+ if( bTableLocked )
+ dbf->LockTable( XB_UNLOCK );
+ if( bMemoLocked )
+ LockMemo( XB_UNLOCK );
+ #endif
+
+ xbString sMsg;
+ sMsg.Sprintf( "xbMemoDbt3::PackMemo() Exception Caught. Error Stop = [%d] rc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+
+/***********************************************************************/
+//! @brief Read dbt header file.
+/*!
+ \param iOption 0 --> read only first four bytes<br>
+ 1 --> read the entire thing
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbMemoDbt3::ReadDbtHeader( xbInt16 iOption ){
+ char *p;
+ char MemoBlock[20];
+ xbInt16 rc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+ xbUInt32 ulReadSize;
+
+ try{
+ if( !FileIsOpen() ){
+ iErrorStop = 10;
+ rc = XB_NOT_OPEN;
+ throw rc;
+ }
+ if( iOption == 0 )
+ ulReadSize = 4;
+ else{
+ xbUInt64 stFileSize = 0;
+ if(( rc = GetFileSize( stFileSize )) != XB_NO_ERROR ){
+ iErrorStop = 20;
+ throw rc;
+ }
+ if( stFileSize < 4 ){
+ iErrorStop = 30;
+ rc = XB_INVALID_BLOCK_NO;
+ throw rc;
+ }
+ else if( stFileSize > 20 )
+ ulReadSize = 40;
+ else
+ ulReadSize = 4;
+ }
+ if( xbFseek( 0, SEEK_SET )){
+ iErrorStop = 50;
+ rc = XB_SEEK_ERROR;
+ throw rc;
+ }
+ if(( xbFread( &MemoBlock, ulReadSize, 1 )) != XB_NO_ERROR ){
+ iErrorStop = 60;
+ 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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+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 = 10;
+ throw rc;
+ }
+ } else {
+ xbUInt32 ulDataLen = sMemoData.Len() + 2;
+ xbUInt32 ulBlocksNeeded = (ulDataLen / 512) + 1;
+ xbUInt32 ulLastDataBlock;
+ if(( rc = CalcLastDataBlock( ulLastDataBlock )) != XB_NO_ERROR ){
+ iErrorStop = 20;
+ throw rc;
+ }
+ if(( rc = xbFseek( ((xbInt64) ulLastDataBlock * 512), SEEK_SET )) != XB_NO_ERROR ){
+ iErrorStop = 30;
+ throw rc;
+ }
+ if(( rc = xbFwrite( sMemoData.Str(), sMemoData.Len(), 1 )) != XB_NO_ERROR ){
+ iErrorStop = 40;
+ throw rc;
+ }
+ if(( rc = xbFputc( 0x1a, 2 )) != XB_NO_ERROR ){
+ iErrorStop = 50;
+ throw rc;
+ }
+ if(( rc = xbFputc( 0x00, (xbInt32) ( ulBlocksNeeded * 512 ) - (xbInt32) ulDataLen )) != XB_NO_ERROR ){
+ iErrorStop = 60;
+ throw rc;
+ }
+ if(( rc = dbf->PutULongField( iFieldNo, ulLastDataBlock )) != XB_NO_ERROR ){
+ iErrorStop = 70;
+ throw rc;
+ }
+ ulHdrNextBlock = ulLastDataBlock + ulBlocksNeeded;
+ if(( rc = UpdateHeadNextNode()) != XB_NO_ERROR ){
+ iErrorStop = 80;
+ 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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+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 = 10;
+ throw iRc;
+ }
+ if(( iRc != xbFwrite( cBuf, 4, 1 ))!= XB_NO_ERROR ){
+ iErrorStop = 20;
+ throw iRc;
+ }
+ if(( iRc != xbTruncate( 512 )) != XB_NO_ERROR ){
+ iErrorStop = 30;
+ 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..4f92d2b
--- /dev/null
+++ b/src/core/xbmemo4.cpp
@@ -0,0 +1,1372 @@
+/* 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 = 10;
+ throw rc;
+ }
+ if(( rc = FreeMemoBlockChain( ulBlockNo )) != XB_NO_ERROR ){
+ iErrorStop = 20;
+ 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 = 10;
+ throw rc;
+ }
+ if(( rc = FreeMemoBlockChain( ulBlockNo )) != XB_NO_ERROR ){
+ iErrorStop = 20;
+ 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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+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 = 10;
+ throw rc;
+ }
+
+ ulHdrNextBlock = 1L;
+ ePutUInt32( cBuf, ulHdrNextBlock );
+ if(( rc = xbFwrite( cBuf, 4, 1 ))!= XB_NO_ERROR ){
+ iErrorStop = 20;
+ 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 = 60;
+ xbFclose();
+ throw rc;
+ }
+ for( xbUInt32 i = 0; i < (GetBlockSize() - 22); i++ )
+ xbFputc( 0x00 );
+ if(( mbb = (void *) malloc( GetBlockSize())) == NULL ){
+ rc = XB_NO_MEMORY;
+ iErrorStop = 80;
+ 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 = 10;
+ throw rc;
+ }
+ if(( rc = CalcLastDataBlock( ulLastDataBlock )) != XB_NO_ERROR ){
+ iErrorStop = 20;
+ 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 = 30;
+ 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<xbUInt32> *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;
+}
+/***********************************************************************/
+//! @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;
+}
+#endif // XB_DEBUG_SUPPORT
+
+/************************************************************************/
+//! @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.<br>
+ 0 if header node
+ \param bFound Output xbFalse - Spot not found in chain.<br>
+ xbTrue - Spot found in chain.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+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 = 10;
+ throw rc;
+ }
+ }
+ if( ulHdrNextBlock < ulLastDataBlock ){
+ ulCurNode = ulHdrNextBlock;
+
+ if(( rc = ReadBlockHeader( ulHdrNextBlock, 2 )) != XB_NO_ERROR ){
+ iErrorStop = 20;
+ throw rc;
+ }
+ while( ulBlocksNeeded > ulFreeBlockCnt && ulNextFreeBlock < ulLastDataBlock ){
+ ulPrevNode = ulCurNode;
+ ulCurNode = ulNextFreeBlock;
+ if(( rc = ReadBlockHeader( ulNextFreeBlock, 2 )) != XB_NO_ERROR ){
+ iErrorStop = 30;
+ 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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+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 = 10;
+ rc =XB_INVALID_BLOCK_NO;
+ throw rc;
+ }
+
+ /* Load the first block */
+ if(( rc = ReadBlockHeader( ulBlockNo, 1 )) != XB_NO_ERROR ){
+ iErrorStop = 20;
+ 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 = 30;
+ throw rc;
+ }
+
+ if(( rc = ReadDbtHeader( 0 )) != XB_NO_ERROR ){
+ iErrorStop = 40;
+ 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 = 50;
+ 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 = 60;
+ 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 = 70;
+ throw rc;
+ }
+
+ ulHdrNextBlock = ulBlockNo;
+ if(( rc = UpdateHeadNextNode()) != XB_NO_ERROR ){
+ iErrorStop = 80;
+ 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 = 90;
+ throw rc;
+ }
+ ulFreeBlockCnt += ulNoOfFreedBlocks;
+
+ ePutUInt32( (char *) mbb, ulSaveNextFreeBlock );
+ ePutUInt32( (char *) mbb+4, ulFreeBlockCnt );
+ if(( rc = WriteBlock( ulLastFreeBlock, 8, mbb )) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ 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 = 110;
+ throw rc;
+ }
+
+ if(( rc = ReadBlockHeader( ulLastFreeBlock, 2 )) != XB_NO_ERROR ){
+ iErrorStop = 120;
+ throw rc;
+ }
+
+ ePutUInt32( (char *) mbb, ulBlockNo );
+ if(( rc = WriteBlock( ulLastFreeBlock, 8, mbb )) != XB_NO_ERROR ){
+ iErrorStop = 130;
+ 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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+
+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 = 10;
+ 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 = 20;
+ throw rc;
+ }
+ }
+ else // remove out of the middle or end
+ {
+ ulNextFreeBlock2 = ulNextFreeBlock;
+ if(( rc = ReadBlockHeader( ulPrevNode, 2 )) != XB_NO_ERROR ){
+ iErrorStop = 30;
+ throw rc;
+ }
+ ulNextFreeBlock = ulNextFreeBlock2;
+ if(( rc = WriteBlockHeader( ulPrevNode, 2 )) != XB_NO_ERROR ){
+ iErrorStop = 40;
+ 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 = 50;
+ throw rc;
+ }
+ ulFreeBlockCnt -= ulBlocksNeeded;
+ if(( rc = WriteBlockHeader( ulHdrNextBlock, 2 )) != XB_NO_ERROR ){
+ iErrorStop = 60;
+ 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 = 70;
+ throw rc;
+ }
+
+ ulNextFreeBlock = ulNextFreeBlock2;
+ if(( rc = WriteBlockHeader( ulPrevNode, 2 )) != XB_NO_ERROR ){
+ iErrorStop = 80;
+ throw rc;
+ }
+ ulFreeBlockCnt = ulNewFreeBlocks;
+ ulNextFreeBlock = ulSaveNextFreeBlock;
+ if(( rc = WriteBlockHeader( ulNextFreeBlock2, 2 )) != XB_NO_ERROR ){
+ iErrorStop = 90;
+ 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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+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 = 10;
+ throw rc;
+ }
+
+ if( ulBlockNo == 0L || ulMemoFieldLen == 0L )
+ sMemoData = "";
+ else{
+ ulMemoFieldDataLen = ulMemoFieldLen - 8;
+
+ if(( p = (char *)calloc(1, ulMemoFieldDataLen+1)) == NULL ){
+ iErrorStop = 20;
+ 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 = 30;
+ 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 = 40;
+ 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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+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 = 10;
+ throw rc;
+ }
+ if( cFieldType != 'M' ){
+ iErrorStop = 20;
+ rc = XB_INVALID_MEMO_FIELD;
+ throw rc;
+ }
+ if(( rc = dbf->GetULongField( iFieldNo, ulBlockNo )) < XB_NO_ERROR ){
+ iErrorStop = 30;
+ throw rc;
+ }
+
+ if( ulBlockNo < 1 ){
+ ulMemoFieldLen = 0;
+ return XB_NO_ERROR;
+ }
+
+ if(( rc = ReadBlockHeader( ulBlockNo, 1 )) != XB_NO_ERROR ){
+ iErrorStop = 40;
+ 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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbMemoDbt4::OpenMemoFile() {
+
+ xbInt16 iErrorStop = 0;
+ xbInt16 rc = XB_NO_ERROR;
+
+ try{
+ if(( rc = xbFopen( dbf->GetOpenMode(), dbf->GetShareMode())) != XB_NO_ERROR ){
+ iErrorStop = 10;
+ throw rc;
+ }
+ if(( rc = ReadDbtHeader( 1 )) != XB_NO_ERROR ){
+ iErrorStop = 20;
+ throw rc;
+ }
+ if(( mbb = (void *) malloc( GetBlockSize())) == NULL ){
+ xbFclose();
+ iErrorStop = 30;
+ 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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbMemoDbt4::PackMemo( void (*memoStatusFunc ) ( xbUInt32 ulItemNum, xbUInt32 ulNumItems )){
+ xbInt16 iRc = 0;
+ xbInt16 iErrorStop = 0;
+ char * cBlock = NULL;
+
+ #ifdef XB_LOCKING_SUPPORT
+ xbInt16 iAutoLock = dbf->GetAutoLock();
+ xbBool bTableLocked = xbFalse;
+ xbBool bMemoLocked = xbFalse;
+ #endif
+
+ try{
+
+ #ifdef XB_LOCKING_SUPPORT
+ if( iAutoLock && !dbf->GetTableLocked() ){
+ if(( iRc = dbf->LockTable( XB_LOCK )) != XB_NO_ERROR ){
+ iErrorStop = 10;
+ throw iRc;
+ } else {
+ bTableLocked = xbTrue;
+ }
+ if(( iRc = LockMemo( XB_LOCK )) != XB_NO_ERROR ){
+ iErrorStop = 20;
+ throw iRc;
+ } else {
+ bMemoLocked = xbTrue;
+ }
+ }
+ #endif
+
+ // create temp file
+ xbString sTempMemoName;
+ if(( iRc = CreateUniqueFileName( GetDirectory(), "dbt", sTempMemoName )) != XB_NO_ERROR ){
+ iErrorStop = 10;
+ throw iRc;
+ }
+
+ xbMemoDbt4 *pMemo = new xbMemoDbt4( dbf, sTempMemoName );
+ if(( iRc = pMemo->CreateMemoFile()) != XB_NO_ERROR ){
+ iErrorStop = 20;
+ 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 = 30;
+ 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 = 40;
+ 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 = 50;
+ throw iRc;
+ }
+ if( cFldType == 'M' ){
+ // copy it to work field
+ if(( iRc = dbf->GetMemoField( lFc, sMemoFldData )) != XB_NO_ERROR ){
+ iErrorStop = 60;
+ throw iRc;
+ }
+ // write it to new field
+ if(( iRc = pMemo->UpdateMemoField( lFc, sMemoFldData )) != XB_NO_ERROR ){
+ iErrorStop = 70;
+ throw iRc;
+ }
+ }
+ }
+ }
+
+ //copy target back to source
+ xbUInt32 ulBlkSize = GetBlockSize();
+ xbUInt64 ullFileSize;
+ if(( iRc = pMemo->GetFileSize( ullFileSize )) != XB_NO_ERROR ){
+ iErrorStop = 80;
+ throw iRc;
+ }
+ // file size should be evenly divisible by block size
+ xbUInt32 ulBlkCnt;
+
+ if( ullFileSize % ulBlkSize ){
+ iErrorStop = 90;
+ throw iRc;
+ } else {
+ ulBlkCnt = (xbUInt32) (ullFileSize / ulBlkSize);
+ }
+ if(( iRc = xbTruncate( 0 )) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+
+ if(( cBlock = (char *) malloc( ulBlkSize )) == NULL ){
+ iErrorStop = 110;
+ 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 = 120;
+ throw iRc;
+ }
+ if(( iRc = WriteBlock( ulBc, ulBlkSize, cBlock )) != XB_NO_ERROR ){
+ iErrorStop = 130;
+ throw iRc;
+ }
+ }
+
+ if(( xbFseek( 8, SEEK_SET )) != XB_NO_ERROR ){
+ iErrorStop = 140;
+ 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 = 150;
+ throw iRc;
+ }
+ if(( iRc = pMemo->xbRemove()) != XB_NO_ERROR ){
+ iErrorStop = 160;
+ throw iRc;
+ }
+ free( cBlock );
+ delete pMemo;
+
+ #ifdef XB_LOCKING_SUPPORT
+ if( iAutoLock && !dbf->GetTableLocked() ){
+ if(( iRc = dbf->LockTable( XB_UNLOCK )) != XB_NO_ERROR ){
+ iErrorStop = 200;
+ throw iRc;
+ }
+ if(( iRc = LockMemo( XB_UNLOCK )) != XB_NO_ERROR ){
+ iErrorStop = 210;
+ throw iRc;
+ }
+ }
+ #endif
+ }
+ catch (xbInt16 iRc ){
+ free( cBlock );
+
+ #ifdef XB_LOCKING_SUPPORT
+ if( bTableLocked )
+ dbf->LockTable( XB_UNLOCK );
+ if( bMemoLocked )
+ LockMemo( XB_UNLOCK );
+ #endif
+
+ xbString sMsg;
+ sMsg.Sprintf( "xbMemoDbt4::PackMemo() Exception Caught. Error Stop = [%d] rc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+/***********************************************************************/
+//! @brief Read block header.
+/*!
+ \param ulBlockNo Block to read
+ \param iOption 1 - Read fields option 1
+ 2 - Read fields option 2
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+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 = 10;
+ 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 = 20;
+ 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<br>
+ 1 --> read the entire thing
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+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 = 10;
+ rc = XB_NOT_OPEN;
+ throw rc;
+ }
+ if( xbFseek( 0, SEEK_SET )){
+ iErrorStop = 20;
+ rc = XB_SEEK_ERROR;
+ throw rc;
+ }
+ if( iOption )
+ iReadLen = 22;
+ else
+ iReadLen = 4;
+
+ if(( xbFread( &MemoBlock, (size_t) iReadLen, 1 )) != XB_NO_ERROR ){
+ iErrorStop = 30;
+ 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 = 10;
+ throw rc;
+ }
+
+ for( int i = 1; i < 9; i++ ){
+ if(( rc = xbFputc( sDbfFileNameWoExt[i] )) != XB_NO_ERROR ){
+ iErrorStop = 20;
+ 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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+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 = 10;
+ 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 = 20;
+ throw rc;
+ }
+ if(( llNewBlocks.RemoveByVal( ulBlockNo )) < XB_NO_ERROR ){
+ iErrorStop = 30;
+ throw rc;
+ }
+ } else {
+ // first revision, save what it was in case of Abort() command
+ if(( llOldBlocks.InsertAtFront( ulBlockNo )) != XB_NO_ERROR ){
+ iErrorStop = 40;
+ throw rc;
+ }
+ }
+
+ if(( rc = dbf->PutField( iFieldNo, "" )) != XB_NO_ERROR ){
+ iErrorStop = 50;
+ 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 = 60;
+ throw rc;
+ }
+ } else {
+ // first revision, save what it was in case of Abort() command
+ if(( rc = llOldBlocks.InsertAtFront( ulBlockNo )) != XB_NO_ERROR ){
+ iErrorStop = 70;
+ 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 = 80;
+ throw rc;
+ }
+ iField1 = -1;
+ iStartPos = 8;
+ ulFieldLen = sMemoData.Len() + 8;
+
+ if( bUsedBlockFound ){
+
+ if(( rc = GetBlockSetFromChain( ulBlocksNeeded, ulHeadBlock, ulPrevNode )) != XB_NO_ERROR ){
+ iErrorStop = 90;
+ throw rc;
+ }
+
+ if(( rc = WriteBlockHeader( ulHeadBlock, 1 )) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw rc;
+ }
+
+ if(( rc = xbFwrite( sMemoData.Str(), sMemoData.Len(), 1 )) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ throw rc;
+ }
+
+ } else { // append to the end
+
+ if(( rc = WriteBlockHeader( ulLastDataBlock, 1 )) != 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( 0x00, (xbInt32)((ulBlocksNeeded * GetBlockSize()) - (sMemoData.Len() + 8)))) != XB_NO_ERROR ){
+ iErrorStop = 140;
+ 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 = 150;
+ 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 = 160;
+ throw rc;
+ }
+ }
+ }
+
+ if(( rc = llNewBlocks.InsertAtFront( ulHeadBlock )) != XB_NO_ERROR ){ // In case of Abort(), this block needs to be freed
+ iErrorStop = 170;
+ throw rc;
+ }
+ if(( rc = dbf->PutLongField( iFieldNo, (xbInt32) ulHeadBlock )) != XB_NO_ERROR ){
+ iErrorStop = 180;
+ 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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+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 = 10;
+ rc = XB_INVALID_OPTION;
+ throw rc;
+ }
+
+ if(( rc = WriteBlock( ulBlockNo, 8, mbb )) != XB_NO_ERROR ){
+ iErrorStop = 20;
+ 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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+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 = 10;
+ throw iRc;
+ }
+ if(( iRc != xbFwrite( cBuf, 4, 1 ))!= XB_NO_ERROR ){
+ iErrorStop = 20;
+ throw iRc;
+ }
+ if(( iRc != xbTruncate( GetBlockSize())) != XB_NO_ERROR ){
+ iErrorStop = 30;
+ 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..c595f6b
--- /dev/null
+++ b/src/core/xbssv.cpp
@@ -0,0 +1,550 @@
+/* xbssv.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{
+
+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_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_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::sDefaultLogDirectory = PROJECT_LOG_DIR;
+xbString xbSsv::sDefaultLogFileName = PROJECT_DFLT_LOGFILE;
+#endif
+
+#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
+
+#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
+
+
+#ifdef XB_MDX_SUPPORT
+xbInt16 xbSsv::iCreateMdxBlockSize = 1024; // 1024 is DBase 7 default size for MDX index blocks
+#endif
+
+
+/*************************************************************************/
+//! @brief Class Constructor.
+xbSsv::xbSsv(){}
+/*************************************************************************/
+#ifdef XB_DEBUG_SUPPORT
+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 );
+}
+#endif
+/*************************************************************************/
+//! @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<br>
+ 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<br>
+ 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 default file format version.
+/*
+ \returns 3 - Dbase III file format<br>
+ 4 - Dbase IV file format<br>
+*/
+
+/*
+xbInt16 xbSsv::GetDefaultFileVersion() const {
+ return iDefaultFileVersion;
+}
+*/
+/*************************************************************************/
+//! @brief Get the Endian type.
+/*!
+ \returns B - Big endian<br>
+ L - Little endian<br>
+*/
+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 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.<br>
+ xbFalse - Disable default auto commit.<br>
+*/
+
+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::GetDefaultLogFileName() const {
+ return sDefaultLogFileName;
+}
+
+
+/*************************************************************************/
+//! @brief Get the default log directory.
+/*!
+ \returns Returns the log directory.
+*/
+xbString & xbSsv::GetDefaultLogDirectory() const {
+ return sDefaultLogDirectory;
+}
+
+
+/*************************************************************************/
+//! @brief Set the default log directory name.
+/*!
+ \param sDefaultLogDirectory Name of desired log directory.
+
+*/
+void xbSsv::SetDefaultLogDirectory( const xbString &sDefaultLogDirectory ){
+
+ this->sDefaultLogDirectory = sDefaultLogDirectory;
+
+ #ifdef WIN32
+ this->sDefaultLogDirectory.SwapChars( '/', '\\' );
+ #else
+ this->sDefaultLogDirectory.SwapChars( '\\', '/' );
+ #endif
+
+
+}
+
+#else
+
+xbString & xbSsv::GetDefaultLogFileName() const {
+ return sNullString;
+}
+
+xbString & xbSsv::GetDefaultLogDirectory() const {
+ return sNullString;
+}
+
+void xbSsv::SetDefaultLogDirectory( const xbString &sDefaultLogDirectory ){
+ 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<br>
+ xbFalse - Turn autolocking off<br>
+*/
+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.<br>
+ xbFalse - Multi user mode turned off.<br>
+*/
+xbBool xbSsv::GetMultiUser() const {
+ return bMultiUser;
+}
+
+//! @brief Get the multi user setting.
+/*!
+ \param bMultiUser xbTrue - Turn on Multi user mode.<br>
+ xbFalse - Turn off Multi user mode.<br>
+*/
+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<br>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
+/************************************************************************/
+
+
+
+
+} /* 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..872062f
--- /dev/null
+++ b/src/core/xbstring.cpp
@@ -0,0 +1,1839 @@
+/* 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.<br>
+ zbFalse - Strings don't match.<br>
+
+*/
+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.<br>
+ zbFalse - Strings don't match.<br>
+*/
+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.<br>
+ xbFalse - Strings match.<br>
+*/
+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.<br>
+ zbFalse - Strings match.<br>
+*/
+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.<br>
+ zbFalse - Left string is not less than the right string.<br>
+*/
+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.<br>
+ zbFalse - Left string is not greater than the right string.<br>
+*/
+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.<br>
+ zbFalse - Left string is not less than or equal to the right string.<br>
+*/
+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.<br>
+ zbFalse - Left string is not greater than or equal to the right string.<br>
+*/
+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.<br>
+ 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<br>
+ 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<br>
+ 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.<br>
+ 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.<br>
+ 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.<br>
+ 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.<br>
+ 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
+ \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 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; i<Len; i++)
+ data[i] = (char)tolower(data[i]);
+ return *this;
+}
+
+/************************************************************************/
+//! @brief Replace all lower case charaters with lower case characters
+/*!
+ \returns Reference to this string.
+*/
+xbString &xbString::ToUpperCase(){
+ xbUInt32 Len = this->Len();
+ for (xbUInt32 i=0;i<Len;i++)
+ data[i] = (char)toupper(data[i]);
+ return *this;
+}
+/************************************************************************/
+//! @brief Trim all leading and trailing white space from string.
+/*!
+ \returns Reference to string.
+*/
+xbString &xbString::Trim(){
+ Rtrim();
+ Ltrim();
+ return *this;
+}
+
+
+/************************************************************************/
+//! @brief Private function used for reallocateing memory
+/*!
+ This function is designed to be a drop in replacement for the realloc
+ function call.
+*/
+/*
+char * xbString::xb_realloc( char * pIn, xbUInt32 iLen ){
+
+ if( iLen == 0 ){
+ if( pIn ){
+ free( pIn );
+ return NULL;
+ }
+ }
+
+ char *pNew = (char *) calloc( 1, (size_t) iLen );
+ if( !pNew ) return NULL;
+ char *s = pIn;
+ char *t = pNew;
+ xbUInt32 iCnt = 0;
+ while( *s && iCnt++ < iLen )
+ *t++ = *s++;
+ return pNew;
+}
+*/
+/************************************************************************/
+//! @brief Private function used for copying a string
+/*!
+ For performance reasons, this is an internal function that does no
+ memory checking and assumes a valid buffer area is available to be copied.
+
+ This function is marked as private because of the above reason and
+ is used by "stronger" calling functions.
+
+ \param sTarget Target destination of copied string
+ \param sSource Source string to copy
+ \returns Reference to string.
+*/
+
+char * xbString::xb_strcpy( char *sTarget, const char *sSource ){
+
+ char *temp = sTarget;
+ while( *sSource != '\0'){
+ *sTarget++ = *sSource++;
+// sTarget++;
+// sSource++;
+ }
+ *sTarget= '\0';
+ return temp;
+}
+
+/************************************************************************/
+//! @brief Check for valid logical field data
+/*!
+ Valid logical data is one 'T', 'F', 'N' or 'Y'.<br>
+
+ \returns xbTrue if the data is valid logical data.<br>
+ 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.<br>
+ 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
new file mode 100755
index 0000000..a71880b
--- /dev/null
+++ b/src/core/xbtag.cpp
@@ -0,0 +1,121 @@
+/* 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.<br>xbFalse - Index is not unique.
+ \param bSort MDX only<br>xbTrue - Descending.<br>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.<br>xbTrue - Descending.<br>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.<br>xbTrue - Unique.<br>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..e1bf496
--- /dev/null
+++ b/src/core/xbtblmgr.cpp
@@ -0,0 +1,257 @@
+/* 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 <string.h>
+//#endif
+
+namespace xb{
+
+/*************************************************************************/
+
+xbTblMgr::xbTblMgr(){
+ TblList = NULL;
+ iOpenTableCount = 0;
+}
+
+/*************************************************************************/
+
+xbTblMgr::~xbTblMgr(){
+ xbTblList *l;
+ if( TblList ){
+ while( TblList ){
+ l = TblList;
+ TblList = TblList->pNext;
+ delete l->psTblName;
+ delete l->psTblAlias;
+ free( l );
+ }
+ }
+}
+
+/*************************************************************************/
+
+xbInt16 xbTblMgr::AddTblToTblList( xbDbf *d, const xbString & sTblName ){
+ return AddTblToTblList( d, sTblName, "" );
+}
+
+/*************************************************************************/
+
+xbInt16 xbTblMgr::AddTblToTblList( xbDbf *d, const xbString & sTblName, const xbString & sTblAlias ) {
+
+ xbTblList *i, *s, *t;
+ xbInt16 iRc = 0;
+ xbInt16 iErrorStop = 0;
+ xbString sAlias;
+
+ try{
+
+ if( sTblName.Len() == 0 ){
+ iErrorStop = 10;
+ iRc = XB_INVALID_TABLE_NAME;
+ throw iRc;
+ }
+
+ if( sTblAlias.Len() == 0 ){
+ sAlias = sTblName;
+ sAlias.SwapChars( '\\', '/' );
+ xbUInt32 iPos = sAlias.GetLastPos( '/' );
+ if( iPos > 0 ) /* get rid of the directory part of the name */
+ sAlias.Ltrunc( iPos );
+ } else {
+ sAlias = sTblAlias;
+ }
+
+ if((i = (xbTblList *) calloc(1, sizeof(xbTblList))) == NULL){
+ iErrorStop = 20;
+ iRc = XB_NO_MEMORY;
+ throw iRc;
+ }
+ 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 = 30;
+ 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 != 30 ){
+ 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 << " Table=[" << 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;
+ while( t ){
+ if( sTblAlias == t->psTblAlias->Str())
+ return t->pDbf;
+ t = t->pNext;
+ }
+
+ t = TblList;
+ while( t ){
+ if( sTblAlias == t->psTblName->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;
+}
+
+/*************************************************************************/
+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->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->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..0a9c2e7
--- /dev/null
+++ b/src/core/xbuda.cpp
@@ -0,0 +1,78 @@
+/* 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<xbString> *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..7dd3ac7
--- /dev/null
+++ b/src/core/xbxbase.cpp
@@ -0,0 +1,712 @@
+/* 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
+}
+/*************************************************************************/
+//! @brief Class Deconstructor.
+xbXBase::~xbXBase(){
+ CloseAllTables();
+ #ifdef XB_LOGGING_SUPPORT
+ delete xLog;
+ #endif
+}
+
+/*************************************************************************/
+//! @brief Close all tables / files.
+/*!
+ This closes everything.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+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 = 10;
+ throw iRc;
+ }
+ } else {
+ iRc = XB_INVALID_OBJECT;
+ iErrorStop = 20;
+ 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;
+}
+/*************************************************************************/
+#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.<br>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<br>
+ 1 = Syslog<br>
+ 2 = Both<br>
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+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 ){
+ 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;
+}
+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<br>
+ 1 = use value specified in lReturnLenVal<br>
+ 2 = use length of operand specified in col 4<br>
+ 3 = use valued of numeric operand specified in col 4<br>
+ 4 = length of parm 1 * numeric value parm<br>
+ 5 = larger length of parm 2 or length of parm 3<br>
+ 6 = if two or more parms, use numeric value from second parm, otherwise use col4 value
+ \param lReturnLenVal Used in combination with iReturnLenCalc.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+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<br>
+ 0 s1 == s2<br>
+ -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 = 10;
+ iRc = XB_FILE_NOT_FOUND;
+ throw iRc;
+ }
+ xbFile *f = new xbFile(this);
+ f->SetFileName( sTableName );
+ if(( iRc = f->FileExists( f->GetFqFileName())) != xbTrue ){
+ iErrorStop = 20;
+ iRc = XB_FILE_NOT_FOUND;
+ throw iRc;
+ }
+ unsigned char cFileTypeByte;
+ if(( iRc = f->GetXbaseFileTypeByte( f->GetFqFileName(), cFileTypeByte )) != XB_NO_ERROR ){
+ iErrorStop = 30;
+ 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 = 40;
+ 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 = 50;
+ iRc = XB_FILE_TYPE_NOT_SUPPORTED;
+ throw iRc;
+ #endif
+
+ } else {
+ iErrorStop = 60;
+ iRc = XB_FILE_TYPE_NOT_SUPPORTED;
+ throw iRc;
+ }
+
+ if( iRc != XB_NO_ERROR ){
+ iErrorStop = 70;
+ throw iRc;
+ }
+ }
+ catch (xbInt16 iRc ){
+
+ std::cout << "Openerror\n";
+
+ 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;
+
+ xbDbf * pwDbf;
+
+ try{
+ xbFile f(this);
+ if( sTableName.Len() == 0 ){
+ iErrorStop = 10;
+ iRc = XB_FILE_NOT_FOUND;
+ throw iRc;
+ }
+ f.SetFileName( sTableName );
+ if(( iRc = f.FileExists( f.GetFqFileName() )) != xbTrue ){
+ iErrorStop = 20;
+ iRc = XB_FILE_NOT_FOUND;
+ throw iRc;
+ }
+ unsigned char cFileTypeByte;
+ if(( iRc = f.GetXbaseFileTypeByte( f.GetFqFileName(), cFileTypeByte )) != XB_NO_ERROR ){
+ iErrorStop = 30;
+ return iRc;
+ }
+ if( f.DetermineXbaseTableVersion( cFileTypeByte ) == 4 ){
+ #ifdef XB_DBF4_SUPPORT
+ 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 = 40;
+ 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 = 50;
+ iRc = XB_FILE_TYPE_NOT_SUPPORTED;
+ throw iRc;
+ #endif
+
+ } else {
+ iErrorStop = 60;
+ iRc = XB_FILE_TYPE_NOT_SUPPORTED;
+ throw iRc;
+ }
+
+ }
+ catch (xbInt16 iRc ){
+
+ std::cout << "OpenHighestVersion error\n";
+
+ xbString sMsg;
+ sMsg.Sprintf( "xbxbase::OpenHighestVersion() Exception Caught. Error Stop = [%d] rc = [%d]", iErrorStop, iRc );
+ WriteLogMessage( sMsg.Str() );
+ WriteLogMessage( GetErrorMessage( iRc ));
+
+ }
+ return iRc;
+}
+/***********************************************************************/
+} /* namespace */