From 4875a3dd9b183dcd2256e2abfc4ccf7484c233b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Frings-F=C3=BCrst?= Date: Wed, 7 Dec 2022 13:17:14 +0100 Subject: New upstream version 4.0.2 --- src/core/xbbcd.cpp | 333 +++ src/core/xbdate.cpp | 802 ++++++ src/core/xbdbf.cpp | 4220 ++++++++++++++++++++++++++++ src/core/xbdbf3.cpp | 577 ++++ src/core/xbdbf4.cpp | 590 ++++ src/core/xbexp.cpp | 2643 ++++++++++++++++++ src/core/xbexpnode.cpp | 562 ++++ src/core/xbfields.cpp | 1122 ++++++++ src/core/xbfile.cpp | 2052 ++++++++++++++ src/core/xbfilter.cpp | 569 ++++ src/core/xbfuncs.cpp | 850 ++++++ src/core/xbixbase.cpp | 792 ++++++ src/core/xbixmdx.cpp | 5067 ++++++++++++++++++++++++++++++++++ src/core/xbixndx.cpp | 2840 +++++++++++++++++++ src/core/xblog.cpp | 208 ++ src/core/xbmemo.cpp | 219 ++ src/core/xbmemo3.cpp | 590 ++++ src/core/xbmemo4.cpp | 1372 +++++++++ src/core/xbssv.cpp | 550 ++++ src/core/xbstring.cpp | 1839 ++++++++++++ src/core/xbtag.cpp | 121 + src/core/xbtblmgr.cpp | 257 ++ src/core/xbuda.cpp | 78 + src/core/xbxbase.cpp | 712 +++++ src/examples/xb_ex_date.cpp | 210 ++ src/examples/xb_ex_sql.cpp | 111 + src/examples/xb_ex_string.cpp | 385 +++ src/examples/xb_ex_v3_create_dbf.cpp | 102 + src/examples/xb_ex_v3_upd_dbf.cpp | 282 ++ src/examples/xb_ex_v4_create_dbf.cpp | 81 + src/examples/xb_ex_v4_upd_dbf.cpp | 283 ++ src/include/xbase.h | 93 + src/include/xbbcd.h | 95 + src/include/xbconfig.h.in | 122 + src/include/xbdate.h | 123 + src/include/xbdbf.h | 538 ++++ src/include/xbexp.h | 215 ++ src/include/xbexpnode.h | 120 + src/include/xbfile.h | 194 ++ src/include/xbfilter.h | 78 + src/include/xbindex.h | 605 ++++ src/include/xblnklst.h | 257 ++ src/include/xblnklstord.h | 367 +++ src/include/xblnknod.h | 94 + src/include/xblog.h | 66 + src/include/xbmemo.h | 223 ++ src/include/xbretcod.h | 94 + src/include/xbsql.h | 69 + src/include/xbssv.h | 177 ++ src/include/xbstring.h | 183 ++ src/include/xbtag.h | 74 + src/include/xbtblmgr.h | 61 + src/include/xbtypes.h | 55 + src/include/xbuda.h | 52 + src/include/xbxbase.h | 222 ++ src/sql/xbcrix.cpp | 279 ++ src/sql/xbcrtbl.cpp | 256 ++ src/sql/xbdrpix.cpp | 143 + src/sql/xbdrptbl.cpp | 125 + src/sql/xbinsert.cpp | 190 ++ src/sql/xbset.cpp | 91 + src/sql/xbsql.cpp | 143 + src/tests/tstfuncs.cpp | 581 ++++ src/tests/xb_test_bcd.cpp | 128 + src/tests/xb_test_date.cpp | 189 ++ src/tests/xb_test_dbf_v3_memos.cpp | 233 ++ src/tests/xb_test_dbf_v3_nomemos.cpp | 323 +++ src/tests/xb_test_dbf_v4_memos.cpp | 335 +++ src/tests/xb_test_dbf_v4_nomemos.cpp | 330 +++ src/tests/xb_test_expnode.cpp | 123 + src/tests/xb_test_expression.cpp | 791 ++++++ src/tests/xb_test_file.cpp | 212 ++ src/tests/xb_test_filter.cpp | 185 ++ src/tests/xb_test_funcs.cpp | 288 ++ src/tests/xb_test_linklist.cpp | 340 +++ src/tests/xb_test_lock.cpp | 874 ++++++ src/tests/xb_test_lock2.cpp | 208 ++ src/tests/xb_test_log.cpp | 88 + src/tests/xb_test_mdx.cpp | 243 ++ src/tests/xb_test_ndx.cpp | 330 +++ src/tests/xb_test_ndx2.cpp | 145 + src/tests/xb_test_sql.cpp | 107 + src/tests/xb_test_string.cpp | 397 +++ src/tests/xb_test_tblmgr.cpp | 167 ++ src/tests/xb_test_uda.cpp | 98 + src/tests/xb_test_xbase.cpp | 106 + src/utils/checkndx.cpp | 71 + src/utils/dbfxtrct.cpp | 182 ++ src/utils/preamble.txt | 19 + src/utils/reindex.cpp | 83 + src/utils/xb_cfg_check.cpp | 370 +++ src/utils/xb_copydbf.cpp | 62 + src/utils/xb_dbfutil.cpp | 3719 +++++++++++++++++++++++++ src/utils/xb_deletall.cpp | 55 + src/utils/xb_dumpdbt.cpp | 87 + src/utils/xb_dumphdr.cpp | 45 + src/utils/xb_dumpix.cpp | 56 + src/utils/xb_dumprecs.cpp | 62 + src/utils/xb_execsql.cpp | 95 + src/utils/xb_pack.cpp | 55 + src/utils/xb_tblinfo.cpp | 91 + src/utils/xb_undelall.cpp | 55 + src/utils/xb_zap.cpp | 52 + 103 files changed, 47803 insertions(+) create mode 100755 src/core/xbbcd.cpp create mode 100755 src/core/xbdate.cpp create mode 100755 src/core/xbdbf.cpp create mode 100755 src/core/xbdbf3.cpp create mode 100755 src/core/xbdbf4.cpp create mode 100755 src/core/xbexp.cpp create mode 100755 src/core/xbexpnode.cpp create mode 100755 src/core/xbfields.cpp create mode 100755 src/core/xbfile.cpp create mode 100755 src/core/xbfilter.cpp create mode 100755 src/core/xbfuncs.cpp create mode 100755 src/core/xbixbase.cpp create mode 100755 src/core/xbixmdx.cpp create mode 100755 src/core/xbixndx.cpp create mode 100755 src/core/xblog.cpp create mode 100755 src/core/xbmemo.cpp create mode 100755 src/core/xbmemo3.cpp create mode 100755 src/core/xbmemo4.cpp create mode 100755 src/core/xbssv.cpp create mode 100755 src/core/xbstring.cpp create mode 100755 src/core/xbtag.cpp create mode 100755 src/core/xbtblmgr.cpp create mode 100755 src/core/xbuda.cpp create mode 100755 src/core/xbxbase.cpp create mode 100755 src/examples/xb_ex_date.cpp create mode 100755 src/examples/xb_ex_sql.cpp create mode 100755 src/examples/xb_ex_string.cpp create mode 100755 src/examples/xb_ex_v3_create_dbf.cpp create mode 100755 src/examples/xb_ex_v3_upd_dbf.cpp create mode 100755 src/examples/xb_ex_v4_create_dbf.cpp create mode 100755 src/examples/xb_ex_v4_upd_dbf.cpp create mode 100755 src/include/xbase.h create mode 100755 src/include/xbbcd.h create mode 100755 src/include/xbconfig.h.in create mode 100755 src/include/xbdate.h create mode 100755 src/include/xbdbf.h create mode 100755 src/include/xbexp.h create mode 100755 src/include/xbexpnode.h create mode 100755 src/include/xbfile.h create mode 100755 src/include/xbfilter.h create mode 100755 src/include/xbindex.h create mode 100755 src/include/xblnklst.h create mode 100755 src/include/xblnklstord.h create mode 100755 src/include/xblnknod.h create mode 100755 src/include/xblog.h create mode 100755 src/include/xbmemo.h create mode 100755 src/include/xbretcod.h create mode 100755 src/include/xbsql.h create mode 100755 src/include/xbssv.h create mode 100755 src/include/xbstring.h create mode 100755 src/include/xbtag.h create mode 100755 src/include/xbtblmgr.h create mode 100755 src/include/xbtypes.h create mode 100755 src/include/xbuda.h create mode 100755 src/include/xbxbase.h create mode 100755 src/sql/xbcrix.cpp create mode 100755 src/sql/xbcrtbl.cpp create mode 100755 src/sql/xbdrpix.cpp create mode 100755 src/sql/xbdrptbl.cpp create mode 100755 src/sql/xbinsert.cpp create mode 100755 src/sql/xbset.cpp create mode 100755 src/sql/xbsql.cpp create mode 100755 src/tests/tstfuncs.cpp create mode 100755 src/tests/xb_test_bcd.cpp create mode 100755 src/tests/xb_test_date.cpp create mode 100755 src/tests/xb_test_dbf_v3_memos.cpp create mode 100755 src/tests/xb_test_dbf_v3_nomemos.cpp create mode 100755 src/tests/xb_test_dbf_v4_memos.cpp create mode 100755 src/tests/xb_test_dbf_v4_nomemos.cpp create mode 100755 src/tests/xb_test_expnode.cpp create mode 100755 src/tests/xb_test_expression.cpp create mode 100755 src/tests/xb_test_file.cpp create mode 100755 src/tests/xb_test_filter.cpp create mode 100755 src/tests/xb_test_funcs.cpp create mode 100755 src/tests/xb_test_linklist.cpp create mode 100755 src/tests/xb_test_lock.cpp create mode 100755 src/tests/xb_test_lock2.cpp create mode 100755 src/tests/xb_test_log.cpp create mode 100755 src/tests/xb_test_mdx.cpp create mode 100755 src/tests/xb_test_ndx.cpp create mode 100755 src/tests/xb_test_ndx2.cpp create mode 100755 src/tests/xb_test_sql.cpp create mode 100755 src/tests/xb_test_string.cpp create mode 100755 src/tests/xb_test_tblmgr.cpp create mode 100755 src/tests/xb_test_uda.cpp create mode 100755 src/tests/xb_test_xbase.cpp create mode 100755 src/utils/checkndx.cpp create mode 100755 src/utils/dbfxtrct.cpp create mode 100755 src/utils/preamble.txt create mode 100755 src/utils/reindex.cpp create mode 100755 src/utils/xb_cfg_check.cpp create mode 100755 src/utils/xb_copydbf.cpp create mode 100755 src/utils/xb_dbfutil.cpp create mode 100755 src/utils/xb_deletall.cpp create mode 100755 src/utils/xb_dumpdbt.cpp create mode 100755 src/utils/xb_dumphdr.cpp create mode 100755 src/utils/xb_dumpix.cpp create mode 100755 src/utils/xb_dumprecs.cpp create mode 100755 src/utils/xb_execsql.cpp create mode 100755 src/utils/xb_pack.cpp create mode 100755 src/utils/xb_tblinfo.cpp create mode 100755 src/utils/xb_undelall.cpp create mode 100755 src/utils/xb_zap.cpp (limited to 'src') 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 + +namespace xb{ + +int xbDate::iDaysInMonths[2][13]; +int xbDate::iAggregatedDaysInMonths[2][13]; + +/*************************************************************************/ +//! @brief Constructor. + +xbDate::xbDate() { + Sysdate(); + SetDateTables(); +} + +/*************************************************************************/ +//! @brief Constructor. +/*! + \param sDate8In - Input date. +*/ + +xbDate::xbDate( const xbString & sDate8In ) { + if( DateIsValid( sDate8In )) + sDate8.Set( sDate8In ); + else + Sysdate(); + SetDateTables(); +} + +/*************************************************************************/ +//! @brief Constructor. +/*! + \param sDate8In - Input date. +*/ +xbDate::xbDate( const char * sDate8In ) { + + if( DateIsValid( sDate8In )) + sDate8.Set( sDate8In ); + else + Sysdate(); + SetDateTables(); +} + +/*************************************************************************/ +//! @brief Constructor. +/*! + \param lJulDate - Input julian date. +*/ +xbDate::xbDate( xbInt32 lJulDate ) { + SetDateTables(); + JulToDate8( lJulDate ); +} + +/*************************************************************************/ +//! @brief Destructor. +xbDate::~xbDate(){} +/*************************************************************************/ +//! @brief Set operator= +/*! + \param dt - Date value for set operation. +*/ +void xbDate::operator=( const xbDate & dt ){ + sDate8.Set( dt.Str()); +} +/*************************************************************************/ +//! @brief operator += +/*! + This routine adds lDays to the date + \param lDays - Number of days to add to the date. +*/ +void xbDate::operator+=( xbInt32 lDays ){ + JulToDate8( JulianDays() + lDays ); +} +/*************************************************************************/ +//! @brief operator -= +/*! + This routine subtracts lDays from the date. + \param lDays - Number of days to subtract from the date. +*/ +void xbDate::operator-=( xbInt32 lDays ){ + JulToDate8( JulianDays() - lDays ); +} +/*************************************************************************/ +//! @brief operator ++ +/*! + This routine adds one day to the date field. +*/ +void xbDate::operator++(xbInt32){ + *this+=1; +} +/*************************************************************************/ +//! @brief operator -- +/*! + This routine subtracts one day from the date field. +*/ +void xbDate::operator--(xbInt32){ + *this-=1; + return; +} +/*************************************************************************/ +//! @brief operator - +/*! + This routine subtracts one date from another date returning the difference. + \param dt - Date to subtract. + \returns Number of days +*/ +xbInt32 xbDate::operator-( const xbDate &dt ) const{ + return JulianDays() - dt.JulianDays(); +} +/*************************************************************************/ +//! @brief operator + +/*! + This routine adds additional days to a date field. + \param lCount - Number of days to add. + \returns New date in CCYYMMDD format. +*/ +const char *xbDate::operator+( xbInt32 lCount ){ + JulToDate8( JulianDays() + lCount ); + return sDate8.Str(); +} +/*************************************************************************/ +//! @brief operator - +/*! + This routine subtracts days from a date field. + \param lCount - Number of days to subtract. + \returns New date in CCYYMMDD format. +*/ +const char *xbDate::operator-( xbInt32 lCount ){ + JulToDate8( JulianDays() - lCount ); + return sDate8; +} +/*************************************************************************/ +//! @brief operator == +/*! + This routine compares two dates for equality. + \param dt - Date to compare. + \returns xbTrue - Dates match.
xbFalse - Dates don't match. +*/ +xbBool xbDate::operator==( const xbDate &dt ) const{ + if( JulianDays() == dt.JulianDays() ) + return xbTrue; + else + return xbFalse; +} +/*************************************************************************/ +//! @brief operator != +/*! + This routine compares two dates for inequality. + \param dt - Date to compare. + \returns xbTrue - Dates don't match.
xbFalse - Dates match. +*/ +xbBool xbDate::operator!=( const xbDate &dt ) const{ + if( JulianDays() != dt.JulianDays() ) + return xbTrue; + else + return xbFalse; +} +/*************************************************************************/ +//! @brief operator < +/*! + This routine compares two dates + \param dt - Date to compare. + \returns xbTrue - Left date is less than right date.
+ xbFalse - Left date is not less than right date. +*/ +xbBool xbDate::operator<( const xbDate &dt ) const { + if( JulianDays() < dt.JulianDays() ) + return xbTrue; + else + return xbFalse; +} +/*************************************************************************/ +//! @brief > +/*! + This routine compares two dates + \param dt - Date to compare. + \returns xbTrue - Left date is greater than right date.
+ xbFalse - Left date is not greater than right date. +*/ +xbBool xbDate::operator>( const xbDate &dt ) const { + if( JulianDays() > dt.JulianDays() ) + return xbTrue; + else + return xbFalse; +} +/*************************************************************************/ +//! @brief operator <= +/*! + This routine compares two dates + \param dt - Date to compare. + \returns xbTrue - Left date is less than or equal to right date.
+ xbFalse - Left date is not less than or equal to right date. +*/ +xbBool xbDate::operator<=( const xbDate &dt ) const { + if( JulianDays() <= dt.JulianDays() ) + return xbTrue; + else + return xbFalse; +} +/*************************************************************************/ +//! @brief operator >= +/*! + This routine compares two dates + \param dt - Date to compare. + \returns xbTrue - Left date is greater than or equal to right date.
+ xbFalse - Left date is not greater than or equal to right date. +*/ +xbBool xbDate::operator>=( const xbDate &dt ) const { + if( JulianDays() >= dt.JulianDays() ) + return xbTrue; + else + return xbFalse; +} +/*************************************************************************/ +//! @brief Calculate century for a given year. +/*! + This routine calculates a century for a given year. It uses an 80/20 + rolling date window to calculate the century. + + \param iCalcYear - Two digit year to calculate a century for. + \returns Century calculated for the two digit year. +*/ +xbInt16 xbDate::CalcRollingCenturyForYear( xbInt16 iCalcYear ) const { + + xbDate d; + xbInt16 iThisYear = d.YearOf(); + xbInt16 iThisCentury = d.CenturyOf(); + iThisYear -= (iThisCentury * 100); + if( iThisYear < 80 && iCalcYear < (iThisYear+20) ) + return iThisCentury; + else if( iThisYear >= 80 && + iCalcYear < iThisYear && + iCalcYear >= (iThisYear-80)) + return iThisCentury; + else + return iThisCentury - 1; +} +/*************************************************************************/ +//! @brief Get century for date. +/*! + \returns This routine returns the century from the date. +*/ +xbInt16 xbDate::CenturyOf() const { + char Century[3]; + Century[0] = sDate8[1]; + Century[1] = sDate8[2]; + Century[2] = 0x00; + return( atoi( Century )); +} +/*************************************************************************/ +//! @brief Get the day of the week. +/*! + \param sOutCharDay - Output character day of week (Sun-Sat). + \returns XB_INVALID_DATE
XB_NO_ERROR +*/ +xbInt16 xbDate::CharDayOf( xbString &sOutCharDay ) { + + struct tm tblock; + char buf[25]; + + tblock.tm_year = YearOf() - 1900; + tblock.tm_mon = MonthOf() - 1; + tblock.tm_mday = DayOf( XB_FMT_MONTH ); + tblock.tm_hour = 0; + tblock.tm_min = 0; + tblock.tm_sec = 1; + tblock.tm_isdst = -1; + if( mktime( &tblock ) == -1 ){ + sOutCharDay = "" ; + return XB_INVALID_DATE; + } else { + strftime( buf, 25, "%A", &tblock ); + sOutCharDay = buf; + } + return XB_NO_ERROR;; +} +/*************************************************************************/ +//! @brief Get the month from the date. +/*! + \param sOutCharMonth - Output character month. + \returns XB_INVALID_DATE
XB_NO_ERROR +*/ +xbInt16 xbDate::CharMonthOf( xbString &sOutCharMonth ) { + struct tm tblock; + char buf[25]; + tblock.tm_year = YearOf() - 1900; + tblock.tm_mon = MonthOf() - 1; + tblock.tm_mday = DayOf( XB_FMT_MONTH ); + tblock.tm_hour = 0; + tblock.tm_min = 0; + tblock.tm_sec = 1; + tblock.tm_isdst = -1; + if( mktime( &tblock ) == -1 ){ + sOutCharMonth = ""; + return XB_INVALID_DATE; + } else { + strftime( buf, 25, "%B", &tblock ); + sOutCharMonth = buf; + } + return XB_NO_ERROR; +} +/*************************************************************************/ +//! @brief Check a date for valid data. +/*! + \param sDateIn - Date to check for valid formaat of CCYYMMDD. + \returns xbTrue - Valid date.
xbFalse - Not a valid date. +*/ +xbBool xbDate::DateIsValid( const xbString &sDateIn ) const { + + xbInt16 iYear, iMonth, iDay; + char sYear[5]; + char sMonth[3]; + char sDay[3]; + + if( sDateIn.Len() != 8 ) + return xbFalse; + + if(!isdigit( sDateIn[1] ) || !isdigit( sDateIn[2] ) || !isdigit( sDateIn[3] ) || + !isdigit( sDateIn[4] ) || !isdigit( sDateIn[5] ) || !isdigit( sDateIn[6] ) || + !isdigit( sDateIn[7] ) || !isdigit( sDateIn[8] ) ) + return xbFalse; + + sDay[0] = sDateIn[7]; + sDay[1] = sDateIn[8]; + sDay[2] = 0x00; + iDay = atoi( sDay ); + + sMonth[0] = sDateIn[5]; + sMonth[1] = sDateIn[6]; + sMonth[2] = 0x00; + iMonth = atoi( sMonth ); + + sYear[0] = sDateIn[1]; + sYear[1] = sDateIn[2]; + sYear[2] = sDateIn[3]; + sYear[3] = sDateIn[4]; + sYear[4] = 0x00; + iYear = atoi( sYear ); + + // valid years are 0001 thru 9999 + if( iYear < 1 || iYear > 9999 || iMonth < 1 || iMonth > 12 || iDay < 1 || iDay > 31 ) + return xbFalse; + + // April, June, September and November have 30 days + if(( iMonth==4 || iMonth==6 || iMonth==9 || iMonth==11 )&& iDay > 30 ) + return xbFalse; + + // check for February with leap year + if( iMonth == 2 ){ + if(( iYear % 4 == 0 && iYear % 100 != 0 ) || iYear % 400 == 0 ){ + if( iDay > 29 ){ + return xbFalse; + } + } else if( iDay > 28 ){ + return xbFalse; + } + } + return xbTrue; +} + +/*************************************************************************/ +//! @brief +/*! + This routine returns the numeric day. + \param iFormat + XB_FMT_WEEK Number of day in WEEK 0-6 ( Sat - Fri )
+ XB_FMT_MONTH Number of day in MONTH 1-31
+ XB_FMT_YEAR Number of day in YEAR 1-366 + \returns XB_INVALID_OPTION
XB_NO_ERROR +*/ + +xbInt16 xbDate::DayOf( xbInt16 iFormat ) const { + + xbInt16 iOutDay = 0; + char sDay[3]; + xbInt16 iDay, iMonth, iYear, iDay2; + + // check for valid format switch + if( iFormat!=XB_FMT_WEEK && iFormat!=XB_FMT_MONTH && iFormat!=XB_FMT_YEAR ) + return XB_INVALID_OPTION; + + if( iFormat == XB_FMT_WEEK ){ + //DayOf( XB_FMT_MONTH, iDay ); + iDay = DayOf( XB_FMT_MONTH ); + iMonth = MonthOf(); + iYear = YearOf(); + + // The following formula uses Zeller's Congruence to determine the day of the week + if( iMonth > 2 ) // init to February + iMonth -= 2; + else { + iMonth += 10; + iYear--; + } + iDay2 = ((13 * iMonth - 1) / 5) + iDay + ( iYear % 100 ) + + (( iYear % 100 ) / 4) + ((iYear /100 ) / 4 ) - 2 * + ( iYear / 100 ) + 77 ; + + iOutDay = iDay2 - 7 * ( iDay2 / 7 ); + iOutDay == 6 ? iOutDay = 0 : iOutDay++; + } + else if( iFormat == XB_FMT_MONTH ){ + sDay[0] = sDate8[7]; + sDay[1] = sDate8[8]; + sDay[2] = 0x00; + iOutDay = atoi( sDay ); + } else { + iOutDay = iAggregatedDaysInMonths[IsLeapYear()][MonthOf()-1] + DayOf( XB_FMT_MONTH ); + } + return iOutDay; +} + +/*************************************************************************/ +#ifdef XB_DEBUG_SUPPORT +//! @brief Dump date information to stdout. +/*! + \param sTitle - Title for output. + \returns Return Codes +*/ +void xbDate::Dump( const char *sTitle ){ + fprintf( stdout, "%s\n sDate = [%s]\n", sTitle, sDate8.Str() ); +} + +/*************************************************************************/ +//! @brief Dump the date tables. +/*! + This dumps the internal date structures to stdout. + \returns void +*/ + +void xbDate::DumpDateTables(){ + fprintf( stdout, "Date Tables\n" ); + fprintf( stdout, "Month *-Aggragated Days-* *--Days In Month--*\n" ); + fprintf( stdout, " *-NonLeap Leap-* *--NonLeap Leap--*\n" ); + for( int i = 1; i < 13; i++ ) + fprintf( stdout, " %2d %3d %3d %3d %3d\n", i, + iAggregatedDaysInMonths[0][i],iAggregatedDaysInMonths[1][i], + iDaysInMonths[0][i], iDaysInMonths[1][i]); +} +#endif + +/*************************************************************************/ +//! @brief Format MM/DD/YY date +/*! + This routine takes an MM/DD/YY format date as input and populates a + date class with the appropriate YYYYMMDD data. + + \param sCtodInDate - MM/DD/YY formatted date as input. + \returns XB_INVALID_OPTION
XB_NO_ERROR +*/ +xbInt16 xbDate::CTOD( const xbString &sCtodInDate ){ + + + if( sCtodInDate[1] != ' ' && ( sCtodInDate[3] == '\\' || sCtodInDate[3] == '/') ){ + char yy[3]; + yy[0] = sCtodInDate[7]; + yy[1] = sCtodInDate[8]; + yy[2] = 0x00; + sDate8.Sprintf( "%02d%c%c%c%c%c%c", CalcRollingCenturyForYear( atoi( yy )), + sCtodInDate[7], sCtodInDate[8], sCtodInDate[1], sCtodInDate[2], sCtodInDate[4], sCtodInDate[5] ); + return XB_NO_ERROR; + } + else{ + return XB_INVALID_DATE; + } +} +/*************************************************************************/ +//! @brief +/*! + This routine will reformat a date based on the format specifiers entered + in sFmtIn. If no input format is specified, the routine will use the + system default date format. + + \param sFmtIn - A format specifier with the following paramaters:
+ + 1) YYDDD - A julian date format + 2) YY or YYYY will print a 2 or 4 digit year + 3) M,MM,MMM or MMMM + M - one digit month if no leading zero + MM - two digit month, contains leading zero + MMM - Jan through Dec + MMMM - January through December + 4) D,DD,DDD or DDDD + D - one digit dayif no leading zero + DD - two digit day, contains leading zero + DDD - Sun through Sat (or julian if YYDDD) + DDDD - Sunday through Saturday + + \param sOutFmtDate - Reformatted output date. + \returns XB_NO_ERROR +

+ Format Examples:
+ MM/DD/YY
+ YYYY-MM-DD
+ DDDDDDDDDDD MMMMMMMMMMM DD,YYYY +*/ +xbInt16 xbDate::FormatDate( const xbString &sFmtIn, xbString &sOutFmtDate ){ + xbUInt32 FmtCtr; + char type; + xbUInt32 iTypeCtr; + xbString ws; + xbString sWrkFmt; + sOutFmtDate = ""; + + /* use format for this specific string if available, else use default format */ + if( strlen( sFmtIn ) > 0 ) + sWrkFmt = sFmtIn; + else + sWrkFmt = GetDefaultDateFormat(); + + if( strstr( sWrkFmt.Str(), "YYDDD" )){ + sOutFmtDate.Sprintf( "%c%c%03d", sDate8[3], sDate8[4], DayOf( XB_FMT_YEAR )); + } else { + FmtCtr = 1; + while( FmtCtr <= sWrkFmt.Len() ){ + if( sWrkFmt[FmtCtr] != 'D' && sWrkFmt[FmtCtr] != 'M' && sWrkFmt[FmtCtr] != 'Y' ){ + sOutFmtDate += sWrkFmt[FmtCtr]; + FmtCtr++; + iTypeCtr = 0; + } else { + type = sWrkFmt[FmtCtr]; + iTypeCtr = 0; + while( sWrkFmt[FmtCtr] == type ) { + iTypeCtr++; + FmtCtr++; + } + switch( type ){ + case 'D': + + if( iTypeCtr == 1 ){ + sOutFmtDate += ws.Sprintf( "%d", DayOf( XB_FMT_MONTH )); + } + else if( iTypeCtr == 2 ){ + sOutFmtDate += ws.Sprintf( "%c%c", sDate8[7], sDate8[8] ); + } else { + xbString sCDO; + CharDayOf( sCDO ); + ws.Assign( sCDO, 1, iTypeCtr ); + sOutFmtDate += ws.Str(); + } + break; + + case 'M': + if( iTypeCtr == 1 ){ + sOutFmtDate += ws.Sprintf( "%d", MonthOf()); + } + else if( iTypeCtr == 2 ){ + sOutFmtDate += ws.Sprintf( "%c%c", sDate8[5], sDate8[6] ); + } else { + xbString sCMO; + CharMonthOf( sCMO ); + ws.Assign( sCMO, 1, iTypeCtr ); + sOutFmtDate += ws.Str(); + } + break; + + case 'Y': + if( iTypeCtr == 2 ){ + sOutFmtDate += ws.Sprintf( "%c%c", sDate8[3], sDate8[4] ); + } + else if( iTypeCtr == 4 ){ + sOutFmtDate += ws.Sprintf( "%c%c%c%c", sDate8[1], sDate8[2], sDate8[3], sDate8[4] ); + } + break; + default: + break; + } + } + } + } + return XB_NO_ERROR; +} +/*************************************************************************/ +//! @brief Return the date value. +/*! + \returns char ptr to date value. +*/ +const char * xbDate::Str() const{ + return sDate8.Str(); +}; +/*************************************************************************/ +//! @brief Determine if date is a leap year. +/*! + \returns xbTrue - Is leapyear.
xbFalse - Not a leap year. +*/ +xbBool xbDate::IsLeapYear() const { + xbInt16 iYear = YearOf(); + if(( iYear % 4 == 0 && iYear % 100 != 0 ) || iYear % 400 == 0 ) + return xbTrue; + else + return xbFalse; +} +/*************************************************************************/ +//! @brief Determine if date is a leap year. +/*! + \param iYear - Year to check for leap year status. + \returns xbTrue - Is leapyear.
xbFalse - Not a leap year. +*/ +xbBool xbDate::IsLeapYear( xbInt16 iYear ) const { + if(( iYear % 4 == 0 && iYear % 100 != 0 ) || iYear % 400 == 0 ) + return xbTrue; + else + return xbFalse; +} +/*************************************************************************/ +//! @brief Calculate julian days for a given date. +/*! + \returns The number of days since 01/01/0001 + JUL_OFFSET. +*/ +xbInt32 xbDate::JulianDays() const{ + xbInt32 ly = YearOf() - 1; + xbInt32 lDays = ly * 365L + ly / 4L - ly / 100L + ly / 400L; + lDays += DayOf( XB_FMT_YEAR ); + return lDays + JUL_OFFSET; +} +/*************************************************************************/ +//! @brief Convert the number of julian days to gregorian date. +/*! + \param lJulDays - Julian days. + \returns XB_NO_ERROR +*/ +xbInt16 xbDate::JulToDate8( xbInt32 lJulDays ) +{ + lJulDays -= JUL_OFFSET; + // calculate the year + xbInt16 iYear = (xbInt16)(lJulDays / 365.24 ); + lJulDays -= (iYear * 365L) + (iYear / 4L) - (iYear / 100L) + (iYear / 400L); + iYear++; + while( lJulDays <= 0 ){ + iYear--; + lJulDays += (365L + IsLeapYear( iYear )); + } + // this for loop calculates the month by comparing the number of days remaining to one of the tables + xbInt16 iIsLeap = IsLeapYear(iYear); + xbInt16 iMonth = 1; + while( ((xbInt16) lJulDays > iAggregatedDaysInMonths[iIsLeap][iMonth]) && (iMonth < 12) ) + iMonth++; + + lJulDays -= iAggregatedDaysInMonths[iIsLeap][iMonth-1]; + sDate8.Sprintf( "%04d%02d%02ld", iYear, iMonth, lJulDays ); + return XB_NO_ERROR; +} +/*************************************************************************/ +//! @brief Set the date to the last day of month for a given date. +/*! + This routine sets the last date of the month. + \returns XB_NO_ERROR +*/ +xbInt16 xbDate::LastDayOfMonth(){ + sDate8.Sprintf( "%4.4d%2.2d%2.2d", YearOf(), MonthOf(), iDaysInMonths[IsLeapYear()][MonthOf()]); + return XB_NO_ERROR; +}; +/*************************************************************************/ +//! @brief Return the month for the date. +/*! + \returns The month of the date. +*/ +xbInt16 xbDate::MonthOf() const { + xbInt16 iOutMonth; + char month[3]; + month[0] = sDate8[5]; + month[1] = sDate8[6]; + month[2] = 0x00; + iOutMonth = atoi( month ); + return iOutMonth; +} + +/*************************************************************************/ +//! @brief Set the date. +/*! + \param sDateIn - Input date. + \returns XB_NO_ERROR
XB_INVALID_DATE +*/ +xbInt16 xbDate::Set( const xbString & sDateIn ){ + if( DateIsValid( sDateIn )){ + sDate8 = sDateIn; + return XB_NO_ERROR; + } + return XB_INVALID_DATE; +} +/*************************************************************************/ +//! @brief This routine sets up static data tables on startup. +/*! + \returns void +*/ +void xbDate::SetDateTables() { + if( iAggregatedDaysInMonths[1][12] != 366 ){ /* first time called ? */ + + iAggregatedDaysInMonths[0][0] = 0; + iAggregatedDaysInMonths[0][1] = 31; + iAggregatedDaysInMonths[0][2] = 59; + iAggregatedDaysInMonths[0][3] = 90; + iAggregatedDaysInMonths[0][4] = 120; + iAggregatedDaysInMonths[0][5] = 151; + iAggregatedDaysInMonths[0][6] = 181; + iAggregatedDaysInMonths[0][7] = 212; + iAggregatedDaysInMonths[0][8] = 243; + iAggregatedDaysInMonths[0][9] = 273; + iAggregatedDaysInMonths[0][10] = 304; + iAggregatedDaysInMonths[0][11] = 334; + iAggregatedDaysInMonths[0][12] = 365; + iAggregatedDaysInMonths[1][0] = 0; + iAggregatedDaysInMonths[1][1] = 31; + iAggregatedDaysInMonths[1][2] = 60; + iAggregatedDaysInMonths[1][3] = 91; + iAggregatedDaysInMonths[1][4] = 121; + iAggregatedDaysInMonths[1][5] = 152; + iAggregatedDaysInMonths[1][6] = 182; + iAggregatedDaysInMonths[1][7] = 213; + iAggregatedDaysInMonths[1][8] = 244; + iAggregatedDaysInMonths[1][9] = 274; + iAggregatedDaysInMonths[1][10] = 305; + iAggregatedDaysInMonths[1][11] = 335; + iAggregatedDaysInMonths[1][12] = 366; + + iDaysInMonths[0][0] = 0; + iDaysInMonths[0][1] = 31; + iDaysInMonths[0][2] = 28; + iDaysInMonths[0][3] = 31; + iDaysInMonths[0][4] = 30; + iDaysInMonths[0][5] = 31; + iDaysInMonths[0][6] = 30; + iDaysInMonths[0][7] = 31; + iDaysInMonths[0][8] = 31; + iDaysInMonths[0][9] = 30; + iDaysInMonths[0][10] = 31; + iDaysInMonths[0][11] = 30; + iDaysInMonths[0][12] = 31; + iDaysInMonths[1][0] = 0; + iDaysInMonths[1][1] = 31; + iDaysInMonths[1][2] = 29; + iDaysInMonths[1][3] = 31; + iDaysInMonths[1][4] = 30; + iDaysInMonths[1][5] = 31; + iDaysInMonths[1][6] = 30; + iDaysInMonths[1][7] = 31; + iDaysInMonths[1][8] = 31; + iDaysInMonths[1][9] = 30; + iDaysInMonths[1][10] = 31; + iDaysInMonths[1][11] = 30; + iDaysInMonths[1][12] = 31; + } +} +/*************************************************************************/ +//! @brief Set the date equal to the system date. +/*! + \returns XB_NO_ERROR +*/ +xbInt16 xbDate::Sysdate(){ + + #ifdef HAVE__LOCALTIME64_S_F + __time64_t timer; + _time64( &timer ); + struct tm tblock; + _localtime64_s( &tblock, &timer ); + tblock.tm_year += 1900; + tblock.tm_mon++; + sDate8.Sprintf( "%4d%02d%02d", tblock.tm_year, tblock.tm_mon, tblock.tm_mday ); + #else + time_t timer; + timer = time( &timer ); + struct tm *tblock; + tblock = localtime( &timer ); + tblock->tm_year += 1900; + tblock->tm_mon++; + sDate8.Sprintf( "%4d%02d%02d",tblock->tm_year,tblock->tm_mon,tblock->tm_mday ); + #endif + + return XB_NO_ERROR; +} +/*************************************************************************/ +//! @brief Returns the year of the date. +/*! + \returns The year of the date. +*/ +xbInt16 xbDate::YearOf() const { + char year[5]; + year[0] = sDate8[1]; + year[1] = sDate8[2]; + year[2] = sDate8[3]; + year[3] = sDate8[4]; + year[4] = 0x00; + xbInt16 iOutYear = atoi( year ); + return iOutYear; +}; +} /* namespace */ \ No newline at end of file diff --git a/src/core/xbdbf.cpp b/src/core/xbdbf.cpp new file mode 100755 index 0000000..8e020fb --- /dev/null +++ b/src/core/xbdbf.cpp @@ -0,0 +1,4220 @@ +/* xbdbf.cpp + +XBase64 Software Library + +Copyright (c) 1997,2003,2014,2022 Gary A Kunkel + +The xb64 software library is covered under the terms of the GPL Version 3, 2007 license. + +Email Contact: + + XDB-devel@lists.sourceforge.net + XDB-users@lists.sourceforge.net + +*/ + +#include "xbase.h" + + +namespace xb{ + +/************************************************************************/ +//! @brief Constructor +/*! + \param x Pointer to xbXbase +*/ +xbDbf::xbDbf( xbXBase * x ) : xbFile( x ){ + xbase = x; + SchemaPtr = NULL; + RecBuf = NULL; + RecBuf2 = NULL; + InitVars(); +} +/************************************************************************/ +void xbDbf::InitVars() +{ + iNoOfFields = 0; + iDbfStatus = XB_CLOSED; + ulCurRec = 0L; + cVersion = 0x00; + cUpdateYY = 0x00; + cUpdateMM = 0x00; + cUpdateDD = 0x00; + ulNoOfRecs = 0L; + uiHeaderLen = 0x00; + uiRecordLen = 0x00; + cTransactionFlag = 0x00; + cEncryptionFlag = 0x00; + cIndexFlag = 0x00; + cLangDriver = 0x00; + iFileVersion = 0; /* Xbase64 file version */ + iAutoCommit = -1; + + SetFileName ( "" ); + sAlias.Set ( "" ); + SetDirectory ( GetDataDirectory()); + + #ifdef XB_LOCKING_SUPPORT + iLockFlavor = -1; + bTableLocked = xbFalse; + bHeaderLocked = xbFalse; + ulAppendLocked = 0; + SetAutoLock( -1 ); + lloRecLocks.SetDupKeys( xbFalse ); + #endif + + #ifdef XB_INDEX_SUPPORT + ixList = NULL; + pCurIx = NULL; + vpCurIxTag = NULL; + sCurIxType = ""; + ClearTagList(); + #endif + #ifdef XB_NDXINF_SUPPORT + llNdxInfData.Clear(); + #endif +} + +/************************************************************************/ +//! @brief Destructor +xbDbf::~xbDbf(){ + + // is there is an uncommited update, discard it. + // as we don't know if it is an append or an update + if( iDbfStatus == XB_UPDATED ) + Abort(); + + if( iDbfStatus != XB_CLOSED ) + Close(); + + if( SchemaPtr ){ + free( SchemaPtr ); + SchemaPtr = NULL; + } + if( RecBuf ){ + free( RecBuf ); + RecBuf = NULL; + } + if( RecBuf2){ + free( RecBuf2 ); + RecBuf2 = NULL; + } + Close(); +} +/************************************************************************/ +//! @brief Abort any uncommited changes for the current record buffer. +/*! + \returns Return Codes +*/ +xbInt16 xbDbf::Abort(){ + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + + try{ + if( iDbfStatus == XB_UPDATED ){ + #ifdef XB_MEMO_SUPPORT + if( MemoFieldsExist()){ + if(( iRc = Memo->Abort()) != XB_NO_ERROR ){ + iErrorStop = 10; + throw iRc; + } + } + #endif + memcpy( RecBuf, RecBuf2, uiRecordLen ); + iDbfStatus = XB_OPEN; + } + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbdbf::Abort() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return iRc; +} +/************************************************************************/ +#ifdef XB_INDEX_SUPPORT +//! @brief Add an index to the internal list of indices for this table. +/*! + The index list is used during any table update process to update any open + index file. Index files can contain one or more tags. + + \param ixIn Pointer to index object for a given index file. + \param sFmt NDX or MDX. + \returns Return Codes + +*/ + +xbInt16 xbDbf::AddIndex( xbIx * ixIn, const xbString &sFmt ){ + + xbIxList *ixt; // this + if(( ixt = (xbIxList *) malloc( sizeof( xbIxList ))) == NULL ) + return XB_NO_ERROR; + + ixt->ix = ixIn; + ixt->next = NULL; + ixt->sFmt = new xbString( sFmt ); + ixt->sFmt->ToUpperCase(); + + if( ixList ){ + xbIxList *ixn = ixList; // next + while( ixn->next ){ + ixn = ixn->next; + } + ixn->next = ixt; + } else { + ixList = ixt; + } + return XB_NO_ERROR; +} +#endif // XB_INDEX_SUPPORT + + +/************************************************************************/ +//! @brief Append the current record to the data file. +/*! + This method attempts to append the contents of the current record buffer + to the end of the DBF file, updates the file date, number of records in the file + and updates any open indices associated with this data file.
+ + To add a record, an application would typically blank the record buffer, + update various fields in the record buffer, then append the record.
+ + The append method performs the following tasks:
+ 1) Create new index key values
+ 2) Lock the table
+ 3) Lock append bytes
+ 4) Lock indices
+ 5) Read the dbf header
+ 6) Check for dup keys
+ 7) Calc last update date, no of recs
+ 8) Add keys
+ 9) Unlock indices
+ 10) Update file header
+ 11) Unlock file header
+ 12) Append record
+ 13) Unlock append bytes
+ +Note: Locking memo files is not needed as the memo file updates are handled outside of the append method.
+ + \returns Return Codes +*/ + +xbInt16 xbDbf::AppendRecord(){ + xbInt16 iErrorStop = 0; + xbInt16 iRc = XB_NO_ERROR; + xbUInt32 ulSaveCurRec = 0; + + try{ + #ifdef XB_INDEX_SUPPORT + xbIxList *ixList = GetIxList(); + // do this step first before anything is locked, reduce lock time as much as possible + while( ixList ){ + + // std::cout << "xbDbf::CreateKeys(x)\n"; + if(( iRc = ixList->ix->CreateKeys( 1 )) != XB_NO_ERROR ){ + iErrorStop = 10; + throw iRc; + } + + ixList = ixList->next; + } + #endif // XB_INDEX_SUPPORT + + // lock everything up for an update + #ifdef XB_LOCKING_SUPPORT + // xbInt16 iAutoLock = GetAutoLock(); + if( iAutoLock ){ + + if(( iRc = LockHeader( XB_LOCK )) != XB_NO_ERROR ){ + if( iRc == XB_LOCK_FAILED ) { + return iRc; + } else { + iErrorStop = 20; + throw iRc; + } + } + if(( iRc = LockAppend( XB_LOCK )) != XB_NO_ERROR ){ + if( iRc == XB_LOCK_FAILED ){ + LockHeader( XB_UNLOCK ); + return iRc; + } else { + iErrorStop = 30; + throw iRc; + } + } + + #ifdef XB_INDEX_SUPPORT + if(( iRc = LockIndices( XB_LOCK )) != XB_NO_ERROR ){ + iErrorStop = 40; + throw iRc; + } + #endif // XB_INDEX_SUPPORT + + } + #endif // XB_LOCKING_SUPPORT + if(( iRc = ReadHeader( 1, 1 )) != XB_NO_ERROR ){ + iErrorStop = 50; + throw iRc; + } + #ifdef XB_INDEX_SUPPORT + ixList = GetIxList(); + + while( ixList ){ + if(( iRc = ixList->ix->CheckForDupKeys()) != 0 ){ + if( iRc < 0 ){ + iErrorStop = 60; + throw iRc; + } + return XB_KEY_NOT_UNIQUE; + } + ixList = ixList->next; + } + + #endif // XB_INDEX_SUPPORT + + // calculate the latest header information + xbDate d; + cUpdateYY = (char) d.YearOf() - 1900; + cUpdateMM = (char) d.MonthOf(); + cUpdateDD = (char) d.DayOf( XB_FMT_MONTH ); + ulSaveCurRec = ulCurRec; + ulNoOfRecs++; + ulCurRec = ulNoOfRecs; + + #ifdef XB_INDEX_SUPPORT + + + ixList = GetIxList(); + while( ixList ){ + if(( iRc = ixList->ix->AddKeys( ulCurRec )) != XB_NO_ERROR ){ + iErrorStop = 50; + throw iRc; + } + ixList = ixList->next; + } + + #ifdef XB_LOCKING_SUPPORT + if( iAutoLock ){ + if(( iRc = LockIndices( XB_UNLOCK )) != XB_NO_ERROR ){ + iErrorStop = 60; + throw iRc; + } + } + #endif // XB_LOCKING_SUPPORT + #endif // XB_INDEX_SUPPORT + + // rewrite the header record + if(( iRc = WriteHeader( 1, 1 )) != XB_NO_ERROR ){ + iErrorStop = 60; + throw iRc; + } + + #ifdef XB_LOCKING_SUPPORT + if( iAutoLock ){ + if(( iRc = LockHeader( XB_UNLOCK )) != XB_NO_ERROR ){ + iErrorStop = 70; + throw iRc; + } + } + #endif + + // write the last record + if(( iRc = xbFseek( (uiHeaderLen+((xbInt64)(ulNoOfRecs-1)*uiRecordLen)), 0 )) != XB_NO_ERROR ){ + iErrorStop = 80; + throw iRc; + } + + if(( iRc = xbFwrite( RecBuf, uiRecordLen, 1 )) != XB_NO_ERROR ){ + iErrorStop = 90; + throw iRc; + } + + // write the end of file marker + if(( iRc = xbFputc( XB_CHAREOF )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + + #ifdef XB_LOCKING_SUPPORT + if( iAutoLock ){ + if(( iRc = LockAppend( XB_UNLOCK )) != XB_NO_ERROR ){ + iErrorStop = 110; + throw( iRc ); + } + } + #endif // XB_LOCKING_SUPPORT + + } + catch (xbInt16 iRc ){ + if( ulSaveCurRec != 0 ){ + ulCurRec = ulSaveCurRec; + ulNoOfRecs--; + } + #ifdef XB_LOCKING_SUPPORT + if( iAutoLock ){ + #ifdef XB_INDEX_SUPPORT + LockIndices( XB_UNLOCK ); + #endif // XB_INDEX_SUPPORT + LockAppend( XB_UNLOCK ); + LockHeader( XB_UNLOCK ); + } + #endif // XB_LOCKING_SUPPORT + + if( iRc != XB_LOCK_FAILED && iRc != XB_KEY_NOT_UNIQUE ){ + xbString sMsg; + sMsg.Sprintf( "xbdbf::Append() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + } + + if( iRc == XB_NO_ERROR ) + iDbfStatus = XB_OPEN; + return iRc; +} + +/************************************************************************/ +#ifdef XB_NDXINF_SUPPORT +//! @brief Asscoiate a non production index to a DBF file. +/*! + + The original Dbase (TM) software supported non production indices (NDX) and production indices (MDX). + The production indices are opened automatically when the DBF file is opened but the non-production + indices are not. This method is specific to the Xbas64 library and providex a means to link non production + NDX index files to the DBF file so they will be opened automatically when the DBF file is opened.
+ + This routine requires NDXINF support be enabled when building the library.
+ This routine creates a file with the same name as the DBF file, but with an extension of INF.
+ + + \param sIxType Currently only NDX. Future versions will support additional non prod index types. + \param sIxName The index name. + \param iOpt 0 - Add index to .INF if not already there
+ 1 - Remove index from .INF if there + + \returns Return Codes + +*/ + +xbInt16 xbDbf::AssociateIndex( const xbString &sIxType, const xbString &sIxName, xbInt16 iOpt ){ + + xbInt16 iRc = 0; + xbInt16 iErrorStop = 0; + + try{ + xbString sIxTypeIn = sIxType; + sIxTypeIn.Trim(); + xbString sIxNameIn = sIxName; + sIxNameIn.Trim(); + + if( sIxTypeIn != "NDX" || sIxName == "" ) + return XB_INVALID_INDEX; + + if(( iRc = LoadNdxInfData()) != XB_NO_ERROR ){ + iErrorStop = 10; + throw iRc; + } + + // check if entry exists + xbLinkListNode * llN = llNdxInfData.GetHeadNode(); + xbBool bFound = xbFalse; + xbString s; + + while( llN && !bFound ){ + s = llN->GetKey(); + if( s.Len() > 0 ){ + if( sIxNameIn == s ) + bFound = xbTrue; + } + llN = llN->GetNextNode(); + } + + xbBool bUpdated = xbFalse; + if( iOpt == 0 && !bFound ){ + s.Sprintf( "%s%c%c", sIxName.Str(), 0x0d, 0x0a ); + llNdxInfData.InsertAtEnd( s ); + bUpdated = xbTrue; + + } else if( iOpt == 1 && bFound ){ + llNdxInfData.RemoveByVal( s ); + bUpdated = xbTrue; + } + + if( bUpdated ){ + if(( iRc = SaveNdxInfData()) != XB_NO_ERROR ){ + iErrorStop = 20; + throw iRc; + } + } + + } catch( xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbdbf::AssociateIndex() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return iRc; +} +#endif // XB_NDXINF_SUPPORT + +/************************************************************************/ +//! @brief Blank the record buffer. +/*! + + This method would typically be called to initialize the record buffer before + updates are applied to append a new record. + + \returns Return Codes +*/ + +xbInt16 xbDbf::BlankRecord() +{ + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + + try{ + + if( iDbfStatus == XB_CLOSED ){ + iErrorStop = 10; + iRc = XB_NOT_OPEN; + throw iRc; + } + + if( iDbfStatus == XB_UPDATED ){ + if( GetAutoCommit() == 1 ){ + if(( iRc = Commit()) != XB_NO_ERROR ){ + iErrorStop = 20; + throw iRc; + } + } else { + if(( iRc = Abort()) != XB_NO_ERROR ){ + iErrorStop = 30; + throw iRc; + } + } + } + ulCurRec = 0; + memset( RecBuf, 0x20, uiRecordLen ); + memset( RecBuf2, 0x20, uiRecordLen ); + } + + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbdbf::BlankRecord() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + + return iRc; +} +/************************************************************************/ +#ifdef XB_INDEX_SUPPORT + +#ifdef XB_DEBUG_SUPPORT + +/*! + This method is used to check an index tag's intgerity. + + \param iTagOpt 0 - Check current tag
+ 1 - Check all tag
+ + \param iOutputOpt Output message destination
+ 0 = stdout
+ 1 = Syslog
+ 2 = Both
+ + \returns Return Codes +*/ + +xbInt16 xbDbf::CheckTagIntegrity( xbInt16 iTagOpt, xbInt16 iOutputOpt ){ + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + + try{ + +// std::cout << "CheckTagIntegrity()\n"; + + if( iTagOpt == 0 ){ + if( pCurIx ) + return pCurIx->CheckTagIntegrity( vpCurIxTag, iOutputOpt ); + else + return XB_INVALID_TAG; + + } else { + + xbLinkListNode *llN = GetTagList(); + xbTag *pTag; + + while( llN ){ + pTag = llN->GetKey(); + if(( iRc = pTag->GetIx()->CheckTagIntegrity( pTag->GetVpTag(), iOutputOpt )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + llN = llN->GetNextNode(); + } + } + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbdbf::CheckTagIntegrity() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + + return iRc; +} + +#endif // XB_DEBUG_SUPPORT + +/************************************************************************/ +/*! + This method is used to reindex / rebuild index tag. + + \param iTagOpt 0 - Reindex current tag
+ 1 - Reindex all tags
+ + \returns Return Codes +*/ + +xbInt16 xbDbf::Reindex( xbInt16 iTagOpt ){ + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + void *vp; + + try{ + if( iTagOpt == 0 ){ + if( pCurIx ){ + + // + // xbString = save current tag name + // + + iRc = pCurIx->Reindex( &vpCurIxTag ); + if( iRc != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + // + // restore current tag name + // + return iRc; + + } else { + + return XB_INVALID_TAG; + + } + + } else { + + xbLinkListNode *llN = GetTagList(); + xbTag *pTag; + + while( llN ){ + pTag = llN->GetKey(); + vp = pTag->GetVpTag(); + if(( iRc = pTag->GetIx()->Reindex( &vp )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + llN = llN->GetNextNode(); + } + + + } + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbdbf::Reindex() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + + return iRc; +} + +/************************************************************************/ +// @brief Clear the index tag list. +/* + Protected method. Clears the list inf index tags. + \returns void. +*/ +void xbDbf::ClearTagList(){ + + xbTag *pTag; + xbBool bDone = xbFalse; + while( llTags.GetNodeCnt() > 0 && !bDone ){ + if( llTags.RemoveFromFront( pTag ) != XB_NO_ERROR ){ + bDone = xbTrue; + } else { + if( pTag ) + delete pTag; + } + } +} +#endif // XB_INDEX_SUPPORT + +/************************************************************************/ +//! @brief Close DBF file/table. +/*! + This routine flushes any remaining updates to disk, closes the DBF file and + any associated memo and index files. + + \returns Return Codes +*/ + +xbInt16 xbDbf::Close(){ + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + + try{ + + if(iDbfStatus == XB_CLOSED) + return XB_NO_ERROR; + + else if( iDbfStatus == XB_UPDATED ){ + + if( GetAutoCommit() == 1 ){ + if(( iRc = Commit()) != XB_NO_ERROR ){ + iErrorStop = 10; + throw iRc; + } + } else { + if(( iRc = Abort()) != XB_NO_ERROR ){ + iErrorStop = 20; + throw iRc; + } + } + } + + if(SchemaPtr){ + free( SchemaPtr ); + SchemaPtr = NULL; + } + if(RecBuf){ + free( RecBuf ); + RecBuf = NULL; + } + if(RecBuf2){ + free( RecBuf2 ); + RecBuf2 = NULL; + } + + + #ifdef XB_MEMO_SUPPORT + if( iMemoFieldCnt > 0 ){ + Memo->CloseMemoFile(); + delete Memo; + Memo = NULL; + } + #endif + + // close any open index files, remove from the ix list + #ifdef XB_INDEX_SUPPORT + while( ixList ){ + ixList->ix->Close(); + RemoveIndex( ixList->ix ); + } + #endif + + #ifdef XB_LOCKING_ON + if( xblfh ){ + fclose( xblfh ); + xblfh = NULL; + } + #endif + + if(( iRc = xbase->RemoveTblFromTblList( this )) != XB_NO_ERROR ){ + xbString sMsg; + sMsg.Sprintf( "Alias = [%s]", sAlias.Str()); + xbase->WriteLogMessage( sMsg.Str() ); + iErrorStop = 30; + throw iRc; + } + + xbFclose(); + InitVars(); + + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbdbf::Close() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return iRc; +} + + +/************************************************************************/ +#ifdef XB_INDEX_SUPPORT +//! @brief Close an open index file +/*! + All index files are automatically closed when the DBF file is closed. + Under normal conditions, it is not necessary to explicitly close an index file + with this routine. Any updates posted to a DBF file while an index is closed + will not be reflected in the closed index file. + + \param pIx Pointer to index object to close. + \returns Return Codes +*/ +xbInt16 xbDbf::CloseIndexFile( xbIx *pIx ){ + + xbInt16 iErrorStop = 0; + xbInt16 iRc = XB_NO_ERROR; + + try{ + + // verify index is open and in the list + xbBool bFound = xbFalse; + xbIxList *p = GetIxList(); + while( p && !bFound ){ + if( pIx == p->ix ) + bFound = xbTrue; + p = p->next; + } + if( !bFound ){ + iErrorStop = 10; + iRc = XB_NOT_OPEN; + throw iRc; + } + + // close it + if(( iRc = pIx->Close()) != XB_NO_ERROR ){ + iErrorStop = 20; + throw iRc; + } + + // remove it from the list + if(( iRc = RemoveIndex( pIx )) != XB_NO_ERROR ){ + iErrorStop = 30; + throw iRc; + } + + // refresh the tag list + if(( iRc = UpdateTagList()) != XB_NO_ERROR ){ + iErrorStop = 40; + throw iRc; + } + + if( pIx == pCurIx ){ + pCurIx = NULL; + vpCurIxTag = NULL; + sCurIxType = ""; + } + + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbdbf::CloseIndexFile() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return iRc; +} +#endif // XB_INDEX_SUPPORT + +/************************************************************************/ +//! @brief Commit updates to disk +/*! + + This routine commits any pending updates to disk. + + \returns Return Codes +*/ + +xbInt16 xbDbf::Commit(){ + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + try{ + + if( iDbfStatus == XB_UPDATED ){ + if( ulCurRec == 0 ){ + if(( iRc = AppendRecord()) != XB_NO_ERROR ){ + iErrorStop = 20; + throw iRc; + } + } else { + if(( iRc = PutRecord( ulCurRec )) != XB_NO_ERROR ){ + iErrorStop = 30; + throw iRc; + } + } + } + } + catch (xbInt16 iRc ){ + if( iRc != XB_LOCK_FAILED ){ + xbString sMsg; + sMsg.Sprintf( "xbdbf::Commit() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + } + return iRc; +} + + +/************************************************************************/ +//! @brief Copy table (dbf) file structure. +/*! + + This routine will copy the structure of a dbf file and if successful + return a pointer to the new table in an open state. + + \param dNewTable Reference to new table object. + \param sNewTableName New table (dbf) name. + \param sNewTableAlias Alias name of new table. + \param iOverlay xbTrue - Overlay existing file.
+ xbFalse - Don't overlay existing file. + \param iShareMode XB_SINGLE_USER
+ XB_MULTI_USER + \returns Return Codes +*/ + +//! Copy DBF structure +/*! +*/ +xbInt16 xbDbf::CopyDbfStructure( xbDbf * dNewTable, const xbString &sNewTableName, + const xbString & sNewTableAlias, xbInt16 iOverlay, xbInt16 iShareMode ) { + +// If successful, the table is returned in an open state after executing this method + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + xbSchema *newTableSchema = NULL; + + try{ + + if( iDbfStatus == XB_CLOSED ){ + iErrorStop = 10; + iRc = XB_DBF_FILE_NOT_OPEN; + throw iRc; + } + + if( !dNewTable ){ + iErrorStop = 20; + iRc = XB_INVALID_OBJECT; + throw iRc; + } + + // Get the number of schema entries for this table + xbInt32 lSchemaRecCnt = GetFieldCnt() + 1; + + // Allocate a Schema = No Of Fields + 1 + if((newTableSchema=(xbSchema *)malloc( (size_t) lSchemaRecCnt * sizeof(xbSchema)))==NULL){ + iErrorStop = 30; + iRc = XB_NO_MEMORY; + throw iRc; + } + + // Populate the Schema + xbInt32 l; + for( l = 0; l < lSchemaRecCnt-1; l++ ){ + memset( newTableSchema[l].cFieldName, 0x00, 11 ); + for( int x = 0; x < 10 && SchemaPtr[l].cFieldName[x]; x++ ) + newTableSchema[l].cFieldName[x] = SchemaPtr[l].cFieldName[x]; + newTableSchema[l].cType = SchemaPtr[l].cType; + newTableSchema[l].iFieldLen = SchemaPtr[l].cFieldLen; + newTableSchema[l].iNoOfDecs = SchemaPtr[l].cNoOfDecs; + } + + // set the last one to zeroes + memset( newTableSchema[l].cFieldName, 0x00, 11 ); + newTableSchema[l].cType = 0; + newTableSchema[l].iFieldLen = 0; + newTableSchema[l].iNoOfDecs = 0; + + dNewTable->SetVersion(); + #ifdef XB_MEMO_SUPPORT + if( MemoFieldsExist()) + dNewTable->SetCreateMemoBlockSize( Memo->GetBlockSize() ); + #endif + + // Call the create a table function + if(( iRc = dNewTable->CreateTable( sNewTableName, sNewTableAlias, newTableSchema, iOverlay, iShareMode )) != XB_NO_ERROR ){ + iErrorStop = 40; + throw iRc; + } + } + + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbDbf::CopyDbfStructure() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + + if( newTableSchema ) + free( newTableSchema ); + + return iRc; +} + + +/************************************************************************/ +#ifdef XB_INDEX_SUPPORT +//! @brief Create a new tag (index) for this dbf file (table). +/*! + This routine creates a new tag (index) on a dbf file. The library currently supports NDX and MDX + indices. If you don't have a specific need for an NDX file, use MDX. + + \param sIxType "MDX" or "NDX". + \param sName Index or tag name. + \param sKey Index key expression, + \param sFilter Filter expression. Not applicable for NDX indices. + \param iDescending xbTrue for descending. Not available for NDX indices.
+ xbFalse - ascending + \param iUnique xbTrue - Unique index
xbFalse - Not unique index. + \param iOverLay xbTrue - Overlay if exists
+ xbFalse - Don't overlay if it exists. + \param pIxOut Pointer to pointer of output index object. + \param vpTagOut Pointer to pointer of newly created tag, + \returns Return Codes +*/ + +xbInt16 xbDbf::CreateTag( const xbString &sIxType, const xbString &sName, const xbString &sKey, const xbString &sFilter, + xbInt16 iDescending, xbInt16 iUnique, xbInt16 iOverLay, xbIx **pIxOut, void **vpTagOut ){ + + // this routine is used to open indices and link to files + + xbInt16 iErrorStop = 0; + xbInt16 iRc = XB_NO_ERROR; + xbBool bLocked = xbFalse; + + try{ + xbString sType = sIxType; + sType.ToUpperCase(); + + #ifdef XB_LOCKING_SUPPORT + if( !bTableLocked ){ + if(( iRc = LockTable( XB_LOCK )) != XB_NO_ERROR ){ + iErrorStop = 10; + throw( iRc ); + } + bLocked = xbTrue; + } + #endif // XB_LOCKING_SUPPORT + + if( sIxType == "" ){ + iErrorStop = 100; + iRc = XB_INVALID_OPTION; + throw iRc; + + #ifdef XB_NDX_SUPPORT + } else if( sIxType == "NDX" ){ + xbIxNdx *ixNdx = new xbIxNdx( this ); + + if(( iRc = ixNdx->CreateTag( sName, sKey, sFilter, iDescending, iUnique, iOverLay, vpTagOut )) != XB_NO_ERROR ){ + iErrorStop = 110; + throw iRc; + } + + if(( iRc = AddIndex( ixNdx, sIxType )) != XB_NO_ERROR ){ + iErrorStop = 120; + throw iRc; + } + *pIxOut = ixNdx; + + // Set the current tag if one not already set + if( sCurIxType == "" ){ + sCurIxType = "NDX"; + pCurIx = ixNdx; + vpCurIxTag = ixNdx->GetTag(0); + } + + #endif + + #ifdef XB_MDX_SUPPORT + } else if( sIxType == "MDX" ){ + + if( GetVersion() == 3 ){ // MDX indexes were version 4 and higher + iErrorStop = 200; + iRc = XB_INVALID_INDEX; + throw iRc; + } + + xbIxMdx *ixMdx; + xbString s; + // look through the index list and see if there is an mdx pointer we can grab + xbBool bMdxFound = xbFalse; + xbIxList *ixList = GetIxList(); + while( ixList && !bMdxFound ){ + s = ixList->sFmt->Str(); + if( s == "MDX" ){ + ixMdx = (xbIxMdx *) ixList->ix; + bMdxFound = xbTrue; + } + } + + if( !bMdxFound ) + ixMdx = new xbIxMdx( this ); + + if(( iRc = ixMdx->CreateTag( sName, sKey, sFilter, iDescending, iUnique, iOverLay, vpTagOut )) != XB_NO_ERROR ){ + iErrorStop = 210; + throw iRc; + } + + if( !bMdxFound ){ + if(( iRc = AddIndex( ixMdx, "MDX" )) != XB_NO_ERROR ){ + iErrorStop = 220; + throw iRc; + } + + cIndexFlag = 0x01; + if(( iRc = WriteHeader( 1, 0 )) != XB_NO_ERROR ){ + iErrorStop = 230; + throw iRc; + } + } + *pIxOut = ixMdx; + + // set the current tag if one not already set + if( sCurIxType == "" ){ + sCurIxType = "MDX"; + pCurIx = ixMdx; + vpCurIxTag = ixMdx->GetTag(0); + } + + #endif + + } else { + iErrorStop = 200; + iRc = XB_INVALID_OPTION; + throw iRc; + } + + if(( iRc = UpdateTagList()) != XB_NO_ERROR ){ + iErrorStop = 300; + throw iRc; + } + + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbdbf::CreateTag() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + + #ifdef XB_LOCKING_SUPPORT + if( bLocked ) + LockTable( XB_UNLOCK ); + #endif // XB_LOCKING_SUPPORT + + + return iRc; +} +#endif // XB_INDEX_SUPPORT + +/************************************************************************/ +//! @brief Delete or undelete all records in a dbf file (table). +/*! + This routine deletes or un-deletes all records. The xbase file format contains + a leading one byte character used for flagging a record as deleted. When a record + is deleted, it's not physically removed from the file, the first byte is flagged as deleted. + + \param iOption 0 - Delete all records.
+ 1 - Un-delete all deleted records. + \returns Return Codes +*/ + +xbInt16 xbDbf::DeleteAll( xbInt16 iOption ) +{ + xbInt16 iErrorStop = 0; + xbInt16 iRc = XB_NO_ERROR; + xbUInt32 ulRecCnt; + + try{ + if(( iRc = GetRecordCnt( ulRecCnt )) != XB_NO_ERROR ){ + iErrorStop = 10; + throw iRc; + } + if( ulRecCnt == 0 ) + return XB_NO_ERROR; + for( xbUInt32 ul = 0; ul < ulRecCnt; ul++ ){ + if(( iRc = GetRecord( ul+1 )) != XB_NO_ERROR ){ + iErrorStop = 20; + throw iRc; + } + if( iOption == 0 ){ /* delete all option */ + if( !RecordDeleted()){ + if(( iRc = DeleteRecord()) != XB_NO_ERROR ){ + iErrorStop = 30; + throw iRc; + } + } + } else { /* undelete all option */ + if( RecordDeleted()){ + if(( iRc = UndeleteRecord()) != XB_NO_ERROR ){ + iErrorStop = 40; + throw iRc; + } + } + } + } + } + catch (xbInt16 iRc ){ + if( iRc != XB_LOCK_FAILED ){ + xbString sMsg; + sMsg.Sprintf( "xbDbf::DeleteAll() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + } + return iRc; +} + +/************************************************************************/ +#ifdef XB_INDEX_SUPPORT +//! @brief +/*! + This routine deletes all indices associated with the dbf file. + + \returns Return Codes +*/ +xbInt16 xbDbf::DeleteAllIndexFiles(){ + + xbInt16 iRc = 0; + xbInt16 iErrorStop = 0; + xbBool bLocked = xbFalse; + + #ifdef XB_NDXINF_SUPPORT + xbString sIxName; + #endif // XB_NDXINF_SUPPORT + + try{ + #ifdef XB_LOCKING_SUPPORT + if( !bTableLocked ){ + if(( iRc = LockTable( XB_LOCK )) != XB_NO_ERROR ){ + iErrorStop = 10; + throw( iRc ); + } + bLocked = xbTrue; + } + #endif // XB_LOCKING_SUPPORT + + // close any open index files, delete it, remove from the ix list + while( ixList ){ + ixList->ix->Close(); + ixList->ix->xbRemove(); + #ifdef XB_NDXINF_SUPPORT + // if XB_NDXINF_SUPPORT is enabled, all open non prod indices should be in here + if( *ixList->sFmt != "MDX" ){ // production indices not stored in .INF dataset + if(( iRc = ixList->ix->GetFileNamePart( sIxName )) != XB_NO_ERROR ){ + iErrorStop = 20; + throw iRc; + } + if(( iRc = AssociateIndex( *ixList->sFmt, sIxName, 1 )) != XB_NO_ERROR ){ + iErrorStop = 30; + throw iRc; + } + } + #endif + RemoveIndex( ixList->ix ); + } + } catch( xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbdbf::DeleteAllIndexFiles() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + #ifdef XB_LOCKING_SUPPORT + if( bLocked ) + LockTable( XB_UNLOCK ); + #endif // XB_LOCKING_SUPPORT + return iRc; +} +#endif // XB_INDEX_SUPPORT + +/************************************************************************/ +//! @brief Delete all records. +/*! + This routine deletes all the records in a table / dbf file. + + \returns Return Codes +*/ + +xbInt16 xbDbf::DeleteAllRecords(){ + return DeleteAll( 0 ); +} + +/************************************************************************/ +#ifdef XB_NDXINF_SUPPORT +//! @brief Delete .INF File +/*! + The original Dbase (TM) software supported non production indices (NDX) and production indices (MDX). + The production indices are opened automatically when the DBF file is opened but the non-production + indices are not. This method is specific to the Xbas64 library and providex a means to link non production + NDX index files to the DBF file so they will be opened automatically when the DBF file is opened.
+ + This routine requires NDXINF support be enabled when building the library.
+ This routine deletes the .INF file.
+ + \returns Return Codes +*/ +xbInt16 xbDbf::DeleteNdxInfData(){ + xbInt16 iRc = 0; + xbInt16 iErrorStop = 0; + + try{ + xbString sNdxInfFileName; + if(( iRc = GetNdxInfFileName( sNdxInfFileName )) != XB_NO_ERROR ){ + iErrorStop = 10; + throw iRc; + } + xbFile f( xbase ); + f.SetFileName( sNdxInfFileName ); + if( f.FileExists()){ + if(( iRc = f.xbRemove()) != XB_NO_ERROR ){ + iErrorStop = 20; + throw iRc; + } + } + } catch( xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbdbf::DeleteNdxInfData() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return iRc; +} +#endif // XB_NDXINF_SUPPORT + +/************************************************************************/ +//! @brief Delete the current record. +/*! + This routine flags the current record for deletion if it's not already flagged. + + + \returns XB_NO_ERROR
+ XB_INVALID_RECORD +*/ + +xbInt16 xbDbf::DeleteRecord(){ + if( RecBuf && ulCurRec > 0 ){ + if( RecBuf[0] != 0x2a){ + if( iDbfStatus != XB_UPDATED ){ + iDbfStatus = XB_UPDATED; + memcpy( RecBuf2, RecBuf, uiRecordLen ); // save off original before making any updates + } + RecBuf[0] = 0x2a; + } + return XB_NO_ERROR; + } + else + return XB_INVALID_RECORD; +} +/************************************************************************/ +//! @brief Delete a table. +/*! + This routine deletes a given table, associated index files if any, the + memo file if any and the .INF file if any. + \returns Return Codes +*/ + +xbInt16 xbDbf::DeleteTable(){ + + xbInt16 iRc = 0; + xbInt16 iErrorStop = 0; + + try{ + + #ifdef XB_LOCKING_SUPPORT + if(( iRc = LockTable( XB_LOCK )) != XB_NO_ERROR ){ + iErrorStop = 10; + throw iRc; + } + #endif // XB_LOCKING_SUPPORT + + #ifdef XB_INDEX_SUPPORT + if(( iRc = DeleteAllIndexFiles()) != XB_NO_ERROR ){ + iErrorStop = 20; + throw iRc; + } + + #ifdef XB_NDXINF_SUPPORT + if(( iRc = DeleteNdxInfData()) != XB_NO_ERROR ){ + iErrorStop = 30; + throw iRc; + } + #endif // XB_NDXINF_SUPPORT + #endif // XB_INDEX_SUPPORT + + #ifdef XB_MEMO_SUPPORT + xbInt16 iMemoFldCnt = GetMemoFieldCnt(); + xbString sMemoFileName; + if(iMemoFldCnt > 0 ){ + sMemoFileName = Memo->GetFqFileName(); + } + #endif // XB_MEMO_SUPPORT + + if(( iRc = Close()) != XB_NO_ERROR ){ + iErrorStop = 40; + throw iRc; + } + + if(( iRc = xbRemove()) != XB_NO_ERROR ){ + iErrorStop = 50; + throw iRc; + } + + #ifdef XB_MEMO_SUPPORT + if( iMemoFieldCnt > 0 ){ + xbFile f( xbase ); + if(( iRc = f.xbRemove( sMemoFileName )) != XB_NO_ERROR ){ + iErrorStop = 60; + throw iRc; + } + } + #endif // XB_MEMO_SUPPORT + + } catch( xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbdbf::DeleteTable() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + #ifdef XB_LOCKING_SUPPORT + LockTable( XB_UNLOCK ); + #endif // XB_LOCKING_SUPPORT + return iRc; +} + +/************************************************************************/ +//! @brief Delete an index tag. + +/*! + This routine deletes an index tag + \param sIxType Either "NDX" or "MDX".
+ \param sName Tag name to delete.
+ \returns Return Codes +*/ + +xbInt16 xbDbf::DeleteTag( const xbString &sIxType, const xbString &sName ){ + + xbInt16 iErrorStop = 0; + xbInt16 iRc = XB_NO_ERROR; + xbIx *pIx = NULL; + + try{ + + #ifdef XB_LOCKING_SUPPORT + // xbInt16 iAutoLock = GetAutoLock(); + if( iAutoLock && !GetTableLocked() ){ + if(( iRc = LockTable( XB_LOCK )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + } + #endif + + if( sIxType == "" ){ + iErrorStop = 110; + iRc = XB_INVALID_OPTION; + throw iRc; + + #ifdef XB_NDX_SUPPORT + } else if( sIxType == "NDX" ){ + + xbIxList *ixl = ixList; + xbBool bDone = xbFalse; + while( ixl && !bDone ){ + + if( ixl->ix->GetTagName( NULL ) == sName ){ + bDone = xbTrue; + + // remove from .INF if it's there + #ifdef XB_NDXINF_SUPPORT + if(( iRc = AssociateIndex( "NDX", sName, 1 )) != XB_NO_ERROR ){ + iErrorStop = 120; + throw iRc; + } + #endif // XB_NDXINF_SUPPORT + + if(( iRc = ixl->ix->DeleteTag( ixl->ix->GetTag( 0 ))) != XB_NO_ERROR ){ + iErrorStop = 130; + throw iRc; + } + + if(( iRc = RemoveIndex( ixl->ix )) != XB_NO_ERROR ){ + iErrorStop = 140; + throw iRc; + } + + if( ixl->ix == pCurIx ) + SetCurTag( "", NULL, NULL ); + + } + ixl = ixl->next; + } + #endif + + #ifdef XB_MDX_SUPPORT + } else if( sIxType == "MDX" ){ + xbIxList *ixl = ixList; + xbIxList *ixlNext; + xbIxList *ixlPrev = NULL; + xbBool bDone = xbFalse; + xbIxMdx *pMdx; + xbMdxTag *pMdxTag; + xbInt16 iTagCnt = 0; + + while( ixl && !bDone ){ + ixlNext = ixl->next; + pMdx = (xbIxMdx *) ixl->ix; + iTagCnt = pMdx->GetTagCount(); + for( xbInt16 i = 0; i < iTagCnt && !bDone; i++ ){ + pMdxTag = (xbMdxTag *) pMdx->GetTag( i ); + if( pMdx->GetTagName( pMdxTag ) == sName ){ + bDone = xbTrue; + iRc = pMdx->DeleteTag( pMdxTag ); + if( iRc > 0 ){ + // Successful delete of only tag in production mdx file - need to remove it from the list, update the dbf header + cIndexFlag = 0x00; + if(( iRc = WriteHeader( 1, 0 )) != XB_NO_ERROR ){ + iErrorStop = 150; + throw iRc; + } + if(( iRc = RemoveIndex( ixl->ix )) != XB_NO_ERROR ){ + iErrorStop = 160; + throw iRc; + } + if( ixlPrev == NULL ){ + // std::cout << "setting ixList to null or should be\n"; + ixList = ixlNext; + } else { + ixlPrev = ixlNext; + } + } else if( iRc < 0 ){ + iErrorStop = 170; + throw iRc; + } + if( ixl->ix == pCurIx ) + SetCurTag( "", NULL, NULL ); + } + } + ixlPrev = ixl; + ixl = ixlNext; + } + + if( !bDone ) + return XB_INVALID_TAG; + + #endif + + } else { + iErrorStop = 180; + iRc = XB_INVALID_OPTION; + throw iRc; + } + + if(( iRc = UpdateTagList()) != XB_NO_ERROR ){ + iErrorStop = 190; + throw iRc; + } + + // unlock as necessary + #ifdef XB_LOCKING_SUPPORT + if( iAutoLock && GetTableLocked() ){ + if(( iRc = LockTable( XB_UNLOCK )) != XB_NO_ERROR ){ + iErrorStop = 200; + throw iRc; + } + } + #endif + + } + catch (xbInt16 iRc ){ + if( pIx ) delete pIx; + xbString sMsg; + sMsg.Sprintf( "xbdbf::DeleteTag() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + + #ifdef XB_LOCKING_SUPPORT + if( iAutoLock && GetTableLocked() ){ + LockTable( XB_UNLOCK ); + } + #endif + + } + return iRc; +} + +/************************************************************************/ +#ifdef XB_DEBUG_SUPPORT +//! @brief Dump dbf file header. +/*! + This routine dumps dbf header information to the console. + + \param iOption 1 = Print header only
+ 2 = Field data only
+ 3 = Header and Field data
+ 4 = Header, Field and Memo header data if applicable + \returns Return Codes +*/ + +xbInt16 xbDbf::DumpHeader( xbInt16 iOption ) const { + int i; + int iMemoCtr = 0; + + if( iOption < 1 || iOption > 4 ) + return XB_INVALID_OPTION; + + if( iDbfStatus == XB_CLOSED ) + return XB_NOT_OPEN; + + std::cout << "\nDatabase file " << GetFqFileName() << std::endl << std::endl; + + if( iOption != 2 ){ + std::cout << "File header data:" << std::endl; + + xbInt16 sVer = DetermineXbaseTableVersion( cVersion ); + + if( sVer == 3 ) + std::cout << "Dbase III file" << std::endl; + else if ( sVer == 4 ) + std::cout << "Dbase IV file" << std::endl << std::endl; + else if ( sVer == 5 ) + std::cout << "Dbase V file" << std::endl << std::endl; + else if ( sVer == 7 ) + std::cout << "Dbase VII file" << std::endl << std::endl; + else + std::cout << "Unknown Version" << std::endl; + + /* display the bit stream */ + unsigned char c, tfv, displayMask = 1 << 7; + tfv = cVersion; + std::cout << "File descriptor bits = "; + for( c = 1; c<= 8; c++ ){ + std::cout << (tfv & displayMask ? '1' : '0'); + tfv <<= 1; + } + std::cout << std::endl; + + std::cout << "Descriptor bits legend:" << std::endl; + std::cout << " 0-2 = version number" << std::endl; + std::cout << " 3 = presence of dBASE IV memo" << std::endl; + std::cout << " 4-6 = SQL table presence" << std::endl; + std::cout << " 7 = Presence of any memo file (dBASE III PLUS or dBASE IV)" << std::endl << std::endl; + + std::cout << "Last update date = " + << (int) cUpdateMM << "/" << (int) cUpdateDD << "/" << (int) cUpdateYY % 100 << std::endl; + + std::cout << "Header length = " << uiHeaderLen << std::endl; + std::cout << "Record length = " << uiRecordLen << std::endl; + std::cout << "Records in file = " << ulNoOfRecs << std::endl << std::endl << std::endl; + + std::cout << "Transaction Flag = "; + xbase->BitDump( cTransactionFlag ); + std::cout << std::endl; + + std::cout << "Encryption Flag = "; + xbase->BitDump( cEncryptionFlag ); + std::cout << std::endl; + + std::cout << "Index Flag = "; + xbase->BitDump( cIndexFlag ); + std::cout << std::endl; + + std::cout << "Lang Driver = " << (int) cIndexFlag << " - "; + xbase->BitDump( cIndexFlag ); + std::cout << std::endl; + + std::cout << "Open Index Files = " << GetPhysicalIxCnt() << std::endl; + } + + if( iOption != 1 ){ + char c; + std::cout << "Field Name Type Length Decimals IxFlag" << std::endl; + std::cout << "---------- ---- ------ -------- ------" << std::endl; + for( i = 0; i < iNoOfFields; i++ ){ + + SchemaPtr[i].cIxFlag ? c = 'Y' : c = ' '; + + if( SchemaPtr[i].cType == 'C' && SchemaPtr[i].cNoOfDecs > 0 ) + printf( "%10s %1c %4d %4d %c\n", SchemaPtr[i].cFieldName, + SchemaPtr[i].cType, SchemaPtr[i].cFieldLen, 0, c ); + else + printf( "%10s %1c %4d %4d %c\n", SchemaPtr[i].cFieldName, + SchemaPtr[i].cType, SchemaPtr[i].cFieldLen, SchemaPtr[i].cNoOfDecs, c ); + + if( SchemaPtr[i].cType == 'M' ) + iMemoCtr++; + } + } + std::cout << std::endl; + +#ifdef XB_MEMO_SUPPORT + if( iOption > 3 && iMemoCtr > 0 ) + Memo->DumpMemoHeader(); +#endif + + return XB_NO_ERROR; +} + +/************************************************************************/ +//! Dump record +/*! + Dump the contents of the specified record + + + \param ulRecNo Record number of record to be dumped. + \param iOutputDest 0 = stdout
+ 1 = Syslog
+ 2 = Both
+ + \param iOutputFmt 0 = with field names
+ 1 = 1 line per rec, no field names
+ 2 = 1 line per rec, first line is a list of field names. + \returns Return Codes +*/ +xbInt16 xbDbf::DumpRecord( xbUInt32 ulRecNo, xbInt16 iOutputDest, xbInt16 iOutputFmt ) { + int i, iRc = XB_NO_ERROR; + + xbString sTemp; + xbString s2; + if( ulRecNo == 0 || ulRecNo > ulNoOfRecs ) + return XB_INVALID_RECORD; + + if( ulCurRec != ulRecNo ){ + iRc = GetRecord( ulRecNo ); + if( iRc != XB_NO_ERROR ) + return iRc; + } + + if( iOutputFmt >= 1 ){ + if( iOutputFmt == 2 ){ + sTemp = "RecNo"; + for( i = 0; i < iNoOfFields; i++ ){ + s2 = SchemaPtr[i].cFieldName; + s2.Trim(); + sTemp += ","; + sTemp += s2; + } + xbase->WriteLogMessage( sTemp.Str(), iOutputDest ); + } + + if( RecordDeleted() ) + s2.Sprintf( "%ld Deleted", ulRecNo ); + + else{ + s2.Sprintf( "%ld ", ulRecNo ); + //xbString s3; + for( i = 0; i < iNoOfFields; i++ ){ + GetField( i, sTemp ); + sTemp.Trim(); + s2.Sprintf( "%s,'%s'", s2.Str(), sTemp.Str()); + } + xbase->WriteLogMessage( s2.Str(),iOutputDest ); + } + + return XB_NO_ERROR; + } + + sTemp.Sprintf( "\nRec Number: %ld", ulRecNo ); + xbase->WriteLogMessage( sTemp.Str(),iOutputDest); + + if( RecordDeleted()) + xbase->WriteLogMessage( "Record flagged as deleted", iOutputDest ); + + + #ifdef XB_MEMO_SUPPORT + xbString sMemo; + #endif + + for( i = 0; i < iNoOfFields; i++ ){ + + #ifdef XB_MEMO_SUPPORT + GetField( i, sTemp ); + sTemp.Trim(); + + if(SchemaPtr[i].cType == 'M'){ + GetMemoField( i, sMemo ); + if( sMemo.Len() > 70 ) + sMemo.Resize( 70 ); + s2.Sprintf ( "%c %s = '%s'", SchemaPtr[i].cType, SchemaPtr[i].cFieldName, sMemo.Str()); + xbase->WriteLogMessage( s2.Str(), iOutputDest); + + /* + xbUInt32 ulMlen; + if( MemoFieldExists( i )){ + Memo->GetMemoFieldLen( i, ulMlen ); + s2.Sprintf( " Len = %d", ulMlen ); + } + xbase->WriteLogMessage( s2.Str(), iOutputDest); + */ + + } + else{ + s2.Sprintf ( "%c %s = '%s'", SchemaPtr[i].cType, SchemaPtr[i].cFieldName, sTemp.Str()); + xbase->WriteLogMessage( s2.Str(), iOutputDest); + } + #else + GetField( i, sTemp ); + sTemp.Trim(); + s2.Sprintf( "%s = '%s'", SchemaPtr[i].cFieldName, sTemp.Str()); + xbase->WriteLogMessage( s2.Str(), iOutputDest); + #endif + + } + return XB_NO_ERROR; +} + + +/************************************************************************/ +#ifdef XB_LOCKING_SUPPORT +//! @brief Dump the table lock status +/*! + Debugging routine. Dumps the table lock status to the console. + \returns void +*/ + +void xbDbf::DumpTableLockStatus() const { + + std::cout << "File Lock Retry Count = [" << GetLockRetryCount() << "]" << std::endl; + std::cout << "File Lock Flavor = ["; + switch (GetLockFlavor()){ + case 1: + std::cout << "Dbase]" << std::endl; + break; + case 2: + std::cout << "Clipper]" << std::endl; + break; + case 3: + std::cout << "Fox]" << std::endl; + break; + case 9: + std::cout << "Xbase64]" << std::endl; + break; + default: + std::cout << "Unknown]" << std::endl; + break; + } + std::cout << "File Auto Lock = ["; + + if( GetAutoLock()) + std::cout << "ON]" << std::endl; + else + std::cout << "OFF]" << std::endl; + if( GetHeaderLocked()) + std::cout << "Header Locked = [TRUE]\n"; + else + std::cout << "Header Locked = [FALSE]\n"; + + if( GetTableLocked()) + std::cout << "Table Locked = [TRUE]\n"; + else + std::cout << "Table Locked = [FALSE]\n"; + + if( GetAppendLocked() > 0 ) + std::cout << "Append Locked = [" << GetAppendLocked() << "]\n"; + else + std::cout << "Append Locked = [FALSE]\n"; + + #ifdef XB_MEMO_SUPPORT + if( GetMemoLocked()) + std::cout << "Memo Locked = [TRUE]\n"; + else + std::cout << "Memo Locked = [FALSE]\n"; + #endif // XB_MEMO_SUPPORT + + xbLinkListNode * llN = GetFirstRecLock(); + if( llN ){ + while( llN ){ + std::cout << "Record Locked = [" << llN->GetKey() << "]\n"; + llN = llN->GetNextNode(); + } + } else { + std::cout << "Record Locked = [None]\n"; + } +} +#endif // XB_LOCKING_SUPPORT +#endif // XB_DEBUG_SUPPORT + +/************************************************************************/ +#ifdef XB_LOCKING_SUPPORT +//! @brief Get the append locked bytes status +/*! + \returns The record number of the new record for the append lock operation. +*/ + +xbUInt32 xbDbf::GetAppendLocked() const { + return this->ulAppendLocked; +} + +#endif // XB_LOCKING_SUPPORT + +/************************************************************************/ +//! @brief Get auto commit setting. +/*! + + This routine returns the table setting if set, otherwise returns the system + level setting. + + \returns Not 0 - Auto commit on for this table.
+ 0 - Auto commit off for this table. +*/ + +xbInt16 xbDbf::GetAutoCommit() const { + return GetAutoCommit( 1 ); +} + +/************************************************************************/ +//! @brief Get auto commit setting. +/*! + + \param iOption 0 - Specific setting for this table
+ 1 - If this table should be auto updated (takes DBMS setting into account) + \returns Not 0 - Auto commit on for this table.
+ 0 - Auto commit off for this table. +*/ + +xbInt16 xbDbf::GetAutoCommit( xbInt16 iOption ) const { + if( iOption == 1 && iAutoCommit == -1 ) + return xbase->GetDefaultAutoCommit(); + else + return iAutoCommit; +} + + +/************************************************************************/ +//! @brief Get Auto Lock setting. +/*! + \returns Auto lock setting. +*/ +#ifdef XB_LOCKING_SUPPORT +xbInt16 xbDbf::GetAutoLock() const{ + return iAutoLock; +} +#endif // XB_LOCKING_SUPPORT + +/************************************************************************/ +#ifdef XB_MEMO_SUPPORT +//! @brief Get the memo file block size used when creating a memo file. +/*! + \returns Memo block size. +*/ +xbUInt32 xbDbf::GetCreateMemoBlockSize() const { + return ulCreateMemoBlockSize; +} +#endif + +/************************************************************************/ +//! @brief Get a pointer to the current index object. +/*! + \returns Pointer to current index. +*/ +#ifdef XB_INDEX_SUPPORT +xbIx *xbDbf::GetCurIx() const { + return pCurIx; +} +/************************************************************************/ +//! @brief Get pointer to current tag for the current index. +/*! + An index file can have one or more tags. An NDX index has one tag. + An MDX file can have up to 47 tags. + + \returns Pointer to current tag. +*/ +void *xbDbf::GetCurTag() const { + return vpCurIxTag; +} +/************************************************************************/ +//! @brief Get the current index type. +/*! + \returns NDX for single tag index.
+ MDX for production multi tag index. +*/ +const xbString &xbDbf::GetCurIxType() const { + return sCurIxType; +} + +/************************************************************************/ +//! @brief Get the current tag name. +/*! + \returns Current Tag Name.
+*/ + +const xbString &xbDbf::GetCurTagName() const { + + if( pCurIx ) + return pCurIx->GetTagName( vpCurIxTag ); + else + return sNullString; +} + +/************************************************************************/ +//! @brief GetFirstKey for tag. +/*! + + Position to the first key for the current tag + \returns Return Codes + +*/ +xbInt16 xbDbf::GetFirstKey(){ + if( pCurIx ) + return pCurIx->GetFirstKey( vpCurIxTag, 1 ); + else + return XB_INVALID_TAG; +} + +/************************************************************************/ +//! @brief GetLastKey for tag. +/*! + + Position to the last key for the current tag + \returns Return Codes + +*/ +xbInt16 xbDbf::GetLastKey(){ + if( pCurIx ) + return pCurIx->GetLastKey( vpCurIxTag, 1 ); + else + return XB_INVALID_TAG; +} + +/************************************************************************/ +//! @brief GetNextKey for tag. +/*! + + Position to the next key for the current tag + \returns Return Codes + +*/ +xbInt16 xbDbf::GetNextKey(){ + if( pCurIx ) + return pCurIx->GetNextKey( vpCurIxTag, 1 ); + else + return XB_INVALID_TAG; +} + +/************************************************************************/ +//! @brief GetPrevKey for tag. +/*! + + Position to the previous key for the current tag + \returns Return Codes + +*/ +xbInt16 xbDbf::GetPrevKey(){ + if( pCurIx ) + return pCurIx->GetPrevKey( vpCurIxTag, 1 ); + else + return XB_INVALID_TAG; +} + +/************************************************************************/ +//! @brief Find record for key. +/*! + + Find a key and position to record if key found + + \param sKey String key to find + \returns Return Codes + +*/ +xbInt16 xbDbf::Find( xbString &sKey ){ + if( pCurIx ) + return pCurIx->FindKey( vpCurIxTag, sKey.Str(), (xbInt32) sKey.Len(), 1 ); + else + return XB_INVALID_TAG; +} + +/************************************************************************/ +//! @brief Find record for key. +/*! + + Find a key and position to record if key found + + \param dtKey Date key to find + \returns Return Codes + +*/ +xbInt16 xbDbf::Find( xbDate &dtKey ){ + + if( pCurIx ) + return pCurIx->FindKey( vpCurIxTag, dtKey, 1 ); + else + return XB_INVALID_TAG; + +} +/************************************************************************/ +//! @brief Find record for key. +/*! + + Find a key and position to record if key found + + \param dtKey Date key to find + \returns Return Codes + +*/ +xbInt16 xbDbf::Find( xbDouble &dKey ){ + + if( pCurIx ) + return pCurIx->FindKey( vpCurIxTag, dKey, 1 ); + else + return XB_INVALID_TAG; + +} + + +#endif // XB_INDEX_SUPPORT + +/************************************************************************/ +//! @brief Return the current record number. +/*! + \returns Returns the current record number. +*/ +xbUInt32 xbDbf::GetCurRecNo() const { + return ulCurRec; +} + +/************************************************************************/ +//! @brief Return the current dbf status. +/*! + \returns 0 = closed
+ 1 = open
+ 2 = updates pending
+*/ +xbInt16 xbDbf::GetDbfStatus() const { + return iDbfStatus; +} +/************************************************************************/ +//! @brief Return the number of fields in the table. +/*! + \returns The number of fields in the table. +*/ +xbInt32 xbDbf::GetFieldCnt() const { + return iNoOfFields; +} + +/************************************************************************/ +#ifdef XB_LOCKING_SUPPORT +//! @brief Get the first first record lock. +/*! + Get the first record lock from a linked list of record locks. + + \returns First record lock. +*/ +xbLinkListNode * xbDbf::GetFirstRecLock() const { + return lloRecLocks.GetHeadNode(); +} +#endif // XB_LOCKING_SUPPORT +/************************************************************************/ +//! @brief Get the first record. +/*! + Get the first not deleted record. This routines skips over any deleted records. + \returns Return Codes +*/ +xbInt16 xbDbf::GetFirstRecord() +{ + return GetFirstRecord( XB_ACTIVE_RECS ); +} + +/************************************************************************/ +//! @brief Get the first record. +/*! + + \param iOption XB_ALL_RECS - Get the first record, deleted or not.
+ XB_ACTIVE_RECS - Get the first active record.
+ XB_DELETED_RECS - Get the first deleted record.
+ \returns Return Codes +*/ +xbInt16 xbDbf::GetFirstRecord( xbInt16 iOption ) +{ + if( ulNoOfRecs == 0 ) + return XB_EMPTY; + + xbInt16 iRc = GetRecord( 1L ); + while( iRc == XB_NO_ERROR && + ((RecordDeleted() && iOption == XB_ACTIVE_RECS) || + (!RecordDeleted() && iOption == XB_DELETED_RECS))) + if( ulCurRec < ulNoOfRecs ) + iRc = GetRecord( ulCurRec + 1 ); + else + return XB_EOF; + + return iRc; +} + +/************************************************************************/ +#ifdef XB_LOCKING_SUPPORT +//! @brief Return lock status of the table header +/*! \returns DBF header lock status +*/ + +xbBool xbDbf::GetHeaderLocked() const { + return this->bHeaderLocked; +} +#endif + +#ifdef XB_INDEX_SUPPORT +//! @brief Return pointer to list of index files for the table. +/*! + \returns Returns an xbIxList * pointer to list of open index files. +*/ + +xbIxList *xbDbf::GetIxList() const{ + return ixList; +} +#endif // XB_INDEX_SUPPORT + + +/************************************************************************/ +//! @brief Get the last record. +/*! + Get the last not deleted record. This routines skips over any deleted records. + \returns Return Codes +*/ +xbInt16 xbDbf::GetLastRecord() +{ + return GetLastRecord( XB_ACTIVE_RECS ); +} +/************************************************************************/ +//! @brief Get the last record. +/*! + + \param iOption XB_ALL_RECS - Get the last record, deleted or not.
+ XB_ACTIVE_RECS - Get the last active record.
+ XB_DELETED_RECS - Get the last deleted record.
+ \returns Return Codes +*/ +xbInt16 xbDbf::GetLastRecord( xbInt16 iOption ) +{ + if( ulNoOfRecs == 0 ) + return XB_EMPTY; + + xbInt16 iRc = GetRecord( ulNoOfRecs ); + while( iRc == XB_NO_ERROR && + ((RecordDeleted() && iOption == XB_ACTIVE_RECS) || + (!RecordDeleted() && iOption == XB_DELETED_RECS))) + if( ulCurRec > 1 ) + iRc = GetRecord( ulCurRec - 1 ); + else + return XB_EOF; + + return iRc; +} + + +/************************************************************************/ +//! @brief Get lock flavor. +/*! + This routine is currently in place to provide structure for future locking + schemes that may differ from the legacy DBase (TM) locking scheme. + \returns Always 1. +*/ + +xbInt16 xbDbf::GetLockFlavor() const{ + if( iLockFlavor == -1 ) + return xbase->GetDefaultLockFlavor(); + else + return iLockFlavor; +} + +/************************************************************************/ +#ifdef XB_MEMO_SUPPORT +/************************************************************************/ +//! @brief Get the lock status of the memo file. +/*! + \returns Lock status of memo file. +*/ +xbBool xbDbf::GetMemoLocked() const { + if( MemoFieldsExist()) + return Memo->GetMemoLocked(); + else + return xbFalse; +} +/************************************************************************/ +//! @brief Get pointer to Memo object. +/*! + \returns This routine returns the pointer to the memo object. +*/ + +xbMemo * xbDbf::GetMemoPtr(){ + return Memo; +} +#endif + + +/************************************************************************/ +#ifdef XB_NDXINF_SUPPORT +//! @brief Return the .INF file name +/*! + If NDXIDX support is enabled in the library, and a non production (ndx) + has been associated with the dbf file, the .INF file name can be retrieved + with this routine. + + \param sNdxInfFileName Output string containing .INF file name. + \returns Return Codes +*/ + +xbInt16 xbDbf::GetNdxInfFileName( xbString &sNdxInfFileName ){ + + sNdxInfFileName = GetFqFileName(); + xbUInt32 lLen = sNdxInfFileName.Len(); + if( lLen < 5 ) + return XB_FILE_NOT_FOUND; + sNdxInfFileName.PutAt(lLen-2, 'I'); + sNdxInfFileName.PutAt(lLen-1, 'N'); + sNdxInfFileName.PutAt(lLen, 'F'); + return XB_NO_ERROR; +} + +/************************************************************************/ +//! @brief Return first node of linked list of .INF items. +/*! + \returns List of .INF entries. +*/ + +xbLinkListNode * xbDbf::GetNdxInfList() const{ + return llNdxInfData.GetHeadNode(); +} +#endif // XB_NDXINF_SUPPORT + + +/************************************************************************/ +//! @brief Get the next record. +/*! + Get the next not deleted record. This routines skips over any deleted records. + \returns Return Codes +*/ +xbInt16 xbDbf::GetNextRecord(){ + return GetNextRecord( XB_ACTIVE_RECS ); +} + +/************************************************************************/ +//! @brief Get the next record. +/*! + \param iOption XB_ALL_RECS - Get the next record, deleted or not.
+ XB_ACTIVE_RECS - Get the next active record.
+ XB_DELETED_RECS - Get the next deleted record.
+ \returns Return Codes +*/ +xbInt16 xbDbf::GetNextRecord( xbInt16 iOption ){ + if( ulNoOfRecs == 0 ) + return XB_EMPTY; + else if( ulCurRec >= ulNoOfRecs ) + return XB_EOF; + xbInt16 iRc = GetRecord( ulCurRec + 1 ); + while( iRc == XB_NO_ERROR && + ((RecordDeleted() && iOption == XB_ACTIVE_RECS) || + (!RecordDeleted() && iOption == XB_DELETED_RECS))) + if( ulCurRec < ulNoOfRecs ) + iRc = GetRecord( ulCurRec + 1 ); + else + return XB_EOF; + return iRc; +} +/************************************************************************/ +//! @brief Get the next record. +/*! + + \param iOption XB_ALL_RECS - Get the next record, deleted or not.
+ XB_ACTIVE_RECS - Get the next active record.
+ XB_DELETED_RECS - Get the next deleted record.
+ \param ulStartRec Get next record, starting from ulStartRec. + \returns Return Codes +*/ + +xbInt16 xbDbf::GetNextRecord( xbInt16 iOption , xbUInt32 ulStartRec ){ + + if( iOption == 0 ) + return GetNextRecord(); + else if( iOption == 1 ){ + if( ulStartRec > 0 ) + ulCurRec = ulStartRec; + xbInt16 iRc = GetNextRecord(); + while( iRc == XB_NO_ERROR && RecordDeleted()) + iRc = GetNextRecord(); + return iRc; + } + else + return XB_INVALID_OPTION; +} + + +/************************************************************************/ +#ifdef XB_INDEX_SUPPORT +//! @brief Physical count of open index files. +/*! + + Returns a physical count of open index files for the dbf file. An index file + can contain one or more tags. + \returns Count of open index files. +*/ + +xbInt32 xbDbf::GetPhysicalIxCnt() const { + + xbInt32 lCnt = 0; + #ifdef XB_INDEX_SUPPORT + xbIxList *p = ixList; + while( p ){ + lCnt++; + p = p->next; + } + #endif + return lCnt; +} +#endif + + +/************************************************************************/ +//! @brief Get the previous record. +/*! + Get the previous not deleted record. This routine skips over any deleted records. + \returns Return Codes +*/ + +xbInt16 xbDbf::GetPrevRecord() +{ + return GetPrevRecord( XB_ACTIVE_RECS ); +} + +/************************************************************************/ +//! @brief Get the previous record. +/*! + + \param iOption XB_ALL_RECS - Get the previous record, deleted or not.
+ XB_ACTIVE_RECS - Get the previous active record.
+ XB_DELETED_RECS - Get the previous deleted record.
+ \returns Return Codes +*/ +xbInt16 xbDbf::GetPrevRecord( xbInt16 iOption ){ + if( ulNoOfRecs == 0 ) + return XB_EMPTY; + else if( ulCurRec <= 1L ) + return XB_BOF; + xbInt16 iRc = GetRecord( ulCurRec - 1 ); + while( iRc == XB_NO_ERROR && + ((RecordDeleted() && iOption == XB_ACTIVE_RECS) || + (!RecordDeleted() && iOption == XB_DELETED_RECS))) + if( ulCurRec > 1 ) + iRc = GetRecord( ulCurRec - 1 ); + else + return XB_BOF; + + return iRc; +} + + +/************************************************************************/ +//! @brief Get record for specified record number. +/*! + Retrieve a record from disk and load it into the record buffer. If auto commit + is enabled and there are pending updates, this routine will flush the updates + to disk before proceeding to ulRecNo. + + \param ulRecNo - Record number to retrieve. + \returns Return Codes +*/ +xbInt16 xbDbf::GetRecord( xbUInt32 ulRecNo ){ + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + + try{ + /* verify the file is open */ + if( iDbfStatus == XB_CLOSED ){ + iErrorStop = 10; + iRc = XB_NOT_OPEN; + throw iRc; + } + if( iDbfStatus == XB_UPDATED ){ + if( GetAutoCommit() == 1 ){ + if(( iRc = Commit()) != XB_NO_ERROR ){ + iErrorStop = 20; + throw iRc; + } + } else { + if(( iRc = Abort()) != XB_NO_ERROR ){ + iErrorStop = 30; + throw iRc; + } + } + } + + if( ulRecNo > ulNoOfRecs || ulRecNo == 0L ){ + iErrorStop = 40; + iRc = XB_INVALID_RECORD; + throw iRc; + } + + if(( xbFseek( (uiHeaderLen+(( (xbInt64) ulRecNo-1L ) * uiRecordLen )), SEEK_SET )) != XB_NO_ERROR ){ + iErrorStop = 50; + iRc = XB_SEEK_ERROR; + throw iRc; + } + + if( xbFread( RecBuf, uiRecordLen, 1 ) != XB_NO_ERROR ){ + iErrorStop = 60; + iRc = XB_READ_ERROR; + throw iRc; + } + ulCurRec = ulRecNo; + } + + catch (xbInt16 iRc ){ + if( iRc != XB_LOCK_FAILED ){ + xbString sMsg; + sMsg.Sprintf( "xbDbf::GetRecord() Exception Caught. Error Stop = [%d] iRc = [%d] record = [%d]", iErrorStop, iRc, ulRecNo ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + } + return iRc; +} +/************************************************************************/ +//! @brief Get pointer to record buffer +/*! + \param iOpt 0 for RecBuf (current) or 1 for RecBuf2 (original contents) + + \returns Pointer to record buffer. +*/ +char * xbDbf::GetRecordBuf( xbInt16 iOpt ) const { + if( iOpt ) + return RecBuf2; + else + return RecBuf; +} + +/************************************************************************/ +//! @brief Get the current number of records in the dbf data file. +/*! + \param ulRecCnt Output number of records in file. + \returns Return Codes +*/ +xbInt16 xbDbf::GetRecordCnt( xbUInt32 & ulRecCnt ) +{ + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + xbBool bIlocked = xbFalse; + try{ + #ifdef XB_LOCKING_SUPPORT + if( iAutoLock && !bTableLocked ){ + if(( iRc = LockHeader( XB_LOCK )) != XB_NO_ERROR ){ + iErrorStop = 10; + throw iRc; + } else + bIlocked = xbTrue; + } + + if((iRc = ReadHeader(1,1)) != XB_NO_ERROR){ + LockHeader( XB_UNLOCK ); + iErrorStop = 20; + throw iRc; + } + + if( iAutoLock && bIlocked ){ + if(( iRc = LockHeader( XB_UNLOCK )) != XB_NO_ERROR ){ + iErrorStop = 30; + throw iRc; + } + } + #endif + } + catch( xbInt16 iRc ){ + if( iRc != XB_LOCK_FAILED ){ + xbString sMsg; + sMsg.Sprintf( "xbDbf::GetRecordCnt() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + } + ulRecCnt = ulNoOfRecs; + return iRc; +} +/************************************************************************/ +//! @brief Get the dbf record length. +/*! + \returns Record length. +*/ +xbUInt16 xbDbf::GetRecordLen() const { + return uiRecordLen; +} +/************************************************************************/ +//! @brief Get table locked status +/*! + \returns Table lock status. +*/ + +xbBool xbDbf::GetTableLocked() const { + return this->bTableLocked; +} +/************************************************************************/ +//! @brief Get tag list for dbf file. +/*! + This routine returns a list of tags for the file.
+ + The library is structured to support one or more files of the same or differing + index types (NDX/MDX), with each file supporting one or more index tags.
+ + \returns Tag list for the file/table. +*/ +xbLinkListNode *xbDbf::GetTagList() const { + return llTags.GetHeadNode(); +} +/************************************************************************/ +//! @brief Get the table alias. +/*! + This routine returns the table alias. + \returns Table alias +*/ +const xbString & xbDbf::GetTblAlias() const { + return this->sAlias; +} + +/************************************************************************/ +//! @brief Get the pointer to the xbXbase structure, +/*! + \returns Pointer to xbXbase structure. +*/ +xbXBase * xbDbf::GetXbasePtr() const { + return xbase; +} +/************************************************************************/ +#ifdef XB_NDXINF_SUPPORT +//! @brief Load .INF data file, +/*! + Protected method. This routine loads the ndx inf file. + \returns Return Codes +*/ + +xbInt16 xbDbf::LoadNdxInfData(){ + + xbInt16 iRc = 0; + xbInt16 iErrorStop = 0; + + try{ + // create file name + xbString sNdxInfFileName; + if(( iRc = GetNdxInfFileName( sNdxInfFileName )) != XB_NO_ERROR ){ + iErrorStop = 10; + throw iRc; + } + // if file does not exist, return no error + xbFile fMd( xbase ); + if( !fMd.FileExists( sNdxInfFileName )) + return XB_NO_ERROR; + + // open file file in read only mode + if(( iRc = fMd.xbFopen( "r", sNdxInfFileName, GetShareMode())) != XB_NO_ERROR ){ + iErrorStop = 20; + throw iRc; + } + // clear the linked list + llNdxInfData.Clear(); + + // for each entry in the file, add a linked list item + xbString sRec; + xbString sLeft3; + xbString sFn; + + while( iRc == XB_NO_ERROR ){ + sRec = ""; + if(( iRc = fMd.xbFgets( 132, sRec )) == XB_NO_ERROR ){ + sLeft3 = sRec; + sLeft3.Left( 3 ); + sLeft3.ToUpperCase(); + if( sLeft3 == "NDX"){ + sFn.ExtractElement(sRec.Str(), '=', 2 ); + sFn.ZapChar( 0x0d ); + sFn.ZapChar( 0x0a ); + llNdxInfData.InsertAtEnd( sFn ); + } + } + } + // close the file + if(( iRc = fMd.xbFclose()) != XB_NO_ERROR ){ + iErrorStop = 40; + throw iRc; + } + + } catch( xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbdbf::LoadNdxInfData() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return iRc; +} +#endif // XB_NDXINF_SUPPORT + +/************************************************************************/ +#ifdef XB_LOCKING_SUPPORT +//! @brief Lock append bytes. +/*! + This routine locks the append bytes and is used by the AppendRecord function. + + \param iLockFunction XB_LOCK
XB_UNLOCK + \returns Return Codes +*/ +xbInt16 xbDbf::LockAppend( xbInt16 iLockFunction ) +{ + xbInt16 iRc = 0; + xbInt16 iErrorStop = 0; + xbUInt32 ulAppendRec; + + try{ + if( iLockFunction == XB_LOCK ){ + iErrorStop = 100; + if( ulAppendLocked > 0 ) /* already have an append lock */ + return XB_NO_ERROR; + + ulAppendRec = ulNoOfRecs + 1; /* record number needing to be locked */ + if( GetLockFlavor() == LK_DBASE ){ + iRc = xbLock( XB_LOCK, LK4026531839, 1 ); + if( iRc != XB_NO_ERROR ){ + if( iRc == XB_LOCK_FAILED ) + return iRc; + else{ + iErrorStop = 120; + throw iRc; + } + } + + xbInt64 llAppendRecLockByte = (xbInt64) LK4026531838 - ulAppendRec; + iRc = xbLock( XB_LOCK, llAppendRecLockByte, 1 ); + if( iRc != XB_NO_ERROR ){ + xbLock( XB_UNLOCK, LK4026531839, 1 ); + if( iRc == XB_LOCK_FAILED ){ + return iRc; + } else { + iErrorStop = 130; + throw iRc; + } + } + ulAppendLocked = ulAppendRec; /* set the append lock switch */ + + + // } else { - other lock-table flavor options go here Clipper, Fox, etc - } + + } else { + iErrorStop = 190; + iRc = XB_INVALID_OPTION; + throw iRc; + } + + } else if( iLockFunction == XB_UNLOCK ){ + + iErrorStop = 200; + + if( ulAppendLocked == 0 ) /* verify we have an active append lock */ + return XB_NO_ERROR; + + if( GetLockFlavor() == LK_DBASE ){ + xbInt64 llAppendRecLockByte =(xbInt64) LK4026531838 - ulAppendLocked; + iRc = xbLock( XB_UNLOCK, llAppendRecLockByte, 1 ); + if( iRc != XB_NO_ERROR ){ + xbLock( XB_UNLOCK, LK4026531839, 1 ); + iErrorStop = 220; + throw iRc; + } + iRc = xbLock( XB_UNLOCK, LK4026531839, 1 ); + if( iRc != XB_NO_ERROR ){ + iErrorStop = 230; + throw iRc; + } + + ulAppendLocked = 0; /* release the append lock switch */ + + // } else { - other unlock-table flavor options go here Clipper, Fox, etc - } + + } else { + iErrorStop = 290; + iRc = XB_INVALID_OPTION; + throw iRc; + } + } else { + iErrorStop = 300; + iRc = XB_INVALID_OPTION; + throw iRc; + } + } catch( xbInt16 iRc ){ + if( iRc != XB_LOCK_FAILED ){ + xbString sMsg; + sMsg.Sprintf( "xbdbf::LockAppendBytes() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + } + + return iRc; +} + + +/************************************************************************/ +//! @brief Lock Header +/*! + This routine locks the file header. + \param iLockFunction XB_LOCK
XB_UNLOCK + \returns Return Codes +*/ +xbInt16 xbDbf::LockHeader( xbInt16 iLockFunction ) +{ + xbInt16 iRc = 0; + xbInt16 iErrorStop = 0; + try{ + + if( iLockFunction == XB_LOCK ){ + iErrorStop = 100; + if( GetHeaderLocked()) + return XB_NO_ERROR; + + iErrorStop = 110; + if( GetLockFlavor() == LK_DBASE ){ + + iRc = xbLock( XB_LOCK, LK4026531838, 1 ); + + if( iRc != XB_NO_ERROR ){ + if( iRc == XB_LOCK_FAILED ) + return iRc; + else{ + iErrorStop = 120; + throw iRc; + } + } + + } else { + iErrorStop = 190; + iRc = XB_INVALID_OPTION; + throw iRc; + } + + SetHeaderLocked( xbTrue ); + + } else if( iLockFunction == XB_UNLOCK ){ + + iErrorStop = 200; + if( !GetHeaderLocked()) + return XB_NO_ERROR; + + iErrorStop = 210; + if( GetLockFlavor() == LK_DBASE ){ + iRc = xbLock( XB_UNLOCK, LK4026531838, 1 ); + if( iRc != XB_NO_ERROR ){ + iErrorStop = 220; + throw iRc; + } + } else { + iErrorStop = 290; + iRc = XB_INVALID_OPTION; + throw iRc; + } + SetHeaderLocked( xbFalse ); + } else { + iErrorStop = 300; + iRc = XB_INVALID_OPTION; + throw iRc; + } + + } catch (xbInt16 iRc ){ + if( iRc != XB_LOCK_FAILED ){ + xbString sMsg; + sMsg.Sprintf( "xbdbf::LockHeader() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + } + return iRc; +} +/************************************************************************/ +#ifdef XB_INDEX_SUPPORT +//! @brief Lock Index files. +/*! + This routine locks all the index files. + \param iLockFunction XB_LOCK
XB_UNLOCK + \returns Return Codes +*/ +xbInt16 xbDbf::LockIndices( xbInt16 iLockFunction ) +{ + // this function doesn't take into account any Lack Flavors other than DBASE, + // would need updated to supprot other lock flavors - Clipper, FoxPro etc + + xbInt16 iRc = 0; + xbInt16 iErrorStop = 0; + + try{ + xbIxList *ixLI = GetIxList(); // index list item + + while( ixLI ){ + if( iLockFunction == XB_LOCK ){ + + #ifdef XB_NDX_SUPPORT + if( *ixLI->sFmt == "NDX" ){ + if( !ixLI->ix->GetLocked()){ + if(( iRc = ixLI->ix->xbLock( XB_LOCK, LK4026531838, 1 )) != XB_NO_ERROR ){ + ixLI->ix->xbLock( XB_UNLOCK, LK4026531838, 1 ); + iErrorStop = 100; + throw iRc; + } + ixLI->ix->SetLocked( xbTrue ); + } + } + #endif + + } else if( iLockFunction == XB_UNLOCK ){ + + #ifdef XB_NDX_SUPPORT + if( *ixLI->sFmt == "NDX" ){ + if( ixLI->ix->GetLocked()){ + if(( iRc = ixLI->ix->xbLock( XB_UNLOCK, LK4026531838, 1 )) != XB_NO_ERROR ){ + ixLI->ix->xbLock( XB_UNLOCK, LK4026531838, 1 ); + iErrorStop = 100; + throw iRc; + } + ixLI->ix->SetLocked( xbFalse ); + } + } + #endif + } + ixLI = ixLI->next; + } + + } catch( xbInt16 iRc ){ + if( iRc != XB_LOCK_FAILED ){ + xbString sMsg; + sMsg.Sprintf( "xbdbf::LockIndices() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + } + return iRc; +} +#endif // XB_INDEX_SUPPORT +/************************************************************************/ +#ifdef XB_MEMO_SUPPORT +//! @brief Lock Memo file. +/*! + This routine locks the memo file for updates. + \param iLockFunction XB_LOCK
XB_UNLOCK + \returns Return Codes +*/ +xbInt16 xbDbf::LockMemo( xbInt16 iLockFunction ){ + if( MemoFieldsExist()) + return Memo->LockMemo( iLockFunction ); + else + return XB_NO_ERROR; +} +#endif // XB_MEMO_SUPPORT + + + +/************************************************************************/ +//! @brief Loc Record +/*! + This routine locks a record for update. + \param iLockFunction XB_LOCK
XB_UNLOCK + \param ulRecNo Record number to lock + \returns Return Codes +*/ + +xbInt16 xbDbf::LockRecord( xbInt16 iLockFunction, xbUInt32 ulRecNo ) +{ + xbInt16 iRc = 0; + xbInt16 iErrorStop = 0; + + try{ + + if( ulRecNo > ulNoOfRecs ) + return XB_INVALID_RECORD; + + if( iLockFunction == XB_LOCK ){ + iErrorStop = 100; + + if( lloRecLocks.KeyExists( ulRecNo )) + return XB_NO_ERROR; + + if( GetLockFlavor() == LK_DBASE ){ + + iRc = xbLock( XB_LOCK, LK4026531838 - ulRecNo, 1 ); + if( iRc != XB_NO_ERROR ){ + if( iRc == XB_LOCK_FAILED ){ + return iRc; + } else { + iErrorStop = 130; + throw iRc; + } + } + // other lock-table flavor options go here Clipper, Fox, etc + + } else { + iErrorStop = 190; + iRc = XB_INVALID_OPTION; + throw iRc; + } + + // add the record lock info to the linked list chain of record locks + iRc = lloRecLocks.InsertKey( ulRecNo ); + if( iRc != XB_NO_ERROR ){ + xbLock( XB_UNLOCK, LK4026531838 - ulRecNo, 1 ); + iErrorStop = 140; + throw iRc; + } + + } else if( iLockFunction == XB_UNLOCK ){ + + iErrorStop = 200; + + if( !lloRecLocks.KeyExists( ulRecNo ) ) + return XB_NO_ERROR; + + if( GetLockFlavor() == LK_DBASE ){ + iRc = xbLock( XB_UNLOCK, LK4026531838 - ulRecNo, 1 ); + if( iRc != XB_NO_ERROR ){ + iErrorStop = 220; + throw iRc; + } + } else { + iErrorStop = 290; + iRc = XB_INVALID_OPTION; + throw iRc; + } + + // remove the record lock info to the linked list chain of record locks + // next line is crashing + iRc = lloRecLocks.RemoveKey( ulRecNo ); + if( iRc != XB_NO_ERROR ){ + iErrorStop = 230; + throw iRc; + } + + } else { + iErrorStop = 300; + iRc = XB_INVALID_OPTION; + throw iRc; + } + + } catch( xbInt16 iRc ){ + if( iRc != XB_LOCK_FAILED ){ + xbString sMsg; + sMsg.Sprintf( "xbdbf::LockRecord() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + } + + return iRc; +} + + +/************************************************************************/ +//! @brief Lock table. +/*! + This routine locks the table for updates. + + \param iLockFunction XB_LOCK
XB_UNLOCK + \returns Return Codes +*/ + +xbInt16 xbDbf::LockTable( xbInt16 iLockFunction ) +{ + xbInt16 iRc = 0; + xbInt16 iErrorStop = 0; + + try{ + if( iLockFunction == XB_LOCK ){ + iErrorStop = 100; + if( GetTableLocked()) + return XB_NO_ERROR; // table already locked + + iErrorStop = 110; + if( GetLockFlavor() == LK_DBASE ){ + + // lOffset = LK4026531838; + // iLen = 2; + iRc = xbLock( XB_LOCK, LK4026531838, 2 ); + if( iRc != XB_NO_ERROR ){ + if( iRc == XB_LOCK_FAILED ) + return iRc; + else{ + iErrorStop = 120; + throw iRc; + } + } + + // lOffset = LK3026531838; + // iLen = LK1000000000; + iRc = xbLock( XB_LOCK, LK3026531838, LK1000000000); + if( iRc != XB_NO_ERROR ){ + + // lOffset = LK4026531838; + // iLen = 2; + xbLock( XB_UNLOCK, LK4026531838, 2 ); + if( iRc == XB_LOCK_FAILED ){ + return iRc; + } else { + iErrorStop = 130; + throw iRc; + } + } + + // iRc = xbLock( XB_UNLOCK, lOffset, iLen ); + iRc = xbLock( XB_UNLOCK, LK3026531838, LK1000000000); + if( iRc != XB_NO_ERROR ){ + // lOffset = LK4026531838; + // iLen = 2; + xbLock( XB_UNLOCK, LK4026531838, 2 ); + iErrorStop = 140; + throw iRc; + } + + // other lock-table flavor options go here Clipper, Fox, etc + + } else { + iErrorStop = 190; + iRc = XB_INVALID_OPTION; + throw iRc; + } + SetTableLocked( xbTrue ); + + } else if( iLockFunction == XB_UNLOCK ){ + + iErrorStop = 200; + if( !GetTableLocked()) + return XB_NO_ERROR; // table already unlocked + + iErrorStop = 210; + if( GetLockFlavor() == LK_DBASE ){ + + // lOffset = LK4026531838; + // iLen = 2; + iRc = xbLock( XB_UNLOCK, LK4026531838, 2 ); + if( iRc != XB_NO_ERROR ){ + iErrorStop = 220; + throw iRc; + } + } else { + iErrorStop = 290; + iRc = XB_INVALID_OPTION; + throw iRc; + } + SetTableLocked( xbFalse ); + + } else { + iErrorStop = 300; + iRc = XB_INVALID_OPTION; + throw iRc; + } + + } catch (xbInt16 iRc ){ + if( iRc != XB_LOCK_FAILED ){ + xbString sMsg; + sMsg.Sprintf( "xbdbf::LockFile() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + } + return iRc; + +} + + +/************************************************************************/ +//! @brief Check for existence of any memo fields. +/*! + \returns xbTrue - Memo fields exist.
xbFalse - Memo fields don't exist. +*/ +xbBool xbDbf::MemoFieldsExist() const { +#ifdef XB_MEMO_SUPPORT + if( iMemoFieldCnt > 0 ) + return xbTrue; +#endif + return xbFalse; +} + +/************************************************************************/ +//! @brief Open a table/dbf file. +/*! + This routine sets the alias name to the same as the table name. + + \param sTableName Table name to open, Include the .dbf or .DBF extension. + \returns Return Codes +*/ +xbInt16 xbDbf::Open( const xbString & sTableName ) { + return Open( sTableName, sTableName ); +} + +/************************************************************************/ +//! @brief Open a table/dbf file. +/*! + \param sTableName Table name to open, Include the .dbf or .DBF extension. + \param sAlias Alias name to assign to this entry. + \returns Return Codes +*/ + +xbInt16 xbDbf::Open( const xbString & sTableName, const xbString & sAlias ){ + + xbInt16 iRc = 0; + xbInt16 iErrorStop = 0; + + + try{ + + if(( iRc = Open( sTableName, sAlias, XB_READ_WRITE, XB_MULTI_USER )) != XB_NO_ERROR ){ + iErrorStop = 10; + throw iRc; + } + + // do any .INF data things on the file, like open indices + #ifdef XB_NDXINF_SUPPORT + if(( iRc = LoadNdxInfData()) != XB_NO_ERROR ){ + iErrorStop = 20; + throw iRc; + } + xbUInt32 llNodeCnt = llNdxInfData.GetNodeCnt(); + if( llNodeCnt > 0 ){ + xbString s2; + xbLinkListNode * llN = llNdxInfData.GetHeadNode(); + for( xbUInt32 i = 0; i < llNodeCnt; i++ ){ + s2 = llN->GetKey(); + if(( iRc = OpenIndex( "NDX", s2 )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc ; + } + llN = llN->GetNextNode(); + } + } + #endif // XB_NDXINF_SUPPORT + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbdbf::Open() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return iRc; +} +#endif // XB_LOCKING_SUPPORT + +/************************************************************************/ +#ifdef XB_INDEX_SUPPORT +//! @brief Open an index. +/*! + Open an index file for the dbf file. + + \param sIxType - "NDX" or "MDX" + \param sFileName - File name of index, + \returns Return Codes +*/ + +xbInt16 xbDbf::OpenIndex( const xbString &sIxType, const xbString &sFileName ){ + + // this routine is used to open indices and set up linkages + + xbInt16 iErrorStop = 0; + xbInt16 iRc = XB_NO_ERROR; + xbIx *pIx = NULL; + + try{ + xbString sType = sIxType; + sType.ToUpperCase(); + + if( sType == "" ){ + iErrorStop = 10; + iRc = XB_INVALID_OPTION; + throw iRc; + + #ifdef XB_NDX_SUPPORT + } else if( sType == "NDX" ){ + pIx = new xbIxNdx( this ); + if(( iRc = pIx->Open( sFileName )) != XB_NO_ERROR ){ + iErrorStop = 20; + throw iRc; + } + #endif + + #ifdef XB_MDX_SUPPORT + } else if( sType == "MDX" ){ + + pIx = new xbIxMdx( this ); + if(( iRc = pIx->Open( sFileName )) != XB_NO_ERROR ){ + iErrorStop = 30; + throw iRc; + } + + #endif + + } else { + iErrorStop = 40; + iRc = XB_INVALID_OPTION; + throw iRc; + } + + if(( iRc = AddIndex( pIx, sIxType )) != XB_NO_ERROR ){ + iErrorStop = 50; + throw iRc; + } + if(( iRc = UpdateTagList()) != XB_NO_ERROR ){ + iErrorStop = 60; + throw iRc; + } + pCurIx = pIx; + sCurIxType = sIxType; + vpCurIxTag = pIx->GetTag( 0 ); + } + catch (xbInt16 iRc ){ + if( pIx ) delete pIx; + xbString sMsg; + sMsg.Sprintf( "xbdbf::OpenIndex() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return iRc; +} +#endif // XB_INDEX_SUPPORT + + + +/************************************************************************/ +//! @brief Pack dbf file. +/*! + This routine eliminates all deleted records from the file. + \returns Return Codes +*/ +xbInt16 xbDbf::Pack() +{ + xbUInt32 ulDeletedRecCnt; + return Pack( ulDeletedRecCnt ); +} + + +/************************************************************************/ +//! @brief Pack dbf file. +/*! + This routine eliminates all deleted records from the file and clears + out any unused blocks in the memo file if one exists. + \param ulDeletedRecCnt - Output - number of recrods removed from the file. + \returns Return Codes +*/ + +xbInt16 xbDbf::Pack( xbUInt32 &ulDeletedRecCnt ) +{ + xbInt16 iRc = 0; + xbInt16 iErrorStop = 0; + xbUInt32 ulLastMovedRec = 0; + xbUInt32 ulStartPos = 0; + xbUInt32 ulLastPackedRec = 0; + xbUInt32 ulMoveRec = 0; + xbUInt32 ulRecCnt = 0; + ulDeletedRecCnt = 0; + + try{ + if( !FileIsOpen() ){ + iErrorStop = 10; + iRc = XB_DBF_FILE_NOT_OPEN; + throw iRc; + } + + #ifdef XB_LOCKING_SUPPORT + if( iAutoLock ){ + if(( iRc = LockTable( XB_LOCK )) != XB_NO_ERROR ){ + iErrorStop = 20; + throw iRc; + } + } + #endif + + if(( iRc = GetRecordCnt( ulRecCnt )) != XB_NO_ERROR ){ + iErrorStop = 30; + throw iRc; + } + + xbBool bDone = xbFalse; + for( xbUInt32 ulI = 1; ulI <= ulRecCnt && !bDone; ulI++ ){ + + + if(( iRc = GetRecord( ulI )) != XB_NO_ERROR ){ + iErrorStop = 40; + throw iRc; + } + + if( RecordDeleted()){ + + ulDeletedRecCnt++; + if( ulI > ulLastMovedRec ) + ulStartPos = ulI; + else + ulStartPos = ulLastMovedRec; + + iRc = GetNextRecord( 1, ulStartPos ); + + if( iRc == XB_NO_ERROR ){ + ulMoveRec = ulCurRec; + } + else if( iRc == XB_EOF ){ + ulMoveRec = 0; + bDone = xbTrue; + } + else{ + iErrorStop = 50; + throw iRc; + } + if( ulMoveRec > 0 ){ + if(( iRc = DeleteRecord()) != XB_NO_ERROR ){ + iErrorStop = 60; + throw iRc; + } + if(( iRc = PutRecord( ulMoveRec )) != XB_NO_ERROR ){ + iErrorStop = 70; + throw iRc; + } + if(( iRc = UndeleteRecord()) != XB_NO_ERROR ){ + iErrorStop = 80; + throw iRc; + } + if(( iRc = PutRecord( ulI )) != XB_NO_ERROR ){ + iErrorStop = 90; + throw iRc; + } + ulLastPackedRec = ulI; + } + + } else { + ulLastPackedRec = ulI; + } + } + + if( ulLastPackedRec < ulRecCnt ){ + // update header record count + + xbDate d; + cUpdateYY = (char) d.YearOf() - 1900; + cUpdateMM = (char) d.MonthOf(); + cUpdateDD = (char) d.DayOf( XB_FMT_MONTH ); + ulNoOfRecs = ulLastPackedRec; + + // rewrite the header record + if(( iRc = WriteHeader( 1, 1 )) != XB_NO_ERROR ){ + iErrorStop = 200; + throw iRc; + } + + // truncate the file to the new size + if(( iRc = xbTruncate( uiHeaderLen + uiRecordLen * ulLastPackedRec )) != XB_NO_ERROR ){ + iErrorStop = 210; + throw iRc; + } + } + + if( ulNoOfRecs > 0 ){ + if(( iRc = GetRecord( 1 )) != XB_NO_ERROR ){ + iErrorStop = 220; + throw iRc; + } + } else { + BlankRecord(); + ulCurRec = 0; + } + + #ifdef XB_MEMO_SUPPORT + if( iMemoFieldCnt > 0 ){ + if(( iRc = Memo->PackMemo( 0 )) != XB_NO_ERROR ){ + iErrorStop = 230; + throw iRc; + } + } + #endif // XB_MEMO_SUPPORT + + #ifdef XB_LOCKING_SUPPORT + if( iAutoLock ){ + if(( iRc = LockTable( XB_UNLOCK )) != XB_NO_ERROR ){ + iErrorStop = 240; + throw iRc; + } + } + + #endif + } + catch (xbInt16 iRc ){ + if( iRc != XB_LOCK_FAILED ){ + xbString sMsg; + sMsg.Sprintf( "xbdbf::Pack() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + } + return iRc; +} + +/************************************************************************/ +//! @brief Write the current record to disk. +/*! + This routine is used to write any updates to the current record buffer to disk. + + \returns Return Codes +*/ +xbInt16 xbDbf::PutRecord() { + return PutRecord(ulCurRec); +} + +/************************************************************************/ +//! @brief Write record to disk. +/*! + This routine is used to write a copy of the current record buffer to disk + for a given record number. + + \param ulRecNo Record number to update. + \returns Return Codes +*/ + +xbInt16 xbDbf::PutRecord(xbUInt32 ulRecNo) +{ + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + try{ + + if( ulRecNo < 1 ){ + iErrorStop = 100; + throw XB_INVALID_RECORD; + } + + xbUInt32 ulRecCnt; + if(( iRc = GetRecordCnt( ulRecCnt )) != XB_NO_ERROR ){ + iErrorStop = 110; + throw iRc; + } + + if( ulRecNo > ulRecCnt ){ + iErrorStop = 120; + throw XB_INVALID_RECORD; + } + + if( iDbfStatus == XB_CLOSED ){ + iErrorStop = 130; + iRc = XB_NOT_OPEN; + throw iRc; + } + /* lock the database */ + #ifdef XB_LOCKING_SUPPORT + // xbInt16 iAutoLock = GetAutoLock(); + if( iAutoLock && !bTableLocked ){ + if(( iRc = LockHeader( XB_LOCK )) != XB_NO_ERROR ){ + if( iRc == XB_LOCK_FAILED ) + return iRc; + else{ + iErrorStop = 140; + throw iRc; + } + } + + if(( iRc = ReadHeader( 1, 1 )) != XB_NO_ERROR ){ + iErrorStop = 150; + LockHeader( XB_UNLOCK ); + throw iRc; + } + } + #endif + + // verify valid record number request + if( ulRecNo > ulNoOfRecs || ulRecNo == 0L ){ + iErrorStop = 160; + iRc = XB_INVALID_RECORD; + throw iRc; + } + + #ifdef XB_LOCKING_SUPPORT + if( iAutoLock ){ + if( !bTableLocked ){ + if(( iRc = LockRecord( XB_LOCK, ulRecNo )) != XB_NO_ERROR ){ + if( iRc == XB_LOCK_FAILED ) + return iRc; + else{ + iErrorStop = 170; + throw iRc; + } + } + } + + #ifdef XB_INDEX_SUPPORT + if(( iRc = LockIndices( XB_LOCK )) != XB_NO_ERROR ){ + if( iRc == XB_LOCK_FAILED ) + return iRc; + else{ + iErrorStop = 180; + throw iRc; + } + } + #endif + } + #endif + + // build keys, check for duplicate keys, add keys + #ifdef XB_INDEX_SUPPORT + xbIxList *ixList = GetIxList(); + + while( ixList ){ + if(( iRc = ixList->ix->CreateKeys( 2 )) != XB_NO_ERROR ){ + iErrorStop = 190; + throw iRc; + } + iRc = ixList->ix->CheckForDupKeys(); + if( iRc != 0 ){ + if( iRc < 0 ){ + iErrorStop = 200; + throw iRc; + } + return XB_KEY_NOT_UNIQUE; + } + ixList = ixList->next; + } + + ixList = GetIxList(); + while( ixList ){ + + if(( iRc = ixList->ix->AddKeys( ulCurRec )) != XB_NO_ERROR ){ + iErrorStop = 210; + throw iRc; + } + ixList = ixList->next; + } + + ixList = GetIxList(); + while( ixList ){ + +// if(( iRc = ixList->ix->DeleteKeys( ulCurRec )) != XB_NO_ERROR ){ + + if(( iRc = ixList->ix->DeleteKeys()) != XB_NO_ERROR ){ + iErrorStop = 220; + throw iRc; + } + ixList = ixList->next; + } + #endif // XB_INDEX_SUPPORT + + // update latest header date if changed + xbDate d; + if( (cUpdateYY != (char)(d.YearOf() - 1900)) || (cUpdateMM != (char) d.MonthOf()) || (cUpdateDD != (char)d.DayOf( XB_FMT_MONTH))){ + cUpdateYY = (char) d.YearOf() - 1900; + cUpdateMM = (char) d.MonthOf(); + cUpdateDD = (char) d.DayOf( XB_FMT_MONTH ); + // rewrite the header record - first 8 bytes + if(( iRc = WriteHeader( 1, 1 )) != XB_NO_ERROR ){ + iErrorStop = 70; + throw iRc; + } + } + + #ifdef XB_LOCKING_SUPPORT + if( iAutoLock && !bTableLocked ){ + if(( iRc = LockHeader( XB_UNLOCK )) != XB_NO_ERROR ){ + if( iRc == XB_LOCK_FAILED ) + return iRc; + else{ + iErrorStop = 230; + throw iRc; + } + } + } + #endif + + // update record + iRc = xbFseek( (uiHeaderLen+(( (xbInt64) ulRecNo-1L ) * uiRecordLen )),0 ); + if( iRc != XB_NO_ERROR ){ + iErrorStop = 240; + throw iRc; + } + + if( xbFwrite( RecBuf, uiRecordLen, 1 ) != XB_NO_ERROR ){ + iErrorStop = 250; + iRc = XB_WRITE_ERROR; + throw iRc; + } + + #ifdef XB_MEMO_SUPPORT + if( MemoFieldsExist() ){ + if(( iRc = Memo->Commit()) != XB_NO_ERROR ){ + iErrorStop = 260; + throw iRc; + } + } + #endif + + // unlock record + #ifdef XB_LOCKING_SUPPORT + if( iAutoLock ){ + #ifdef XB_INDEX_SUPPORT + LockIndices( XB_UNLOCK ); + #endif // XB_INDEX_SUPPORT + if( !bTableLocked ){ + if(( iRc = LockRecord( XB_UNLOCK, ulRecNo )) != XB_NO_ERROR ){ + if( iRc == XB_LOCK_FAILED ) + return iRc; + else{ + iErrorStop = 270; + throw iRc; + } + } + } + } + + #endif + ulCurRec = ulRecNo; + iDbfStatus = XB_OPEN; + } + + catch (xbInt16 iRc ){ + + #ifdef XB_LOCKING_SUPPORT + if( iAutoLock ){ + LockHeader( XB_UNLOCK ); + LockAppend( XB_UNLOCK ); + LockRecord( XB_UNLOCK, ulRecNo ); + LockIndices( XB_UNLOCK ); + } + #endif // XB_LOCKING_SUPPORT + + if( iRc != XB_LOCK_FAILED ){ + xbString sMsg; + sMsg.Sprintf( "xbdbf::PutRecord() Exception Caught. Error Stop = [%d] iRc = [%d] record = [%d]", iErrorStop, iRc, ulRecNo ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + } + return iRc; +} + +/************************************************************************/ +//! @brief Read dbf file header information. +/*! + This method assumes the header has been locked appropriately + in a multi user environment + + + \param iPositionOption 0 - Don't fseek to beginning of file before read.
+ 1 - Start from beginning of file. + + \param iReadOption 0 - Read entire 32 byte header
+ 1 - Read first eight bytes which includes the last update date and number of records. + + \returns Return Codes +*/ +xbInt16 xbDbf::ReadHeader( xbInt16 iPositionOption, xbInt16 iReadOption ){ + + char buf[32]; + size_t iReadSize; + + if(iPositionOption) + xbRewind(); + if( iReadOption == 1 ) + iReadSize = 8; + else + iReadSize = 32; + + if(xbFread(buf, iReadSize, 1) != XB_NO_ERROR) + return XB_READ_ERROR; + memcpy(&cVersion, buf, 4); + ulNoOfRecs = eGetUInt32(&buf[4]); + if( iReadOption == 1 ) + return XB_NO_ERROR; + + uiHeaderLen = eGetUInt16(&buf[8]); + uiRecordLen = eGetUInt16(&buf[10]); + cTransactionFlag = buf[14]; + cEncryptionFlag = buf[15]; + cIndexFlag = buf[28]; + cLangDriver = buf[29]; + return XB_NO_ERROR; +} + +/************************************************************************/ +//! @brief Return record deletion status. +/*! + This routine returns the record deletion status. + \param iOpt 0 = Current record buffer, 1 = Original record buffer + \returns xbTrue - Record deleted.
xbFalse - Record not deleted. +*/ +xbInt16 xbDbf::RecordDeleted( xbInt16 iOpt ) const { + if( !iOpt && RecBuf && RecBuf[0] == 0x2a ) + return xbTrue; + else if( iOpt && RecBuf2 && RecBuf2[0] == 0x2a ) + return xbTrue; + else + return xbFalse; +} + +/************************************************************************/ +#ifdef XB_INDEX_SUPPORT +//! @brief Remove an index from the internal list of indices for this table +/* + The index list is used during any table update process to update any open + index file. Index files can contain one or more tags. + + \param ixIn Pointer to index object for a given index file. + \returns Return Codes +*/ + +xbInt16 xbDbf::RemoveIndex( xbIx * ixIn ){ + + xbIxList *p = ixList; + // if index is the first entry in the list + if( ixList->ix == ixIn ){ + ixList = ixList->next; + delete p->sFmt; + delete p->ix; + free( p ); + return XB_NO_ERROR; + } + + // spin down to the correct ix + xbIxList *p2 = NULL; + while( p && p->ix != ixIn ){ + p2 = p; + p = p->next; + } + if( p ){ + p2->next = p->next; + delete p->sFmt; + delete p->ix; + free( p ); + } + return XB_NO_ERROR; +} +#endif // XB_INDEX_SUPPORT + +/************************************************************************/ +// @brief Reset number of records. +/* + Protected method. Resets number of records to 0. + \returns void +*/ +void xbDbf::ResetNoOfRecords() { + ulNoOfRecs = 0UL; +} + +/************************************************************************/ +#ifdef XB_NDXINF_SUPPORT +// @brief Update .INF data file. +/* + Protected method. + \returns Return Codes +*/ + +xbInt16 xbDbf::SaveNdxInfData(){ + + xbInt16 iRc = 0; + xbInt16 iErrorStop = 0; + xbFile fMd( xbase ); + + try{ + + xbUInt32 llNodeCnt = llNdxInfData.GetNodeCnt(); + + xbString sNdxInfFileName; + if(( iRc = GetNdxInfFileName( sNdxInfFileName )) != XB_NO_ERROR ){ + iErrorStop = 10; + throw iRc; + } + + // open the file + if(( iRc = fMd.xbFopen( "w", sNdxInfFileName, GetShareMode())) != XB_NO_ERROR ){ + iErrorStop = 20; + throw iRc; + } + + xbString s1; + xbString s2; + s2.Sprintf( "[dbase]%c%c", 0x0d, 0x0a ); + if(( iRc = fMd.xbFputs( s2 )) != XB_NO_ERROR ){ + iErrorStop = 30; + throw iRc; + } + + + // for each entry in the linked list, write a line + xbLinkListNode * llN = llNdxInfData.GetHeadNode(); + for( xbUInt32 i = 0; i < llNodeCnt; i++ ){ + s2 = llN->GetKey(); + if( i > 0 ) + s1.Sprintf( "NDX%d=%s%c%c", i, s2.Str(), 0x0d, 0x0a ); + else + s1.Sprintf( "NDX=%s%c%c", s2.Str(), 0x0d, 0x0a ); + + if(( iRc = fMd.xbFputs( s1 )) != XB_NO_ERROR ){ + iErrorStop = 40; + throw iRc; + } + llN = llN->GetNextNode(); + } + + // close the file + if(( iRc = fMd.xbFclose()) != XB_NO_ERROR ){ + iErrorStop = 50; + throw iRc; + } + + } catch( xbInt16 iRc ){ + if( fMd.FileIsOpen()) + fMd.xbFclose(); + xbString sMsg; + sMsg.Sprintf( "xbdbf::SaveNdxInfData() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return iRc; +} +#endif // XB_NDXINF_SUPPORT +/************************************************************************/ +//! @brief Set auto commit. +/*! + This routine sets the auto commit setting for this table. + \returns XB_NO_ERROR; +*/ +xbInt16 xbDbf::SetAutoCommit( xbBool iAutoCommit ) { + this->iAutoCommit = iAutoCommit; + return XB_NO_ERROR; +} + + +/************************************************************************/ +//! @brief Set auto lock. +/*! + This routine sets the auto lock setting for this table. + There is an overall system level auto lock default setting and each table + can have it's own autolock setting. This method controls the table level + auto lock setting. + + \param iAutoLock 1 - Use auto lock for this table.
+ 0 - Don't use auto lock for this table.
+ -1 - (minus one) Use system default.
+ \returns Return Codes +*/ + +void xbDbf::SetAutoLock( xbInt16 iAutoLock ){ + if( iAutoLock == -1 ) + this->iAutoLock = xbase->GetDefaultAutoLock(); + else + this->iAutoLock = iAutoLock; +} + + + +/************************************************************************/ +#ifdef XB_INDEX_SUPPORT +//! @brief Set the current tag for the dbf file. +/*! + \param sTagName - Tag Name + \returns Return Codes +*/ +xbInt16 xbDbf::SetCurTag( const xbString &sTagName ){ + + if( sTagName == "" ){ + SetCurTag( "", 0, 0 ); + return XB_NO_ERROR; + + } else { + xbLinkListNode *llN = GetTagList(); + xbTag *pTag; + while( llN ){ + pTag = llN->GetKey(); + if( pTag->GetTagName() == sTagName ){ + SetCurTag( pTag->GetType(), pTag->GetIx(), pTag->GetVpTag()); + return XB_NO_ERROR; + } + llN = llN->GetNextNode(); + } + } + + return XB_INVALID_TAG; +} + +/************************************************************************/ +//! @brief Set the current tag for the dbf file. +/*! + + \param sIxType - One of "NDX" or MDX", + \param pIx - Pointer to index object. + \param vpTag - Pointer to tag object. + \returns Return Codes +*/ + +void xbDbf::SetCurTag( const xbString &sIxType, xbIx *pIx, void *vpTag ){ + pCurIx = pIx; + vpCurIxTag = vpTag; + sCurIxType.Set( sIxType ); +} +#endif // XB_INDEX_SUPPORT + +/************************************************************************/ +//! @brief Set the header locked status. +/*! + \param bHeaderLocked xbTrue - Locked
xbFalse - Not locked. + \returns void +*/ +void xbDbf::SetHeaderLocked( xbBool bHeaderLocked ){ + this->bHeaderLocked = bHeaderLocked; +} + +/************************************************************************/ +//! @brief Set lock flavor. +/*! + This routine is for future expansion. + \param iLockFlavor 1 - Use Dbase (tm) style locking. + \returns void +*/ + +void xbDbf::SetLockFlavor( xbInt16 iLockFlavor ){ + this->iLockFlavor = iLockFlavor; +} + +/************************************************************************/ +//! @brief Set table locked status. +/*! + \param bTableLocked - xbTrue Table locked.
xbFalse Table unlocked. + \returns void +*/ + +void xbDbf::SetTableLocked( xbBool bTableLocked ){ + this->bTableLocked = bTableLocked; +} +/************************************************************************/ +//! @brief Undelete all records. +/*! + This routine will remove the deletion flag on any deleted records in the table. + \returns Return Codes +*/ +xbInt16 xbDbf::UndeleteAllRecords(){ + return DeleteAll( 1 ); +} + +/************************************************************************/ +//! @brief Undelete one record. +/*! + This routine will undelete the current record, if it is deleted. + \returns XB_NO_ERROR
XB_INVALID_RECORD +*/ +xbInt16 xbDbf::UndeleteRecord() +{ + if( RecBuf && ulCurRec > 0 ){ + if( RecBuf[0] != 0x20 ){ + if( iDbfStatus != XB_UPDATED ){ + iDbfStatus = XB_UPDATED; + memcpy( RecBuf2, RecBuf, uiRecordLen ); // save off original before making updates + } + RecBuf[0] = 0x20; + } + return XB_NO_ERROR; + } + else + return XB_INVALID_RECORD; +} + +/************************************************************************/ +#ifdef XB_MEMO_SUPPORT +//! @brief Update memo field +/*! + This routine updates a memo field. + \param iFieldNo - Memo field number to update. + \param sMemoData - Memo data for update. + \returns Return Codes +*/ +xbInt16 xbDbf::UpdateMemoField( xbInt16 iFieldNo, const xbString &sMemoData ){ + return Memo->UpdateMemoField( iFieldNo, sMemoData ); +} +/************************************************************************/ +//! @brief Update memo field +/*! + This routine updates a memo field. + \param sFieldName - Memo field name to update. + \param sMemoData - Memo data for update. + \returns Return Codes +*/ +xbInt16 xbDbf::UpdateMemoField( const xbString & sFieldName, const xbString & sMemoData ){ + return Memo->UpdateMemoField( GetFieldNo( sFieldName ), sMemoData ); +} +#endif + + +/************************************************************************/ +#ifdef XB_INDEX_SUPPORT +//! @brief Update SchemaIxFlag +/*! + This routine can be called from the DeleteTag routine if a tag has been deleted and the flag needs reset + \param iFldNo - Which field the index flag needs changed on + \param cVal - Value to change it to +*/ + +void xbDbf::UpdateSchemaIxFlag( xbInt16 iFldNo, unsigned char cVal ){ + if( cVal != 0x00 || cVal != 0x01 ) + SchemaPtr[iFldNo].cIxFlag = cVal; +} + +#endif // XB_INDEX_SUPPORT +/************************************************************************/ +//! @brief Update tag list. +/*! + This routine updates the internal tag list of open index tags. + \returns Return Codes +*/ +xbInt16 xbDbf::UpdateTagList(){ + + xbInt16 iErrorStop = 0; + xbInt16 iRc = XB_NO_ERROR; + xbInt32 lTagCnt; + + try{ + ClearTagList(); + + // For each active index + xbIxList *p = GetIxList(); + xbIx *ixp; + while( p ){ + ixp = p->ix; + // for each tag within the file + lTagCnt = ixp->GetTagCount(); + for( xbInt32 l = 0; l < lTagCnt; l++ ){ + xbTag *pTag = new xbTag( ixp, ixp->GetTag( l ), *p->sFmt, ixp->GetTagName( ixp->GetTag( l )), + ixp->GetKeyExpression( ixp->GetTag( l )), ixp->GetKeyFilter( ixp->GetTag( l )), + ixp->GetUnique( ixp->GetTag( l )), ixp->GetSortOrder( ixp->GetTag( l ))); + + // append it to the llTags list + llTags.InsertAtEnd( pTag ); + } + p = p->next; + } + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbdbf::UpdateTagList() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return iRc; +} + +/************************************************************************/ +// @brief Write Header +/* + Protected method. + + \param iPositionOption 0 - Don't fseek to beginning of file before read.
+ 1 - Go to beginning of file before read. + \param iWriteOption 0 - Write entire 32 byte header.
+ 1 - Write first eight bytes needed for table updates - last update date and number of records. + \returns Return Codes +*/ +xbInt16 xbDbf::WriteHeader( xbInt16 iPositionOption, xbInt16 iWriteOption ) +{ + char buf[32]; + xbInt32 lWriteLen; + + if(iPositionOption) + xbRewind(); + + memset(buf, 0, 32); + if( iWriteOption == 1 ) + lWriteLen = 8; + else{ + lWriteLen = 32; + ePutUInt16( &buf[8], uiHeaderLen ); + ePutUInt16( &buf[10], uiRecordLen ); + buf[14] = cTransactionFlag; + buf[15] = cEncryptionFlag; + buf[28] = cIndexFlag; + buf[29] = cLangDriver; + } + memcpy(&buf[0], &cVersion, 4); + ePutUInt32( &buf[4], ulNoOfRecs); + if(xbFwrite(buf, (size_t) lWriteLen, 1) != XB_NO_ERROR) + return XB_WRITE_ERROR; + + return XB_NO_ERROR; +} + +/************************************************************************/ +//! @brief Zap (remove) everything from the file, +/*! + This routine eliminates everything from the dbf file and dbt memo file. + \returns Return Codes +*/ +xbInt16 xbDbf::Zap(){ + + xbInt16 iRc = 0; + xbInt16 iErrorStop = 0; + + try{ + if( iDbfStatus != XB_OPEN ){ + iErrorStop = 10; + iRc = XB_DBF_FILE_NOT_OPEN; + throw iRc; + } + #ifdef XB_LOCKING_SUPPORT + if( iAutoLock ){ + if(( iRc = LockTable( XB_LOCK )) != XB_NO_ERROR ){ + iErrorStop = 20; + throw iRc; + } + } + #endif + + xbDate d; + cUpdateYY = (char) d.YearOf() - 1900; + cUpdateMM = (char) d.MonthOf(); + cUpdateDD = (char) d.DayOf( XB_FMT_MONTH ); + ulNoOfRecs = 0; + + // rewrite the header record + if(( iRc = WriteHeader( 1, 1 )) != XB_NO_ERROR ){ + iErrorStop = 30; + throw iRc; + } + + // truncate the file to the new size + if(( iRc = xbTruncate( uiHeaderLen )) != XB_NO_ERROR ){ + iErrorStop = 40; + throw iRc; + } + BlankRecord(); + ulCurRec = 0; + + #ifdef XB_MEMO_SUPPORT + if( iMemoFieldCnt ){ + if(( iRc = Memo->Zap()) != XB_NO_ERROR ){ + iErrorStop = 50; + throw iRc; + } + } + #endif + #ifdef XB_INDEX_SUPPORT + xbLinkListNode *llN = GetTagList(); + xbTag *pTag; + xbIx *pIx; + void *vpTag; + while( llN ){ + pTag = llN->GetKey(); + pIx = pTag->GetIx(); + vpTag = pTag->GetVpTag(); + if(( iRc = pIx->Reindex( &vpTag )) != XB_NO_ERROR ){ + iErrorStop = 60; + throw iRc; + } + llN = llN->GetNextNode(); + } + + #endif // XB_INDEX_SUPPORT + #ifdef XB_LOCKING_SUPPORT + if( iAutoLock ){ + if(( iRc = LockTable( XB_UNLOCK )) != XB_NO_ERROR ){ + iErrorStop = 70; + throw iRc; + } + } + #endif // XB_LOCKING_SUPPORT + } + catch (xbInt16 iRc ){ + if( iRc != XB_LOCK_FAILED ){ + xbString sMsg; + sMsg.Sprintf( "xbdbf::Zap() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + } + return iRc; +} +/************************************************************************/ +} /* namespace */ \ No newline at end of file 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.
xbFalse - Don't overlay. + \param iShareMode XB_SINGLE_USER
XB_MULTI_USER + \returns Return Codes + +*/ +xbInt16 xbDbf3::CreateTable( const xbString & sTableName, const xbString & sAlias, xbSchema *pSchema, xbInt16 iOverlay, xbInt16 iShareMode ){ + + xbInt16 i, k, k2; + xbInt16 rc = 0; + xbInt16 iErrorStop = 0; + iDbfStatus = XB_CLOSED; + + xbString sNfn; + try{ + sNfn = sTableName; + xbase->GetLogStatus(); + + rc = NameSuffixMissing( sNfn, 1 ); + if( rc > 0 ) + sNfn += ".DBF"; + + SetFileName( sNfn ); + this->sAlias = sAlias; + + /* check if the file already exists */ + if( FileExists( 0 )){ + if( !iOverlay ){ + iErrorStop = 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
XB_READ_WRITE + \param iShareMode XB_SINGLE_USER
XB_MULTI_USER + \returns Return Codes +*/ + +xbInt16 xbDbf3::Open( const xbString & sTableName, const xbString & sAlias, + xbInt16 iOpenMode, xbInt16 iShareMode ){ + xbInt16 i, j, iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + char buf[33]; + char *p; + + #ifdef XB_MEMO_SUPPORT + iMemoFieldCnt = 0; + #endif + + try{ + + /* verify the file is not already open */ + if( iDbfStatus != XB_CLOSED ){ + iErrorStop = 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
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.
xbFalse - Don't overlay. + \param iShareMode XB_SINGLE_USER
XB_MULTI_USER + \returns Return Codes +*/ + +xbInt16 xbDbf4::CreateTable( const xbString &sTableName, const xbString &sAlias, xbSchema * pSchema, xbInt16 iOverlay, xbInt16 iShareMode ){ + + xbInt16 i, k, k2; + xbInt16 rc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + iDbfStatus = XB_CLOSED; + + xbString sNfn; + + try{ + sNfn = sTableName; + xbase->GetLogStatus(); + + rc = NameSuffixMissing( sNfn, 1 ); + if( rc > 0 ) + sNfn += ".DBF"; + + SetFileName( sNfn ); + this->sAlias = sAlias; + + /* check if the file already exists */ + if( FileExists( 0 )){ + if( !iOverlay ){ + iErrorStop = 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
XB_READ_WRITE + \param iShareMode XB_SINGLE_USER
XB_MULTI_USER + \returns Return Codes +*/ + +xbInt16 xbDbf4::Open( const xbString & sTableName, const xbString & sAlias, + xbInt16 iOpenMode, xbInt16 iShareMode ){ + xbInt16 i, j, iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + char buf[33]; + char *p; + + #ifdef XB_MEMO_SUPPORT + iMemoFieldCnt = 0; + #endif + + try{ + /* verify the file is not already open */ + if( iDbfStatus != XB_CLOSED ){ + iErrorStop = 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
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 Return Codes +*/ + +xbInt16 xbExp::CalcFunctionResultLen( xbExpNode * n ) const{ + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + xbInt16 iReturnLenCalc = 0;; + xbInt32 lReturnLenVal = 0; + xbString sNodeText; + + + try{ + + n->GetNodeText( sNodeText ); + char cReturnType = 0; + if(( iRc = xbase->GetFunctionInfo( sNodeText, cReturnType, iReturnLenCalc, lReturnLenVal )) != XB_NO_ERROR ){ + iErrorStop = 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 Return Codes +*/ + + +xbInt16 xbExp::CheckParensAndQuotes( const xbString &sExpression ){ + + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + + xbBool bInQuotes = xbFalse; + xbInt16 iLparenCtr = 0; + xbInt16 iRparenCtr = 0; + xbInt16 iQuoteType = 0; + const char *s = sExpression; + + try{ + + while( *s ){ + if( !bInQuotes ){ + if( *s == '(' ){ + iLparenCtr++; + } else if( *s == ')' ){ + iRparenCtr++; + } else if( *s == '\'' ){ + bInQuotes++; + iQuoteType = 0; + } else if( *s == '"' ){ + bInQuotes++; + iQuoteType = 1; + } + } else { + if(( *s == '\'' && iQuoteType == 0 ) || (*s == '"' && iQuoteType == 1 )) + bInQuotes--; + } + s++; + } + if( iLparenCtr != iRparenCtr ){ + iErrorStop = 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 Return Codes +*/ + +xbInt16 xbExp::GetDateResult( xbDate &dtResult ){ + if( nTree ){ + dtResult.JulToDate8( (xbInt32) nTree->GetNumericResult() ); + return XB_NO_ERROR; + } + else{ + //dtResult = ?; + return XB_PARSE_ERROR; + } +} +/*************************************************************************/ +//! Get bool result. +/*! + If the expression generates a boolean return type, this method retrieves the boolean value. + \param bResult - Output boolean value. + \returns Return Codes +*/ +xbInt16 xbExp::GetBoolResult( xbBool &bResult){ + if( nTree ){ + bResult = nTree->GetBoolResult(); + return XB_NO_ERROR; + } + else{ + return XB_PARSE_ERROR; + } +} + +/*************************************************************************/ +//! Get the next node in the tree. +/*! + \param n Node to starting point. To get the first node of the entire tree, set n = NULL + \returns Pointer to next node. +*/ + +xbExpNode *xbExp::GetNextNode( xbExpNode * n ) const { + + // to get the first node of the entire tree, set n = NULL + // std::cout << "In GetNextNode\n"; + + if( n == nTree ) + return NULL; + + else if( !n ){ + if( !nTree ) + return NULL; + else + return nTree->GetFirstNode(); + } + return n->GetNextNode(); +} + +/*************************************************************************/ +//! GetNextToken +/*! This method returns the next token in an expression of one or more tokens + \param t Token + \returns Return Codes +*/ + +xbInt16 xbExp::GetNextToken( xbExpToken &t ){ + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + + try{ + t.iSts = XB_NO_ERROR; + t.sExpression.Ltrim(); + + if( t.sExpression.Len() == 0 ){ + t.iSts = XB_END_OF_EXPRESSION; + return XB_NO_ERROR; + } + + // Check for date constant + if((t.sExpression.Len() >= 10 && t.sExpression[1] == '{' && t.sExpression[4] == '/' && t.sExpression[7] == '/') && + (t.sExpression[10] == '}' || (t.sExpression.Len() >= 12 && t.sExpression[12] == '}'))){ + if(( iRc = GetTokenDateConstant( t )) != XB_NO_ERROR ){ + iErrorStop = 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 Return Codes +*/ + +xbInt16 xbExp::GetNumericResult( xbDouble &dResult){ + if( nTree ){ + dResult = nTree->GetNumericResult(); + return XB_NO_ERROR; + } + else{ + dResult = 0; + return XB_PARSE_ERROR; + } +} + +/*************************************************************************/ +//! Get result length. +/*! + This routine returns the result length. + \returns Result length. +*/ + +xbInt16 xbExp::GetResultLen() const{ + if( nTree ) + return nTree->GetResultLen(); + else + return 0; +} + +/*************************************************************************/ +//! Get return type. +/*! + \returns Expression return type. +*/ + +char xbExp::GetReturnType() const{ + if( nTree ) + return nTree->GetReturnType(); + else + return ' '; +} + +/*************************************************************************/ +//! Get string result. +/*! + If the expression generates a string return type, this method retrieves the string value. + \param sResult - Output string value. + \returns Return Codes +*/ + +xbInt16 xbExp::GetStringResult( xbString &sResult){ + if( nTree ){ + sResult = nTree->GetStringResult(); + return XB_NO_ERROR; + } + else{ + sResult = ""; + return XB_PARSE_ERROR; + } +} + +/*************************************************************************/ +//! Get string result. +/*! + If the expression generates a string return type, this method retrieves the string value. + \param vpResult - Pointer to user supplied buffer for result. + \param ulLen - Max size of buffer. + \returns Return Codes +*/ + + +xbInt16 xbExp::GetStringResult( char * vpResult, xbUInt32 ulLen ){ + if( nTree ){ + nTree->GetStringResult().strncpy((char *) vpResult, ulLen ); + return XB_NO_ERROR; + } + else{ + return XB_PARSE_ERROR; + } +} + + + +/*************************************************************************/ +//! GetTokenCharConstant +/*! This method returns the character constant in a pair of quotes + + This routine returns the tokens inside a set of matching quotes in sOutToken + If there is nothing between the quotes then sOutToken is returned empty + sOutRemainder contains whatever remains to the right of the right quote + + \param t Token + \returns Return Codes + +*/ + +xbInt16 xbExp::GetTokenCharConstant( xbExpToken &t ){ + + const char *s = t.sExpression; + const char *sToken; // pointer to beginning of token + xbInt16 iQuoteType; + xbUInt32 ulTokenLen = 0; + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + xbBool bDone = xbFalse; + + try{ + if( *s == '"' ) + iQuoteType = 0; + else + iQuoteType = 1; + s++; + sToken = s; + while( *s && !bDone ){ + if(( *s == '"' && iQuoteType == 0 ) || (*s == '\'' && iQuoteType == 1 )) + bDone = xbTrue; + s++; + ulTokenLen++; + } + if( bDone ){ // found matching paren + t.cNodeType = XB_EXP_CONSTANT; + t.cReturnType = XB_EXP_CHAR; + t.sToken.Set( sToken, ulTokenLen - 1 ); + t.sExpression.Ltrunc( ulTokenLen + 1 ); + } else { + iRc = XB_PARSE_ERROR; + t.iSts = XB_UNBALANCED_QUOTES; + iErrorStop = 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 Return Codes +*/ + +xbInt16 xbExp::GetTokenDateConstant( xbExpToken &t ){ + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + char wBuf[13]; + xbDate dt; + + try{ + memset( wBuf, 0x00, 13 ); + t.cNodeType = XB_EXP_CONSTANT; + t.cReturnType = XB_EXP_DATE; + + if( t.sExpression[10] == '}' ){ + for( xbInt16 i = 0; i < 8; i++ ) + wBuf[i] = t.sExpression[i+2]; + + if(( iRc = dt.CTOD( wBuf )) != XB_NO_ERROR ){ + iErrorStop = 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 Return Codes +*/ + +xbInt16 xbExp::GetTokenDatabaseField( xbExpToken &t ){ + + const char *s = t.sExpression; + xbUInt32 ulTokenLen = 0; + xbUInt32 ulTokenLen2 = 0; + + while( *s && !IsTokenSeparator( *s ) && !IsWhiteSpace( *s )) { + ulTokenLen++; + s++; + } + + // go past any white space + while( *s && !IsTokenSeparator( *s ) && IsWhiteSpace( *s )) { + ulTokenLen2++; + s++; + } + + // look for -> + // remove the 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 Return Codes + +*/ + +xbInt16 xbExp::GetTokenFunction( xbExpToken &t ){ + + xbUInt32 lPos = t.sExpression.Pos( '(' ); + if( lPos == 0 ) + return XB_PARSE_ERROR; + + xbBool bDone = xbFalse; + xbUInt32 lLen = t.sExpression.Len(); + xbInt16 iDepthCtr = 1; + + while( ++lPos <= lLen && !bDone ){ + if( t.sExpression[lPos] == ')' ){ + iDepthCtr--; + if( iDepthCtr == 0 ) + bDone = xbTrue; + } else if( t.sExpression[lPos] == '(' ){ + iDepthCtr++; + } + } + + t.cNodeType = XB_EXP_FUNCTION; + t.sToken.Set( t.sExpression, lPos-1 ); + t.sExpression.Ltrunc( lPos-1 ); + +// std::cout << "lPos = [" << lPos << "] done= [" << bDone << "][" << t.sExpression << "] len=[" << lLen << "] return type = [" << t.cReturnType << "]\n"; + + return XB_NO_ERROR; +} + +/*************************************************************************/ +//! GetTokenCharConstant +/*! This method returns the character constant in a pair of quotes + + This routine returns the tokens inside a set of matching quotes in sOutToken + If there is nothing between the quotes then sOutToken is returned empty + sOutRemainder contains whatever remains to the right of the right quote + + \param t Token + \returns Return Codes +*/ + +xbInt16 xbExp::GetTokenLogicalConstant( xbExpToken &t ){ + + t.cNodeType = XB_EXP_CONSTANT; + t.cReturnType = XB_EXP_LOGICAL; + t.sToken = t.sExpression[2]; + + if( t.sExpression[3] == '.' ) + t.sExpression.Ltrunc( 3 ); + else if( t.sExpression[6] == '.' ) + t.sExpression.Ltrunc( 6 ); + else if( t.sExpression[7] == '.' ) + t.sExpression.Ltrunc( 7 ); + + return XB_NO_ERROR; +} + + +/*************************************************************************/ +//! GetTokenNumericConstant +/*! This method returns a numeric constant in + + This routine returns a numeric constant token + sOutRemainder contains whatever remains to the right of the right quote + + \param t Token + \returns Return Codes +*/ + +xbInt16 xbExp::GetTokenNumericConstant( xbExpToken &t ){ + + const char * s = t.sExpression; + xbUInt32 ulTokenLen = 0; + t.sToken = ""; + + t.cNodeType = XB_EXP_CONSTANT; + t.cReturnType = XB_EXP_NUMERIC; + + // capture the leading sign + if( *s == '-' || *s == '+' || *s == '.' ){ + t.sToken = *s; + ulTokenLen++; + s++; + + // go past any white space between sign and number + while( *s && IsWhiteSpace( *s )){ + s++; + ulTokenLen++; + } + } + + // add the number to the token + while( *s && (isdigit( *s ) || *s == '.' )){ + t.sToken += *s; + s++; + ulTokenLen++; + } + t.sExpression.Ltrunc( ulTokenLen ); + return XB_NO_ERROR; +} + +/*************************************************************************/ +//! GetTokenOperator +/*! This method returns the operator + + \param t Token + \returns Return Codes + +*/ + +xbInt16 xbExp::GetTokenOperator( xbExpToken &t ){ + + const char *s = t.sExpression; + + // Logical operators + if((strncmp( s, "<>", 2 ) == 0 ) || (strncmp( s, "!=", 2 ) == 0 ) || + (strncmp( s, "<=", 2 ) == 0 ) || (strncmp( s, ">=", 2 ) == 0 )){ + t.cReturnType = XB_EXP_LOGICAL; + t.sToken.Assign( s, 1, 2 ); + t.sExpression.Ltrunc( 2 ); + t.cNodeType = XB_EXP_OPERATOR; + return XB_NO_ERROR; + } + + if( *s == '=' || *s == '<' || *s == '>' || *s == '$' || *s == '#' ){ + t.cReturnType = XB_EXP_LOGICAL; + t.sToken.Assign( s, 1, 1 ); + t.sExpression.Ltrunc( 1 ); + t.cNodeType = XB_EXP_OPERATOR; + return XB_NO_ERROR; + } + + if( (strncmp( s, ".NOT.", 5 ) == 0 ) || (strncmp( s, ".AND.", 5 ) == 0 )){ + t.cReturnType = XB_EXP_LOGICAL; + t.sToken.Assign( s, 1, 5 ); + t.sExpression.Ltrunc( 5 ); + t.cNodeType = XB_EXP_OPERATOR; + return XB_NO_ERROR; + } + + if( (strncmp( s, "NOT ", 4 ) == 0 ) || (strncmp( s, "AND ", 4 ) == 0 )){ + t.cReturnType = XB_EXP_LOGICAL; + t.sToken.Assign( s, 1, 3 ); + t.sExpression.Ltrunc( 3 ); + t.cNodeType = XB_EXP_OPERATOR; + return XB_NO_ERROR; + } + + if( strncmp( s, ".OR.", 4 ) == 0 ) { + t.cReturnType = XB_EXP_LOGICAL; + t.sToken.Assign( s, 1, 4 ); + t.sExpression.Ltrunc( 4 ); + t.cNodeType = XB_EXP_OPERATOR; + return XB_NO_ERROR; + } + + if( strncmp( s, "OR ", 3 ) == 0 ) { + t.cReturnType = XB_EXP_LOGICAL; + t.sToken.Assign( s, 1, 2 ); + t.sExpression.Ltrunc( 2 ); + t.cNodeType = XB_EXP_OPERATOR; + return XB_NO_ERROR; + } + + // Numeric operators + if(( strncmp( s, "**", 2 ) == 0 ) || ( strncmp( s, "+=", 2 ) == 0 ) || + ( strncmp( s, "-=", 2 ) == 0 ) || ( strncmp( s, "*=", 2 ) == 0 ) || ( strncmp( s, "/=", 2 ) == 0 )){ + t.cReturnType = XB_EXP_NUMERIC; + t.sToken.Assign( s, 1, 2 ); + t.sExpression.Ltrunc( 2 ); + t.cNodeType = XB_EXP_OPERATOR; + return XB_NO_ERROR; + } + + // Pre/post increment/decrement operators ++ or -- + if(( strncmp( s, "--", 2 ) == 0 ) || ( strncmp( s, "++", 2 ) == 0 )){ + t.cReturnType = XB_EXP_NUMERIC; + t.sToken.Assign( s, 1, 2 ); + t.sExpression.Ltrunc( 2 ); + if( t.sExpression.Len() > 0 && (isdigit( t.sExpression[1] ) || isalpha( t.sExpression[1] ))) + t.cNodeType = XB_EXP_PRE_OPERATOR; + else + t.cNodeType = XB_EXP_POST_OPERATOR; + + return XB_NO_ERROR; + } + + if( *s == '*' || *s == '/' || *s == '%' || *s == '^' ){ + t.cReturnType = XB_EXP_NUMERIC; + t.sToken.Assign( s, 1, 1 ); + t.sExpression.Ltrunc( 1 ); + t.cNodeType = XB_EXP_OPERATOR; + return XB_NO_ERROR; + } + + // multi return type operators + t.cReturnType = XB_EXP_UNKNOWN; + if( *s == '+' || *s == '-' ){ + t.sToken.Assign( s, 1, 1 ); + t.sExpression.Ltrunc( 1 ); + t.cNodeType = XB_EXP_OPERATOR; + return XB_NO_ERROR; + } + return XB_PARSE_ERROR; +} + +/*************************************************************************/ +//! GetTokenParen +/*! This method returns the tokens in a pair of enclosed parens + + This routine returns the tokens inside a set of matching parens in sOutToken + If there is nothing between the parens then sOutToken is returned empty + sOutRemainder contains whatever remains to the right of the right paren + + \param t Token + \returns Return Codes +*/ + + +xbInt16 xbExp::GetTokenParen( xbExpToken &t ){ + + const char * s = t.sExpression; + const char * sToken; // pointer to beginning of token + xbInt16 iParenType = 0; + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + xbInt16 iDepthCtr = 0; // depth inside of nested parens + xbUInt32 ulTokenLen = 0; + xbBool bDone = xbFalse; + + try{ + if( *s == '{' ) + iParenType = 0; + else + iParenType = 1; + iDepthCtr = 1; + s++; + sToken = s; + + while( *s && !bDone ){ + if(( *s == ')' && iParenType == 1 ) || (*s == '}' && iParenType == 0 )){ + iDepthCtr--; + if( iDepthCtr == 0 ) + bDone = xbTrue; + } else if(( *s == '(' && iParenType == 1 ) || (*s == '{' && iParenType == 0 )){ + iDepthCtr++; + } + s++; + ulTokenLen++; + } + + if( bDone ){ // found matching paren + t.cNodeType = XB_EXP_NOTROOT; + t.cReturnType = XB_EXP_UNKNOWN; + t.sToken.Set( sToken, ulTokenLen - 1 ); + t.sExpression.Ltrunc( ulTokenLen + 1 ); + } else { + t.sToken = ""; + t.cNodeType = XB_EXP_NOTROOT; + t.cReturnType = XB_EXP_UNKNOWN; + t.iSts = XB_UNBALANCED_PARENS; + iRc = XB_PARSE_ERROR; + iErrorStop = 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.
+ xbFalse - Is not a function. +*/ + +xbBool xbExp::IsFunction( const xbString & sExpression, char &cReturnType ){ + + xbInt16 i = 0; + xbInt32 l = 0; + if( sExpression.Pos( '(' ) > 0 ){ + if( xbase->GetFunctionInfo( sExpression, cReturnType, i, l ) == XB_NO_ERROR ) + return xbTrue; + } + return xbFalse; +} + +/*************************************************************************/ +//! Is Logical constant +/*! This method determines if the next token is a logical constant (T/F, etc). + + \param sExpression - String expression to be evaluated. + \returns xbTrue - Is a logical constant.
+ xbFalse - Is not a logical constant. +*/ + +xbBool xbExp::IsLogicalConstant( const xbString & sExpression ){ + + const char *s = sExpression; + if(( strncmp( s, ".T.", 3 ) == 0 ) || ( strncmp( s, ".F.", 3 ) == 0 )) + return xbTrue; + else if( strncmp( s, ".TRUE.", 6 ) == 0 ) + return xbTrue; + else if( strncmp( s, ".FALSE.", 7 ) == 0 ) + return xbTrue; + + return xbFalse; +} + +/*************************************************************************/ +//! Is Numeric constant +/*! This method determines if the next token is a numeric constant. + + \param sExpression - String expression to be evaluated. + \param cPrevNodeType - Type of previous node. + \returns xbTrue - Is a numeric constant.
+ xbFalse - Is not a numeric constant. +*/ +xbBool xbExp::IsNumericConstant( const xbString & sExpression, char cPrevNodeType ){ + + // check for positive, negative or decimal number constants + + const char *s = sExpression; + if(( *s == '-' && ( cPrevNodeType == 'O' || cPrevNodeType == 0 )) || + ( *s == '+' && ( cPrevNodeType == 'O' || cPrevNodeType == 0 ))){ + s++; + while( *s && IsWhiteSpace( *s )) + s++; + } + if( *s == '.' ) + s++; + + if( isdigit( *s )) + return xbTrue; + else + return xbFalse; +} + +/*************************************************************************/ +//! Is Operator. +/*! This method determines if the next token is an operator. + + \param sExpression - String expression to be evaluated. + \returns xbTrue - Is an operator.
+ xbFalse - Is not an operator. +*/ +xbBool xbExp::IsOperator( const xbString & sExpression ){ + + const char *s = sExpression; + if( *s == '+' || *s == '-' || *s == '/' || *s == '^' || *s == '=' || *s == '$' || + *s == '#' || *s == '*' || *s == '<' || *s == '>' || *s == '%' ) + return xbTrue; + + if( strncmp( s, "!=", 2 ) == 0 ) + return xbTrue; + + if((strncmp( s, ".NOT.", 5 ) == 0 ) || (strncmp( s, ".OR.", 4 ) == 0 ) || (strncmp( s, ".AND.", 5 ) == 0 )) + return xbTrue; + + if((strncmp( s, "NOT ", 4 ) == 0 ) || (strncmp( s, "OR ", 3 ) == 0 ) || (strncmp( s, "AND ", 4 ) == 0 )) + return xbTrue; + + return xbFalse; +} + +/*************************************************************************/ +//! Is Token seperator +/*! This method determines if the next token is a seperator. + + \param sExpression - String expression to be evaluated. + \returns xbTrue - Is a token seperator.
+ xbFalse - Is not a token seperator. +*/ +char xbExp::IsTokenSeparator( char c ){ + if( c == '-' || c == '+' || c == '*' || c == '/' || c == '$' || c == '#' || + c == '<' || c == '>' || c == '^' || c == '=' || c == '.' || c == '!' ) + return c; + else + return 0; +} +/*************************************************************************/ +//! Is White space +/*! This method determines if a given character is white space. + + \param c - Character to be evaluated. + \returns xbTrue - Is white space.
+ xbFalse - Is not white space. +*/ +xbBool xbExp::IsWhiteSpace( char c ){ + return(( c == 0x20 )? 1 : 0 ); +} + +/*************************************************************************/ +//! Get operator weight. +/*! + This method determines the priority of an operator + + Operator precendence + 10 .AND. .OR. .NOT. (not really an operator) + 9 > or < (includes <= or >=) + 6 unary plus or minus (+,-) -- not passed this routine + 5 prefix increment and/or decrement (++,--) + 4 exponentiation ** or ^ + 3 multiplication,division or modulus (*,/,%) + 2 Addition, subtraction (+,-) + 1 Postfix increment and/or decrement (++,--) + + \param sOper - Operator. + \returns Operator weight + +*/ + +xbInt16 xbExp::OperatorWeight( const xbString &sOper ){ + + if( sOper == "" || sOper.Len() > 5 ) + return 0; + + else if( sOper == "--0" || sOper == "++0" ) // 0 is prefix + return 9; + else if( sOper == "**" || sOper == "^" ) + return 8; + else if( sOper == "*" || sOper == "/" || sOper == "%" || sOper == "*=" || sOper == "/=" ) + return 7; + else if( sOper == "+" || sOper == "-" || sOper == "+=" || sOper == "-=" ) + return 6; + else if( sOper == "--1" || sOper == "++1" ) // 1 is post fix + return 5; + else if( sOper == ">" || sOper == ">=" || sOper == "<" || sOper == "<=" || + sOper == "<>" || sOper == "!=" || sOper == "#" || sOper == "$" || sOper == "=" ) + return 4; + else if( sOper == ".NOT." || sOper == "NOT" ) + return 3; + else if( sOper == ".AND." || sOper == "AND" ) + return 2; + else if( sOper == ".OR." || sOper == "OR" ) + return 1; + + return 0; +} + + +/*************************************************************************/ +//! Parse expression. +/*! + \param sExpression - Expression to parse. + \returns Return Codes +*/ + +xbInt16 xbExp::ParseExpression( const xbString &sExpression ){ + return ParseExpression( sExpression, (xbInt16) 0 ); +} + +/*************************************************************************/ +//! Parse expression. +/*! + \param dbf - Pointer to xbDbf instance. + \param sExpression - Expression to parse. + \returns Return Codes +*/ + +xbInt16 xbExp::ParseExpression( xbDbf *dbf, const xbString &sExpression ){ + this->dbf = dbf; + return ParseExpression( sExpression, (xbInt16) 0 ); +} + +/*************************************************************************/ +//! Parse expression. +/*! + \param sExpression - Expression to parse. + \param iWeight. + \returns Return Codes +*/ +xbInt16 xbExp::ParseExpression( const xbString &sExpression, xbInt16 iWeight ){ + + xbExpNode *n; + xbExpNode *nLastNode = NULL; // pointer to the last node processed + xbExpToken t; + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + xbString s; + + try { + + if( nTree ) + delete nTree; + + if(( iRc = CheckParensAndQuotes( sExpression )) != XB_NO_ERROR ){ + iErrorStop = 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 Return Codes +*/ +xbInt16 xbExp::ParseExpressionConstant( xbExpToken &t, xbExpNode *n ){ + + xbDate dtWork; + n->SetReturnType( t.cReturnType ); + + // std::cout << "parse expression constant[" << t.sToken << "]\n"; + + switch( t.cReturnType ){ + case XB_EXP_CHAR: + n->SetResultLen( t.sToken.Len() ); + n->SetResult( t.sToken ); + break; + + case XB_EXP_DATE: + n->SetResultLen( 8 ); + dtWork.Set( t.sToken ); + n->SetResult( dtWork ); + break; + + case XB_EXP_LOGICAL: + n->SetResultLen( 1 ); + if( strncmp( t.sToken, "T", 1 ) == 0 ) + n->SetResult( (xbBool) xbTrue ); + else + n->SetResult( (xbBool) xbFalse ); + break; + + case XB_EXP_NUMERIC: + n->SetResultLen( 4 ); + n->SetResult( strtod( t.sToken, 0 )); + n->SetResult( t.sToken ); + break; + + default: + return XB_PARSE_ERROR; + // break; + } + return XB_NO_ERROR; +} + +/*************************************************************************/ +//! Parse expression field. +/*! + \param t - Token. + \param n - Node. + \returns Return Codes +*/ +xbInt16 xbExp::ParseExpressionField( xbExpToken &t, xbExpNode *n ){ + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + xbDbf * pDbf; + xbString sFieldName; + + // do the db lookup and set the field number for the field + + try{ + + xbUInt32 lPos; + if(( lPos = t.sToken.Pos( "->" )) > 0 ){ + // table name is part of the token + xbString sTableName = t.sToken; + sTableName.Left( lPos-1 ); + sFieldName = t.sToken; + sFieldName.Mid( lPos + 2, t.sToken.Len() - lPos - 1 ); + pDbf = (xbDbf *) xbase->GetDbfPtr( sTableName ); + } 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 Return Codes +*/ +xbInt16 xbExp::ParseExpressionFunction( xbExpToken &t, xbExpNode *n, xbInt16 iWeight ){ + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + + try{ + // find the first "(" + xbUInt32 lPos = t.sToken.Pos( '(' ); + if( lPos == 0 ){ + iErrorStop = 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 llParms; + if(( iRc = ParseExpressionFunctionParms( sParms, llParms )) != XB_NO_ERROR ){ + iErrorStop = 40; + throw iRc; + } + + // for each function parm, recursively process it + xbLinkListNode * llN = llParms.GetHeadNode(); + xbString sParm; + while( llN ){ + sParm = llN->GetKey(); + if(( iRc = enr.ParseExpression( sParm, iWeight + 10 )) != XB_NO_ERROR ){ + iErrorStop = 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 Return Codes +*/ + +xbInt16 xbExp::ParseExpressionFunctionParms( const xbString &sParms, xbLinkList & llParms ){ + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + xbInt16 iParenCtr = 0; + xbInt16 iInQuotes = 0; + xbInt16 iDoubleQuotes = 0; + xbInt16 iSingleQuotes = 0; + xbInt32 lStartPos = 0; + xbInt32 lParmLen = 0; + + xbString sParm; + + try{ + const char *sp = sParms; + + while( *sp ){ + if( *sp == '(') + iParenCtr++; + else if( *sp == ')' ) + iParenCtr--; + else if( !iInQuotes && *sp == '"' ){ + iInQuotes++; + iDoubleQuotes++; + } else if( iInQuotes && *sp == '"' ){ + iInQuotes--; + iDoubleQuotes--; + } + else if( !iInQuotes && *sp == '\'' ){ + iInQuotes++; + iSingleQuotes++; + } else if( iInQuotes && *sp == '\'' ){ + iInQuotes--; + iSingleQuotes--; + + } else if( !iInQuotes && !iParenCtr && *sp == ',' ){ + // found a valid comma - at the end of a parm + // add it to the end of the linked list + sParm = sParms; + sParm.Mid( (xbUInt32) lStartPos + 1, (xbUInt32) lParmLen ).Trim(); // mid is one based + llParms.InsertAtEnd( sParm ); + + // set the start pos for the next one on the list + lStartPos += lParmLen + 1; + lParmLen = -1; + // lParmLen = 0; + } + lParmLen++; + sp++; + } + if( lParmLen > 0 ){ + // get the last parm, it didn't end with a comma + sParm = sParms; + sParm.Mid( (xbUInt32) lStartPos + 1, (xbUInt32) lParmLen ).Trim(); + llParms.InsertAtEnd( sParm ); + } + + } + // try / catch not used in this method, structure added for potential future use + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbexp::ParseExpressionFunctionParms() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + } + return iRc; +} +/*************************************************************************/ +//! Parse expression operator. +/*! + \param t - Token. + \param n - Node. + \param iWeight + \returns Return Codes +*/ + +xbInt16 xbExp::ParseExpressionOperator( xbExpToken &t, xbExpNode *n, xbInt16 iWeight ){ + + n->SetResult( t.sToken ); + n->SetWeight( iWeight + OperatorWeight( t.sToken) ); + +// std::cout << "ParseExpressionOperator [" << t.cPrevNodeType << "][" << t.sToken << "] Weight = [" << iWeight; +// std::cout << "] PrevReturnType [" << t.cPrevReturnType; +// std::cout << "] Operator weight [" << OperatorWeight( t.sToken ) << "] getweight [" << n->GetWeight() << "]\n"; + + if( t.sToken == "**" || t.sToken == "^" || + t.sToken == "*" || t.sToken == "/" || t.sToken == "%" || t.sToken == "*=" || t.sToken == "/=" ) + n->SetReturnType( XB_EXP_NUMERIC ); + + else if( t.sToken == "--" || t.sToken == "++" || t.sToken == "+=" || t.sToken == "-=" ) // could be date or numeric + n->SetReturnType( XB_EXP_UNKNOWN ); + + else if( t.cPrevReturnType == XB_EXP_CHAR && ( t.sToken == "+" || t.sToken == "-" )) + n->SetReturnType( XB_EXP_CHAR ); + + else if( t.sToken == ".AND." || t.sToken == ".OR." || t.sToken == ".NOT." || + t.sToken == "AND" || t.sToken == "OR" || t.sToken == "NOT" || + t.sToken == ">" || t.sToken == ">=" || t.sToken == "<" || + t.sToken == "<=" || t.sToken == "<>" || t.sToken == "!=" || + t.sToken == "$" || t.sToken == "#" || t.sToken == "=" ) + n->SetReturnType( XB_EXP_LOGICAL ); + + + else if( t.cPrevReturnType == XB_EXP_UNKNOWN ) + n->SetReturnType( XB_EXP_UNKNOWN ); + + // added for date constant logic 10/28/17 + else if(( t.sToken == "+" || t.sToken == "-" ) && t.cPrevReturnType == XB_EXP_DATE ) + n->SetReturnType( XB_EXP_DATE ); + + else if( t.sToken == "+" || t.sToken == "-" ) + n->SetReturnType( XB_EXP_NUMERIC ); + + return XB_NO_ERROR; +} + + +/************************************************************************/ +//! ProcessExpression +/*! This method processes an expression tree leaving the result in the + root node of the tree +*/ +xbInt16 xbExp::ProcessExpression(){ + return ProcessExpression( 0 ); +} +/************************************************************************/ +//! ProcessExpression +/*! This method processes a parsed expression tree leaving the result in the + root node of the tree + \param iRecBufSw Record buffer to use when evaluating expression.
+ 0 - Current record buffer.
+ 1 - Original record buffer. +*/ + +xbInt16 xbExp::ProcessExpression( xbInt16 iRecBufSw ){ + +// iRecBufSw 0 - Record Buffer +// 1 - Original Record Buffer + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + + try{ + + xbExpNode * nWork = GetNextNode( NULL ); + xbExpNode * nChild1; + xbDbf * dbf; + xbString sWork1; + xbString sWork2; + xbString sOperator; + xbDate dtWork1; + + xbBool bWork; + xbDouble dWork; + + while( nWork ){ + switch( nWork->GetNodeType() ){ + + case XB_EXP_CONSTANT: + break; + + case XB_EXP_PRE_OPERATOR: // increment value before setting in head node + if( nWork->GetChildCnt() != 1 ){ + iErrorStop = 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 Return Codes +*/ +xbInt16 xbExpNode::AddChild( xbExpNode *n ){ + n->SetParent( this ); + return llChildren.InsertAtEnd( n ); +} + +/************************************************************************/ +#ifdef XB_DEBUG_SUPPORT + +//! @brief Dump Node. +/*! + \param iOption xbTrue - Print child info if they exist. + xbFalse - Don't print child info. + \returns void. +*/ +void xbExpNode::DumpNode( xbInt16 iOption ) const { + xbString sMsg; + std::cout << "Me=[" << this << "] "; + if( nParent ) + std::cout << "Par=[" << nParent << "] "; + + std::cout << "nTyp=[" << cNodeType << "] "; + std::cout << "dTyp=[" << cReturnType << "] "; + if( iWeight != 0 ) + std::cout << "W=[" << iWeight << "] "; + + if( cNodeType == XB_EXP_FIELD ) + std::cout << "FieldNo=[" << iFieldNo << "] "; + + std::cout << "Txt=[" << sNodeText << "] "; + if( sResult != "" ) + std::cout << "sVal=[" << sResult << "] "; + + if( ulResultLen > 0 ) + std::cout << "Len=[" << ulResultLen << "] "; + if( cReturnType == XB_EXP_DATE ){ + xbDate d( (xbInt32) dResult ); + std::cout << "dtVal=[" << d.Str() << "] "; + } + + if( cReturnType == XB_EXP_DATE || cReturnType == XB_EXP_NUMERIC ){ + sMsg.Sprintf( "dVal=[%f]\n", dResult ); + std::cout << sMsg.Str(); + } + + if( cReturnType == XB_EXP_LOGICAL ){ + sMsg.Sprintf( "lVal=[%d]\n", (xbInt32) dResult ); + std::cout << sMsg.Str(); + } + + if( iOption ){ + xbLinkListNode *lln = llChildren.GetHeadNode(); + xbExpNode *n; + if( lln ){ + std::cout << " Children: "; + while( lln ){ + n = lln->GetKey(); + std::cout << " [" << n << "]"; + lln = lln->GetNextNode(); + } + std::cout << std::endl; + + lln = llChildren.GetHeadNode(); + while( lln ){ + n = lln->GetKey(); + n->DumpNode( iOption ); + lln = lln->GetNextNode(); + } + } + } + + std::cout << std::endl; +} +#endif + +/************************************************************************/ +//! @brief Get boolean result. +/*! + \returns Boolean result. +*/ + +xbBool xbExpNode::GetBoolResult() const { + return (xbBool) dResult; +} + +/************************************************************************/ +//! @brief Get pointer to child. +/*! + \param ulChildNo - Which child? 1,2 or3 + \returns Pointer to child node or null if none exists. +*/ +xbExpNode *xbExpNode::GetChild( xbUInt32 ulChildNo ) const { + + xbLinkListNode *lln = llChildren.GetNodeForNo( ulChildNo ); + if( lln ) + return lln->GetKey(); + else + return 0x00; +} +/************************************************************************/ +//! @brief Get child count. +/*! + \returns Child count. +*/ +xbUInt32 xbExpNode::GetChildCnt() const{ + return llChildren.GetNodeCnt(); +} + +/************************************************************************/ +//! @brief Get the current child number for this node. +/*! + \returns Child number: 1, 2 or 3. +*/ + +xbUInt32 xbExpNode::GetChildNo() const { + + if( !nParent ) + return 0; + + for( xbUInt32 i = 0; i < nParent->GetChildCnt(); i++ ){ + if( this == nParent->GetChild( i )){ + // std::cout << "get child no [" << this << "][" << nParent->GetChild(i) << "]\n"; + return i; + } + } + return 0; +} + +/************************************************************************/ +//! @brief Get the dbf pointer. +/*! + \returns Pointer to dbf. +*/ +xbDbf *xbExpNode::GetDbf() const { + return dbf; +} +/************************************************************************/ +//! @brief Get the field number. +/*! + \returns Field number for field node. +*/ + +xbInt16 xbExpNode::GetFieldNo() const { + return iFieldNo; +} + +/*************************************************************************/ +//! @brief Get the first node. +/*! + \returns Pointer to left most child node or *this if childless. +*/ + +xbExpNode *xbExpNode::GetFirstNode() { + xbExpNode *n = this; + while( n && n->GetChildCnt() > 0 ) + n = n->GetChild(0); + return n; +} + +/*************************************************************************/ +//! @brief Get the next node. +/*! + \returns Pointer to right node or parent if right node does not exist. +*/ + +xbExpNode *xbExpNode::GetNextNode() const { + + if( HasRightSibling()) + return GetRightSibling()->GetFirstNode(); + else + return nParent; +} + +/************************************************************************/ +//! @brief Get the node text. +/*! + \param sOutText Output string containing node text. + \returns void +*/ + +void xbExpNode::GetNodeText( xbString &sOutText ) const{ + sOutText = sNodeText; +} + +/************************************************************************/ +//! @brief Get the node type. +/*! + \returns Node type. +*/ + +char xbExpNode::GetNodeType() const{ + return cNodeType; +} + +/************************************************************************/ +//! @brief Get numeric result. +/*! + \returns Numeric result. +*/ + +xbDouble xbExpNode::GetNumericResult() const { + return dResult; +} + +/************************************************************************/ +//! @brief Get parent. +/*! + \returns Pointer to parent node. +*/ + +xbExpNode *xbExpNode::GetParent() const{ + return nParent; +} + +/************************************************************************/ +//! @brief Get result length. +/*! + \returns Result length. +*/ +xbUInt32 xbExpNode::GetResultLen() const{ + return ulResultLen; +} +/************************************************************************/ +//! @brief Get result type. +/*! + \returns Result type. +*/ +char xbExpNode::GetReturnType() const{ + return cReturnType; +} + +/*************************************************************************/ +//! @brief Get right sibling. +/*! + \returns Pointer to right sibling. +*/ + +xbExpNode *xbExpNode::GetRightSibling() const { + + xbExpNode * nParent; + if(( nParent = GetParent()) == NULL ) + return NULL; + + if( nParent->GetChildCnt() <= 0 ) + return NULL; + + xbUInt32 ulChildNo = GetChildNo(); + + if( ulChildNo < (nParent->GetChildCnt() - 1)) + return nParent->GetChild( ulChildNo + 1 ); + else + return NULL; +} +/************************************************************************/ +//! @brief Get string result. +/*! + \returns String result. +*/ +xbString &xbExpNode::GetStringResult(){ + return sResult; +} +/************************************************************************/ +//! @brief Get node weight. +/*! + Each node is assigned a weight used internally to detmerine processing sequence. + \returns Node weight. +*/ + +xbInt16 xbExpNode::GetWeight() const { + return iWeight; +} + +/*************************************************************************/ +//! @brief Determine if node has a right sibling. +/*! + \returns xbTrue - Node has right sibling.
+ xbFalse - Node has no right sibling. +*/ + +xbBool xbExpNode::HasRightSibling() const { + + // std::cout << "in HasRightSibling [" << sNodeText << "]\n"; + + if( nParent == NULL ) + return xbFalse; + + xbUInt32 ulChildNo = GetChildNo(); + + if( ulChildNo < (nParent->GetChildCnt() - 1)){ + // std::cout << "Has Right Sibling = " << iChildNo << "] of [" << nParent->GetChildCnt() << "]\n"; + return xbTrue; +} + else + return xbFalse; +} + + +/*************************************************************************/ +//! @brief Determine if node is an operator. +/*! + \returns xbTrue - Node is an operator.
+ xbFalse - Node is not an operator. +*/ + +xbBool xbExpNode::IsOperator() const { + + if( cNodeType == XB_EXP_OPERATOR ) + return xbTrue; + else + return xbFalse; +} + +/*************************************************************************/ +//! @brief Determine if node is a unary operator. +/*! + \returns xbTrue - Node is a unary operator.
+ xbFalse - Node is not a unary operator. +*/ +xbBool xbExpNode::IsUnaryOperator() const { + + if( cNodeType == XB_EXP_PRE_OPERATOR || cNodeType == XB_EXP_POST_OPERATOR ) + return xbTrue; + else if( cNodeType == XB_EXP_OPERATOR && (sNodeText == ".NOT." || sNodeText == "NOT" )) + return xbTrue; + else + return xbFalse; +} +/************************************************************************/ +//! @brief Remove last child from node. +/*! + \returns void. +*/ + +void xbExpNode::RemoveLastChild(){ + xbExpNode *n; + llChildren.RemoveFromEnd( n ); +} + +/************************************************************************/ +//! @brief Set dbf info on node. +/*! + \param dbf Pointer to dbf. + \param iFieldNo Field number of field. + \returns void. +*/ +void xbExpNode::SetDbfInfo( xbDbf *dbf, xbInt16 iFieldNo ){ + this->dbf = dbf; + this->iFieldNo = iFieldNo; +} + +/************************************************************************/ +//! @brief Set dbf info on node. +/*! + \param dbf Pointer to dbf. + \returns void. +*/ +void xbExpNode::SetDbfInfo( xbDbf *dbf ){ + this->dbf = dbf; +} +/************************************************************************/ +//! @brief Set node type. +/*! + \param cNodeType Node type. + \returns void. +*/ +void xbExpNode::SetNodeType( char cNodeType ){ + this->cNodeType = cNodeType; +} + +/************************************************************************/ +//! @brief Set node text. +/*! + \param sNodeText Node text. + \returns void. +*/ +void xbExpNode::SetNodeText( xbString & sNodeText ){ + this->sNodeText = sNodeText; +} + +/************************************************************************/ +//! @brief Set parent. +/*! + \param n Pointer to parent. + \returns void. +*/ +void xbExpNode::SetParent( xbExpNode *n ){ + this->nParent = n; +} + +/************************************************************************/ +//! @brief Set date result. +/*! + \param dtResult Date result. + \returns void. +*/ +void xbExpNode::SetResult( xbDate &dtResult ){ + this->dResult = dtResult.JulianDays(); +} + +/************************************************************************/ +//! @brief Set boolean result. +/*! + \param bResult Boolean result. + \returns void. +*/ +void xbExpNode::SetResult( xbBool bResult ){ + this->dResult = bResult; +} +/************************************************************************/ +//! @brief Set numeric result. +/*! + \param dResult Double numeric result. + \returns void. +*/ +void xbExpNode::SetResult( xbDouble dResult ){ + this->dResult = dResult; +} +/************************************************************************/ +//! @brief Set string result. +/*! + \param sResult String result. + \returns void. +*/ +void xbExpNode::SetResult( xbString &sResult ){ + this->sResult = sResult; +} +/************************************************************************/ +//! @brief Set result length. +/*! + \param ulResultLen Set result length. + \returns void. +*/ +void xbExpNode::SetResultLen( xbUInt32 ulResultLen ){ + this->ulResultLen = ulResultLen; +} +/************************************************************************/ +//! @brief Set return type. +/*! + \param cReturnType Return Type. + \returns void. +*/ +void xbExpNode::SetReturnType( char cReturnType ){ + this->cReturnType = cReturnType; +} +/************************************************************************/ +//! @brief Set weight. +/*! + \param iWeight Weight to set this node at. + \returns void. +*/ +void xbExpNode::SetWeight( xbInt16 iWeight ){ + this->iWeight = iWeight; +} +/*************************************************************************/ +}; // namespace +#endif // XB_EXPRESSION_SUPPORT +/*************************************************************************/ diff --git a/src/core/xbfields.cpp b/src/core/xbfields.cpp new file mode 100755 index 0000000..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:
+ XB_NO_ERROR
XB_INVALID_DATA
XB_INVALID_FIELD_NO
XB_INVALID_FIELD_NAME
+ XB_INVALID_FIELD_TYPE
XB_INVALID_DATA +*/ + +xbInt16 xbDbf::GetDoubleField( const xbString &sFieldName, xbDouble &dFieldValue ) const { + return GetDoubleField( GetFieldNo( sFieldName ), dFieldValue ); +} + +/************************************************************************/ +//! @brief Get xbDouble field for field number. +/*! + \param iFieldNo Number of field to retrieve. + \param dFieldValue Output field value. + \returns The field routines return one of:
+ XB_NO_ERROR
XB_INVALID_DATA
XB_INVALID_FIELD_NO
XB_INVALID_FIELD_NAME
+ XB_INVALID_FIELD_TYPE
XB_INVALID_DATA +*/ +xbInt16 xbDbf::GetDoubleField( xbInt16 iFieldNo, xbDouble &dFieldValue ) const { + xbInt16 iRc = XB_NO_ERROR; + char buf[21]; + memset( buf, 0x00, 21 ); + if(( iRc = GetRawField( iFieldNo, buf, 21, 0 )) >= XB_NO_ERROR ){ + dFieldValue = strtod( buf, NULL ); + return XB_NO_ERROR; + } else + return iRc; +} +/************************************************************************/ +//! @brief Get xbDouble field for field number. +/*! + \param iFieldNo Number of field to retrieve. + \param dFieldValue Output field value. + \param iRecBufSw 0 - Record buffer with any updates.
1 - Record buffer with original data. + \returns The field routines return one of:
+ XB_NO_ERROR
XB_INVALID_DATA
XB_INVALID_FIELD_NO
XB_INVALID_FIELD_NAME
+ XB_INVALID_FIELD_TYPE
XB_INVALID_DATA +*/ + +xbInt16 xbDbf::GetDoubleField( xbInt16 iFieldNo, xbDouble &dFieldValue, xbInt16 iRecBufSw ) const { + xbInt16 iRc = XB_NO_ERROR; + char buf[21]; + memset( buf, 0x00, 21 ); + if(( iRc = GetRawField( iFieldNo, buf, 21, iRecBufSw )) >= XB_NO_ERROR ){ + dFieldValue = strtod( buf, NULL ); + return XB_NO_ERROR; + } else + return iRc; +} + +/************************************************************************/ +//! @brief Get xbDate field for field number. +/*! + \param iFieldNo Number of field to retrieve. + \param dtFieldValue Output field value. + \returns The field routines return one of:
+ XB_NO_ERROR
XB_INVALID_DATA
XB_INVALID_FIELD_NO
XB_INVALID_FIELD_NAME
+ XB_INVALID_FIELD_TYPE
XB_INVALID_DATA +*/ + +xbInt16 xbDbf::GetDateField( xbInt16 iFieldNo, xbDate &dtFieldValue ) const{ + xbString s; + xbInt16 iRc; + if(( iRc = GetField( iFieldNo, s )) != XB_NO_ERROR ) + return iRc; + dtFieldValue.Set( s ); + return XB_NO_ERROR; +} + +/************************************************************************/ +//! @brief Get xbDate field for field name. +/*! + \param sFieldName Name of field to retrieve. + \param dtFieldValue Output field value. + \returns The field routines return one of:
+ XB_NO_ERROR
XB_INVALID_DATA
XB_INVALID_FIELD_NO
XB_INVALID_FIELD_NAME
+ XB_INVALID_FIELD_TYPE
XB_INVALID_DATA +*/ + +xbInt16 xbDbf::GetDateField( const xbString &sFieldName, xbDate &dtFieldValue ) const{ + xbString s; + xbInt16 iRc; + if(( iRc = GetField( sFieldName, s )) != XB_NO_ERROR ) + return iRc; + dtFieldValue.Set( s ); + return XB_NO_ERROR; +} + +/************************************************************************/ +//! @brief Get xbString field for field name. +/*! + \param sFieldName Name of field to retrieve + \param sFieldValue Output field value. + \returns The field routines return one of:
+ XB_NO_ERROR
XB_INVALID_DATA
XB_INVALID_FIELD_NO
XB_INVALID_FIELD_NAME
+ XB_INVALID_FIELD_TYPE
XB_INVALID_DATA +*/ + +xbInt16 xbDbf::GetField( const xbString &sFieldName, xbString &sFieldValue ) const{ + return GetField( GetFieldNo( sFieldName ), sFieldValue, 0 ); +} +/************************************************************************/ +//! @brief Get field data for field number. +/*! + \param iFieldNo Number of field to retrieve. + \param sFieldValue Output field value. + \returns The field routines return one of:
+ XB_NO_ERROR
XB_INVALID_DATA
XB_INVALID_FIELD_NO
XB_INVALID_FIELD_NAME
+ XB_INVALID_FIELD_TYPE
XB_INVALID_DATA +*/ + +xbInt16 xbDbf::GetField( xbInt16 iFieldNo, xbString &sFieldValue ) const{ + return GetField( iFieldNo, sFieldValue, 0 ); +} +/************************************************************************/ +//! @brief Get field data for field number. +/*! + \param iFieldNo Number of field to retrieve. + \param sFieldValue Output field value. + \param iRecBufSw 0 - Record buffer with any updates.
1 - Record buffer with original data. + \returns The field routines return one of:
+ XB_NO_ERROR
XB_INVALID_DATA
XB_INVALID_FIELD_NO
XB_INVALID_FIELD_NAME
+ XB_INVALID_FIELD_TYPE
XB_INVALID_DATA +*/ + +xbInt16 xbDbf::GetField( xbInt16 iFieldNo, xbString &sFieldValue, xbInt16 iRecBufSw) const +{ + xbUInt32 iLen; + if( iFieldNo < 0 || iFieldNo >= iNoOfFields ) { + sFieldValue = ""; + return XB_INVALID_FIELD_NO; + } + iLen = SchemaPtr[iFieldNo].cFieldLen; + sFieldValue = ""; + if( iRecBufSw ) + sFieldValue.Append( (char *) SchemaPtr[iFieldNo].pAddress2, iLen ); // original record buffer + else + sFieldValue.Append( (char *) SchemaPtr[iFieldNo].pAddress, iLen ); // current record buffer + return XB_NO_ERROR; +} + +/************************************************************************/ +//! @brief Get decimal for field name. +/*! + This routine retreives a field's decimal length. + \param sFieldName Name of field to retrieve + \param iFieldDecimal Output field value. + \returns The field routines return one of:
+ XB_NO_ERROR
XB_INVALID_DATA
XB_INVALID_FIELD_NO
XB_INVALID_FIELD_NAME
+ XB_INVALID_FIELD_TYPE
XB_INVALID_DATA +*/ + +xbInt16 xbDbf::GetFieldDecimal( const xbString &sFieldName, xbInt16 & iFieldDecimal ) const { + return GetFieldDecimal( GetFieldNo( sFieldName ), iFieldDecimal ); +} + + +/************************************************************************/ +//! @brief Get decimal for field number. +/*! + This routine retreives a field's decimal length. + \param iFieldNo Number of field to retrieve + \param iFieldDecimal Output field value. + \returns The field routines return one of:
+ XB_NO_ERROR
XB_INVALID_DATA
XB_INVALID_FIELD_NO
XB_INVALID_FIELD_NAME
+ XB_INVALID_FIELD_TYPE
XB_INVALID_DATA +*/ + +xbInt16 xbDbf::GetFieldDecimal( xbInt16 iFieldNo, xbInt16 & iFieldDecimal ) const { + + if( iFieldNo < 0 || iFieldNo >= iNoOfFields ) { + return XB_INVALID_FIELD_NO; + } + iFieldDecimal = SchemaPtr[iFieldNo].cNoOfDecs; + return XB_NO_ERROR; +} + +/************************************************************************/ +//! @brief Get field length for field name. +/*! + + This function retrieves a field's length. + + \param sFieldName Name of field to retrieve + \param iFieldLen Output field length value. + \returns The field routines return one of:
+ XB_NO_ERROR
XB_INVALID_DATA
XB_INVALID_FIELD_NO
XB_INVALID_FIELD_NAME
+ XB_INVALID_FIELD_TYPE
XB_INVALID_DATA +*/ + +xbInt16 xbDbf::GetFieldLen( const xbString &sFieldName, xbInt16 &iFieldLen ) const { + return GetFieldLen( GetFieldNo( sFieldName ), iFieldLen ); +} + + +/************************************************************************/ +//! @brief Get field length for field number. +/*! + This function retrieves a field's length. + + \param iFieldNo Name of field to retrieve + \param iFieldLen Output field length value. + \returns The field routines return one of:
+ XB_NO_ERROR
XB_INVALID_DATA
XB_INVALID_FIELD_NO
XB_INVALID_FIELD_NAME
+ XB_INVALID_FIELD_TYPE
XB_INVALID_DATA +*/ + +xbInt16 xbDbf::GetFieldLen( xbInt16 iFieldNo, xbInt16 &iFieldLen ) const { + if( iFieldNo >= 0 && iFieldNo < iNoOfFields ){ + iFieldLen = SchemaPtr[iFieldNo].cFieldLen; + return XB_NO_ERROR; + } else + return XB_INVALID_FIELD_NO; +} + +/************************************************************************/ +//! @brief Get the field number for name. +/*! Returns the field number for the named field. + + All field get/put methods require either a field number or field name as + one of the parameters. Using the methods that take the field numbers will + yield slightly better performance because the methods that take a name, have + to look up the number. + + \param sFieldName Name of field. + \param iFieldNo Output field number for the given name. + \returns Number of field named fldName. +*/ + +xbInt16 xbDbf::GetFieldNo( const xbString & sFieldName, xbInt16 &iFieldNo ) const +{ + int i; + + if( sFieldName.Len() > 10 ) + return XB_INVALID_FIELD_NAME; + + for( i = 0; i < iNoOfFields; i++ ){ + if( sFieldName == SchemaPtr[i].cFieldName ){ + iFieldNo = i; + return XB_NO_ERROR; + } + } + return XB_INVALID_FIELD_NAME; +} + +/************************************************************************/ +//! Get field ID number for a given field name. +/*! Returns the field number for the named field. + + \param sFieldName Name of field. + \returns Number of field or XB_INVALID_FIELD_NAME. +*/ + +xbInt16 xbDbf::GetFieldNo( const xbString &sFieldName ) const { + int i; + + if( sFieldName.Len() > 10 ) + return XB_INVALID_FIELD_NAME; + + for( i = 0; i < iNoOfFields; i++ ){ + if( sFieldName == SchemaPtr[i].cFieldName ) + return i; + } + return XB_INVALID_FIELD_NAME; +} + +/************************************************************************/ +//! Get field type for field number. +/*! + \param iFieldNo Field number. + \param cFieldType Output field type. + \returns The field routines return one of:
+ XB_NO_ERROR
XB_INVALID_DATA
XB_INVALID_FIELD_NO
XB_INVALID_FIELD_NAME
+ XB_INVALID_FIELD_TYPE
XB_INVALID_DATA +*/ +xbInt16 xbDbf::GetFieldType( xbInt16 iFieldNo, char & cFieldType ) const{ + + if( iFieldNo >= 0 && iFieldNo < iNoOfFields ){ + cFieldType = SchemaPtr[iFieldNo].cType; + return XB_NO_ERROR; + } + else + return XB_INVALID_FIELD_NO; +} + +/************************************************************************/ +//! Get field type for field name. +/*! + \param sFieldName Field name. + \param cFieldType Output field type. + \returns The field routines return one of:
+ XB_NO_ERROR
XB_INVALID_DATA
XB_INVALID_FIELD_NO
XB_INVALID_FIELD_NAME
+ XB_INVALID_FIELD_TYPE
XB_INVALID_DATA +*/ + +xbInt16 xbDbf::GetFieldType( const xbString &sFieldName, char & cFieldType ) const{ + return( GetFieldType( GetFieldNo( sFieldName ), cFieldType)); +} + +/************************************************************************/ +//! @brief Returns the name of the specified field. +/*! Returns a pointer to the name for the field specified by iFieldNo. + + \param iFieldNo Number of field. + \param sFieldName Output variable containing the field name. + \returns The field routines return one of:
+ XB_NO_ERROR
XB_INVALID_DATA
XB_INVALID_FIELD_NO
XB_INVALID_FIELD_NAME
+ XB_INVALID_FIELD_TYPE
XB_INVALID_DATA +*/ +xbInt16 xbDbf::GetFieldName( xbInt16 iFieldNo, xbString &sFieldName ) const{ + if( iFieldNo >= 0 && iFieldNo < iNoOfFields ){ + sFieldName = SchemaPtr[iFieldNo].cFieldName; + return XB_NO_ERROR; + } + else + return XB_INVALID_FIELD_NO; +} + +/************************************************************************/ +//! @brief Get xbFloat field for field number. +/*! + \param iFieldNo Number of field to retrieve. + \param fFieldValue Output field value. + \returns The field routines return one of:
+ XB_NO_ERROR
XB_INVALID_DATA
XB_INVALID_FIELD_NO
XB_INVALID_FIELD_NAME
+ XB_INVALID_FIELD_TYPE
XB_INVALID_DATA +*/ +xbInt16 xbDbf::GetFloatField( xbInt16 iFieldNo, xbFloat & fFieldValue ) const { + + char cFieldType; + xbInt16 rc = GetFieldType( iFieldNo, cFieldType ); + if( rc < 0 ) + return rc; + + if( cFieldType != 'N' && cFieldType != 'F' ) + return XB_INVALID_FIELD_TYPE; + + xbString sTemp; + rc = GetField( iFieldNo, sTemp, 0 ); + if( rc < 0 ) + return rc; + + fFieldValue = (xbFloat) atof( sTemp.Str()); + return XB_NO_ERROR; +} + + +/************************************************************************/ +//! @brief Get xbFloat field for field name. +/*! + \param sFieldName Number of field to retrieve. + \param fFieldValue Output field value. + \returns The field routines return one of:
+ XB_NO_ERROR
XB_INVALID_DATA
XB_INVALID_FIELD_NO
XB_INVALID_FIELD_NAME
+ XB_INVALID_FIELD_TYPE
XB_INVALID_DATA +*/ + +xbInt16 xbDbf::GetFloatField( const xbString & sFieldName, xbFloat & fFieldValue ) const { + return GetFloatField( GetFieldNo(sFieldName ), fFieldValue ); +} + +/************************************************************************/ +//! @brief Get logical field for field number. +/*! + \param iFieldNo Number of field to retrieve. + \param sFieldValue Output field value. + \returns The field routines return one of:
+ XB_NO_ERROR
XB_INVALID_DATA
XB_INVALID_FIELD_NO
XB_INVALID_FIELD_NAME
+ XB_INVALID_FIELD_TYPE
XB_INVALID_DATA +*/ +xbInt16 xbDbf::GetLogicalField( xbInt16 iFieldNo, xbString &sFieldValue ) const { + + char cFieldType; + xbInt16 iRc = GetFieldType( iFieldNo, cFieldType ); + if( iRc < 0 ) + return iRc; + else if( cFieldType != 'L' ) + return XB_INVALID_FIELD_TYPE; + + if(( iRc = GetField( iFieldNo, sFieldValue )) < XB_NO_ERROR ) + return iRc; + else + return XB_NO_ERROR; +} + +/************************************************************************/ +//! @brief Get logical field for field name. +/*! + \param sFieldName Name of field to retrieve. + \param sFieldValue Output field value. + \returns The field routines return one of:
+ XB_NO_ERROR
XB_INVALID_DATA
XB_INVALID_FIELD_NO
XB_INVALID_FIELD_NAME
+ XB_INVALID_FIELD_TYPE
XB_INVALID_DATA +*/ + +xbInt16 xbDbf::GetLogicalField( const xbString &sFieldName, xbString &sFieldValue ) const { + return GetLogicalField( GetFieldNo( sFieldName ), sFieldValue ); +} + + +/************************************************************************/ +//! @brief Get logical field for field number. +/*! + \param iFieldNo Number of field to retrieve. + \param bFieldValue Output field value. + \returns The field routines return one of:
+ XB_NO_ERROR
XB_INVALID_DATA
XB_INVALID_FIELD_NO
XB_INVALID_FIELD_NAME
+ XB_INVALID_FIELD_TYPE
XB_INVALID_DATA +*/ + +xbInt16 xbDbf::GetLogicalField( xbInt16 iFieldNo, xbBool &bFieldValue ) const { + return GetLogicalField( iFieldNo, bFieldValue, 0 ); +} + +/************************************************************************/ +//! @brief Get logical field for field number. +/*! + \param iFieldNo Number of field to retrieve. + \param bFieldValue Output field value. + \param iRecBufSw 0 - Record buffer with any updates.
1 - Record buffer with original data. + \returns The field routines return one of:
+ XB_NO_ERROR
XB_INVALID_DATA
XB_INVALID_FIELD_NO
XB_INVALID_FIELD_NAME
+ XB_INVALID_FIELD_TYPE
XB_INVALID_DATA +*/ + +xbInt16 xbDbf::GetLogicalField( xbInt16 iFieldNo, xbBool &bFieldValue, xbInt16 iRecBufSw ) const { + + char cFieldType; + xbInt16 iRc = GetFieldType( iFieldNo, cFieldType ); + if((iRc = GetFieldType( iFieldNo, cFieldType )) != XB_NO_ERROR ) + return iRc; + + if( cFieldType != 'L' ) + return XB_INVALID_FIELD_TYPE; + + xbString sFieldValue; + if(( iRc = GetField( iFieldNo, sFieldValue, iRecBufSw )) < XB_NO_ERROR ) + return iRc; + + if( sFieldValue == 'T' || sFieldValue == 't' || sFieldValue == 'Y' || sFieldValue == 'y' ) + bFieldValue = xbTrue; + else + bFieldValue = xbFalse; + + return XB_NO_ERROR; +} + +/************************************************************************/ +//! @brief Get logical field for field name. +/*! + \param sFieldName Name of field to retrieve. + \param bFieldValue Output field value. + \returns The field routines return one of:
+ XB_NO_ERROR
XB_INVALID_DATA
XB_INVALID_FIELD_NO
XB_INVALID_FIELD_NAME
+ XB_INVALID_FIELD_TYPE
XB_INVALID_DATA +*/ +xbInt16 xbDbf::GetLogicalField( const xbString &sFieldName, xbBool &bFieldValue ) const { + return GetLogicalField( GetFieldNo( sFieldName ), bFieldValue ); +} +/************************************************************************/ +//! @brief Get long field for field number. +/*! + \param iFieldNo Number of field to retrieve. + \param lFieldValue Output field value. + \returns The field routines return one of:
+ XB_NO_ERROR
XB_INVALID_DATA
XB_INVALID_FIELD_NO
XB_INVALID_FIELD_NAME
+ XB_INVALID_FIELD_TYPE
XB_INVALID_DATA +*/ +xbInt16 xbDbf::GetLongField( xbInt16 iFieldNo, xbInt32 & lFieldValue ) const { + + xbInt16 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:
+ XB_NO_ERROR
XB_INVALID_DATA
XB_INVALID_FIELD_NO
XB_INVALID_FIELD_NAME
+ XB_INVALID_FIELD_TYPE
XB_INVALID_DATA +*/ +xbInt16 xbDbf::GetMemoField( xbInt16 iFieldNo, xbString &sMemoValue ){ + return Memo->GetMemoField( iFieldNo, sMemoValue ); +} +/************************************************************************/ +//! @brief Get memo field for field name. +/*! + + \param sFieldName Name of field to retrieve. + \param sMemoValue Output field value. + \returns The field routines return one of:
+ XB_NO_ERROR
XB_INVALID_DATA
XB_INVALID_FIELD_NO
XB_INVALID_FIELD_NAME
+ XB_INVALID_FIELD_TYPE
XB_INVALID_DATA +*/ +xbInt16 xbDbf::GetMemoField( const xbString & sFieldName, xbString & sMemoValue ){ + return Memo->GetMemoField( GetFieldNo( sFieldName ), sMemoValue ); +} + +/************************************************************************/ +//! @brief Get the memo field count for this table. +/*! + \returns Returns the number of memo fields in the table, +*/ +xbInt16 xbDbf::GetMemoFieldCnt() const { + return iMemoFieldCnt; +} +/************************************************************************/ +//! @brief Get memo field length for field number. +/*! + \param iFieldNo Number of field to retrieve. + \param ulMemoFieldLen Output memo field value length. + \returns The field routines return one of:
+ XB_NO_ERROR
XB_INVALID_DATA
XB_INVALID_FIELD_NO
XB_INVALID_FIELD_NAME
+ XB_INVALID_FIELD_TYPE
XB_INVALID_DATA +*/ + +xbInt16 xbDbf::GetMemoFieldLen( xbInt16 iFieldNo, xbUInt32 & ulMemoFieldLen ){ + return Memo->GetMemoFieldLen( iFieldNo, ulMemoFieldLen ); +} +/************************************************************************/ +//! @brief Get memo field length for field name. +/*! + \param sFieldName Name of field to retrieve. + \param ulMemoFieldLen Output memo field value length. + \returns The field routines return one of:
+ XB_NO_ERROR
XB_INVALID_DATA
XB_INVALID_FIELD_NO
XB_INVALID_FIELD_NAME
+ XB_INVALID_FIELD_TYPE
XB_INVALID_DATA +*/ +xbInt16 xbDbf::GetMemoFieldLen( const xbString &sFieldName, xbUInt32 &ulMemoFieldLen ){ + return Memo->GetMemoFieldLen( GetFieldNo( sFieldName ), ulMemoFieldLen ); +} + +#endif // XB_MEMO_SUPPORT + +/************************************************************************/ +//! @brief Get unsigned long field for field number. +/*! + \param iFieldNo Number of field to retrieve. + \param ulFieldValue Output field value. + \returns The field routines return one of:
+ XB_NO_ERROR
XB_INVALID_DATA
XB_INVALID_FIELD_NO
XB_INVALID_FIELD_NAME
+ XB_INVALID_FIELD_TYPE
XB_INVALID_DATA +*/ +xbInt16 xbDbf::GetULongField( xbInt16 iFieldNo, xbUInt32 & ulFieldValue ) const { + + xbInt16 rc = 0; + xbInt16 iErrorStop = 0; + char cFieldType; + + try{ + + if(( rc = GetFieldType( iFieldNo, cFieldType )) != XB_NO_ERROR ){ + iErrorStop = 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:
+ XB_NO_ERROR
XB_INVALID_DATA
XB_INVALID_FIELD_NO
XB_INVALID_FIELD_NAME
+ XB_INVALID_FIELD_TYPE
XB_INVALID_DATA +*/ + +xbInt16 xbDbf::GetLongField( const xbString &sFieldName, xbInt32 &lFieldValue ) const { + return GetLongField( GetFieldNo( sFieldName ), lFieldValue ); +} + +/************************************************************************/ +//! @brief Get unsigned long field for field name. +/*! + \param sFieldName Number of field to retrieve. + \param ulFieldValue Output field value. + \returns The field routines return one of:
+ XB_NO_ERROR
XB_INVALID_DATA
XB_INVALID_FIELD_NO
XB_INVALID_FIELD_NAME
+ XB_INVALID_FIELD_TYPE
XB_INVALID_DATA +*/ + +xbInt16 xbDbf::GetULongField( const xbString &sFieldName, xbUInt32 &ulFieldValue ) const { + return GetULongField( GetFieldNo( sFieldName ), ulFieldValue ); +} + +/************************************************************************/ +//! @brief Get raw field data for field number. +/*! + + This is a protected method, used by other methods. This method would be + subject to buffer overflows if made public. + + \param iFieldNo Number of field to retrieve. + \param cBuf Pointer to buffer area provided by calling application program. + \param ulBufSize Size of data to copy + \param iRecBufSw 0 - Record buffer with any updates.
1 - Record buffer with original data. + \returns The field routines return one of:
+ XB_NO_ERROR
XB_INVALID_DATA
XB_INVALID_FIELD_NO
XB_INVALID_FIELD_NAME
+ XB_INVALID_FIELD_TYPE
XB_INVALID_DATA +*/ + + +xbInt16 xbDbf::GetRawField( xbInt16 iFieldNo, char *cBuf, xbUInt32 ulBufSize, xbInt16 iRecBufSw ) const +{ + if( iFieldNo < 0 || iFieldNo >= iNoOfFields ) { + return XB_INVALID_FIELD_NO; + } + + size_t stCopySize; + if( ulBufSize > (size_t) (SchemaPtr[iFieldNo].cFieldLen )) + stCopySize = (size_t) (SchemaPtr[iFieldNo].cFieldLen ); + else + stCopySize = ulBufSize - 1; + + if( iRecBufSw ) + memcpy( cBuf, SchemaPtr[iFieldNo].pAddress2, stCopySize ); + else + memcpy( cBuf, SchemaPtr[iFieldNo].pAddress, stCopySize ); + + cBuf[stCopySize] = 0x00; + return XB_NO_ERROR; +} + + +/************************************************************************/ +//! @brief Put double field for field name. +/*! + \param sFieldName Name of field to update. + \param dFieldValue Field value. + \returns The field routines return one of:
+ XB_NO_ERROR
XB_INVALID_DATA
XB_INVALID_FIELD_NO
XB_INVALID_FIELD_NAME
+ XB_INVALID_FIELD_TYPE
XB_INVALID_DATA +*/ +xbInt16 xbDbf::PutDoubleField( const xbString &sFieldName, xbDouble dFieldValue ){ + return PutDoubleField( GetFieldNo( sFieldName ), dFieldValue ); +} + +/************************************************************************/ +//! @brief Put double field for field number. +/*! + \param iFieldNo Number of field to update. + \param dFieldValue Field value. + \returns The field routines return one of:
+ XB_NO_ERROR
XB_INVALID_DATA
XB_INVALID_FIELD_NO
XB_INVALID_FIELD_NAME
+ XB_INVALID_FIELD_TYPE
XB_INVALID_DATA +*/ + +xbInt16 xbDbf::PutDoubleField( xbInt16 iFieldNo, xbDouble dFieldValue ){ + + xbInt16 rc; + xbString sDoubleFmt; + xbString sDoubleFmt2; + xbString sDoubleVal; + xbInt16 iFieldLen; + xbInt16 iNoOfDecs; + + if(( rc = GetFieldDecimal( iFieldNo, iNoOfDecs )) != XB_NO_ERROR ) + return rc; + + if(( rc = GetFieldLen( iFieldNo, iFieldLen )) != XB_NO_ERROR ) + return rc; + + sDoubleFmt.Sprintf( "%d.%df", iFieldLen, iNoOfDecs ); + sDoubleFmt2 = "%-"; + sDoubleFmt2 += sDoubleFmt; + sDoubleVal.Sprintf( sDoubleFmt2.Str(), dFieldValue ); + sDoubleVal.Rtrim(); + return PutField( iFieldNo, sDoubleVal ); +} + +/************************************************************************/ +//! @brief Put date field for field name. +/*! + \param sFieldName Name of field to update. + \param dtFieldValue Field value. + \returns The field routines return one of:
+ XB_NO_ERROR
XB_INVALID_DATA
XB_INVALID_FIELD_NO
XB_INVALID_FIELD_NAME
+ XB_INVALID_FIELD_TYPE
XB_INVALID_DATA +*/ + +xbInt16 xbDbf::PutDateField(const xbString &sFieldName, const xbDate &dtFieldValue ){ + return PutField( GetFieldNo( sFieldName ), dtFieldValue.Str() ); +} + +/************************************************************************/ +//! @brief Put date field for field number. +/*! + \param iFieldNo Number of field to update. + \param dtFieldValue Field value. + \returns The field routines return one of:
+ XB_NO_ERROR
XB_INVALID_DATA
XB_INVALID_FIELD_NO
XB_INVALID_FIELD_NAME
+ XB_INVALID_FIELD_TYPE
XB_INVALID_DATA +*/ +xbInt16 xbDbf::PutDateField( xbInt16 iFieldNo, const xbDate &dtFieldValue ){ + return PutField( iFieldNo, dtFieldValue.Str() ); +} + +/************************************************************************/ +//! @brief Put field for field name. +/*! + \param sFieldName Name of field to update. + \param sFieldValue Field value. + \returns The field routines return one of:
+ XB_NO_ERROR
XB_INVALID_DATA
XB_INVALID_FIELD_NO
XB_INVALID_FIELD_NAME
+ XB_INVALID_FIELD_TYPE
XB_INVALID_DATA +*/ +xbInt16 xbDbf::PutField(const xbString &sFieldName, const xbString &sFieldValue ) { + return PutField( GetFieldNo( sFieldName ), sFieldValue ); +} +/************************************************************************/ +//! @brief Put field for field number. +/*! + \param iFieldNo Number of field to update. + \param sFieldValue Field value. + \returns The field routines return one of:
+ XB_NO_ERROR
XB_INVALID_DATA
XB_INVALID_FIELD_NO
XB_INVALID_FIELD_NAME
+ XB_INVALID_FIELD_TYPE
XB_INVALID_DATA +*/ + +xbInt16 xbDbf::PutField( xbInt16 iFieldNo, const xbString &sFieldValue ) { + xbInt16 iLen; + xbInt16 iDecPos; /* Decimal Position */ + char * startpos; + char * tp; /* Target Pointer */ + + if( iFieldNo < 0 || iFieldNo >= iNoOfFields ) + return XB_INVALID_FIELD_NO; + + if( SchemaPtr[iFieldNo].cType == 'L' && !sFieldValue.ValidLogicalValue()) + return XB_INVALID_DATA; + + else if( SchemaPtr[iFieldNo].cType == 'D' ){ + xbDate d; + if( !d.DateIsValid( sFieldValue )) + return XB_INVALID_DATA; + iLen = 8; + } + else + { + iLen = (xbInt16) sFieldValue.Len(); + if( SchemaPtr[iFieldNo].cType == 'F' || SchemaPtr[iFieldNo].cType == 'N' || SchemaPtr[iFieldNo].cType == 'M' ){ + if( !sFieldValue.ValidNumericValue()) { + return XB_INVALID_DATA; + } + else { + iDecPos = (xbInt16) sFieldValue.Pos( "." ); // 0 is no decimal + int mlod; // max no of digits left of decimal point digit count + + + if( SchemaPtr[iFieldNo].cNoOfDecs > 0 ) + mlod = SchemaPtr[iFieldNo].cFieldLen - SchemaPtr[iFieldNo].cNoOfDecs - 1; + else + mlod = iLen; + + if( iDecPos == 0 ){ // no decimal in incoming data + + // check digits to the left of the decimal + if( SchemaPtr[iFieldNo].cNoOfDecs > 0 && iLen > mlod ) /* no decimal in incoming data */ + return XB_INVALID_DATA; + + else if( SchemaPtr[iFieldNo].cNoOfDecs == 0 && iLen > SchemaPtr[iFieldNo].cFieldLen ) + return XB_INVALID_DATA; + + } + else // decimal in incoming data + { + if( (iDecPos-1) > mlod ) // too many digits to the left of dec in incoming data + return XB_INVALID_DATA; + + // check digits to the right of the decimal + else if((iLen - iDecPos) > SchemaPtr[iFieldNo].cNoOfDecs ) + return XB_INVALID_DATA; + } + } + } + } + + // do all field edits before this point + if( iDbfStatus != XB_UPDATED ){ + iDbfStatus = XB_UPDATED; + memcpy( RecBuf2, RecBuf, uiRecordLen ); // save the original record bufer before making updates + } + + memset( SchemaPtr[iFieldNo].pAddress, 0x20, SchemaPtr[iFieldNo].cFieldLen ); + + if( iLen > SchemaPtr[iFieldNo].cFieldLen ) + iLen = SchemaPtr[iFieldNo].cFieldLen; + + if( SchemaPtr[iFieldNo].cType == 'F' || SchemaPtr[iFieldNo].cType == 'N' + || SchemaPtr[iFieldNo].cType == 'M') { + + xbInt16 iDecPos = (xbInt16) sFieldValue.Pos( "." ); + if( iDecPos == 0 ){ + iLen = (xbInt16) sFieldValue.Len(); + iDecPos = 0; + } + else{ + iLen = iDecPos - 1; + } + + if( SchemaPtr[iFieldNo].cNoOfDecs > 0 ){ + tp = SchemaPtr[iFieldNo].pAddress; + tp += SchemaPtr[iFieldNo].cFieldLen - SchemaPtr[iFieldNo].cNoOfDecs - 1; + *tp++ = '.'; + + if( iDecPos == 0 ){ + for( xbInt32 i = 0; i < SchemaPtr[iFieldNo].cNoOfDecs; i++ ) + *tp++ = '0'; + } else { + xbInt32 j = iDecPos + 1; + for( xbInt32 i = 0; i < SchemaPtr[iFieldNo].cNoOfDecs; i++, j++ ){ + if( j <= (xbInt32) sFieldValue.Len()) + *tp++ = sFieldValue[j]; + else + *tp++ = '0'; + } + } + startpos= SchemaPtr[iFieldNo].pAddress + + SchemaPtr[iFieldNo].cFieldLen - + SchemaPtr[iFieldNo].cNoOfDecs - iLen - 1; + + } + else + startpos=SchemaPtr[iFieldNo].pAddress+SchemaPtr[iFieldNo].cFieldLen-iLen; + } + else + startpos = SchemaPtr[iFieldNo].pAddress; + + memcpy( startpos, sFieldValue.Str(), (size_t) iLen ); + return 0; +} + +/************************************************************************/ +//! @brief Put float field for field number. +/*! + \param iFieldNo Number of field to update. + \param fFieldValue Field value. + \returns The field routines return one of:
+ XB_NO_ERROR
XB_INVALID_DATA
XB_INVALID_FIELD_NO
XB_INVALID_FIELD_NAME
+ XB_INVALID_FIELD_TYPE
XB_INVALID_DATA +*/ +xbInt16 xbDbf::PutFloatField( xbInt16 iFieldNo, xbFloat fFieldValue ){ + + xbInt16 rc; + xbString sFloatFmt; + xbString sFloatFmt2; + xbString sFloatVal; + xbInt16 iFieldLen; + xbInt16 iNoOfDecs; + + if(( rc = GetFieldDecimal( iFieldNo, iNoOfDecs )) != XB_NO_ERROR ) + return rc; + + if(( rc = GetFieldLen( iFieldNo, iFieldLen )) != XB_NO_ERROR ) + return rc; + + sFloatFmt.Sprintf( "%d.%df", iFieldLen, iNoOfDecs ); + sFloatFmt2 = "%-"; + sFloatFmt2 += sFloatFmt; + sFloatVal.Sprintf( sFloatFmt2.Str(), fFieldValue ); + sFloatVal.Rtrim(); + return PutField( iFieldNo, sFloatVal ); +} + +/************************************************************************/ +//! @brief Put float field for field name. +/*! + \param sFieldName Name of field to update. + \param fFieldValue Field value. + \returns The field routines return one of:
+ XB_NO_ERROR
XB_INVALID_DATA
XB_INVALID_FIELD_NO
XB_INVALID_FIELD_NAME
+ XB_INVALID_FIELD_TYPE
XB_INVALID_DATA +*/ + +xbInt16 xbDbf::PutFloatField( const xbString &sFieldName, xbFloat fFieldValue ){ + return PutFloatField( GetFieldNo( sFieldName ), fFieldValue ); +} + +/************************************************************************/ +//! @brief Put logical field for field number. +/*! + \param iFieldNo Number of field to update. + \param sFieldValue Field value. + \returns The field routines return one of:
+ XB_NO_ERROR
XB_INVALID_DATA
XB_INVALID_FIELD_NO
XB_INVALID_FIELD_NAME
+ XB_INVALID_FIELD_TYPE
XB_INVALID_DATA +*/ + +xbInt16 xbDbf::PutLogicalField( xbInt16 iFieldNo, const xbString &sFieldValue ) { + return PutField( iFieldNo, sFieldValue ); +} + +/************************************************************************/ +//! @brief Put logical field for field name. +/*! + \param sFieldName Name of field to update. + \param sFieldValue Field value. + \returns The field routines return one of:
+ XB_NO_ERROR
XB_INVALID_DATA
XB_INVALID_FIELD_NO
XB_INVALID_FIELD_NAME
+ XB_INVALID_FIELD_TYPE
XB_INVALID_DATA +*/ +xbInt16 xbDbf::PutLogicalField( const xbString &sFieldName, const xbString &sFieldValue ) { + return PutField( GetFieldNo( sFieldName ), sFieldValue ); +} + +/************************************************************************/ +//! @brief Put logical field for field number. +/*! + \param iFieldNo Number of field to update. + \param bFieldValue Field value. + \returns The field routines return one of:
+ XB_NO_ERROR
XB_INVALID_DATA
XB_INVALID_FIELD_NO
XB_INVALID_FIELD_NAME
+ XB_INVALID_FIELD_TYPE
XB_INVALID_DATA +*/ + +xbInt16 xbDbf::PutLogicalField( xbInt16 iFieldNo, xbBool bFieldValue ) { + + if( bFieldValue ) + return PutField( iFieldNo, "T" ); + else + return PutField( iFieldNo, "F" ); +} + +/************************************************************************/ +//! @brief Put logical field for field name. +/*! + \param sFieldName Name of field to update. + \param bFieldValue Field value. + \returns The field routines return one of:
+ XB_NO_ERROR
XB_INVALID_DATA
XB_INVALID_FIELD_NO
XB_INVALID_FIELD_NAME
+ XB_INVALID_FIELD_TYPE
XB_INVALID_DATA +*/ +xbInt16 xbDbf::PutLogicalField( const xbString &sFieldName, xbBool bFieldValue ) { + return PutLogicalField( GetFieldNo( sFieldName ), bFieldValue ); +} + +/************************************************************************/ +//! @brief Put long field for field number. +/*! + \param iFieldNo Number of field to update. + \param lFieldValue Field value. + \returns The field routines return one of:
+ XB_NO_ERROR
XB_INVALID_DATA
XB_INVALID_FIELD_NO
XB_INVALID_FIELD_NAME
+ XB_INVALID_FIELD_TYPE
XB_INVALID_DATA +*/ + +xbInt16 xbDbf::PutLongField( xbInt16 iFieldNo, xbInt32 lFieldValue ) { + xbString sLong; + sLong.Sprintf( "%ld", (xbInt32) lFieldValue ); + return PutField( iFieldNo, sLong.Str() ); +} + +/************************************************************************/ +//! @brief Put long field for field name. +/*! + \param sFieldName Name of field to update. + \param lFieldValue Field value. + \returns The field routines return one of:
+ XB_NO_ERROR
XB_INVALID_DATA
XB_INVALID_FIELD_NO
XB_INVALID_FIELD_NAME
+ XB_INVALID_FIELD_TYPE
XB_INVALID_DATA +*/ + +xbInt16 xbDbf::PutLongField( const xbString &sFieldName, xbInt32 lFieldValue ) { + return PutLongField( GetFieldNo( sFieldName ), lFieldValue ); +} + +/************************************************************************/ +//! @brief Put unsigned long field for field number. +/*! + \param iFieldNo Number of field to update. + \param ulFieldValue Field value. + \returns The field routines return one of:
+ XB_NO_ERROR
XB_INVALID_DATA
XB_INVALID_FIELD_NO
XB_INVALID_FIELD_NAME
+ XB_INVALID_FIELD_TYPE
XB_INVALID_DATA +*/ + +xbInt16 xbDbf::PutULongField( xbInt16 iFieldNo, xbUInt32 ulFieldValue ) { + xbString sLong; + sLong.Sprintf( "%lu", (xbInt32) ulFieldValue ); + return PutField( iFieldNo, sLong.Str() ); +} + +/************************************************************************/ +//! @brief Put unsigned long field for field name. +/*! + \param sFieldName Name of field to update. + \param ulFieldValue Field value. + \returns The field routines return one of:
+ XB_NO_ERROR
XB_INVALID_DATA
XB_INVALID_FIELD_NO
XB_INVALID_FIELD_NAME
+ XB_INVALID_FIELD_TYPE
XB_INVALID_DATA +*/ + +xbInt16 xbDbf::PutULongField( const xbString &sFieldName, xbUInt32 ulFieldValue ) { + return PutLongField( GetFieldNo( sFieldName ), (xbInt32) ulFieldValue ); +} + +/************************************************************************/ +#ifdef XB_MEMO_SUPPORT +//! @brief Check if memo field exists for field name. +/*! + \param sFieldName Name of field to check. + \returns xbTrue Field exists.
xbFale Field does not exist. +*/ + +xbBool xbDbf::MemoFieldExists( const xbString &sFieldName ) const{ + return MemoFieldExists( GetFieldNo( sFieldName )); +} + +/************************************************************************/ +//! @brief Check if memo field exists for field number. +/*! + \param iFieldNo Number of field to check. + \returns xbTrue Field exists.
xbFale Field does not exist. +*/ + +xbBool xbDbf::MemoFieldExists( xbInt16 iFieldNo ) const{ + + xbInt32 lFld = 0L; + GetLongField( iFieldNo, lFld ); + if( lFld == 0L ) + return xbFalse; + else + return xbTrue; +} + +#endif +} /* namespace */ + diff --git a/src/core/xbfile.cpp b/src/core/xbfile.cpp new file mode 100755 index 0000000..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 Return Codes +*/ +xbInt16 xbFile::CreateUniqueFileName( const xbString & sDirIn, const xbString & sExtIn, xbString &sFqnOut ){ + return CreateUniqueFileName( sDirIn, sExtIn, sFqnOut, 0 ); +} + +/************************************************************************/ +//! @brief Create a unique file name. +/*! + Given a directory and file extension as inputs, create a unique file name. + + \param sDirIn Directory + \param sExtIn File Extension + \param iOption 0 - look only for one file for a given directory and extension
+ 1 - if file name extension is "dbf" or "DBF", verify unique dbt or DBT (memo) file is also available
+ \param sFqnOut A fully qualifed unique file name as output + \returns Return Codes +*/ + +xbInt16 xbFile::CreateUniqueFileName( const xbString & sDirIn, const xbString & sExtIn, xbString &sFqnOut, xbInt16 iOption ){ + + xbBool bUniqueFileNameFound = xbFalse; + xbFile f( xbase); + xbInt32 l = 1; + xbString sMemoFileName; + + + while( !bUniqueFileNameFound ){ + sFqnOut.Sprintf( "%sxbTmp%03d.%s", sDirIn.Str(), l, sExtIn.Str()); + if( iOption == 1 && sExtIn == "DBF" ){ + sMemoFileName.Sprintf( "%sxbTmp%03d.DBT", sDirIn.Str(), l ); + } + else if( iOption == 1 && sExtIn == "dbf" ){ + sMemoFileName.Sprintf( "%sxbTmp%03d.dbt", sDirIn.Str(), l ); + } + if( f.FileExists( sFqnOut ) || ( sMemoFileName.Len() > 0 && f.FileExists( sMemoFileName ))){ + l++; + } + else + { + bUniqueFileNameFound = xbTrue; + } + if( l > 999 ) + return XB_FILE_EXISTS; + } + return XB_NO_ERROR; +} + +/*************************************************************************/ +//! @brief Determine which version the memo (dbt) file is. +/*! + + This routine uses the first byte in the dbf file to determine which memo + file version is in use. The main difference between version 3 and 4 is that + version 4 will reuse blocks if they become available. Version 3 does not. + + \param cFileTypeByte is an output field and is one of:
+
+ 0 - none
+ 3 - Dbase III+
+ 4 - Dbase IV
+ + \returns Return Codes +*/ + +xbInt16 xbFile::DetermineXbaseMemoVersion( unsigned char cFileTypeByte ) const { + + + if( BitSet( cFileTypeByte, 3 ) && BitSet( cFileTypeByte, 7 )) + return 4; + else if( BitSet( cFileTypeByte, 7 )) + return 3; + + return 0; +} +/*************************************************************************/ +//! @brief Determine xbase dbf version. +/*! + + This routine is used to determine which version of the Xbase classes can + be used for a given DBF file.
+ + It attempts to use the highest version compiled into the library.
+ + References:
+ This routine uses the first byte from the dbf file.
+ Per DBase documentation:
+ Valid dBASE for Windows table file, bits 0-2 indicate version number: 3 for dBASE Level 5, 4 for dBASE Level 7.
+ Bit 3 and bit 7 indicate presence of a dBASE IV or dBASE for Windows memo file;
+ Bits 4-6 indicate the presence of a dBASE IV SQL table;
+ Bit 7 indicates the presence of any .DBT memo file (either a dBASE III PLUS type or a dBASE IV or dBASE for Windows memo file).
+
+ Bachmann spec (used extensively in library build), page 7 - does not match DBase documentation
+
+ returns
+ 0 - unknown
+ 3 - Dbase level 3
+ 4 - Dbase level 4
+ 5 - Dbase Level 5 (future)
+ 7 - Dbase Level 7 (future)
+
+ 1x - Clipper files (future)
+ 2x - Foxbase files (future)
+
+*/ + +xbInt16 xbFile::DetermineXbaseTableVersion( unsigned char cFileTypeByte ) const { + + xbInt16 iMemoVersion = DetermineXbaseMemoVersion(cFileTypeByte); + char cDbfLevel = cFileTypeByte & 0x07; + + #ifdef XB_DBF4_SUPPORT + if( cDbfLevel == 3 && iMemoVersion != 3 ) + return 4; + #endif + + #ifdef XB_DBF3_SUPPORT + if( cDbfLevel == 3 && iMemoVersion != 4 ) + return 3; + #endif + + return 0; +} + +/*************************************************************************/ +//! @brief Get a portable double value. +/*! + + This routine returns a double value from an 8 byte character stream, + accounting for endian type. + + Converts a double (64 bit floating point) value stored at p from a portable + format to the machine format. + + \param p pointer to memory containing the portable double value + + \returns the double value. +*/ + +xbDouble xbFile::eGetDouble( const char *p ) const { + xbDouble d; + const char *sp; + char *tp; + xbInt16 i; + + tp = (char *) &d; + sp = p; + if( iEndianType == 'L' ) + for( i = 0; i < 8; i++ ) *tp++ = *sp++; + else + { + sp+=7; + for( i = 0; i < 8; i++ ) *tp++ = *sp--; + } + + return d; +} + +/*************************************************************************/ +//! @brief Get a portable long value. + +/*! + This routine returns a long int value from a 4 byte character stream, + accounting for endian type. + + \param p pointer to memory containing the portable long value + + \returns the long value. +*/ + +xbInt32 xbFile::eGetInt32( const char *p ) const { + xbInt32 l; + char *tp; + xbInt16 i; + + tp = (char *) &l; + if( iEndianType == 'L' ) + for( i = 0; i < 4; i++ ) *tp++ = *p++; + else { + p+=3; + for( i = 0; i < 4; i++ ) *tp++ = *p--; + } + return l; + +} +/*************************************************************************/ +//! @brief Get a portable unsigned long value. + +/*! + This routine returns an unsigned long int value from a 4 byte character stream, + accounting for endian type. + + \param p pointer to memory containing the portable long value + + \returns the unsigned long value. +*/ + +xbUInt32 xbFile::eGetUInt32( const char *p ) const { + xbUInt32 ul; + xbInt16 i; + char *tp; + + tp = (char *) &ul; + if( iEndianType == 'L' ) + for( i = 0; i < 4; i++ ) *tp++ = *p++; + else{ + p+=3; + for( i = 0; i < 4; i++ ) *tp++ = *p--; + } + return ul; +} + +/*************************************************************************/ +//! @brief Get a portable short value. + +/*! + This routine returns a short int value from a 2 byte character stream, + accounting for endian type. + + \param p pointer to memory containing the portable long value + + \returns the short value. +*/ + +xbInt16 xbFile::eGetInt16(const char *p) const { + xbInt16 s, i; + char *tp; + tp = (char *) &s; + if( iEndianType == 'L' ) + for( i = 0; i < 2; i++ ) *tp++ = *p++; + else{ + p++; + for( i = 0; i < 2; i++ ) *tp++ = *p--; + } + return s; +} +/*************************************************************************/ +//! @brief Get a portable unsigned short value. + +/*! + This routine returns a short unsigned int value from a 2 byte character stream, + accounting for endian type. + + \param p pointer to memory containing the portable long value + + \returns the short value. +*/ + +xbUInt16 xbFile::eGetUInt16(const char *p) const { + xbInt16 i; + xbUInt16 uI; + char *tp; + + tp = (char *) &uI; + if( iEndianType == 'L' ) + for( i = 0; i < 2; i++ ) *tp++ = *p++; + else{ + p++; + for( i = 0; i < 2; i++ ) *tp++ = *p--; + } + return uI; +} + + +/*************************************************************************/ +//! @brief Write a portable double value to memory location. +/*! + Converts a double (64 bit floating point) value from machine format to a + portable format and stores the converted value in the memory referenced + by c. + + This routine puts a double value to an 8 byte character stream + + \param c Pointer to memory to hold converted value + \param d Input value to be converted +*/ + +void xbFile::ePutDouble( char *c, xbDouble d ){ + const char *sp; + char *tp; + xbInt16 i; + + tp = c; + sp = (const char *) &d; + if( iEndianType == 'L' ) + for( i = 0; i < 8; i++ ) *tp++ = *sp++; + else + { + sp+=7; + for( i = 0; i < 8; i++ ) *tp++ = *sp--; + } + return; +} + +/*************************************************************************/ +//! @brief Write a portable short value to memory location. +/*! + Converts a short (16 bit integer) value from machine format to a + portable format and stores the converted value in the memory referenced + by c. + + This routine puts a short value to a 2 byte character stream + + \param c Pointer to memory to hold converted value + \param s Input value to be converted +*/ + +void xbFile::ePutInt16( char * c, xbInt16 s ){ + const char *sp; + char *tp; + xbInt16 i; + + tp = c; + sp = (const char *) &s; + + if( iEndianType == 'L' ) + for( i = 0; i < 2; i++ ) *tp++ = *sp++; + else{ /* big endian */ + sp++; + for( i = 0; i < 2; i++ ) *tp++ = *sp--; + } + return; +} +/*************************************************************************/ +//! @brief Write a portable unsigned short value to memory location. +/*! + Converts an unsigned short (16 bit integer) value from machine format to a + portable format and stores the converted value in the memory referenced + by c. + + This routine puts an unsigned short value to a 2 byte character stream + + \param c Pointer to memory to hold converted value + \param s Input value to be converted +*/ + +void xbFile::ePutUInt16( char * c, xbUInt16 s ){ + const char *sp; + char *tp; + xbInt16 i; + + tp = c; + sp = (const char *) &s; + + if( iEndianType == 'L' ) + for( i = 0; i < 2; i++ ) *tp++ = *sp++; + else{ /* big endian */ + sp++; + for( i = 0; i < 2; i++ ) *tp++ = *sp--; + } + return; +} + +/*************************************************************************/ +//! @brief Write a portable long value to memory location. +/*! + Converts a long (32 bit integer) value from machine format to a + portable format and stores the converted value in the memory referenced + by c. + + This routine puts a long value to a 4 byte character stream. + + \param c Pointer to memory to hold converted value + \param l Input value to be converted +*/ + +void xbFile::ePutInt32( char * c, xbInt32 l ) +{ + const char *sp; + char *tp; + xbInt16 i; + + tp = c; + sp = (const char *) &l; + if( iEndianType == 'L' ) + for( i = 0; i < 4; i++ ) *tp++ = *sp++; + else { + sp+=3; + for( i = 0; i < 4; i++ ) *tp++ = *sp--; + } + return; +} + + +/*************************************************************************/ +//! @brief Write a portable unsigned long value to memory location. +/*! + Converts an unsigned long (32 bit integer) value from machine format to a + portable format and stores the converted value in the memory referenced + by c. + + This routine puts an unsigned long value to a 4 byte character stream. + + \param c Pointer to memory to hold converted value + \param ul Input value to be converted +*/ + +void xbFile::ePutUInt32( char * c, xbUInt32 ul ) +{ + const char *sp; + char *tp; + xbInt16 i; + + tp = c; + sp = (const char *) &ul; + if( iEndianType == 'L' ) + for( i = 0; i < 4; i++ ) *tp++ = *sp++; + else + { + sp+=3; + for( i = 0; i < 4; i++ ) *tp++ = *sp--; + } + return; +} + +/************************************************************************/ +//! @brief Determines if a file exists. +/*! + \returns xbTrue if file exists
+ xbFalse if file does not exist + +*/ +xbBool xbFile::FileExists() const { + return FileExists( sFqFileName, 0 ); +} +/************************************************************************/ +//! @brief Determines if a file exists. +/*! + \param iOption if 1, assume this is a request for a dbf file and + check for the a dbt memo file also, returns true if both files are found + + \returns xbTrue if both files exist
+ xbFalse if file does not exist + +*/ +xbBool xbFile::FileExists( xbInt16 iOption ) const { + return FileExists( sFqFileName, iOption ); +} + +/************************************************************************/ +//! @brief Determines if a file exists. +/*! + \param sFileName - file name to check for + + \returns xbTrue if file exists
+ xbFalse if file does not exist +*/ + +xbBool xbFile::FileExists(const xbString &sFileName ) const { + return FileExists( sFileName, 0 ); +} + +/************************************************************************/ +//! @brief Determines if a file exists. +/*! + \param sFileName - file name to check for + \param iOption if 1, assume this is a request for a dbf file and + check for the a dbt memo file also, returns true if both files are found + + \returns xbTrue if both dbf and dbt files exist
+ xbFalse if file does not exist +*/ + +xbBool xbFile::FileExists( const xbString & sFileName, xbInt16 iOption ) const { + + struct stat buffer; + if(( stat( sFileName.Str(), &buffer ) != 0 )){ + return xbFalse; + } + + #ifdef XB_MEMO_SUPPORT + if( iOption == 1 ){ + xbString sFileName2 = sFileName; + + if( sFileName2[sFileName2.Len()] == 'F' ) + sFileName2.PutAt( sFileName2.Len(), 'T' ); + else + sFileName2.PutAt( sFileName2.Len(), 't' ); + + if(( stat( sFileName2.Str(), &buffer) != 0 )) + return xbFalse; + } + #endif + return xbTrue; +} + +/************************************************************************/ +//! @brief Determines if file is open. +/*! + + \returns xbTrue if file is open
+ xbFalse if file is not open +*/ + +xbBool xbFile::FileIsOpen() const { + return bFileOpen; +} +/************************************************************************/ +//! @brief Get the block size. +/*! + \returns Block Size +*/ + +xbUInt32 xbFile::GetBlockSize() const { + return ulBlockSize; +} + +/************************************************************************/ +//! @brief Get the directory name. +/*! + \returns the directory name of the file +*/ + +const xbString & xbFile::GetDirectory() const { + return sDirectory; +} + +/************************************************************************/ +//! @brief Get the directory part of the file name. +/*! + \param sFileDirPartOut - the returned directory name + \returns Return Codes +*/ + + +xbInt16 xbFile::GetFileDirPart( xbString & sFileDirPartOut ) const { + return GetFileDirPart( sFqFileName, sFileDirPartOut ); +} + +/************************************************************************/ +//! @brief Get the directory part of the file name. +/*! + \param sCompleteFileNameIn - a fully qualfied input file name + \param sFileDirPartOut - the returned directory name part out of sCompleteFileNameIn + \returns Return Codes +*/ + + +xbInt16 xbFile::GetFileDirPart( const xbString & sCompleteFileNameIn, xbString & sFileDirPartOut ) const { + + sFileDirPartOut = sCompleteFileNameIn; + sFileDirPartOut.SwapChars( '\\', '/' ); + xbUInt32 iPos = sFileDirPartOut.GetLastPos( '/' ); + + if( iPos > 0 ){ + xbString sTemp = sFileDirPartOut; + sFileDirPartOut.Assign( sTemp, 1, iPos ); + return XB_NO_ERROR; + } + return XB_INVALID_DATA; +} + +/************************************************************************/ +//! @brief Get the extension part of the file name. +/*! + \param sFileNameExtOut - the returned extension part out of sCompleteFileNameIn + \returns Return Codes +*/ +xbInt16 xbFile::GetFileExtPart( xbString & sFileNameExtOut ) const { + return GetFileExtPart( sFqFileName, sFileNameExtOut ); +} + +/************************************************************************/ +//! @brief Get the extension part of the file name. +/*! + \param sCompleteFileNameIn - a fully qualfied input file name + + \param sFileExtPartOut - the returned directory name part out of sCompleteFileNameIn + \returns Return Codes +*/ + + +xbInt16 xbFile::GetFileExtPart( const xbString & sCompleteFileNameIn, xbString & sFileExtPartOut ) const { + + + sFileExtPartOut = sCompleteFileNameIn; + xbUInt32 iPos = sFileExtPartOut.GetLastPos( '.' ); + if( iPos > 0 ){ /* get rid of the directory part of the name */ + sFileExtPartOut.Ltrunc( iPos ); + return XB_NO_ERROR; + } + return XB_INVALID_DATA; +} +/************************************************************************/ +//! @brief Get the time of last file modification timestamp as reported by the OS. +/*! + \param mtime - returned time of last file modification + \returns Return Codes +*/ + +xbInt16 xbFile::GetFileMtime( time_t &mtime ){ + + struct stat buffer; + if( stat( sFqFileName.Str(), &buffer )) + return XB_FILE_NOT_FOUND; + else{ + mtime = buffer.st_mtime; + return XB_NO_ERROR; + } +} +/************************************************************************/ +//! @brief Get the file name. +/*! + \returns the file name portion of the file +*/ + +const xbString & xbFile::GetFileName() const { + return sFileName; +} + + +/************************************************************************/ +//! @brief Get the name part of the file name. +/*! + \param sFileNamePartOut - the returned file name part out of sCompleteFileNameIn + \returns Return Codes +*/ + + +xbInt16 xbFile::GetFileNamePart( xbString & sFileNamePartOut ) const { + return GetFileNamePart( sFqFileName, sFileNamePartOut ); +} + +/************************************************************************/ +//! @brief Get the name part of the file name. +/*! + \param sCompleteFileNameIn - a fully qualified input file name + \param sFileNamePartOut - the returned file name part out of sCompleteFileNameIn + \returns Return Codes +*/ + +//*********** fixme should this be static????? + +xbInt16 xbFile::GetFileNamePart( const xbString & sCompleteFileNameIn, xbString & sFileNamePartOut ) const { + /* extract the file name part out of the string */ + + sFileNamePartOut = sCompleteFileNameIn; + sFileNamePartOut.SwapChars( '\\', '/' ); + xbUInt32 iPos = sFileNamePartOut.GetLastPos( '/' ); + if( iPos > 0 ) /* get rid of the directory part of the name */ + sFileNamePartOut.Ltrunc( iPos ); + iPos = sFileNamePartOut.Pos( '.' ); + if( iPos > 0 ){ /* get rid of the extension part of the name */ + xbString sTemp = sFileNamePartOut; + sFileNamePartOut.Assign( sTemp, 1, iPos-1 ); + } + return XB_NO_ERROR; +} + + +/************************************************************************/ +//! @brief Get the size of the file as reported by the OS. +/*! + \param ullFileSize - unsigned long long field as output + \returns Return Codes +*/ + +xbInt16 xbFile::GetFileSize( xbUInt64 &ullFileSize ){ + + xbInt16 iRc = 0; + xbInt16 iErrorStop = 0; + + try{ + + if(( iRc = xbFseek( 0, SEEK_END )) != XB_NO_ERROR ){ + iErrorStop = 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 Return Codes +*/ +xbInt16 xbFile::GetFileType( xbString & sFileTypeOut ) const { + + xbInt16 iRc = GetFileExtPart( sFqFileName, sFileTypeOut ); + sFileTypeOut.ToUpperCase(); + return iRc; +} +/************************************************************************/ +//! @brief Get the fully qualified file name. +/*! + \returns the fully qualfied name of the file +*/ + +const xbString & xbFile::GetFqFileName() const { + return sFqFileName; +} + +/************************************************************************/ +//! @brief Get the open mode of the file. +/*! + \returns XB_READ
+ XB_READ_WRITE
+ XB_WRITE
+*/ + +xbInt16 xbFile::GetOpenMode() const { + return iOpenMode; +} + +/************************************************************************/ +//! @brief Get the share mode of the file. +/*! + \returns XB_SINGLE_USER - (file buffering on>
+ XB_MULTI_USER - (file buffering off)
+*/ + + +xbInt16 xbFile::GetShareMode() const { + return iShareMode; +} + + +/************************************************************************/ +//! @brief Get the file type byte and version of the dbf file. +/*! + + Pull the first bye off the DBF file for further inspection. + First byte has various bits set to determine what the file format is. + + \param sFileName - Name of file to examine + \param iVersion - Returned file version + \returns Return Codes +*/ + +xbInt16 xbFile::GetXbaseFileTypeByte( const xbString &sFileName, xbInt16 &iVersion ) +{ + unsigned char cFileTypeByte; + return GetXbaseFileTypeByte( sFileName, cFileTypeByte, iVersion ); +} + +/************************************************************************/ +//! @brief Get the file type byte and version of the dbf file. +/*! + Pull the first bye off the DBF file for further inspection. + First byte has various bits set to determine what the file format is. + + \param sFileName - Name of file to examine + \param cFileTypeByte - Retruned first byte of dbf file + \returns Return Codes +*/ + + +xbInt16 xbFile::GetXbaseFileTypeByte( const xbString &sFileName, unsigned char &cFileTypeByte ) +{ + xbInt16 iVersion; + return GetXbaseFileTypeByte( sFileName, cFileTypeByte, iVersion ); +} + +/************************************************************************/ +//! @brief Get the file type byte and version of the dbf file. +/*! + Pull the first bye off the DBF file for further inspection. + First byte has various bits set to determine what the file format is. + + + \param sFileName - Name of file to examine + \param cFileTypeByte - Returned first byte of dbf file + \param iVersion - Returned file version + \returns Return Codes +*/ + +xbInt16 xbFile::GetXbaseFileTypeByte( const xbString &sFileName, unsigned char &cFileTypeByte, xbInt16 &iVersion ){ + xbInt16 iErrorStop = 0; + xbInt16 iRc = XB_NO_ERROR; + size_t stRc; + FILE *tfp; + + try{ + + iVersion = 0; + cFileTypeByte = 0x00; + #ifdef HAVE__FSOPEN_F + // 0x40 is SH_DENYNO or _SH_DENYNO + if(( tfp = _fsopen( sFileName.Str(), "r", 0x40 )) == NULL ){ + iErrorStop = 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
+ 1 check for DBF
+ 2 check for NDX
+ 3 check for MDX
+ 4 check for NTX
+ + \returns 0 if suffix found
+ 1 if suffix not found, lower case
+ 2 is suffix not found, upper case
+ +*/ + +xbInt16 xbFile::NameSuffixMissing( const xbString & sFileName, xbInt16 iOption ) const { + + xbUInt32 ulLen = sFileName.Len(); + if( ulLen <= 4 ){ + if( sFileName[ulLen] >= 'A' && sFileName[ulLen] <= 'Z' ) + return 2; + else + return 1; + } + if( iOption == 1 && sFileName[ulLen-3] == '.' && + ( sFileName[ulLen-2] == 'd' || sFileName[ulLen-2] == 'D' ) && + ( sFileName[ulLen-1] == 'b' || sFileName[ulLen-1] == 'B' ) && + ( sFileName[ulLen] == 'f' || sFileName[ulLen] == 'F' ) + ) + return 0; + if( iOption == 2 && sFileName[ulLen-3] == '.' && + ( sFileName[ulLen-2] == 'n' || sFileName[ulLen-2] == 'N' ) && + ( sFileName[ulLen-1] == 'd' || sFileName[ulLen-1] == 'D' ) && + ( sFileName[ulLen] == 'x' || sFileName[ulLen] == 'X' ) + ) + return 0; + if( iOption == 3 && sFileName[ulLen-3] == '.' && + ( sFileName[ulLen-2] == 'm' || sFileName[ulLen-2] == 'M' ) && + ( sFileName[ulLen-1] == 'd' || sFileName[ulLen-1] == 'D' ) && + ( sFileName[ulLen] == 'x' || sFileName[ulLen] == 'X' ) + ) + return 0; + if( iOption == 4 && sFileName[ulLen-3] == '.' && + ( sFileName[ulLen-2] == 'n' || sFileName[ulLen-2] == 'N' ) && + ( sFileName[ulLen-1] == 't' || sFileName[ulLen-1] == 'T' ) && + ( sFileName[ulLen] == 'x' || sFileName[ulLen] == 'X' ) + ) + return 0; + // next line might be problematic if file naem has mixed case and extension is missing + if( sFileName[ulLen-4] >= 'A' && sFileName[ulLen-4] <= 'Z' ) + return 2; + else + return 1; +} + +/***********************************************************************/ +//! @brief Read a block of data from file. +/*! + + \param ulBlockNo - block number to read + \param lReadSize - size of data to read at block location, set to 0 to read blocksize + \param *buf - pointer to buffer to write output data, assumed to be previosuly allocated + and large enough to contain data + \returns Return Codes + +*/ + +xbInt16 xbFile::ReadBlock( xbUInt32 ulBlockNo, size_t lReadSize, void * buf ){ + return ReadBlock( ulBlockNo, ulBlockSize, lReadSize, buf ); +} + + +/***********************************************************************/ +//! @brief Read a block of data from file. +/*! + + \param ulBlockNo - block number to read + \param ulBlockSize - block size + \param lReadSize - size of data to read at block location, set to 0 to read blocksize + \param buf - pointer to buffer to write output data, assumed to be previosuly allocated + and large enough to contain data + \returns Return Codes + +*/ +xbInt16 xbFile::ReadBlock( xbUInt32 ulBlockNo, xbUInt32 ulBlockSize, size_t lReadSize, void * buf ){ + + xbInt16 iErrorStop = 0; + xbInt16 iRc = XB_NO_ERROR; + + try{ + if( ulBlockSize <= 0 ){ + iErrorStop = 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 Return Codes +*/ + +xbInt16 xbFile::SetBlockSize( xbUInt32 ulBlockSize ){ + if( ulBlockSize %512 != 0 ) + return XB_INVALID_BLOCK_SIZE; + + this->ulBlockSize = ulBlockSize; + return XB_NO_ERROR; +} +/************************************************************************/ +//! @brief Set the directory. +/*! + \param sDirectory - Valid directory name +*/ + + +void xbFile::SetDirectory( const xbString & sDirectory ){ + + this->sDirectory = sDirectory; + char cLastChar = sDirectory[sDirectory.Len()]; + if( cLastChar != '/' && cLastChar != '\\' ) + sFqFileName.Sprintf( "%s/%s", sDirectory.Str(), sFileName.Str()); + else + sFqFileName.Sprintf( "%s%s", sDirectory.Str(), sFileName.Str()); + + #ifdef WIN32 + sFqFileName.SwapChars( '/', '\\' ); + #else + sFqFileName.SwapChars( '\\', '/' ); + #endif +} + +/************************************************************************/ +//! @brief Set the filename. +/*! + This routine builds out two internal variables from the input file name
+ sFileName - the file name part
+ sFqFileName - the fully qualified file name
+ + + \param sFileName - Input file name +*/ +void xbFile::SetFileName( const xbString & sFileName ){ + + if( sFileName == "" ){ + sFqFileName = ""; + return; + } + + char cPathSep = sFileName.GetPathSeparator(); + + if( cPathSep ){ + + xbString sName; + xbString sExt; + // GetFileDirPart( this->sDirectory ); + GetFileNamePart( sFileName, sName ); + GetFileExtPart( sFileName, sExt ); + this->sFileName.Sprintf( "%s.%s", sName.Str(), sExt.Str()); + sFqFileName = sFileName; + + } else { + + this->sFileName = sFileName; + if( sDirectory.Len() == 0 ){ + sDirectory = GetDataDirectory(); + char cLastChar = sDirectory[sDirectory.Len()]; + if( cLastChar != '/' && cLastChar != '\\' ) + sFqFileName.Sprintf( "%s/%s", sDirectory.Str(), sFileName.Str() ); + else + sFqFileName = sDirectory + sFileName; + } + else{ + char cLastChar = sDirectory[sDirectory.Len()]; + if( cLastChar != '/' && cLastChar != '\\' ) + sFqFileName.Sprintf( "%s/%s", sDirectory.Str(), sFileName.Str() ); + else + sFqFileName = sDirectory + sFileName; + } + } + + #ifdef WIN32 + sFqFileName.SwapChars( '/', '\\' ); + #else + sFqFileName.SwapChars( '\\', '/' ); + #endif +} + +/************************************************************************/ +//! @brief Set the fully qualifed filename. +/*! + \param sFqFileName - Fully qualifed input file name +*/ + +void xbFile::SetFqFileName( const xbString & sFqFileName ){ + this->sFqFileName = sFqFileName; + + xbString sDir; + xbString sName; + xbString sExt; + + GetFileDirPart ( sFqFileName, sDir ); + GetFileNamePart( sFqFileName, sName ); + GetFileExtPart ( sFqFileName, sExt ); + + sDirectory = sDir; + sFileName.Sprintf( "%s.%s", sName.Str(), sExt.Str() ); + + #ifdef WIN32 + this->sDirectory.SwapChars ( '/', '\\' ); + this->sFqFileName.SwapChars( '/', '\\' ); + #else + this->sDirectory.SwapChars ( '\\', '/' ); + this->sFqFileName.SwapChars( '\\', '/' ); + #endif +} + +/************************************************************************/ +//! @brief Write a block of data to file. +/*! + + \param ulBlockNo - block number to write + \param lWriteSize - size of data to write, set to 0 to write blocksize + \param *buf - pointer to buffer of data to be written + \returns Return Codes + +*/ +xbInt16 xbFile::WriteBlock( xbUInt32 ulBlockNo, size_t lWriteSize, void * buf ){ + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + + try{ + + if( ulBlockSize == 0 ){ + iErrorStop = 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 Return Codes +*/ + +xbInt16 xbFile::xbFclose(){ + + int iRc = 0; + if( bFileOpen ){ + iRc = fclose( fp ); + if( iRc != 0 ){ + return XB_CLOSE_ERROR; + } + else{ + bFileOpen = xbFalse; + } + iFileNo = 0; + } + return XB_NO_ERROR; +} + + + +/************************************************************************/ +//! @brief Xbase wrapper for standard libary feof. +/*! + \returns non zero if end-of-file is set for the stream. +*/ +xbInt16 xbFile::xbFeof(){ + return feof( fp ); +} + +/************************************************************************/ +//! @brief Xbase wrapper for standard libary fflush. +/*! + \returns Return Codes +*/ + +xbInt16 xbFile::xbFflush() { + + if( fflush( fp ) ) + return XB_WRITE_ERROR; + else + return XB_NO_ERROR; +} + +/************************************************************************/ +//! @brief Xbase wrapper for standard libary fgetc. +/*! + \param c - output integer returned by fgetc + \returns Return Codes +*/ + +xbInt16 xbFile::xbFgetc( xbInt32 &c ) { + + int i; + + i = fgetc( fp ); + if( i == EOF ) + return XB_EOF; + + c = i; + return XB_NO_ERROR; +} + +/************************************************************************/ +//! @brief Xbase wrapper for standard libary fgetc. +/*! + \param c - output character returned by fgetc + \returns Return Codes +*/ + +xbInt16 xbFile::xbFgetc( char &c ) { + + int i; + i = fgetc( fp ); + if( i == EOF ) + return XB_EOF; + + c = (char) i; + return XB_NO_ERROR; +} +/************************************************************************/ +//! @brief Xbase wrapper for standard libary fgets. +/*! + \param lSize - reads in at most, one character less than lSize + \param s - an xbString containing data returned by fseek + \returns Return Codes +*/ + +xbInt16 xbFile::xbFgets( size_t lSize, xbString &s ) { + + s = ""; + if( feof( fp )) + return XB_EOF; + + char *sBuf = (char *) malloc( lSize + 1 ); + + if( fgets( sBuf, (xbInt32) lSize, fp ) == NULL ){ + free( sBuf ); + return XB_EOF; + } + s.Set( sBuf ); + free( sBuf ); + return XB_NO_ERROR; +} +/************************************************************************/ +//! @brief Xbase wrapper for standard libary fopen. +/*! + + This routine supports all the standard C library open modes. The Xbase routines only + use "r" and "r+b". + + \param sOpenMode + +
OpenModeDescription +
rReading +
r+Reading and Writing +
wOpen for writing. Truncate to zero bytes if it exists +
w+Open for reading and writing, truncate to zero bytes if it exists +
aOpen for append +
a+Open for reading and writing (at end). +
+ The mode can also include the letter "b" for binary ie; "r+b". The "b" is ignored on + POSIX compliant systems, but is included for cross platform compatibility. + \param sFileName File name to open + \param iShareMode + XB_SINGLE_USER
+ XB_MULTI_USER
+ \returns Return Codes +*/ + +xbInt16 xbFile::xbFopen( const xbString &sOpenMode, const xbString &sFileName, xbInt16 iShareMode ) { + if( sFileName == "" || sFqFileName == "" ) + SetFileName( sFileName ); + return xbFopen( sOpenMode, iShareMode ); +} + +/************************************************************************/ +//! @brief Xbase wrapper for standard libary fopen. +/*! + + This routine supports all the standard C library open modes. The Xbase routines only + use "r" and "r+". + + + \param sOpenMode + +
OpenModeDescription +
rReading +
r+Reading and Writing +
wOpen for writing. Truncate to zero bytes if it exists +
w+Open for reading and writing, truncate to zero bytes if it exists +
aOpen for append +
a+Open for reading and writing (at end). +
+ The mode can also include the letter "b" for binary ie; "r+b". The "b" is ignored on + POSIX compliant systems, but is included for cross platform compatibility. + \param iShareMode + XB_SINGLE_USER
+ XB_MULTI_USER
+ \returns Return Codes +*/ + +xbInt16 xbFile::xbFopen( const xbString & sOpenMode, xbInt16 iShareMode ) { + + #ifdef HAVE__FSOPEN_F + if(( fp = _fsopen( sFqFileName.Str(), sOpenMode.Str(), 0x40 )) != NULL ){ + #else + if(( fp = fopen( sFqFileName.Str(), sOpenMode.Str())) != NULL ){ + #endif + + if( sOpenMode == "r" ) + iOpenMode = XB_READ; + else if( sOpenMode == "w" ) + iOpenMode = XB_WRITE; + else + iOpenMode = XB_READ_WRITE; + + bFileOpen = xbTrue; + this->iShareMode = iShareMode; + + #ifdef HAVE__FILENO_F + iFileNo = _fileno( fp ); + #else + iFileNo = fileno( fp ); + #endif + + #ifdef HAVE_SETENDOFFILE_F + //used by visual studio, 32 bit + fHandle = (HANDLE) _get_osfhandle( iFileNo ); + #endif + + #ifdef XB_LOCKING_SUPPORT + if( iShareMode ) + xbFTurnOffFileBuffering(); + #endif + + return XB_NO_ERROR; + } + else + return XB_OPEN_ERROR; +} +/************************************************************************/ +//! @brief Xbase wrapper for standard libary fopen. +/*! + \param iOpenMode + XB_READ
+ XB_READ_WRITE
+ \param iShareMode + XB_SINGLE_USER
+ XB_MULTI_USER
+ \returns Return Codes +*/ + +xbInt16 xbFile::xbFopen( xbInt16 iOpenMode, xbInt16 iShareMode ) { + this->iOpenMode = iOpenMode; + if( iOpenMode == XB_READ_WRITE ) + return xbFopen( "r+b", iShareMode ); + else if( iOpenMode == XB_READ ) + return xbFopen( "r", iShareMode ); + else + return XB_INVALID_OPTION; +} + +/************************************************************************/ +//! @brief Xbase wrapper for standard libary fputc. +/*! + \param c Character to write + \returns Return Codes +*/ + + +xbInt16 xbFile::xbFputc( xbInt32 c ) { + return xbFputc( c, 1 ); +} + +/************************************************************************/ +//! @brief Xbase wrapper for standard libary fputc. +/*! + \param c Character to write + \param iNoOfTimes Number of times to write the character + \returns Return Codes +*/ + + +xbInt16 xbFile::xbFputc( xbInt32 c, xbInt32 iNoOfTimes ) { + + for( xbInt32 l = 0; l < iNoOfTimes; l++ ) + if( fputc( c, fp ) != (int) c ) + return XB_WRITE_ERROR; + + return XB_NO_ERROR; +} + +/************************************************************************/ +//! @brief Xbase wrapper for standard libary fputs. +/*! + \param s xbString to write to file + \returns Return Codes +*/ + +xbInt16 xbFile::xbFputs( const xbString & s ){ + if( fputs( s.Str(), fp ) < 0 ) + return XB_WRITE_ERROR; + else + return XB_NO_ERROR; +} + +/************************************************************************/ +//! @brief Xbase wrapper for standard libary fread. +/*! + \param p Pointer to data to write + \param size size of write + \param nmemb Number of times to write it + \returns Return Codes +*/ + +xbInt16 xbFile::xbFread( void *p, size_t size, size_t nmemb ) { + + size_t iRc; + iRc = fread( p, size, nmemb, fp ); + if( iRc == nmemb ) + return XB_NO_ERROR; + else + return XB_READ_ERROR; +} + +/************************************************************************/ +//! @brief Xbase wrapper for standard libary fseek. +/*! + \param lOffset Position in file to seek to + \param iWhence SEEK_SET - from beginning of file
+ SEEK_CUR - from current position
+ SEEK_END - from end of file
+ \returns Return Codes +*/ + +xbInt16 xbFile::xbFseek( xbInt64 lOffset, xbInt32 iWhence ) { + + xbInt32 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + try { + + #if defined(HAVE_FSEEKO_F) + iRc = fseeko( fp, lOffset, iWhence ); + if( iRc != 0 ){ + iErrorStop = 100; + throw iRc; + } + + #elif defined(HAVE__FSEEKI64_F) + iRc = _fseeki64( fp, lOffset, iWhence ); + if( iRc != 0 ){ + iErrorStop = 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 Return Codes +*/ +xbInt16 xbFile::xbFwrite( const void *p, size_t size, size_t nmemb ) { + + size_t iRc; + iRc = fwrite( p, size, nmemb, fp ); + if( iRc == nmemb ) + return XB_NO_ERROR; + else + return XB_READ_ERROR; +} + +/************************************************************************/ +//! @brief Read file until a particular character is encountered on input stream. +/*! + This routine will read until cDelim is encountered or eof, which ever occurs first. + + \param cDelim Delimiter to stop writing at. + \param sOut Output xbString containing data read + \returns Return Codes +*/ + +xbInt16 xbFile::xbReadUntil( const char cDelim, xbString &sOut ){ + + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + char c; + + try{ + sOut = ""; + if(( iRc = xbFgetc( c )) != XB_NO_ERROR ){ + iErrorStop = 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 Return Codes +*/ +xbInt16 xbFile::xbRemove() { + return xbRemove( sFqFileName.Str(), 0 ); +} + +/************************************************************************/ +//! @brief Delete file. +/*! + \param sFileNameIn Name of file to delete + \returns Return Codes +*/ + +xbInt16 xbFile::xbRemove( const xbString & sFileNameIn ) { + return xbRemove( sFileNameIn, 0 ); +} + + +/************************************************************************/ +//! @brief Delete file. +/*! + \param sFileNameIn Name of file to delete + \param iOption If Set to 1, assume this is a delete request for a dbf file, and should rename the dbt file also + \returns Return Codes +*/ +xbInt16 xbFile::xbRemove( const xbString & sFileNameIn, xbInt16 iOption ) { + + xbInt32 iRc = remove( sFileNameIn.Str()); + if( iRc != 0 ) + return XB_DELETE_FAILED; + + if( iOption == 1 ){ + xbString sFileName2 = sFileNameIn; + + if( sFileName2[sFileName2.Len()] == 'F' ) + sFileName2.PutAt( sFileName2.Len(), 'T' ); + else + sFileName2.PutAt( sFileName2.Len(), 't' ); + + iRc = remove( sFileName2.Str()); + if( iRc != 0 ) + return XB_DELETE_FAILED; + } + + return XB_NO_ERROR; +} + +/************************************************************************/ +//! @brief Rename file. +/*! + \param sOldName Original file name + \param sNewName New file name + \returns Return Codes +*/ + +xbInt16 xbFile::xbRename( const xbString & sOldName, const xbString & sNewName ){ + 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 Return Codes +*/ +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 Return Codes +*/ +xbInt16 xbFile::xbTruncate( xbInt64 llSize ) { + + xbInt16 iRc = 0; + xbInt16 iErrorStop = 0; + try{ + #ifdef HAVE_FTRUNCATE_F + if(( iRc = ftruncate( iFileNo, llSize )) != 0 ){ + iErrorStop = 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
+ XB_UNLOCK
+ \param lOffset Position in file to lock + \param stLen Length to lock + \returns Return Codes +*/ +xbInt16 xbFile::xbLock( xbInt16 iFunction, xbInt64 lOffset, size_t stLen ){ + + xbInt16 iRc = 0; + xbInt16 iErrorStop = 0; + xbInt16 iTries = 0; + + try{ + #ifdef HAVE_FCNTL_F + /* Unix lock function */ + + if(( iRc = xbFseek( lOffset, SEEK_SET )) != XB_NO_ERROR ){ + iErrorStop = 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 Return Codes +*/ +xbInt16 xbFile::DumpBlockToDisk( xbUInt32 ulBlockNo, size_t lBlkSize ){ + + + xbInt16 iRc = 0; + xbInt16 iErrorStop = 0; + + xbUInt32 ulStartBlock; + xbUInt32 ulEndBlock; + + char *p = 0x00; + + xbString sDir; + xbString sFn; + char *buf = NULL; + FILE *fpd = NULL; + try{ + 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 Return Codes +*/ + +xbInt16 xbXBase::ABS( xbDouble dIn, xbDouble &dOut ){ + if( dIn < 0 ) + dOut = dIn * -1; + else + dOut = dIn; + return XB_NO_ERROR; +} + +/*************************************************************************/ +//! @brief Trim leading and trailing white space from a string. +/*! + Expression function ALLTRIM(). + \param sIn Input - Input string to trim. + \param sOut Output - Trimmed string. + \returns Return Codes +*/ + +xbInt16 xbXBase::ALLTRIM( const xbString &sIn, xbString &sOut ){ + sOut = sIn; + sOut.Trim(); + return XB_NO_ERROR; +} + +/*************************************************************************/ +//! @brief Return ASCII code for the first character in a string. +/*! + Expression function ASC(). + \param sIn Input - Input character string. + \param dAscOut Output - Ascii code of first character in string. + \returns Return Codes +*/ +xbInt16 xbXBase::ASC( const xbString &sIn, xbDouble &dAscOut ){ + if( sIn == "" ) + return XB_PARSE_ERROR; + + dAscOut = sIn[1]; + return XB_NO_ERROR; +} +/*************************************************************************/ +//! @brief Return number indicating starting position of string within a string. +/*! + Expression function AT(). + \param s1 Input - Input string to search for. + \param s2 Input - Input string to search. + \param dPos Output - Position of string s1 within s2. + \returns Return Codes +*/ +xbInt16 xbXBase::AT( const xbString &s1, const xbString &s2, xbDouble &dPos ){ + /* looks for s1 in s2 */ + xbInt32 lCnt = 0; + const char *p; + const char *p2 = s2; + if( strlen( s1 ) > strlen( s2 )) return 0; + if(( p = strstr( s2, s1 )) == NULL ) + return XB_NO_ERROR; + while( p2++ != p ) lCnt++; + dPos = lCnt + 1; + return XB_NO_ERROR; +} + +/*************************************************************************/ +//! @brief Return character weekday name for date. +/*! + Expression function CDOW(). + \param dInDate Input - Input date. + \param sOutDow Output - Character day of week. + \returns Return Codes +*/ +xbInt16 xbXBase::CDOW( xbDate &dInDate, xbString &sOutDow ){ + return dInDate.CharDayOf( sOutDow ); +} + +/*************************************************************************/ +//! @brief Convert numeric expression to a character. +/*! + Expression function CHR(). + \param dAsciCd Input - Numeric expression. + \param sOut Output - Character result. + \returns Return Codes +*/ +xbInt16 xbXBase::CHR( xbDouble dAsciCd, xbString &sOut ){ + static char buf[2]; + buf[0] = (char) dAsciCd; + buf[1] = 0x00; + sOut = buf; + return XB_NO_ERROR; +} + +/*************************************************************************/ +//! @brief Return character month name for date. +/*! + Expression function CMONTH(). + \param dInDate Input - Input date. + \param sOutMonth Output - Character month. + \returns Return Codes +*/ +xbInt16 xbXBase::CMONTH( xbDate &dInDate, xbString &sOutMonth ){ + return dInDate.CharMonthOf( sOutMonth ); +} +/*************************************************************************/ +//! @brief Return date from character input date. +/*! + Expression function CTOD(). + \param sInDate Input - Input date in MM/DD/YY format. + \param dOutDate Output - Output date. + \returns Return Codes +*/ +xbInt16 xbXBase::CTOD( const xbString &sInDate, xbDate &dOutDate ){ + return dOutDate.CTOD( sInDate ); +} +/*************************************************************************/ +//! @brief Return system date. +/*! + Expression function DATE(). + \param dOutDate Output - Output date. + \returns Return Codes +*/ +xbInt16 xbXBase::DATE( xbDate &dOutDate ){ + return dOutDate.Sysdate(); +} +/*************************************************************************/ +//! @brief Return the day of the month from a date. +/*! + Expression function DAY(). + \param dInDate Input - Input date. + \param dOutDay Output - Output day of month. + \returns Return Codes +*/ +xbInt16 xbXBase::DAY( const xbDate &dInDate, xbDouble &dOutDay ){ + xbInt16 iOutDay;; + iOutDay = dInDate.DayOf( XB_FMT_MONTH ); + if( iOutDay < 0 ){ + return iOutDay; + }else{ + dOutDay = iOutDay; + return XB_NO_ERROR; + } +} +/*************************************************************************/ +//! @brief Return record deletion status for current record. +/*! + Expression function DEL(). + \param dbf Input - Table to check record deletion status. + \param iRecBufSw Input - Which buffer. 0 = Current record buffer, 1 = Original record buffer + \param sOut Output - "*" if record is deleted, otherise space. + \returns XB_NO_ERROR
XB_PARSE_ERROR. + +*/ +xbInt16 xbXBase::DEL( xbDbf *dbf , xbString &sOut, xbInt16 iRecBufSw ) { + + if( dbf ){ + if( dbf->RecordDeleted( iRecBufSw )) + sOut = "*"; + else + sOut = " "; + return XB_NO_ERROR; + } else { + return XB_PARSE_ERROR; + } +} + +/*************************************************************************/ +//! @brief Return record deletion status for current record. +/*! + Expression function DELETED(). + \param dbf Input - Table to check record deletion status for. + \param iRecBufSw Input - Which buffer. 0 = Current record buffer, 1 = Original record buffer + \param bOut Output - xbTrue if record is deleted.
xbFalse if record is not deleted. + \returns XB_NO_ERROR
XB_PARSE_ERROR. +*/ +xbInt16 xbXBase::DELETED( xbDbf *dbf , xbBool &bOut, xbInt16 iRecBufSw ) { + + if( dbf ){ + bOut = dbf->RecordDeleted( iRecBufSw ); + return XB_NO_ERROR; + } else { + return XB_PARSE_ERROR; + } +} + +/*************************************************************************/ +//! @brief Clipper DESCEND function. +/*! + Expression function DESCEND(). + \param dtInDate Input - Input date. + \param dtOutDate Output - Output date. + \returns XB_NO_ERROR. +*/ +xbInt16 xbXBase::DESCEND( const xbDate &dtInDate, xbDate &dtOutDate ){ + xbDate d( "29991231" ); + dtOutDate.JulToDate8( 2415021 + d.JulianDays() - dtInDate.JulianDays()); + return XB_NO_ERROR; +} + +/*************************************************************************/ +//! @brief Clipper DESCEND function. +/*! + Expression function DESEND(). + \param dIn Input - Input number. + \param dOut Output - Output number. + \returns XB_NO_ERROR. +*/ +xbInt16 xbXBase::DESCEND( xbDouble dIn, xbDouble &dOut ){ + dOut = dIn * -1; + return XB_NO_ERROR; +} + +/*************************************************************************/ +//! @brief Clipper DESCEND function. +/*! + Expression function DESEND(). + \param sIn Input - Input string. + \param sOut Output - Output string. + \returns XB_NO_ERROR. +*/ +xbInt16 xbXBase::DESCEND( const xbString &sIn, xbString &sOut ){ + + sOut = sIn; + for( xbUInt32 l = 0; l < sIn.Len(); l++ ) + sOut.PutAt( l+1, (char) (255 - sOut[l+1])); + return XB_NO_ERROR; +} + +/*************************************************************************/ +//! @brief Return number of day of week. +/*! + Expression function DOW(). + \param dtInDate Input - Input date. + \param dDowOut Output - Output day of week. + \returns Return Codes +*/ +xbInt16 xbXBase::DOW( const xbDate &dtInDate, xbDouble &dDowOut ){ + xbInt16 iDow; + iDow = dtInDate.DayOf( XB_FMT_WEEK ); + if( iDow < 0 ){ + return XB_PARSE_ERROR; + }else{ + dDowOut = iDow; + return XB_NO_ERROR; + } +} + +/*************************************************************************/ +//! @brief Return character date from input date. +/*! + Expression function DTOC(). + \param dInDate Input - Input date. + \param sOutFmtDate Output - Output date. + \returns Return Codes +*/ +xbInt16 xbXBase::DTOC( xbDate &dInDate, xbString &sOutFmtDate ){ + return dInDate.FormatDate( "MM/DD/YY", sOutFmtDate ); +} +/*************************************************************************/ +//! @brief Return char CCYYMMDD date from input date. +/*! + Expression function DTOS(). + \param dtInDate Input - Input date. + \param sOutFmtDate Output - Output date. + \returns Return Codes +*/ +xbInt16 xbXBase::DTOS( xbDate &dtInDate, xbString &sOutFmtDate ){ + return dtInDate.FormatDate( "YYYYMMDD", sOutFmtDate ); +} +/*************************************************************************/ +//! @brief Return exponent value. +/*! + Expression function EXP(). + This function returns e**x where e is approximately 2.71828 and x is dIn. + + \param dIn Input - exp value. + \param dOut Output - value. + \returns XB_NO_ERROR +*/ +xbInt16 xbXBase::EXP( xbDouble dIn, xbDouble &dOut ) +{ + dOut = exp( dIn ); + return XB_NO_ERROR; +} +/*************************************************************************/ +//! @brief Immediate if. +/*! + Expression function IIF(). + \param bResult Input - boolean expression. + \param sTrueResult Input - value if boolean expression is true. + \param sFalseResult Input - value if boolean expression is false. + \param sResult Output - sTrueResult or sFalseResult depending on bResultvalue. + \returns XB_NO_ERROR +*/ +xbInt16 xbXBase::IIF( xbBool bResult, const xbString &sTrueResult, const xbString &sFalseResult, xbString &sResult ) +{ + if( sFalseResult.Len() != sTrueResult.Len()) + return XB_INCONSISTENT_PARM_LENS; + if( bResult ) + sResult = sTrueResult; + else + sResult = sFalseResult; + return XB_NO_ERROR; +} +/*************************************************************************/ +//! @brief Convert number to integer, truncate any decimals. +/*! + Expression function INT(). + \param dIn Input - Input number. + \param dOut Output - integer. + \returns XB_NO_ERROR +*/ + +xbInt16 xbXBase::INT( xbDouble dIn, xbDouble &dOut ) +{ + xbInt64 ll = (xbInt64) dIn; + dOut = (xbDouble) ll; + return XB_NO_ERROR; +} + +/*************************************************************************/ +//! @brief Check if string begins with alpha character. +/*! + Expression function ISALPHA(). + \param sIn Input - Input string. + \param bResult Output - xbTrue if string begins with alpha character.
xbFalse if string does not begin with alpha character. + \returns XB_NO_ERROR +*/ +xbInt16 xbXBase::ISALPHA( const xbString &sIn, xbBool &bResult ){ + if( isalpha(sIn[1])) + bResult = 1; + else + bResult = 0; + return XB_NO_ERROR; +} + +/*************************************************************************/ +//! @brief Check if string begins with lower case alpha character. +/*! + Expression function ISLOWER(). + \param sIn Input - Input string. + \param bResult Output - xbTrue if string begins with lower case alpha character.
+ xbFalse if string does not begin with lower case alpha character. + \returns XB_NO_ERROR +*/ + +xbInt16 xbXBase::ISLOWER( const xbString &sIn, xbBool &bResult ){ + if( islower(sIn[1])) + bResult = 1; + else + bResult = 0; + return XB_NO_ERROR; +} +/*************************************************************************/ +//! @brief Check if string begins with upper case alpha character. +/*! + Expression function ISUPPER(). + \param sIn Input - Input string. + \param bResult Output - xbTrue if string begins with upper case alpha character.
+ xbFalse if string does not begin with upper case alpha character. + \returns XB_NO_ERROR +*/ + +xbInt16 xbXBase::ISUPPER( const xbString &sIn, xbBool &bResult ){ + if( isupper(sIn[1])) + bResult = 1; + else + bResult = 0; + return XB_NO_ERROR; +} + +/*************************************************************************/ +//! @brief Return left characters from string. +/*! + Expression function LEFT(). + \param sIn Input - string. + \param ulCharCnt Input - number of characters to extract from string. + \param sOut Output - resultant string. + \returns XB_NO_ERROR +*/ +xbInt16 xbXBase::LEFT( const xbString &sIn, xbUInt32 ulCharCnt, xbString &sOut ){ + sOut.Assign( sIn, 1, ulCharCnt ); + return XB_NO_ERROR; +} + +/*************************************************************************/ +//! @brief Return length of string. +/*! + Expression function LEN(). + \param sIn Input - Input string. + \param dOut Output - string length. + \returns XB_NO_ERROR +*/ + +xbInt16 xbXBase::LEN( const xbString &sIn, xbDouble &dOut ){ + dOut = sIn.Len(); + return XB_NO_ERROR; +} + +/*************************************************************************/ +//! @brief Calculate logarithm. +/*! + Expression function LOG(). + \param dIn Input - numeric expression. + \param dOut Output - numeric log value. + \returns XB_NO_ERROR +*/ +xbInt16 xbXBase::LOG( xbDouble dIn, xbDouble &dOut ){ + dOut = log( dIn ); + return XB_NO_ERROR; +} +/*************************************************************************/ +//! @brief Trim left side of string. +/*! + Expression function LTRIM(). + \param sIn Input - string. + \param sOut Output - string result. + \returns XB_NO_ERROR +*/ +xbInt16 xbXBase::LTRIM( const xbString &sIn, xbString &sOut ){ + sOut = sIn; + sOut.Ltrim(); + return XB_NO_ERROR; +} +/*************************************************************************/ +//! @brief Convert upper case to lower case. +/*! + Expression function LOWER(). + \param sIn Input - string. + \param sOut Output - string result. + \returns XB_NO_ERROR +*/ + +xbInt16 xbXBase::LOWER( const xbString &sIn, xbString &sOut ){ + sOut = sIn; + sOut.ToLowerCase(); + return XB_NO_ERROR; +} + +/*************************************************************************/ +//! @brief Return higher of two values. +/*! + Expression function MAX(). + \param d1 Input - Numeric value 1. + \param d2 Input - Numeric value 2. + \param dOut Output - Higher of d1 or d2. + \returns XB_NO_ERROR +*/ +xbInt16 xbXBase::MAX( xbDouble d1, xbDouble d2, xbDouble &dOut ) +{ + if( d1 > d2 ) + dOut = d1; + else + dOut = d2; + return XB_NO_ERROR; +} + +/*************************************************************************/ +//! @brief Return lessor of two values. +/*! + Expression function MIN(). + \param d1 Input - Numeric value 1. + \param d2 Input - Numeric value 2. + \param dOut Output - Lessor of d1 or d2. + \returns XB_NO_ERROR +*/ +xbInt16 xbXBase::MIN( xbDouble d1, xbDouble d2, xbDouble &dOut ) +{ + if( d1 < d2 ) + dOut = d1; + else + dOut = d2; + return XB_NO_ERROR; +} + +/*************************************************************************/ +//! @brief Return number of month for a given date. +/*! + Expression function MONTH(). + \param dInDate Input date. + \param dOutMonth - Month number. + \returns XB_NO_ERROR.
XB_PARSE_ERROR. +*/ +xbInt16 xbXBase::MONTH( xbDate &dInDate, xbDouble &dOutMonth ){ + + xbInt16 iRc = dInDate.MonthOf(); + if( iRc < 0 ) + return XB_PARSE_ERROR; + else{ + dOutMonth = iRc; + return XB_NO_ERROR; + } +} + +/*************************************************************************/ +//! @brief Return number of records in a given table. +/*! + Expression function RECCOUNT(). + \param dbf - Table. + \param dOut - Number of records. + \returns Return Codes +*/ +xbInt16 xbXBase::RECCOUNT( xbDbf *dbf , xbDouble &dOut ) { + + xbUInt32 ulRecCnt; + xbInt16 iRc = dbf->GetRecordCnt( ulRecCnt ); + dOut = (xbDouble) ulRecCnt; + return iRc; + +} + +/*************************************************************************/ +//! @brief Return current record number for a given table. +/*! + Expression function RECNO(). + \param dbf - Table. + \param dOut - Record number. + \returns XB_NO_ERROR.
XB_PARSE_ERROR. +*/ + +xbInt16 xbXBase::RECNO( xbDbf *dbf , xbDouble &dOut ) { + if( dbf ){ + dOut = (xbDouble) dbf->GetCurRecNo(); + return XB_NO_ERROR; + } else { + dOut = -1; + return XB_PARSE_ERROR; + } +} + +/*************************************************************************/ +//! @brief Repeat character expression N times. +/*! + Expression function REPLICATE(). + \param sIn Inout - String to replicate. + \param ulRepCnt Input - Number of times to repeat. + \param sOut Output - String result. + \returns XB_NO_ERROR.
XB_PARSE_ERROR. +*/ +xbInt16 xbXBase::REPLICATE( const xbString &sIn, xbUInt32 ulRepCnt, xbString &sOut ){ + sOut = ""; + for( xbUInt32 i = 0; i < ulRepCnt; i++ ) + sOut += sIn; + return XB_NO_ERROR; +} + +/*************************************************************************/ +//! @brief Return right characters from string. +/*! + Expression function RIGHT(). + \param sIn Input - string. + \param ulCharCnt Input - number of characters to extract from string. + \param sOut Output - resultant string. + \returns XB_NO_ERROR +*/ + +xbInt16 xbXBase::RIGHT( const xbString &sIn, xbUInt32 ulCharCnt, xbString &sOut ){ + if( sIn.Len() < ulCharCnt ) + sOut = sIn; + else + sOut.Assign( sIn, sIn.Len() - ulCharCnt + 1, ulCharCnt ); + return XB_NO_ERROR; +} + +/*************************************************************************/ +//! @brief Trim right side of string. +/*! + Expression function RTRIM(). + \param sIn Input - string. + \param sOut Output - string result. + \returns XB_NO_ERROR +*/ + +xbInt16 xbXBase::RTRIM( const xbString &sIn, xbString &sOut ){ + sOut = sIn; + sOut.Rtrim(); + return XB_NO_ERROR; +} + +/*************************************************************************/ +//! @brief Generate a string of N spaces. +/*! + Expression function SPACE(). + \param lCnt Input - Number of spaces. + \param sOut Output - Output String. + \returns XB_NO_ERROR. +*/ +xbInt16 xbXBase::SPACE( xbInt32 lCnt, xbString &sOut ){ + sOut = ""; + sOut.PadLeft( ' ', (xbUInt32) lCnt ); + return XB_NO_ERROR; +} + +/*************************************************************************/ +//! @brief Calculate a square root. +/*! + Expression function SQRT(). + \param dBase Input - Base number. + \param dSqrRoot Output - Square root. + \returns XB_NO_ERROR. +*/ + +xbInt16 xbXBase::SQRT( xbDouble dBase, xbDouble &dSqrRoot ) +{ + dSqrRoot = sqrt( dBase ); + return XB_NO_ERROR; +} + +/*************************************************************************/ +//! @brief Converts a valid 8 byte (CCYYMMDD) input date into a date class. +/*! + Expression function STOD(). + \param sInDate Input - Input date. + \param dtOutDate Output - Output date. + \returns XB_NO_ERROR.
XB_INVALID_DATE. +*/ + +xbInt16 xbXBase::STOD( const xbString &sInDate, xbDate &dtOutDate ){ + + if( dtOutDate.DateIsValid( sInDate )){ + dtOutDate = sInDate; + return XB_NO_ERROR; + } else { + return XB_INVALID_DATE; + } +} + +/*************************************************************************/ +//! @brief Convert number to a character string. +/*! + Expression function STR(). + \param dIn Input - Number. + \param sOut Output - String. + \returns XB_NO_ERROR. +*/ + +xbInt16 xbXBase::STR( xbDouble dIn, xbString &sOut) { + xbString sPadChar = " "; + return STR( dIn, 10, 0, sPadChar, sOut ); +} + +/*************************************************************************/ +//! @brief Convert number to a character string. +/*! + Expression function STR(). + \param dIn Input - Number. + \param ulLen Input - Length. + \param sOut Output - String. + \returns XB_NO_ERROR. +*/ +xbInt16 xbXBase::STR( xbDouble dIn, xbUInt32 ulLen, xbString &sOut) { + xbString sPadChar = " "; + return STR( dIn, ulLen, 0, sPadChar, sOut ); +} + +/*************************************************************************/ +//! @brief Convert number to a character string. +/*! + Expression function STR(). + \param dIn Input - Number. + \param ulLen Input - Length. + \param ulDec Input - Number of decimals. + \param sOut Output - String. + \returns XB_NO_ERROR. +*/ + +xbInt16 xbXBase::STR( xbDouble dIn, xbUInt32 ulLen, xbUInt32 ulDec, xbString &sOut) { + xbString sPadChar = " "; + return STR( dIn, ulLen, ulDec, sPadChar, sOut ); +} + +/*************************************************************************/ +//! @brief Convert number to a character string. +/*! + Expression function STR(). + \param dIn Input - Number. + \param ulLen Input - Length. + \param ulDec Input - Number of decimals. + \param sPadChar Input - Left pad character, typically zero or space. + \param sOut Output - String. + \returns XB_NO_ERROR. +*/ + +xbInt16 xbXBase::STR( xbDouble dIn, xbUInt32 ulLen, xbUInt32 ulDec, xbString &sPadChar, xbString &sOut) { + + xbString sFmt; + sFmt.Sprintf( "%c%d.%df", '%', ulLen, ulDec ); + sOut.Sprintf( sFmt.Str(), dIn, 0 ); + + // convert to all "*" if result is too long + if( sOut.Len() > ulLen ){ + sOut = "*"; + do{ + sOut += "*"; + } while( ulLen > sOut.Len()); + } else if( sPadChar.Len() > 0 && sPadChar != " " ){ + // this logic doesn't make sense when processing negative numbers, + // but it does behave like the original dbase + // you could end up with something like 0000-12.17 when you really want -000012.17 + // that is probably why the STRZERO function came into being + xbUInt32 l = 1; + while( sOut[l] == ' ' ){ + sOut.PutAt( l, sPadChar[1]); + l++; + } + } + return XB_NO_ERROR; +} + +/*************************************************************************/ +//! @brief Convert number to a character string with leading zeroes. +/*! + Expression function STRZERO(). + \param dIn Input - Number. + \param ulLen Input - Length. + \param ulDec Input - Number of decimals. + \param sOut Output - String. + \returns XB_NO_ERROR. +*/ +xbInt16 xbXBase::STRZERO( xbDouble dIn, xbUInt32 ulLen, xbUInt32 ulDec, xbString &sOut){ + + xbString sFmt; + if( dIn < 0 ) + sFmt.Sprintf( "%c+0%d.%df", '%', ulLen, ulDec ); + else + sFmt.Sprintf( "%c0%d.%df", '%', ulLen, ulDec ); + sOut.Sprintf( sFmt.Str(), dIn ); + + // convert to all "*" if result is too long + if( sOut.Len() > ulLen ){ + sOut = "*"; + do{ + sOut += "*"; + } while( ulLen > sOut.Len()); + } + return XB_NO_ERROR; +} + +/*************************************************************************/ +//! @brief Extract a portion of a string from another string. +/*! + Expression function SUBSTR(). + \param sIn Input - Source string. + \param ulStartPos Input - Starting position for string extraction. + \param ulLen Input - Number of characters to extract. + \param sOut Output - String. + \returns XB_NO_ERROR. +*/ +xbInt16 xbXBase::SUBSTR( const xbString &sIn, xbUInt32 ulStartPos, xbUInt32 ulLen, xbString &sOut ){ + sOut = sIn; + sOut.Mid( ulStartPos, ulLen ); + return XB_NO_ERROR; +} + +/*************************************************************************/ +//! @brief Trim left and right sides of string. +/*! + Expression function TRIM(). + \param sIn Input - string. + \param sOut Output string result. + \returns XB_NO_ERROR +*/ +xbInt16 xbXBase::TRIM( const xbString &sIn, xbString &sOut ){ + return RTRIM( sIn, sOut ); +} + +/*************************************************************************/ +//! @brief Convert lower case to upper case. +/*! + Expression function UPPER(). + \param sIn Input - string. + \param sOut Output - string result. + \returns XB_NO_ERROR +*/ +xbInt16 xbXBase::UPPER( const xbString &sIn, xbString &sOut ){ + sOut = sIn; + sOut.ToUpperCase(); + return XB_NO_ERROR; +} + +/*************************************************************************/ +//! @brief Convert numeric characters to number. +/*! + Expression function VAL(). + \param sIn Input - string. + \param dOut Output - numeric result. + \returns XB_NO_ERROR +*/ + +xbInt16 xbXBase::VAL( const xbString &sIn, xbDouble &dOut ) +{ + if( sIn ) + // strtod(nptr,NULL); + dOut = atof( sIn ); + else + dOut = 0; + return XB_NO_ERROR; +} +/*************************************************************************/ +//! @brief Return year for a given date. +/*! + Expression function YEAR(). + \param dInDate Input date. + \param dOutYear - Year. + \returns XB_NO_ERROR.
XB_PARSE_ERROR. +*/ + +xbInt16 xbXBase::YEAR( xbDate &dInDate, xbDouble &dOutYear ){ + + xbInt16 iRc = dInDate.YearOf(); + if( iRc < 0 ) + return XB_PARSE_ERROR; + else{ + dOutYear = iRc; + return XB_NO_ERROR; + } +} +/*************************************************************************/ +}; // namespace +#endif // XB_FUNCTION_SUPPORT +/*************************************************************************/ diff --git a/src/core/xbixbase.cpp b/src/core/xbixbase.cpp 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 Return Codes +*/ + +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
Pointer to newly allocated xbIxNode on success +*/ +xbIxNode * xbIx::AllocateIxNode( xbUInt32 ulBufSize, xbInt16 ) +{ + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + + try{ + xbIxNode * pNode = (xbIxNode *) calloc( 1, sizeof( xbIxNode )); + if( pNode == NULL ){ + iErrorStop = 100; + iRc = XB_NO_MEMORY; + throw iRc; + } + if( ulBufSize == 0 ) + ulBufSize = GetBlockSize(); + + pNode->ulBufSize = ulBufSize; + pNode->cpBlockData = (char *) calloc( 1, ulBufSize ); + if( pNode->cpBlockData == NULL ){ + free( pNode ); + iErrorStop = 110; + iRc = XB_NO_MEMORY; + throw iRc; + } + return pNode; + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbIx::AllocateIxNode() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return NULL; +} + + +/***********************************************************************/ +//! @brief Binary search for given value on an index node. +/*! + + Binary search for key lookups + \param cKeyType Key type + \param npNode Pointer to index node for search + \param lKeyItemLen Lenth of key plus pointer values + \param vpKey Pointer to key value + \param lSearchKeyLen length of key to search + \param iCompRc output return code from the CompareKey routine. CompareKey returns an + integer value less than, equal to or greater than zero in when comparing values + + \param bDescending xbTrue for descending index key lookup.
+ xbFalse for ascending index key lookup. + \return The position in the node the key was found, if multiples it returns the first occurrence. + If the key is not found, it returns the slot it should be in. +*/ + +xbInt16 xbIx::BSearchBlock( char cKeyType, xbIxNode *npNode, xbInt32 lKeyItemLen, const void *vpKey, + xbInt32 lSearchKeyLen, xbInt16 &iCompRc, xbBool bDescending ) const { + xbInt32 lLo = 0; + xbInt32 lHi = 0; + xbInt32 lMid = 0; + xbInt32 lKeyCnt = GetKeyCount( npNode ); + + + if( !bDescending ){ + lHi = lKeyCnt - 1; + + while( lLo <= lHi ){ + lMid = (lLo + lHi) / 2; + iCompRc = CompareKey( cKeyType, GetKeyData( npNode, lMid, lKeyItemLen ), vpKey, (size_t) lSearchKeyLen ); + if( iCompRc > 0 ) + lHi = lMid - 1; + else if( iCompRc < 0 ) + lLo = lMid + 1; + else{ // found match, look for leftmost occurrence + + xbInt32 lFoundPos = lMid; + lMid--; + while( lMid >= 0 && CompareKey( cKeyType, GetKeyData( npNode, lMid, lKeyItemLen ), vpKey, (size_t) lSearchKeyLen ) == 0 ){ + lFoundPos = lMid; + lMid--; + } + iCompRc = 0; + lLo = lFoundPos; + lHi = -1; + } + } + // update the compare key results + if( lMid != lLo ){ + if( lLo >= lKeyCnt ) + iCompRc = 1; + else + iCompRc = CompareKey( cKeyType, GetKeyData( npNode, lLo, lKeyItemLen ), vpKey, (size_t) lSearchKeyLen ); + } + return (xbInt16) lLo; + + } else { // descending key + + lLo = lKeyCnt - 1; + while( lLo >= lHi && lHi != -1 ){ + lMid = (lLo + lHi) / 2; + iCompRc = CompareKey( cKeyType, GetKeyData( npNode, lMid, lKeyItemLen ), vpKey, (size_t) lSearchKeyLen ); + + if( iCompRc > 0 ) { + lHi = lMid + 1; + } + else if( iCompRc < 0) { + lLo = lMid - 1; + } + else{ // found match, look for leftmost occurrence + + xbInt32 lFoundPos = lMid; + lMid--; + while( lMid >= 0 && CompareKey( cKeyType, GetKeyData( npNode, lMid, lKeyItemLen ), vpKey, (size_t) lSearchKeyLen ) == 0 ){ + lFoundPos = lMid; + lMid--; + } + iCompRc = 0; + lHi = lFoundPos; + lLo = -1; + } + } + + // std::cout << "BSB1 lo = " << lLo << " mid = " << lMid << " hi = " << lHi << " keycnt = " << lKeyCnt << " iCompRc = " << iCompRc << "\n"; // key=" << (char *) vpKey << "\n"; + if( lLo < 0 && iCompRc < 0 ) + iCompRc = 1; + else if( iCompRc != 0 ) { + iCompRc = CompareKey( cKeyType, GetKeyData( npNode, (lLo < 0 ? 0 : lLo), lKeyItemLen ), vpKey, (size_t) lSearchKeyLen ); + } + // std::cout << "BSB2 lo = " << lLo << " mid = " << lMid << " hi = " << lHi << " keycnt = " << lKeyCnt << " iCompRc = " << iCompRc << "\n"; // key=" << (char *) vpKey << "\n"; + return (xbInt16) lHi; + } + + // should never get here + // return (xbInt16) 0; +} + +/***********************************************************************/ +//! @brief Check for duplicate keys. +/*! + \returns Return Codes +*/ +xbInt16 xbIx::CheckForDupKeys(){ + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + xbInt16 i = 0; + + try{ + void * vpTag; + xbInt16 iTagCount = GetTagCount(); + for( i = 0; i < iTagCount; i++ ){ + vpTag = GetTag( i ); + if(( iRc = CheckForDupKey( vpTag )) < XB_NO_ERROR ){ + iErrorStop = 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 Return Codes +*/ + +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.
+ N - Numeric BCD compare.
+ D - Numeric compare.
+ F - Numeric compare.
+ \param v1 Left compare.
v2 - Right Compare. + \param iSearchKeyLen Length of key compare. + \returns 1 - Left operand is greater then right operand.
+ 0 - Left operand is equal to right operand.
+ -1 - Left operand is less than right operand. +*/ +inline xbInt16 xbIx::CompareKey( char cKeyType, const void *v1, const void *v2, size_t iSearchKeyLen ) const{ + if( cKeyType == 'C' ){ // character compare + return memcmp( v1, v2, iSearchKeyLen ); + } else if( cKeyType == 'N' ){ // numeric bcd compare, mdx bcd numeric indices + xbBcd bcdk1( v1 ); + return bcdk1.Compare( v2 ); + } else if( cKeyType == 'D' || cKeyType == 'F' ){ // julian date compare, ndx float numeric indices + xbDouble *d1 = (xbDouble *) v1; + xbDouble *d2 = (xbDouble *) v2; + if( *d1 < *d2 ) + return -1; + else if( *d1 > *d2 ) + return 1; + else + return 0; + } else { +// std::cout << "Unhandled key type [" << cKeyType << "]\n"; + } + return XB_NO_ERROR; +} +/***********************************************************************/ +//! @brief Create Keys for record number +/*! + \param iOpt 0 Build a key for FindKey usage, only rec buf 0.
+ 1 Append Mode, Create key for an append, only use rec buf 0, set updated switch.
+ 2 Update Mode, Create old version and new version keys, check if different, set update switch appropriately. + \returns Return Codes +*/ +xbInt16 xbIx::CreateKeys( xbInt16 iOpt ) { + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + xbInt16 i = 0; + + try{ + void * vpTag; + xbInt16 iTagCount = GetTagCount(); + + for( i = 0; i < iTagCount; i++ ){ + vpTag = GetTag( i ); + if(( iRc = CreateKey( vpTag, iOpt )) < XB_NO_ERROR ){ + iErrorStop = 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 Return Codes +*/ +//xbInt16 xbIx::DeleteKeys( xbUInt32 ulRecNo ){ + +xbInt16 xbIx::DeleteKeys(){ + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + xbInt16 i = 0; + + try{ + void * vpTag; + xbInt16 iTagCount = GetTagCount(); + + for( i = 0; i < iTagCount; i++ ){ + vpTag = GetTag( i ); + if( GetKeySts( vpTag ) > 1 ){ // 0 = no update 1 = add 2 = update, 3 = delete + if(( iRc = UpdateTagKey( 'D', vpTag )) != XB_NO_ERROR ){ + iErrorStop = 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
+ 1 = Syslog
+ 2 = Both
+ \returns Return Codes +*/ + +xbInt16 xbIx::DumpNode( void *, xbIxNode *pNode, xbInt16 iOption ) const +{ + xbString s; + s.Sprintf( "Dump Node Block=[%d] CurKey=[%d]", pNode->ulBlockNo, pNode->iCurKeyNo ); + xbase->WriteLogMessage( s, iOption ); + + return XB_NO_ERROR; +} +#endif // XB_DEBUG_SUPPORT + +/***********************************************************************/ +//! @brief Find double key +/*! + \param vpTag Pointer to tag to search. + \param dKey Double value to search for. + \param iRetrieveSw xbTrue - Retrieve the record if key found.
+ xbFalse - Don't retrieve record, check for key existence only. + \returns XB_NO_ERROR - Key found.
+ XB_NOT_FOUND - Key not found.
+ Return Codes +*/ +xbInt16 xbIx::FindKey( void *vpTag, xbDouble dKey, xbInt16 iRetrieveSw ){ + return FindKey( vpTag, &dKey, 8, iRetrieveSw ); +} +/***********************************************************************/ +//! @brief Find string key +/*! + \param vpTag Pointer to tag to search. + \param sKey String data to search for. + \param iRetrieveSw xbTrue - Retrieve the record if key found.
+ xbFalse - Don't retrieve record, check for key existence only. + \returns XB_NO_ERROR - Key found.
+ XB_NOT_FOUND - Key not found.
+ Return Codes +*/ +xbInt16 xbIx::FindKey( void *vpTag, const xbString &sKey, xbInt16 iRetrieveSw ){ + return FindKey( vpTag, sKey.Str(), (xbInt32) sKey.Len(), iRetrieveSw ); +} +/***********************************************************************/ +//! @brief Find character key +/*! + \param vpTag Pointer to tag to search. + \param cKey String data to search for. + \param lKeyLen Length of key to search for. + \param iRetrieveSw xbTrue - Retrieve the record if key found.
+ xbFalse - Don't retrieve record, check for key existence only. + \returns XB_NO_ERROR - Key found.
+ XB_NOT_FOUND - Key not found.
+ Return Codes +*/ +xbInt16 xbIx::FindKey( void *vpTag, const char *cKey, xbInt32 lKeyLen, xbInt16 iRetrieveSw ){ + return FindKey( vpTag, (void *) cKey, lKeyLen, iRetrieveSw ); +} +/***********************************************************************/ +//! @brief Find bcd key +/*! + \param vpTag Pointer to tag to search. + \param bcd BCD data to search for. + \param iRetrieveSw xbTrue - Retrieve the record if key found.
+ xbFalse - Don't retrieve record, check for key existence only. + + \returns XB_NO_ERROR - Key found.
+ XB_NOT_FOUND - Key not found.
+ Return Codes +*/ +xbInt16 xbIx::FindKey( void *vpTag, const xbBcd &bcd, xbInt16 iRetrieveSw ){ + return FindKey( vpTag, bcd.GetBcd(), 12, iRetrieveSw ); +} +/***********************************************************************/ +//! @brief Find date key +/*! + \param vpTag Pointer to tag to search. + \param dtKey Date data to search for. + \param iRetrieveSw xbTrue - Retrieve the record if key found.
+ xbFalse - Don't retrieve record, check for key existence only. + \returns XB_NO_ERROR - Key found.
+ XB_NOT_FOUND - Key not found.
+ Return Codes +*/ +xbInt16 xbIx::FindKey( void *vpTag, const xbDate &dtKey, xbInt16 iRetrieveSw ){ + xbDouble d = (xbDouble) dtKey.JulianDays(); + return FindKey( vpTag, &d, 8, iRetrieveSw ); +} +/***********************************************************************/ +//! @brief Free all nodes in a linked list. +/*! + \param np Pointer to first node in linked list to free. + \returns NULL. +*/ +xbIxNode *xbIx::FreeNodeChain( xbIxNode *np ){ + + // routine returns NULL + if( np ){ + // free memory for a given chain of nodes + xbIxNode * np2; + + // Clear the previous node's next pointer + if( np->npPrev ) + np->npPrev->npNext = NULL; + + // Clear out the tree + while( np ){ + np2 = np->npNext; + NodeFree( np ); + np = NULL; + np = np2; + } + } + return NULL; +} +/***********************************************************************/ +//! @brief Read block for block number. +/*! + Routine to read a node/block out of an index file and store in xbIxNode structure + \param vpTag Pointer to tag. + \param ulBlockNo Block number to read off disk. + \param iOpt + 0 = Node is read into block buffer, not added to the node chain
+ 1 = Node is read into new xbIxNode, then added to the node chain, and sets CurNode with new node
+ 2 = Node is read into new xbIxNode, not added to the node chain
+ CurNode points to new node
+ \param ulAddlBuf Additional buffer size added to memory + \returns Return Codes + +*/ + +xbInt16 xbIx::GetBlock( void *vpTag, xbUInt32 ulBlockNo, xbInt16 iOpt, xbUInt32 ulAddlBuf ){ + xbInt16 iRc = 0; + xbInt16 iErrorStop = 0; + xbIxNode *np = NULL; + try{ + + if( !vpTag && iOpt == 1 ){ + iErrorStop = 100; + throw iRc; + } + + // set target location of block read to read + char *cp; + if( iOpt == 0 ) + cp = cNodeBuf; + else{ + if(( np = AllocateIxNode(GetBlockSize() + ulAddlBuf )) == NULL ){ + iErrorStop = 110; + iRc = XB_NO_MEMORY; + throw( iRc ); + } + cp = np->cpBlockData; + } + if(( iRc = ReadBlock( ulBlockNo, GetBlockSize(), cp )) != XB_NO_ERROR ){ + iErrorStop = 120; + throw iRc; + } + if( iOpt == 0 ) + return iRc; + np->ulBlockNo = ulBlockNo; + np->iCurKeyNo = 0; + if( iOpt == 1 ) + AppendNodeChain( vpTag, np ); + else if( iOpt == 2 ){ + std::cout << "Future use stub. xbIxbase::GetBlock() option 2 not coded.\n"; + iErrorStop = 130; + iRc = XB_INVALID_OPTION; + throw iRc; + // SetCurNode( vpTag, np ); + } + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbIx::GetBlock() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + if( np ) NodeFree( np ); + } + return iRc; +} + +/***********************************************************************/ +//! @brief Get pointer to current tag. +/*! + \returns Pointer to current tag. +*/ + +void *xbIx::GetCurTag() const { + return vpCurTag; +} +/***********************************************************************/ +//! @brief Get pointer to dbf. +/*! + \returns Pointer to dbf. +*/ +xbDbf *xbIx::GetDbf() const { + return this->dbf; +} +/***********************************************************************/ +//! @brief Get the first key for the current tag. +/*! + \returns Return Codes +*/ +xbInt16 xbIx::GetFirstKey(){ + return GetFirstKey( vpCurTag, 0 ); +} + +/***********************************************************************/ +//! @brief Get the first key for a given tag. +/*! + \param vpTag Tag for get first key operation. + \returns Return Codes +*/ +xbInt16 xbIx::GetFirstKey( void *vpTag ){ + return GetFirstKey( vpTag, 0 ); +} +/***********************************************************************/ +//! @brief Get the last key for the current tag. +/*! + \returns Return Codes +*/ +xbInt16 xbIx::GetLastKey(){ + return GetLastKey( 0, vpCurTag, 0 ); +} +/***********************************************************************/ +//! @brief Get the last key for a given tag. +/*! + \param vpTag Tag for get last key operation. + \returns Return Codes +*/ +xbInt16 xbIx::GetLastKey( void *vpTag ){ + return GetLastKey( 0, vpTag, 0 ); +} + +/***********************************************************************/ +//! @brief Get the file lock status. +/*! + \returns xbTrue - Index file is locked.
xbFalse - Index file is not locked. +*/ +xbBool xbIx::GetLocked() const { + return bLocked; +} + +/***********************************************************************/ +//! @brief Get the key count for number of keys on a node. +/*! + \param np Given node for key count. + \returns Number of keys on the node. +*/ +xbInt32 xbIx::GetKeyCount( xbIxNode *np ) const { + // assumes the first four bytes of the block is a four byte number + // representing the number of keys contained on the block + return eGetInt32( np->cpBlockData ); +} +/***********************************************************************/ +//! @brief Get key data for a given key number. +/*! + \param np Given node for key retrieval. + \param iKeyNo Which key to pull. + \param iKeyItemLen Length of key plus pointers. + \returns Pointer to a given key. +*/ +char * xbIx::GetKeyData( xbIxNode *np, xbInt16 iKeyNo, xbInt16 iKeyItemLen ) const { + if( !np ) return NULL; + char *p = np->cpBlockData; + xbUInt32 ulKeyCnt = eGetUInt32( p ); + if( iKeyNo < 0 || iKeyNo > (xbInt16) ulKeyCnt ) return NULL; + xbInt16 iOffset = 12 + (iKeyNo * iKeyItemLen); + p+=iOffset; + return p; +} +/***********************************************************************/ +//! @brief Get the next key for the current tag. +/*! + \returns Return Codes +*/ +xbInt16 xbIx::GetNextKey(){ + return GetNextKey( vpCurTag, 0 ); +} +/***********************************************************************/ +//! @brief Get the next key for the given tag. +/*! + \param vpTag Tag for next key operation. + \returns Return Codes +*/ +xbInt16 xbIx::GetNextKey( void *vpTag ){ + return GetNextKey( vpTag, 0 ); +} +/***********************************************************************/ +//! @brief Get the prev key for the current tag. +/*! + \returns Return Codes +*/ +xbInt16 xbIx::GetPrevKey(){ + return GetPrevKey( vpCurTag, 0 ); +} +/***********************************************************************/ +//! @brief Get the previous key for the given tag. +/*! + \param vpTag Tag for previous key operation. + \returns Return Codes +*/ +xbInt16 xbIx::GetPrevKey( void *vpTag ){ + return GetPrevKey( vpTag, 0 ); +} +/***********************************************************************/ +//! @brief Free an index node +/*! + \param ixNode Pointer to index node to free. + \returns void +*/ +void xbIx::NodeFree( xbIxNode *ixNode ){ + if( ixNode ){ + if( ixNode->cpBlockData ){ + free( ixNode->cpBlockData ); + ixNode->cpBlockData = NULL; + } + free( ixNode ); + ixNode = NULL; + } +} +/***********************************************************************/ +//! @brief Open an index file. +/*! + MDX files are opened automatically and don't need opened. + NDX files that are associated with the DBF file are opened automatically. + + Non production indexes that haven't been opened will need to be opened to be used. + \param sFileName Index file name to open. + \returns Return Codes +*/ +xbInt16 xbIx::Open( const xbString & sFileName ){ + + // There are no locking requirements when opening an NDX index + xbInt16 iRc = 0; + xbInt16 iErrorStop = 0; + + try{ + /* copy the file name to the class variable */ + this->SetFileName( sFileName ); + + if( !FileExists()){ + iErrorStop = 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.
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 Return Codes +*/ + +xbInt16 xbIxMdx::AddKey( void * vpTag, xbUInt32 ulRecNo ){ + + xbMdxTag * npTag = (xbMdxTag *) vpTag; + if( GetUniqueKeyOpt() == XB_EMULATE_DBASE && npTag->bFoundSts ) + return XB_NO_ERROR; + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + xbInt16 iHeadNodeUpdateOpt = 2; + xbIxNode * npRightNode = NULL; + xbUInt32 ulNewRightChild = 0; + + try{ + if(( iRc = xbIxMdx::KeySetPosAdd( npTag, ulRecNo )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + + xbInt32 lKeyCnt = GetKeyCount( npTag->npCurNode ); + + // std::cout << "xbIxMdx::AddKeys() lKeyCnt = " << lKeyCnt << " KeysPerBlock = " << npTag->iKeysPerBlock << " npBlockNo = " << npTag->npCurNode->ulBlockNo << "\n"; + + if( lKeyCnt < npTag->iKeysPerBlock ){ + // Section A - add key to appropriate position if space available + // std::cout << "AddKey Section A begin\n"; + if(( iRc = InsertNodeL( npTag, npTag->npCurNode, npTag->npCurNode->iCurKeyNo, npTag->cpKeyBuf, ulRecNo )) != XB_NO_ERROR ){ + iErrorStop = 110; + throw iRc; + } + //std::cout << "AddKey Section A end\n"; + } else { + + // land here with a full leaf node + //std::cout << "Section B begin split leaf node\n"; + + iHeadNodeUpdateOpt = 1; + npRightNode = AllocateIxNode( npTag, GetBlockSize() + (xbUInt32) npTag->iKeyItemLen, npTag->npCurNode->ulBlockNo * (xbUInt32) iBlockFactor ); + if( !npRightNode ){ + iErrorStop = 120; + iRc = XB_NO_MEMORY; + throw iRc; + } + + if(( npTag->npCurNode->ulBlockNo * (xbUInt32) iBlockFactor ) == npTag->ulRightChild ){ + ulNewRightChild = npRightNode->ulBlockNo * (xbUInt32) iBlockFactor; + } + + //std::cout << "ulNewRightChild = " << ulNewRightChild << "\n"; + + if(( iRc = xbIxMdx::SplitNodeL( npTag, npTag->npCurNode, npRightNode, npTag->npCurNode->iCurKeyNo, npTag->cpKeyBuf, ulRecNo )) != XB_NO_ERROR ){ + iErrorStop = 130; + throw iRc; + } + xbUInt32 ulTempBlockNo = npRightNode->ulBlockNo; + + + //std::cout << "ulTempBlockNo = " << ulTempBlockNo << "\n"; + //std::cout << "key count left block " << GetKeyCount( npTag->npCurNode ) << "\n"; + //std::cout << "key count right block " << GetKeyCount( npRightNode ) << "\n"; + + + //std::cout << "Section B end\n"; + + // section C - go up the tree, splitting nodes as necessary + xbIxNode * npParent = npTag->npCurNode->npPrev; + + + while( npParent && GetKeyCount( npParent ) >= npTag->iKeysPerBlock ){ + //std::cout << "Section C begin interior node is full\n"; + npRightNode = FreeNodeChain( npRightNode ); + npRightNode = AllocateIxNode( npTag, GetBlockSize() + (xbUInt32) npTag->iKeyItemLen, npParent->ulBlockNo * (xbUInt32) iBlockFactor ); + //std::cout << "Section C - B new right node block number for interior node split= " << npRightNode->ulBlockNo << "\n"; + + if( !npRightNode ){ + iErrorStop = 140; + iRc = XB_NO_MEMORY; + throw iRc; + } + //std::cout << "Section C - going to split interior node C\n"; + + if(( iRc = SplitNodeI( npTag, npParent, npRightNode, npParent->iCurKeyNo, BlockToPage( ulTempBlockNo ))) != XB_NO_ERROR ){ + iErrorStop = 150; + throw iRc; + } + // std::cout << "Section C - interior node split \n"; + ulTempBlockNo = npRightNode->ulBlockNo; + npTag->npCurNode = npParent; + npParent = npParent->npPrev; + } + + // std::cout << "Past Section C Cur Node Block No = " << npTag->npCurNode->ulBlockNo << " root page = " << npTag->ulRootPage << "\n"; + // section D - if cur node is split root, create new root + + if(( npTag->npCurNode->ulBlockNo * (xbUInt32) iBlockFactor ) == npTag->ulRootPage ){ + // std::cout << "Section D begin\n"; + + if(( iRc = AddKeyNewRoot( npTag, npTag->npCurNode, npRightNode )) != XB_NO_ERROR ){ + iErrorStop = 160; + throw iRc; + } + + if( npRightNode ) + npRightNode = FreeNodeChain( npRightNode ); + + //std::cout << "Section D end\n"; + + } else { + // std::cout << "Section E, put key in parent\n"; + if(( iRc = InsertNodeI( (void *) vpTag, (xbIxNode *) npParent, (xbInt16) npParent->iCurKeyNo, BlockToPage( npRightNode->ulBlockNo ))) != XB_NO_ERROR ){ + iErrorStop = 170; + throw iRc; + } + } + } + + // update the header + if(( iRc = WriteHeadBlock( iHeadNodeUpdateOpt )) != XB_NO_ERROR ){ + iErrorStop = 180; + throw iRc; + } + + // if adding the first key, set the cHasKeys field + if( !npTag->cHasKeys ){ + npTag->cHasKeys = 0x01; + if(( iRc = xbFseek( ((npTag->ulTagHdrPageNo * 512) + 246), SEEK_SET )) != XB_NO_ERROR ){ + iErrorStop = 190; + throw iRc; + } + if(( iRc = xbFwrite( &npTag->cHasKeys, 1, 1 )) != XB_NO_ERROR ){ + iErrorStop = 200; + throw iRc; + } + } + + if( ulNewRightChild > 0 ){ + +// std::cout << "ulRightChild was = " << npTag->ulRightChild << " changed to " << ulNewRightChild << "\n"; + + char cBuf[4]; + ePutUInt32( cBuf, ulNewRightChild ); + if(( iRc = xbFseek( ((npTag->ulTagHdrPageNo * 512) + 252), SEEK_SET )) != XB_NO_ERROR ){ + iErrorStop = 210; + throw iRc; + } + if(( iRc = xbFwrite( &cBuf, 4, 1 )) != XB_NO_ERROR ){ + iErrorStop = 220; + throw iRc; + } +// std::cout << "setting right child\n"; + npTag->ulRightChild = ulNewRightChild; + } + + if( npRightNode ) + npRightNode = FreeNodeChain( npRightNode ); + npTag->npNodeChain = FreeNodeChain( npTag->npNodeChain ); + npTag->npCurNode = NULL; + + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbIxMdx::AddKey() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return iRc; +} + +/***********************************************************************/ +void xbIxMdx::AppendNodeChain( void *vpTag, xbIxNode * npNode ){ + xbMdxTag * mdxTag = (xbMdxTag *) vpTag; + if( mdxTag->npNodeChain == NULL ){ + mdxTag->npNodeChain = npNode; + mdxTag->npCurNode = npNode; + } else { + npNode->npPrev = mdxTag->npCurNode; + mdxTag->npCurNode->npNext = npNode; + mdxTag->npCurNode = npNode; + } + // time stamp the node chain + GetFileMtime( mdxTag->tNodeChainTs ); +} + +/***********************************************************************/ +//! @brief Add new root node. +/*! + \param mpTag Tag to update. + \param npLeft Left node. + \param npRight Right node. + \returns Return Codes +*/ +xbInt16 xbIxMdx::AddKeyNewRoot( xbMdxTag *npTag, xbIxNode *npLeft, xbIxNode *npRight ){ + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + char *pLastKey = NULL; + + try{ + xbIxNode *npRoot = AllocateIxNode( npTag, GetBlockSize() + (xbUInt32) npTag->iKeyItemLen, npRight->ulBlockNo * (xbUInt32) iBlockFactor ); + if( !npRoot ){ + iErrorStop = 100; + iRc = XB_NO_MEMORY; + throw iRc; + } + npTag->ulRootPage = npRoot->ulBlockNo; + + // std::cout << "AddKeyNewRoot - RootBlock = " << npRoot->ulBlockNo << "\n"; + // std::cout << "AddKeyNewRoot - LeftBlock = " << npLeft->ulBlockNo << "\n"; + // std::cout << "AddKeyNewRoot - RightBlock = " << npRight->ulBlockNo << "\n"; + + pLastKey = (char *) malloc( (size_t) npTag->iKeyLen ); + + if(( iRc = GetLastKeyForBlockNo( npTag, npLeft->ulBlockNo, pLastKey )) != XB_NO_ERROR ){ + iErrorStop = 110; + throw iRc; + } + char * pTrg = npRoot->cpBlockData; + + // set no of keys to 1 + ePutUInt32( pTrg, 1 ); + + // set the left node number + pTrg += 8; + ePutUInt32( pTrg, npLeft->ulBlockNo * (xbUInt32) iBlockFactor ); + + // set the key + pTrg+= 4; + 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
XB_NO_ERROR +*/ + +xbInt16 xbIxMdx::CheckForDupKey( void *vpTag ) +{ + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + xbMdxTag *mpTag = (xbMdxTag *) vpTag; + mpTag->bFoundSts = xbFalse; + try{ + if( GetUnique( mpTag )){ + if( mpTag->iKeySts == XB_ADD_KEY || mpTag->iKeySts == XB_UPD_KEY ) + if( KeyExists( mpTag )){ + if( GetUniqueKeyOpt() == XB_EMULATE_DBASE ){ + mpTag->bFoundSts = xbTrue; + return 0; + } else { + return XB_KEY_NOT_UNIQUE; + } + } + } + return 0; + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbIxMdx::CheckForDupKey() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return iRc; +} +/***********************************************************************/ +//! @brief Check tag integrity. +/*! + Check a tag for accuracy. + + \param vpTag Tag to create key for. + \param iOpt Output message destination
+ 0 = stdout
+ 1 = Syslog
+ 2 = Both
+ \returns Return Codes +*/ +xbInt16 xbIxMdx::CheckTagIntegrity( void *vpTag, xbInt16 iOpt ){ + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iRc2; + xbInt16 iRc3; + xbInt16 iErrorStop = 0; + xbUInt32 ulIxCnt = 0; + xbUInt32 ulThisRecNo = 0; + xbUInt32 ulPrevRecNo = 0; + xbBool bDone = false; + xbString sMsg; + char cKeyType; + char *pPrevKeyBuf = NULL; + xbMdxTag *npTag = (xbMdxTag *) vpTag; + 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
XB_NO_ERROR + + iKeySts 0 - No Updates + 1 - Add Key XB_ADD_KEY + 2 - Update Key XB_UPD_KEY + 3 - Delete Key XB_DEL_KEY + + bKeyFiltered xbFalse - Key filtered out + xbTrue - Key filtered in + + cpKeyBuf - Key buffer for add + cpKeyBuf2 - Key buffer for delete + +*/ + +xbInt16 xbIxMdx::CreateKey( void *vpTag, xbInt16 iOpt ){ + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + + xbBool bFilter0 = xbFalse; // filter against RecBuf, updated record buffer + xbBool bFilter1 = xbFalse; // filter against recBuf2, original record buffer + + try{ + + xbMdxTag *npTag = (xbMdxTag *) vpTag; + npTag->iKeySts = 0; + + // char *p0 = dbf->GetRecordBuf(0); + // char *p1 = dbf->GetRecordBuf(1); + + // do tag filter logic + if( npTag->cHasFilter ){ + if(( iRc = npTag->filter->ProcessExpression( 0 )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + if(( iRc = npTag->filter->GetBoolResult( bFilter0 )) != XB_NO_ERROR ){ + iErrorStop = 110; + throw iRc; + } + // std::cout << "cp1 f0 = " << bFilter0 << "\n"; + // printf( "del byte 0 [%x] 1 [%x]\n", *p0, *p1 ); + } else { + bFilter0 = xbTrue; + } + + // if add request and filtered out, we're done + if( iOpt == 1 && !bFilter0 ) + return XB_NO_ERROR; + + if(( iRc = npTag->exp->ProcessExpression( 0 )) != XB_NO_ERROR ){ + iErrorStop = 120; + throw iRc; + } + if( npTag->exp->GetReturnType() == XB_EXP_CHAR ) + npTag->exp->GetStringResult( npTag->cpKeyBuf, (xbUInt32) npTag->iKeyLen ); + else if( npTag->exp->GetReturnType() == XB_EXP_NUMERIC ){ + xbDouble d; + npTag->exp->GetNumericResult( d ); + xbBcd bcd( d ); + bcd.ToChar( npTag->cpKeyBuf ); + } + else if( npTag->exp->GetReturnType() == XB_EXP_DATE ){ + xbDouble d; + npTag->exp->GetNumericResult( d ); + memcpy( npTag->cpKeyBuf, &d, 8 ); + } + + if( iOpt == 1 ) // Append + npTag->iKeySts = XB_ADD_KEY; + + else if( iOpt == 2 ){ // Update + if( npTag->cHasFilter ){ + if(( iRc = npTag->filter->ProcessExpression( 1 )) != XB_NO_ERROR ){ + iErrorStop = 130; + throw iRc; + } + if(( iRc = npTag->filter->GetBoolResult( bFilter1 )) != XB_NO_ERROR ){ + iErrorStop = 140; + throw iRc; + } + } else { + bFilter1 = xbTrue; + } + + if(( iRc = npTag->exp->ProcessExpression( 1 )) != XB_NO_ERROR ){ + iErrorStop = 150; + throw iRc; + } + + if( npTag->exp->GetReturnType() == XB_EXP_CHAR ){ + npTag->exp->GetStringResult( npTag->cpKeyBuf2, (xbUInt32) npTag->iKeyLen ); + + } else if( npTag->exp->GetReturnType() == XB_EXP_NUMERIC ){ + xbDouble d; + npTag->exp->GetNumericResult( d ); + xbBcd bcd( d ); + bcd.ToChar( npTag->cpKeyBuf2 ); + + } else if( npTag->exp->GetReturnType() == XB_EXP_DATE ){ + xbDouble d; + npTag->exp->GetNumericResult( d ); + memcpy( npTag->cpKeyBuf2, &d, 8 ); + + } + + if( bFilter1 ){ // original key was indexed + if( bFilter0 ){ // new key s/b indexed, update it if changed + if( memcmp( npTag->cpKeyBuf, npTag->cpKeyBuf2, (size_t) npTag->iKeyLen )){ + npTag->iKeySts = XB_UPD_KEY; + } + } else { // original key indexed, new key not indexed, delete it + npTag->iKeySts = XB_DEL_KEY; + } + } else { // original key not indexed + if( bFilter0 ) + npTag->iKeySts = XB_ADD_KEY; + } + } + +// std::cout << "xbIxMdx::CreateKey key sts = " << npTag->iKeySts << " iOpt = " << iOpt << " type = " << npTag->exp->GetReturnType() << " name = " << npTag->cTagName; +// std::cout << " f0 = " << bFilter0 << " f1 = " << bFilter1 << "\n"; + + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbIxMdx::CreateKey() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + + return iRc; +} + +/***********************************************************************/ +//! @brief Create new tag. +/*! + This routine creates a new tag. When complete, sets the cur tag pointer to + the newly created tag. + + \param sName Tag Name, including .MDX suffix + \param sKey Key Expression + \param sFilter Filter expression. + \param iDescending + \param iUnique xbtrue - Unique.
xbFalse - Not unique. + \param iOverLay xbTrue - Overlay if file already exists.
xbFalse - Don't overlay. + \param vpTag Output from method Pointer to vptag pointer. + \returns Return Codes +*/ + + +xbInt16 xbIxMdx::CreateTag( const xbString &sName, const xbString &sKey, const xbString &sFilter, xbInt16 iDescending, xbInt16 iUnique, xbInt16 iOverlay, void **vpTag ){ + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + xbMdxTag *tte = NULL; + + + // std::cout << "CreateTag() name=[" << sName.Str() << "] key=[" << sKey.Str() << "] sFilter=[" << sFilter.Str() << "]\n"; + // std::cout << "TagUseCnt = " << iTagUseCnt << std::endl; + + + try{ + // verify room for new tag + if( !( iTagUseCnt < 47 )){ + iErrorStop = 100; + iRc = XB_LIMIT_REACHED; + throw iRc; + } + + // verify valid tag name + xbString sWorker = sName; + sWorker.Trim(); + if( sWorker.Len() > 10 ){ + iErrorStop = 110; + iRc = XB_INVALID_TAG; + throw iRc; + } + + // verify tag not already defined + if( iTagUseCnt > 0 ){ + if( GetTag( sWorker )){ + iErrorStop = 120; + iRc = XB_INVALID_TAG; + throw iRc; + } + } + + // allocate a tag structure here + if(( tte = (xbMdxTag *) calloc( 1, (size_t) sizeof( xbMdxTag ))) == NULL ){ + iErrorStop = 130; + iRc = XB_NO_MEMORY; + throw iRc; + } + *vpTag = tte; + tte->sTagName = new xbString( sWorker ); + + //set up the key expression + sWorker = sFilter; + sWorker.Trim(); + if( sWorker.Len() > 0 ){ + if( sWorker.Len() == 0 || sWorker.Len() > 220 ){ + iRc = XB_INVALID_TAG; + iErrorStop = 140; + throw iRc; + } + tte->sFiltExp = new xbString( sWorker ); + tte->filter = new xbExp( dbf->GetXbasePtr()); + if(( iRc = tte->filter->ParseExpression( dbf, sWorker )) != XB_NO_ERROR ){ + iErrorStop = 150; + throw iRc; + } + + // tte->filter->DumpTree( 1 ); + + if((tte->filter->GetReturnType()) != 'L' ){ + iRc = XB_INVALID_TAG; + iErrorStop = 160; + throw iRc; + } + tte->cHasFilter = 0x01; + } + + //set up the key expression + sWorker = sKey; + sWorker.Trim(); + if( sWorker.Len() == 0 || sWorker.Len() > 100 ){ + iRc = XB_INVALID_TAG; + iErrorStop = 170; + throw iRc; + } + tte->sKeyExp = new xbString( sWorker ); + tte->exp = new xbExp( dbf->GetXbasePtr()); + if(( iRc = tte->exp->ParseExpression( dbf, sWorker )) != XB_NO_ERROR ){ + iErrorStop = 180; + throw iRc; + } + + xbDate d; + + if( iTagUseCnt == 0 ){ + // first tag, new mdx file + // create the file name + xbString sIxFileName = dbf->GetFqFileName(); + sIxFileName.Trim(); + xbUInt32 lLen = sIxFileName.Len(); + sIxFileName.PutAt( lLen-2, 'M' ); + sIxFileName.PutAt( lLen-1, 'D' ); + sIxFileName.PutAt( lLen, 'X' ); + + // copy the file name to the class variable + this->SetFileName( sIxFileName ); + if( FileExists() && !iOverlay ){ + iErrorStop = 190; + iRc = XB_FILE_EXISTS; + throw iRc; + } + + // first tag, need to create the file + if(( iRc = xbFopen( "w+b", dbf->GetShareMode())) != XB_NO_ERROR ){ + iErrorStop = 200; + throw iRc; + } + cVersion = 2; + cCreateYY = (char) d.YearOf() - 1900; + cCreateMM = (char) d.MonthOf(); + cCreateDD = (char) d.DayOf( XB_FMT_MONTH ); + + GetFileNamePart( sFileName ); + sFileName.ToUpperCase(); + + SetBlockSize( (xbUInt32) dbf->GetCreateMdxBlockSize()); + iBlockFactor = GetBlockSize() / 512; + + cProdIxFlag = 1; + cTagEntryCnt = 48; + iTagLen = 32; + ulPageCnt = 4; + ulFirstFreePage = 0; + ulNoOfBlockAvail = 0; + cNextTag = 1; + c1B = 0x1B; + cUpdateYY = cCreateYY; + cUpdateMM = cCreateMM; + cUpdateDD = cCreateDD; + + if(( iRc = WriteHeadBlock( 0 )) != XB_NO_ERROR ){ + iErrorStop = 210; + throw iRc; + } + } + + // populate the tag table entry structure + tte->ulTagHdrPageNo = ulPageCnt; + ulPageCnt += (xbUInt32) iBlockFactor; + tte->sTagName->strncpy( tte->cTagName, 10 ); + + // cKeyFmt is always 0x10; + // tested 2+ZIPCD CITY+STATE or just standalone field - always 0x10 + tte->cKeyFmt = 0x10; // = CalcTagKeyFmt( *tte->exp ); + + switch( tte->exp->GetReturnType()){ + case XB_EXP_CHAR: + tte->cKeyType = 'C'; + tte->iKeyLen = tte->exp->GetResultLen(); + tte->iSecKeyType = 0; + break; + + case XB_EXP_NUMERIC: + tte->cKeyType = 'N'; + tte->iKeyLen = 12; + tte->iSecKeyType = 0; + break; + + case XB_EXP_DATE: + tte->cKeyType = 'D'; + tte->iKeyLen = 8; + tte->iSecKeyType = 1; + break; + + default: + iErrorStop = 200; + iRc = XB_INVALID_INDEX; + throw iRc; + } + + tte->cpKeyBuf = (char *) malloc( (size_t) tte->iKeyLen + 1 ); + tte->cpKeyBuf2 = (char *) malloc( (size_t) tte->iKeyLen + 1 ); + +// if( 0 ){ +// printf( "ulTagHdrPageNo=[%d] cTagName=[%-11s], cLeftChild=[%d] cRightChild=[%d] cParent=[%d] cKeyType=[%c]\n\n", +// tte->ulTagHdrPageNo, tte->cTagName, tte->cLeftChild, tte->cRightChild, tte->cParent, tte->cKeyType ); +// } + + // write the new tte entry here + char tteBuf[21]; + memset( tteBuf, 0x00, 21 ); + + ePutUInt32( &tteBuf[0], tte->ulTagHdrPageNo ); + for( xbUInt32 l = 0; l < tte->sTagName->Len() && l < 10; l++ ){ + tteBuf[l+4] = tte->sTagName->GetCharacter(l+1); + } + tteBuf[15] = tte->cKeyFmt; + tteBuf[19] = 0x02; // appears to always be a 0x02 + tteBuf[20] = tte->cKeyType; + + if(( iRc = xbFseek( (iTagUseCnt * 32) + 544, SEEK_SET )) != XB_NO_ERROR ){ + iErrorStop = 210; + throw iRc; + } + if(( iRc = xbFwrite( tteBuf, 21, 1 )) != XB_NO_ERROR ){ + iErrorStop = 220; + throw iRc; + } + + + // Begin Tag Header + tte->ulRootPage = ulPageCnt; + tte->ulTagSize = (xbUInt32) iBlockFactor; + ulPageCnt += 2; + tte->cKeyFmt2 = 0x10; + if( iDescending ) + tte->cKeyFmt2 += 0x08; + if( iUnique ){ + tte->cKeyFmt2 += 0x40; + tte->cUnique = 0x01; + } + + tte->cTag11 = 0x1B; // always 0x1b ? + tte->cSerialNo = 0x01; // version incremented with each tag update + tte->ulLeftChild = tte->ulRootPage; + tte->ulRightChild = tte->ulRootPage; + + tte->cTagYY = (char) d.YearOf() - 1900; + tte->cTagMM = (char) d.MonthOf(); + tte->cTagDD = (char) d.DayOf( XB_FMT_MONTH ); + + tte->cKeyType2 = tte->cKeyType; + tte->iKeyItemLen = tte->iKeyLen + 4; + + 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 Return Codes +*/ +xbInt16 xbIxMdx::DeleteFromNode( void *vpTag, xbIxNode * npNode, xbInt16 iSlotNo ) +{ + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + xbMdxTag * npTag = (xbMdxTag *) vpTag; + + try{ + xbInt32 lKeyCnt = GetKeyCount( npNode ); + xbInt16 iLen = (lKeyCnt - iSlotNo - 1) * npTag->iKeyItemLen; + xbBool bLeaf = IsLeaf( vpTag, npNode ); + if( !bLeaf ) + iLen += 4; + + char *pTrg = npNode->cpBlockData; + if( iLen > 0 ){ + pTrg += (8 + (npTag->iKeyItemLen * (iSlotNo)) ); //lTrgPos; + // std::cout << "TrgSpot = " << (8 + (npTag->iKeyItemLen * (iSlotNo)) ) << "\n"; + char *pSrc = pTrg; + pSrc += npTag->iKeyItemLen; + memmove( pTrg, pSrc, (size_t) iLen ); + } + + // zap out the right most key + pTrg = npNode->cpBlockData; + if( bLeaf ){ + pTrg += (8 + (npTag->iKeyItemLen * ( lKeyCnt-1 ))); + + } else { + pTrg += (12 + (npTag->iKeyItemLen * ( lKeyCnt-1 ))); + + } + + for( xbInt16 i = 0; i < npTag->iKeyItemLen; i++ ) + *pTrg++ = 0x00; + + // set the new number of keys + ePutUInt32( npNode->cpBlockData, (xbUInt32) lKeyCnt - 1 ); + + // if node empty, add it to the free node chain + if( lKeyCnt < 2 ){ + if( bReuseEmptyNodes ){ + if( bLeaf && lKeyCnt == 1 ){ + if(( iRc = HarvestEmptyNode( npTag, npNode, 0 )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + } + } + } + + // write out the block + if(( iRc = WriteBlock( npNode->ulBlockNo, GetBlockSize(), npNode->cpBlockData )) != XB_NO_ERROR ){ + iErrorStop = 110; + throw iRc; + } + + // do any empty node processing here +// if( bReuseEmptyNodes ){ +// if( bLeaf && lKeyCnt == 1 ){ +// std::cout << "Empty node ready for reuse\n"; +// } +// } + + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbIxMdx::DeleteFromNode() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return XB_NO_ERROR; +} + +/***********************************************************************/ +//! @brief Delete a key. +/*! + This routine deletes a key. It assumes the key to delete + is the current key in the node chain. + + \param vpTag Tag to delete key on. + + \returns Return Codes +*/ + +xbInt16 xbIxMdx::DeleteKey( void *vpTag ){ + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + xbMdxTag * npTag = (xbMdxTag *) vpTag; + + // save copy of node chain to reset to after delete completed + xbIxNode *npSaveNodeChain = npTag->npNodeChain; + npTag->npNodeChain = NULL; + xbIxNode * npSaveCurNode = npTag->npCurNode; + + // std::cout << "xbIxMdx::DeleteKey()\n"; + + try{ + xbString sMsg; + + if(( iRc = xbIxMdx::KeySetPosDel( npTag )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + // Delete key needs to handle two scenarios + // 1 - if delete is on the only key of leaf, + // traverse up tree, trim as needed + // 2 - if last key on node is deleted, and key value is not the same + // as prev key, ascend tree looking for an interior node needing + // updated key value + + + xbInt32 lOrigKeyCnt = GetKeyCount( npTag->npCurNode ); + if(( iRc = DeleteFromNode( npTag, npTag->npCurNode, npTag->npCurNode->iCurKeyNo )) != XB_NO_ERROR ){ + iErrorStop = 110; + throw iRc; + } + + if( lOrigKeyCnt == 1 ){ + + // scenario 1 + xbBool bDone = xbFalse; + xbBool bIsLeaf = xbFalse; + xbInt32 lKeyCnt; + npTag->npCurNode = npTag->npCurNode->npPrev; + + while( npTag->npCurNode && !bDone ){ + lKeyCnt = GetKeyCount( npTag->npCurNode ); + bIsLeaf = IsLeaf( npTag, npTag->npCurNode ); + if( lKeyCnt > 0 ){ + if(( iRc = DeleteFromNode( npTag, npTag->npCurNode, npTag->npCurNode->iCurKeyNo )) != XB_NO_ERROR ){ + iErrorStop = 120; + throw iRc; + } + } else if(( iRc = HarvestEmptyNode( npTag, npTag->npCurNode, 1 )) != XB_NO_ERROR ){ + iErrorStop = 130; + throw iRc; + } + + if( (bIsLeaf && lKeyCnt > 1) || (!bIsLeaf && lKeyCnt > 0) ) + bDone = xbTrue; + else + npTag->npCurNode = npTag->npCurNode->npPrev; + } + + } else if( npTag->npCurNode->iCurKeyNo == (xbUInt32) lOrigKeyCnt - 1 ){ + + // scenario 2 + // if last two keys identical, then nothing to do, else go up looking for a key to change + if( memcmp( GetKeyData( npTag->npCurNode, npTag->npCurNode->iCurKeyNo, npTag->iKeyItemLen ), + GetKeyData( npTag->npCurNode, npTag->npCurNode->iCurKeyNo-1, npTag->iKeyItemLen ), + (size_t) npTag->iKeyLen )){ + + xbIxNode *pNode = npTag->npCurNode->npPrev; + char *pSrc = GetKeyData( npTag->npCurNode, npTag->npCurNode->iCurKeyNo-1, npTag->iKeyItemLen ); + + while( pNode && pNode->ulBlockNo != npTag->ulRootPage && pNode->iCurKeyNo == (xbUInt32) GetKeyCount( pNode ) ) + pNode = pNode->npPrev; + + if( pNode ){ + if( pNode->iCurKeyNo < (xbUInt32) GetKeyCount( pNode )){ + char *pTrg = pNode->cpBlockData; + pTrg += 12 + (pNode->iCurKeyNo * (xbUInt32) npTag->iKeyItemLen); + for( xbInt16 i = 0; i < npTag->iKeyLen; i++ ) + *pTrg++ = *pSrc++; + // write out the block + if(( iRc = WriteBlock( pNode->ulBlockNo, GetBlockSize(), pNode->cpBlockData )) != XB_NO_ERROR ){ + iErrorStop = 140; + throw iRc; + } + } + } + } + } + // restore node chain to pre delete status (which should be post add status) + npTag->npNodeChain = FreeNodeChain( npTag->npNodeChain ); + npTag->npNodeChain = npSaveNodeChain; + npTag->npCurNode = npSaveCurNode; + + // update the serial number + if(( iRc = TagSerialNo( 3, npTag )) != XB_NO_ERROR ){ + iErrorStop = 150; + throw iRc; + } + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbIxMdx::DeleteKey() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + if( npSaveNodeChain ){ + npTag->npNodeChain = npSaveNodeChain; + npSaveNodeChain = FreeNodeChain( npSaveNodeChain ); + npTag->npCurNode = npSaveCurNode; + } + } + return iRc; +} + +/***********************************************************************/ +//! @brief Dump a given block for a tag +/*! + \param vpTag Input tag ptr for tag to be deleted
+ \returns Return Codes
+ 1 = Deleted entire MDX file, only had one tag + +*/ + +xbInt16 xbIxMdx::DeleteTag( void *vpTag ){ + + xbInt16 iRc = 0; + xbInt16 iErrorStop = 0; + xbMdxTag * mpTag = (xbMdxTag *) vpTag; + xbIxNode *n = NULL; + xbBool bLoneTag = xbFalse; + + try{ + + if( !vpTag ){ + iErrorStop = 100; + iRc = XB_INVALID_TAG; + throw iRc; + } + + char cSaveHasFilter = mpTag->cHasFilter; + char cSaveKeyFmt3 = mpTag->cKeyFmt3; + xbString sSaveKey = mpTag->sKeyExp->Str(); + + if( iTagUseCnt == 1 ){ + // std::cout << "xbIxMdx::DeleteTag - one tag found, delete the mdx file\n"; + + // close the mdx file + if(( iRc = xbIxMdx::Close()) != XB_NO_ERROR ){ + iErrorStop = 110; + throw iRc; + } + + // delete the file + xbRemove(); + + // init variables - needed? + // Init(); + // iRc > 0 defines this as the only tag in an MDX file, MDX file deleted. + // signals to the calling process to drop the MDX file from the + // list of updateable indices. + bLoneTag = xbTrue; + + } else { + + // harvest tag nodes + + if(( iRc = HarvestTagNodes( mpTag, xbTrue )) != XB_NO_ERROR ){ + iErrorStop = 120; + throw iRc; + } + + // remove an entry from tag table + // which tag is this? + xbInt16 iTagNo = 0; + xbMdxTag *mp = mdxTagTbl; + xbMdxTag *mpPrev = NULL; + while( mp && mp->ulTagHdrPageNo != mpTag->ulTagHdrPageNo ){ + iTagNo++; + mpPrev = mp; + mp = mp->next; + } + + // remove it from the linked list of tags + if( !mpPrev ){ + mdxTagTbl = mp->next; + } else { + mpPrev->next = mp->next; + } + if( mp ){ + if( mp->cpKeyBuf ) free( mp->cpKeyBuf ); + if( mp->cpKeyBuf2 ) free( mp->cpKeyBuf2 ); + if( mp->exp ) delete mp->exp; + if( mp->filter ) delete mp->filter; + if( mp->sKeyExp ) delete mp->sKeyExp; + if( mp->sFiltExp ) delete mp->sFiltExp; + if( mp->sTagName ) delete mp->sTagName; + free( mp ); + } + xbInt32 iTarg = iTagNo * 32; + xbInt32 iSrc = iTarg + 32; + xbInt32 iLen = (iTagUseCnt - iTagNo) * 32; + + if(( iRc = xbFseek( 544, SEEK_SET )) != XB_NO_ERROR ){ + iErrorStop = 160; + throw iRc; + } + char Buf[1536]; // 47 tags + 1 in case tag #47 is deleted + memset( Buf, 0x00, 1536 ); + if(( iRc = xbFread( Buf, 1504, 1 )) != XB_NO_ERROR ){ + iErrorStop = 170; + throw iRc; + } + char *pTrg = Buf; + pTrg += iTarg; + char *pSrc = Buf; + pSrc += iSrc; + for( xbInt32 i = 0; i < iLen; i++ ) + *pTrg++ = *pSrc++; + + if(( iRc = xbFseek( 544, SEEK_SET )) != XB_NO_ERROR ){ + iErrorStop = 180; + throw iRc; + } + if(( iRc = xbFwrite( Buf, 1504, 1 )) != XB_NO_ERROR ){ + iErrorStop = 190; + throw iRc; + } + + iTagUseCnt--; + if(( iRc = WriteHeadBlock( 1 )) != XB_NO_ERROR ){ + iErrorStop = 200; + throw iRc; + } + + /* update the btree pointers */ + CalcBtreePointers(); + char bBuf[3]; + xbMdxTag *tteWork = mdxTagTbl; + + if(( iRc = xbFseek( 560, SEEK_SET )) != XB_NO_ERROR ){ + iErrorStop = 210; + throw iRc; + } + while( tteWork ){ + bBuf[0] = tteWork->cLeftChild; + bBuf[1] = tteWork->cRightChild; + bBuf[2] = tteWork->cParent; + + if(( iRc = xbFwrite( bBuf, 3, 1 )) != XB_NO_ERROR ){ + iErrorStop = 320; + throw iRc; + } + if( tteWork->next ){ + if(( iRc = xbFseek( 29, SEEK_CUR )) != XB_NO_ERROR ){ + iErrorStop = 330; + throw iRc; + } + } + tteWork = tteWork->next; + } + } + + // update the dbf file if needed, if discreet field with no filter + // printf( "cSaveKeyFmt3 = [%x] cSaveHasFilter=[%x] SaveKey = [%s]\n", cSaveKeyFmt3, cSaveHasFilter, sSaveKey.Str()); + + if( cSaveKeyFmt3 == 0x01 && !cSaveHasFilter ){ + xbInt16 iFldNo; + if(( iRc = dbf->GetFieldNo( sSaveKey, iFldNo )) != XB_NO_ERROR ){ + iErrorStop = 340; + throw iRc; + } + xbInt64 lOffset = 31 + ((iFldNo + 1) * 32 ); + + if(( iRc = dbf->xbFseek( lOffset, SEEK_SET )) != XB_NO_ERROR ){ + iErrorStop = 350; + iRc = XB_SEEK_ERROR; + throw iRc; + } + char cBuf[2]; + cBuf[0] = 0x00; + cBuf[1] = 0x00; + if(( iRc = dbf->xbFwrite( cBuf, 1, 1 )) != XB_NO_ERROR ){ + iErrorStop = 360; + throw iRc; + } + dbf->UpdateSchemaIxFlag( iFldNo, 0x00 ); + } + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbIxMdx::DeleteTag() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + if( n ) + free( n ); + } + if( bLoneTag && !iRc ) + return 1; + else + return iRc; +} + +/***********************************************************************/ +#ifdef XB_DEBUG_SUPPORT + + +//! @brief Dump a given block for a tag +/*! + \param iOpt Output message destination
+ 0 = stdout
+ 1 = Syslog
+ 2 = Both
+ \param ulBlockNo Block number to dump + \param mpTag Index tag pointer + \returns Return Codes + +*/ + +xbInt16 xbIxMdx::DumpBlock( xbInt16 iOpt, xbUInt32 ulBlockNo, xbMdxTag *mpTag ){ + + xbInt16 iRc = 0; + xbInt16 iErrorStop = 0; + xbString s, s2; + xbBool bLeaf; + char *p; + + try{ + if(( iRc = GetBlock( mpTag, ulBlockNo, 0 )) != XB_NO_ERROR ){ + iErrorStop = 10; + throw iRc; + } + p = cNodeBuf; + xbInt32 lNoOfKeys = eGetInt32( p ); + p+=4; + xbUInt32 ulNode2 = eGetUInt32( p ); + + if( !mpTag ){ + // if no tag info, print what is available without tag info and exit + s.Sprintf( "--- BlkNo = %ld Page = %ld NoOfKeys = %ld Node2 (opt NextFreePage) = %ld", ulBlockNo, BlockToPage( ulBlockNo ), lNoOfKeys, ulNode2 ); + xbase->WriteLogMessage( s, iOpt ); + return XB_NO_ERROR; + } + + p+=4; + p+= mpTag->iKeyItemLen * lNoOfKeys; + if( eGetUInt32( p ) == 0 ){ + bLeaf = xbTrue; + // std::cout << "No of keys = " << lNoOfKeys << "\n"; + s.Sprintf( "--- Leaf Node KeyCnt %d\t Page %d\t Block %d", lNoOfKeys, BlockToPage( ulBlockNo ), ulBlockNo ); + } else { + bLeaf = xbFalse; + s.Sprintf( "--- Interior Node KeyCnt %d\t Page %d\t Block %d", lNoOfKeys, BlockToPage( ulBlockNo ), ulBlockNo ); + } + if( ulNode2 > 0 ) + s.Sprintf( "%s Node2 (opt NextFreePage) = %d", s.Str(), ulNode2 ); + + xbase->WriteLogMessage( s, iOpt ); + + xbInt32 l; + for( l = 0; l < lNoOfKeys; l++ ){ + p = cNodeBuf + (8 + (l * mpTag->iKeyItemLen )); + s.Sprintf( "%08ld\t", eGetUInt32( p )); + p+=4; + if( mpTag->cKeyType2 == 'C' ){ //CHAR + for( xbInt32 l = 0; l < (mpTag->iKeyItemLen-4); l++ ) + s += *p++; + s.Trim(); + } else if( mpTag->cKeyType2 == 'N' ){ // NUMERIC + xbBcd bcd( p ); + bcd.ToString( s2 ); + s += s2; + } else if( mpTag->cKeyType2 == 'D' ){ // DATE + xbInt32 lDate = (xbInt32) eGetDouble( p ); + xbDate d( lDate ); + s.Sprintf( "%s\t%ld\t(%s)", s.Str(), lDate, d.Str()); + } else { + s.Sprintf( "Unknown key type [%c]", mpTag->cKeyType2 ); + } + xbase->WriteLogMessage( s, iOpt ); + } + if( !bLeaf ){ + // interior node has one extra key at the right most position + p = cNodeBuf + (8 + (l * mpTag->iKeyItemLen )); + s.Sprintf( "\t%08ld", eGetUInt32( p )); + xbase->WriteLogMessage( s, iOpt ); + } + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbIxMdx::DumpBlock() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return iRc; +} + +/**************************************************************************************************************/ +//! @brief Dump free blocks. +/*! + Dump free blocks for index debugging purposes. + + \param iOpt Output message destination
+ 0 = stdout
+ 1 = Syslog
+ 2 = Both
+ \returns Return Codes + +*/ +xbInt16 xbIxMdx::DumpFreeBlocks( xbInt16 iOpt ){ + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + xbString s; + char *pBuf = NULL; + char *pNextPage; + xbUInt32 ulNextPage; + + try{ + + if(( iRc = ReadHeadBlock( 1 )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + + xbUInt32 ulLastBlock = PageToBlock( ulPageCnt ); + + pBuf = (char *) malloc( (size_t) GetBlockSize()); + if( !pBuf ){ + iErrorStop = 110; + iRc = XB_NO_MEMORY; + throw iRc; + } + + if( ulFirstFreePage > 0 ){ + xbUInt32 ulThisFreePage = ulFirstFreePage; + xbUInt32 ulNextFreePage = 0; + xbUInt32 ulCnt = 0; + xbase->WriteLogMessage( "*** Free Blocks ***", iOpt ); + s.Sprintf( "File Header - FirstFreePage = %ld Block = %ld", ulFirstFreePage, PageToBlock( ulFirstFreePage )); + xbase->WriteLogMessage( s, iOpt ); + while( ulThisFreePage > 0 ){ + if(( iRc = ReadBlock( PageToBlock( ulThisFreePage ), GetBlockSize(), pBuf )) != XB_NO_ERROR ){ + iErrorStop = 120; + throw iRc; + } + pNextPage = pBuf; + pNextPage+=4; + ulNextFreePage = eGetUInt32( pNextPage ); + s.Sprintf( "Free Page# = %ld\t(Block# = %ld)\tNext Free Page = %ld\t(Block = %ld)", ulThisFreePage, PageToBlock( ulThisFreePage ), ulNextFreePage, PageToBlock( ulNextFreePage )); + xbase->WriteLogMessage( s, iOpt ); + ulThisFreePage = ulNextFreePage; + ulCnt++; + } + s.Sprintf( "%ld free blocks (%ld pages)", ulCnt, BlockToPage( ulCnt )); + xbase->WriteLogMessage( s, iOpt ); + xbase->WriteLogMessage( "*** End Of Free Blocks ***", iOpt ); + } + + pNextPage = pBuf; + pNextPage+=4; + + s = "*** Beginning of Block2 Info ***"; + xbase->WriteLogMessage( s, iOpt ); + s = "ulBlock2 info. ulBlock2 is either one of a linked list of free nodes, or the id of the original node that this node was split from."; + xbase->WriteLogMessage( s, iOpt ); + s = "Stored in physical file as pages, processed in blocks"; + xbase->WriteLogMessage( s, iOpt ); + + xbUInt32 ulFirstBlock = 3; + + for( xbUInt32 ul = ulFirstBlock; ul < ulLastBlock; ul++ ){ + if(( iRc = ReadBlock( ul, GetBlockSize(), pBuf )) != XB_NO_ERROR ){ + iErrorStop = 120; + throw iRc; + } + ulNextPage = eGetUInt32( pNextPage ); + if( ulNextPage > 0 ){ + s.Sprintf( " Block# = %ld\tPage# = %ld\tulBlock2 = %ld\tulBlock2(Page) = %ld", ul, BlockToPage( ul ), PageToBlock( ulNextPage ), ulNextPage ); + xbase->WriteLogMessage( s, iOpt ); + } + } + s = "*** End of Block2 Info ***"; + xbase->WriteLogMessage( s, iOpt ); + + if( pBuf ) free( pBuf ); + } + + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbIxMdx::DumpFreeBlocks() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + if( pBuf ) free( pBuf ); + } + return iRc; +} + +/**************************************************************************************************************/ + +//! @brief Dump interior and leaf blocks for a given tag. +/*! + Dump blocks for given tag for index debugging purposes. + + A page is 512 bytes
+ A block is one or more pages
+ The default mdx block size is 2 pages, or 1024 bytes
+ The first four pages or header pages
+ + \param iOpt Output message destination
+ 0 = stdout
+ 1 = Syslog
+ 2 = Both
+ \param vpTag Index tag pointer, defaults to all tags if null. + \returns Return Codes + +*/ + +xbInt16 xbIxMdx::DumpTagBlocks( xbInt16 iOpt, void * vpTag ){ + + xbInt16 iRc = 0; + xbInt16 iErrorStop = 0; + xbInt16 iCurTag = 0; + xbString s; + xbInt16 iBlockCtr = 0; + + try{ + + xbMdxTag * mpTag; + if( vpTag == NULL ) + mpTag = (xbMdxTag *) GetTag( iCurTag++ ); + else + mpTag = (xbMdxTag *) vpTag; + + if( mpTag == NULL ){ + iErrorStop = 100; + iRc = XB_INVALID_TAG; + throw iRc; + } + + xbIxNode *n; + xbString s; + xbString s2; + xbBool bDone = xbFalse; + + xbUInt32 ulBlkNo; + xbLinkListOrd ll; + xbLinkListNode * llN; + + ll.SetDupKeys( xbFalse ); + + s.Sprintf( "%s Root Page %ld (Block %ld)", mpTag->cTagName, mpTag->ulRootPage, PageToBlock( mpTag->ulRootPage ) ); + xbase->WriteLogMessage( s, iOpt ); + + // for each tag + while( !bDone ){ + + // clear out any history + ll.Clear(); + if( mpTag->npNodeChain ){ + mpTag->npNodeChain = FreeNodeChain( mpTag->npNodeChain ); + mpTag->npCurNode = NULL; + } + + while( GetNextKey( mpTag, 0 ) == 0 ){ + n = mpTag->npNodeChain; + + while(n){ + ll.InsertKey( n->ulBlockNo, (xbUInt32) n->iCurKeyNo ); + n = n->npNext; + } + } + llN = ll.GetHeadNode(); + + while( llN ){ + + ulBlkNo = llN->GetKey(); + xbIxMdx::DumpBlock( iOpt, ulBlkNo, mpTag ); + llN = llN->GetNextNode(); + iBlockCtr++; + } + + if( vpTag || iCurTag >= GetTagCount()) + bDone = xbTrue; + else + mpTag = (xbMdxTag *) GetTag( iCurTag++ ); + } + + s.Sprintf( "\nTotal Blocks: %d", iBlockCtr ); + xbase->WriteLogMessage( s, iOpt ); + + if( mpTag->npNodeChain ){ + mpTag->npNodeChain = FreeNodeChain( mpTag->npNodeChain ); + mpTag->npCurNode = NULL; + } + + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbIxMdx::DumpTagBlocks() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return iRc; +} +/***********************************************************************/ +//! @brief Dump index file header. +/*! + Dump a index file header for debugging purposes. + \param iOpt Output message destination
+ 0 = stdout
+ 1 = Syslog
+ 2 = Both
+ \param iFmtOpt Output Format
+ 0, 1 = Header info only
+ 2 = Tag info
+ 3 = Header && Tag info
+ \returns Return Codes +*/ +xbInt16 xbIxMdx::DumpHeader( xbInt16 iOpt, xbInt16 iFmtOpt ) +{ + + xbInt16 iRc = XB_NO_ERROR; + xbString s; + if(( iRc = ReadHeadBlock( 1 )) != XB_NO_ERROR ) + return iRc; + +// std::cout << "xbIxMdx::DumpHeader options - " << iDestOpt << " fmtopt = " << iFmtOpt << "\n"; + + char c, tfv, cDisplayMask = 1; + cDisplayMask = cDisplayMask << 7; + if( iFmtOpt != 2 && iFmtOpt != 4 ){ + s = "*** MDX Index Header ***"; + xbase->WriteLogMessage( s, iOpt ); + s = "Version = "; + tfv = cVersion; + for( c = 1; c<= 8; c++ ){ + //std::cout << (tfv & cDisplayMask ? '1' : '0'); + s+= (tfv & cDisplayMask ? '1' : '0'); + tfv <<= 1; + } + xbase->WriteLogMessage( s, iOpt ); + + +// std::cout << std::endl; +// std::cout << + s.Sprintf( "Create Date = %d/%d/%d", (int) cCreateMM, (int) cCreateDD, (int) cCreateYY % 100 ); + xbase->WriteLogMessage( s, iOpt ); + + s.Sprintf( "File Name = %s", sFileName.Str() ); + xbase->WriteLogMessage( s, iOpt ); + + s.Sprintf( "Block Factor = %d", iBlockFactor ); + xbase->WriteLogMessage( s, iOpt ); + + s.Sprintf( "Block Size = %d", GetBlockSize() ); + xbase->WriteLogMessage( s, iOpt ); + + s.Sprintf( "Prod Ix Flag = %d", (xbInt16) cProdIxFlag ); + xbase->WriteLogMessage( s, iOpt ); + + s.Sprintf( "Tag Entry Cnt = %d", (xbInt16) cTagEntryCnt ); + xbase->WriteLogMessage( s, iOpt ); + + s.Sprintf( "Tag Len = %d", iTagLen ); + xbase->WriteLogMessage( s, iOpt ); + + s.Sprintf( "Next Tag = %d", (xbInt16) cNextTag ); + xbase->WriteLogMessage( s, iOpt ); + + s.Sprintf( "Tag Use Cnt = %d", iTagUseCnt ); + xbase->WriteLogMessage( s, iOpt ); + + s.Sprintf( "Page Cnt = %d", ulPageCnt ); + xbase->WriteLogMessage( s, iOpt ); + + s.Sprintf( "First Free Page = %d", ulFirstFreePage ); + xbase->WriteLogMessage( s, iOpt ); + + s.Sprintf( "No Of Block Avail = %d\n", ulNoOfBlockAvail ); + xbase->WriteLogMessage( s, iOpt ); + + s.Sprintf( "Last update date = %d/%d/%d", (int) cCreateMM, (int) cCreateDD, (int) cCreateYY % 100 ); + xbase->WriteLogMessage( s, iOpt ); + + if( ulFirstFreePage > 0 ){ + xbString s; + xbUInt32 ulNfp = ulFirstFreePage; // next free page + xbInt16 lc = 0; + while( ulNfp && lc++ < 5 ){ + if( s.Len() > 0 ) + s += ","; + s.Sprintf( "%s%ld", s.Str(), ulNfp ); + if(( iRc = GetBlock( NULL, (xbUInt32) (ulNfp / (xbUInt32) iBlockFactor), 0 )) != 0 ) + return iRc; + ulNfp = eGetUInt32( cNodeBuf+4 ); + } + xbase->WriteLogMessage( s, iOpt ); + } + } + if( iFmtOpt > 1 ){ + xbMdxTag *tt = mdxTagTbl; + xbString s; + xbInt16 i = 0; + + if( tt ){ + while( tt ){ + i++; + if(( iRc = LoadTagDetail( 2, tt )) != XB_NO_ERROR ) + return iRc; + + s.Sprintf( "TTE (%d)\tName HdrPage\tFormat\tLeftChild\tRightChild\tParent\tKeyType", i ); + xbase->WriteLogMessage( s, iOpt ); + s.Sprintf( "TTE (%d)\t%-12s %d\t\t%d\t%d\t\t%d\t\t%d\t%c\n", i, tt->cTagName, tt->ulTagHdrPageNo, tt->cKeyFmt, tt->cLeftChild, tt->cRightChild, tt->cParent, tt->cKeyType ); + xbase->WriteLogMessage( s, iOpt ); + + s.Sprintf( "TTH (%d)\tRoot\tTagSize\tKeyFmt2\tType2\tKeyLen\tKeysPerBlock\tSecType\tKeyItemLen\tSerial#\tHasKeys\tFilter\tDesc\tUnique\tLchild\tRchild\tKeyFmt3\tTagDate", i ); + xbase->WriteLogMessage( s, iOpt ); + + s.Sprintf( "TTH (%d)\t%d\t%d\t%d\t%c\t%d\t%d\t\t%d\t%d\t\t%x\t%x\t%d\t%d\t%d\t%d\t%d\t%d\t%d/%d/%d", + i, tt->ulRootPage, tt->ulTagSize, tt->cKeyFmt2, tt->cKeyType2, tt->iKeyLen, tt->iKeysPerBlock, tt->iSecKeyType, tt->iKeyItemLen, tt->cSerialNo, tt->cHasKeys, tt->cHasFilter, + (((tt->cKeyFmt2 & 0x08) > 0) ? 1 : 0), // descending? + tt->cUnique, tt->ulLeftChild, tt->ulRightChild, tt->cKeyFmt3, (int) tt->cTagMM, (int) tt->cTagDD, (int) tt->cTagYY % 100 ); + + xbase->WriteLogMessage( s, iOpt ); + + s.Sprintf( "Key (%d) %s", i, tt->sKeyExp->Str()); + xbase->WriteLogMessage( s, iOpt ); + + if( tt->cHasFilter ){ + s.Sprintf( "Flt (%d) %s", i, tt->sFiltExp->Str()); + xbase->WriteLogMessage( s, iOpt ); + } + xbase->WriteLogMessage( "", iOpt ); + tt = tt->next; + + } + } + } + return iRc; +} + +/***********************************************************************/ +xbInt16 xbIxMdx::DumpIxForTag( void *vpTag, xbInt16 iOutputOpt ) +{ + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + xbInt16 iDepth = 0; + xbUInt32 lKeyCtr = 0; + xbInt32 iMinDepth = 999999; + xbInt32 iMaxDepth = 0; + + try{ + /* + get first node + while interior node + print the left key + level++ + go down one on the left + */ + + xbMdxTag * mpTag = (xbMdxTag *) vpTag; + if( mpTag->npNodeChain ){ + mpTag->npNodeChain = FreeNodeChain( mpTag->npNodeChain ); + mpTag->npCurNode = NULL; + } + + // Get the root + if(( iRc = LoadTagDetail( 2, mpTag )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + // lRootBlock is now available + if(( iRc = GetBlock( vpTag, (mpTag->ulRootPage / (xbUInt32) iBlockFactor), 1 )) != XB_NO_ERROR ){ + iErrorStop = 110; + throw iRc; + } + // if no keys on this node, then the index is empty + xbUInt32 ulKeyPtr = eGetUInt32( mpTag->npCurNode->cpBlockData ); + if( ulKeyPtr == 0 ){ + iErrorStop = 120; + iRc = XB_EMPTY; + throw iRc; + } + while( !IsLeaf( vpTag, mpTag->npCurNode )){ // go down the chain looking for a leaf node + PrintKey( vpTag, mpTag->npCurNode , 0, iDepth++, 'I', iOutputOpt ); + if(( iRc = GetKeyPtr( vpTag, 0, mpTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){ + iErrorStop = 130; + throw iRc; + } + if(( iRc = GetBlock( vpTag, (ulKeyPtr / (xbUInt32) iBlockFactor), 1 )) != XB_NO_ERROR ){ + iErrorStop = 140; + throw iRc; + } + } + if(( iRc = GetKeyPtr( vpTag, 0, mpTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){ + iErrorStop = 150; + throw iRc; + } + // loop through the leaf entries of left most leaf + if( iDepth < iMinDepth ) iMinDepth = iDepth; + if( iDepth > iMaxDepth ) iMaxDepth = iDepth; + xbUInt32 ulNoOfKeys = eGetUInt32( mpTag->npCurNode->cpBlockData ); + for( xbUInt32 ul = 0; ul < ulNoOfKeys; ul++ ){ + PrintKey( vpTag, mpTag->npCurNode , ul, iDepth, 'L', iOutputOpt ); + lKeyCtr++; + } + + // if head node = start node, return + if( mpTag->npCurNode->ulBlockNo == ( mpTag->ulRootPage / (xbUInt32) iBlockFactor )) + return XB_NO_ERROR; + + xbBool bEof = false; + while( !bEof ){ + + // go up the chain, looking for an interior node with more keys on it + xbIxNode * TempIxNode = mpTag->npCurNode; + mpTag->npCurNode = mpTag->npCurNode->npPrev; + mpTag->npCurNode->npNext = NULL; + TempIxNode->npPrev = NULL; + TempIxNode = FreeNodeChain( TempIxNode ); + iDepth--; + + while( mpTag->npCurNode->iCurKeyNo >= eGetUInt32( mpTag->npCurNode->cpBlockData ) && + mpTag->npCurNode->ulBlockNo != ( mpTag->ulRootPage / (xbUInt32) iBlockFactor) ){ + TempIxNode = mpTag->npCurNode; + mpTag->npCurNode = mpTag->npCurNode->npPrev; + mpTag->npCurNode->npNext = NULL; + TempIxNode->npPrev = NULL; + TempIxNode = FreeNodeChain( TempIxNode ); + iDepth--; + } + // if head node && right most key, return + if( mpTag->npCurNode->ulBlockNo == (mpTag->ulRootPage / (xbUInt32) iBlockFactor) && + mpTag->npCurNode->iCurKeyNo == eGetUInt32( mpTag->npCurNode->cpBlockData )) + bEof = true; + + if( !bEof ){ + mpTag->npCurNode->iCurKeyNo++; + if(( iRc = GetKeyPtr( vpTag, mpTag->npCurNode->iCurKeyNo, mpTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){ + iErrorStop = 160; + throw iRc; + } + PrintKey( vpTag, mpTag->npCurNode , mpTag->npCurNode->iCurKeyNo, iDepth++, 'I', iOutputOpt ); + + if(( iRc = GetBlock( vpTag, (ulKeyPtr / (xbUInt32) iBlockFactor), 1 )) != XB_NO_ERROR ){ + iErrorStop = 170; + throw iRc; + } + + // traverse down the left side of the tree + while( !IsLeaf( vpTag, mpTag->npCurNode )) // go down the chain looking for a leaf node + { + PrintKey( vpTag, mpTag->npCurNode , 0, iDepth++, 'I', iOutputOpt ); + if(( iRc = GetKeyPtr( vpTag, 0, mpTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){ + iErrorStop = 180; + throw iRc; + } + if(( iRc = GetBlock( vpTag, (ulKeyPtr / (xbUInt32) iBlockFactor), 1 )) != XB_NO_ERROR ){ + iErrorStop = 190; + throw iRc; + } + } + if( iDepth < iMinDepth ) iMinDepth = iDepth; + if( iDepth > iMaxDepth ) iMaxDepth = iDepth; + ulNoOfKeys = eGetUInt32( mpTag->npCurNode->cpBlockData ); + for( xbUInt32 ul = 0; ul < ulNoOfKeys; ul++ ){ + PrintKey( vpTag, mpTag->npCurNode , ul, iDepth, 'L', iOutputOpt ); + lKeyCtr++; + } + } + } + xbString s; + s.Sprintf( "Total keys = [%ld] Min Depth = [%d] Max Depth = [%d]", lKeyCtr, iMinDepth, iMaxDepth ); + xbase->WriteLogMessage( s.Str(), 2 ); + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbIxMdx::DumpIxForTag() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return iRc; +} +/**************************************************************************************************************/ +void xbIxMdx::DumpIxNodeChain( void *vpTag, xbInt16 iOutputOpt ) const +{ + xbMdxTag * mpTag = (xbMdxTag *) vpTag; + xbString s( "Dump Node Chain" ); + xbase->WriteLogMessage( s, 2 ); + + if( mpTag->npNodeChain ){ + xbIxNode *n = mpTag->npNodeChain; + xbInt16 iCtr = 0; + char cLeaf; + s.Sprintf( "Cnt\tThis Prev Next CurKeyNo BlockNo Page NoOfKeys Type" ); + xbase->WriteLogMessage( s, iOutputOpt ); + while( n ){ + IsLeaf( vpTag, n ) ? cLeaf = 'L' : cLeaf = 'I'; + s.Sprintf( "%d\t%08x %08x %08x %08d %08d %08d %08ld %c", + iCtr++, n, n->npPrev, n->npNext, n->iCurKeyNo, + n->ulBlockNo, n->ulBlockNo * (xbUInt32) iBlockFactor, + eGetUInt32( n->cpBlockData ), cLeaf ); + xbase->WriteLogMessage( s, 2 ); + n = n->npNext; + } + } else { + s = "Empty Node Chain"; + xbase->WriteLogMessage( s, 2 ); + } +} +#endif + +/***********************************************************************************************/ +xbInt16 xbIxMdx::FindKey( void *vpTag, xbDouble dKey, xbInt16 iRetrieveSw ){ + + xbMdxTag * mpTag = (xbMdxTag *) vpTag; + if( mpTag->cKeyType2 == 'N' ){ // mdx indices store numeric keys as bcd values + xbBcd bcd( dKey ); + return xbIx::FindKey( vpTag, bcd, iRetrieveSw ); + } else // this would be a julian date inquiry + return FindKey( vpTag, &dKey, 8, iRetrieveSw ); +} + +/***********************************************************************************************/ +// iRetrieveSw = 1 - position db file to index position +// 0 - do not position dbf file + +xbInt16 xbIxMdx::FindKey( void *vpTag, const void * vpKey, + xbInt32 lSearchKeyLen, xbInt16 iRetrieveSw ){ + + xbInt16 iRc = 0; + xbInt16 iErrorStop = 0; + + try{ + // clean up any previous table updates before moving on + if( iRetrieveSw ){ + if( dbf->GetDbfStatus() == XB_UPDATED ){ + if( dbf->GetAutoCommit() == 1 ){ + if(( iRc = dbf->Commit()) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + } else { + if(( iRc = dbf->Abort()) != XB_NO_ERROR ){ + iErrorStop = 110; + throw iRc; + } + } + } + } + + xbUInt32 ulNoOfKeys; + xbMdxTag * mpTag = (xbMdxTag *) vpTag; + char cKeyType = GetKeyType( vpTag ); + xbBool bDescending = mpTag->cKeyFmt2 & 0x08; + + if( mpTag->npNodeChain ){ + + // determine if the index has been updated since the last time it was used + time_t tFileTs; + if(( iRc = GetFileMtime( tFileTs )) != XB_NO_ERROR ){ + iErrorStop = 120; + throw iRc; + } + if( mpTag->tNodeChainTs < tFileTs ){ + mpTag->npNodeChain = FreeNodeChain( mpTag->npNodeChain ); + mpTag->npCurNode = NULL; + + } else { + // pop up the chain looking for appropriate starting point + xbBool bDone = false; + xbIxNode * TempIxNode; + while( mpTag->npCurNode && !bDone && + (mpTag->npCurNode->ulBlockNo != ( mpTag->ulRootPage / (xbUInt32) iBlockFactor ))){ // not root node + //if no keys on the node, pop up one + ulNoOfKeys = eGetUInt32( mpTag->npCurNode->cpBlockData ); + if( ulNoOfKeys == 0 ){ + TempIxNode = mpTag->npCurNode; + mpTag->npCurNode = mpTag->npCurNode->npPrev; + TempIxNode = FreeNodeChain( TempIxNode ); + + } else { + + iRc = CompareKey( cKeyType, vpKey, GetKeyData( mpTag->npCurNode, 0, mpTag->iKeyItemLen ), (size_t) lSearchKeyLen ); + if( (!bDescending && iRc <= 0) || (bDescending && iRc >= 0 )){ + TempIxNode = mpTag->npCurNode; + mpTag->npCurNode = mpTag->npCurNode->npPrev; + TempIxNode = FreeNodeChain( TempIxNode ); + } else { + // get the number of keys on the block and compare the key to the rightmost key + xbUInt32 ulKeyCtr = eGetUInt32( mpTag->npCurNode->cpBlockData ) - 1; // IsLeaf( vpTag, mpTag->npCurNode ); + iRc = CompareKey( cKeyType, vpKey, GetKeyData( mpTag->npCurNode, ulKeyCtr, mpTag->iKeyItemLen), (size_t) lSearchKeyLen ); + + if( (!bDescending && iRc > 0) || (bDescending && iRc < 0 )){ + TempIxNode = mpTag->npCurNode; + mpTag->npCurNode = mpTag->npCurNode->npPrev; + TempIxNode = FreeNodeChain( TempIxNode ); + } else { + bDone = true; + } + } + } + } + } + } + + // either started empty, or cleared due to file time diff + if( !mpTag->npNodeChain ){ + // Get the root + if(( iRc = LoadTagDetail( 2, mpTag )) != XB_NO_ERROR ){ + iErrorStop = 130; + throw iRc; + } + // lRootBlock is now available + if(( iRc = GetBlock( vpTag, (mpTag->ulRootPage / (xbUInt32) iBlockFactor), 1 )) != XB_NO_ERROR ){ + iErrorStop = 140; + throw iRc; + } + // if this is a leaf node and no keys on this node, then the index is empty + ulNoOfKeys = eGetUInt32( mpTag->npCurNode->cpBlockData ); + if( ulNoOfKeys == 0 && IsLeaf( vpTag, mpTag->npCurNode )){ +// iRc = XB_EMPTY; + iRc = XB_NOT_FOUND; + return iRc; + } + } + + // should be in the appropriate position in the node chain to continue the search from here + // run down through the interior nodes + xbInt16 iSearchRc = 0; + xbUInt32 ulKeyPtr = 0; + + while( vpTag && !IsLeaf( vpTag, mpTag->npCurNode ) ){ + // get the number of keys on the block and compare the key to the rightmost key + ulNoOfKeys = eGetUInt32( mpTag->npCurNode->cpBlockData ); + + if( ulNoOfKeys == 0 ){ + mpTag->npCurNode->iCurKeyNo = 0; + + } else { + + iRc = CompareKey( cKeyType, vpKey, GetKeyData( mpTag->npCurNode, ulNoOfKeys - 1, mpTag->iKeyItemLen), (size_t) lSearchKeyLen ); + if( (!bDescending && iRc > 0) || (bDescending && iRc < 0)){ + mpTag->npCurNode->iCurKeyNo = ulNoOfKeys; + } + else + { + mpTag->npCurNode->iCurKeyNo = (xbUInt32) BSearchBlock( cKeyType, mpTag->npCurNode, + (xbInt32) mpTag->iKeyItemLen, vpKey, (xbInt32) lSearchKeyLen, iSearchRc, bDescending ); + } + } + + if(( iRc = GetKeyPtr( vpTag, mpTag->npCurNode->iCurKeyNo, mpTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){ + iErrorStop = 150; + throw iRc; + } + + if(( iRc = GetBlock( vpTag, (ulKeyPtr / (xbUInt32)iBlockFactor), 1 )) != XB_NO_ERROR ){ + iErrorStop = 160; + throw iRc; + } + } + + ulNoOfKeys = eGetUInt32( mpTag->npCurNode->cpBlockData ); + xbInt16 iCompRc = 0; + + if( ulNoOfKeys == 0 ){ + // iCompRc = -1; + // iRc = XB_EMPTY; + iRc = XB_NOT_FOUND; + return iRc; + + } else { + + iRc = BSearchBlock( cKeyType, mpTag->npCurNode, mpTag->iKeyItemLen, vpKey, lSearchKeyLen, iCompRc, bDescending ); + // iCompRc + // 0 found + // < 0 eof encountered, search key > last key in file + // > 0 not found, positioned to next key + + if( iCompRc >= 0 ){ + mpTag->npCurNode->iCurKeyNo = (xbUInt32) iRc; + + + if( iRetrieveSw ){ + + xbUInt32 ulKey = mpTag->npCurNode->iCurKeyNo; + if( ulKey >= ulNoOfKeys ) // if position past end of keys, need to go back one and position to last key + ulKey--; + + // if(( iRc = GetKeyPtr( vpTag, mpTag->npCurNode->iCurKeyNo, mpTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){ + + if(( iRc = GetKeyPtr( vpTag, ulKey, mpTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){ + iErrorStop = 170; + throw iRc; + } + if(( iRc = dbf->GetRecord( ulKeyPtr )) != XB_NO_ERROR ){ + iErrorStop = 180; + throw iRc; + } + } + } + } + + if( iCompRc == 0 ) + return XB_NO_ERROR; + else if( iCompRc > 0 ) + return XB_NOT_FOUND; + else + return XB_EOF; + + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbIxMdx::FindKey() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return iRc; +} + +/***********************************************************************/ +xbInt16 xbIxMdx::FindKeyForCurRec( void * vpTag ) +{ + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + xbInt16 i = 0; + + try{ + if(( iRc = CreateKey( vpTag, 0 )) < XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbIxMdx::FindKeyForCurRec() Exception Caught. Error Stop = [%d] iRc = [%d] Tag=[%d]", iErrorStop, iRc, i ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return XB_NO_ERROR; +} + +/***********************************************************************/ +//! @brief Get dbf record number for given key number. +/*! + \param vpTag Tag to retrieve dbf rec number on. + \param iKeyNo Key number for retrieval + \param np Pointer to node + \param ulDbfPtr- Output dbf record number + \returns Return Codes +*/ +xbInt16 xbIxMdx::GetDbfPtr( void *vpTag, xbInt16 iKeyNo, xbIxNode *np, xbUInt32 &ulDbfPtr ) const { + + xbInt16 iRc = 0; + xbInt16 iErrorStop = 0; + + try{ + #ifdef XB_DEBUG_SUPPORT + // turn this off in production mode for better performance + xbUInt32 ulNoOfKeys = eGetUInt32 ( np->cpBlockData ); + if( iKeyNo < 0 || iKeyNo > (xbInt16) --ulNoOfKeys ){ + iErrorStop = 100; + throw XB_INVALID_KEYNO; + } + #endif + + xbMdxTag * mpTag = (xbMdxTag *) vpTag; + char *p = ( np->cpBlockData); + p += (8 + (iKeyNo * mpTag->iKeyItemLen)); + ulDbfPtr = eGetUInt32 ( p ); + + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbIxMdx::GetDbfPtr() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return iRc; +} + +/***********************************************************************/ +/*! + \param vpTag Tag to retrieve first key on. + \param iRetrieveSw xbTrue - Retrieve the record on success.
+ xbFalse - Don't retrieve record. + \returns Return Codes +*/ + +xbInt16 xbIxMdx::GetFirstKey( void *vpTag, xbInt16 iRetrieveSw = 0 ){ + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + + // convert the tag pointer to mdx tag pointer + xbMdxTag * mpTag = (xbMdxTag *) vpTag; + + try{ + // clear out any history + if( mpTag->npNodeChain ){ + mpTag->npNodeChain = FreeNodeChain( mpTag->npNodeChain ); + mpTag->npCurNode = NULL; + } + // Get the root + if(( iRc = LoadTagDetail( 2, mpTag )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + // lRootPage is available + if(( iRc = GetBlock( vpTag, (mpTag->ulRootPage / (xbUInt32) iBlockFactor), 1 )) != XB_NO_ERROR ){ + iErrorStop = 110; + throw iRc; + } + + // if no keys on this node, then the index is empty + // this is not true + + xbUInt32 ulKeyPtr = eGetUInt32( mpTag->npCurNode->cpBlockData ); + if( ulKeyPtr == 0 && IsLeaf( vpTag, mpTag->npCurNode )){ + iRc = XB_EMPTY; + return iRc; + } + + while( !IsLeaf( vpTag, mpTag->npCurNode )) // go down the chain looking for a leaf node + { + if(( iRc = GetKeyPtr( vpTag, 0, mpTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){ + iErrorStop = 120; + throw iRc; + } + + if(( iRc = GetBlock( vpTag, (ulKeyPtr / (xbUInt32) iBlockFactor), 1 )) != XB_NO_ERROR ){ + iErrorStop = 130; + throw iRc; + } + } + + // retrieve record to data buf + if( iRetrieveSw ){ + if(( iRc = GetKeyPtr( vpTag, 0, mpTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){ + iErrorStop = 140; + throw iRc; + } + if(( iRc = dbf->GetRecord( ulKeyPtr )) != XB_NO_ERROR ){ + iErrorStop = 150; + throw iRc; + } + } + // else { + // throw iRc; + // } + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbIxMdx::GetFirstKey() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return iRc; +} + +/***********************************************************************/ +//xbBool xbIxMdx::GetIndexUpdated() const { +// std::cout << "xbIxMdx::GetIndexUpdate() FIX ME \n"; +// return xbFalse; +//} + +/***********************************************************************/ +//! @brief Get the key expression for the given tag. +/*! + \param vpTag Tag to retrieve key expression from tag. + \returns Key expression. +*/ + +xbString &xbIxMdx::GetKeyExpression( const void * vpTag ) const{ + xbMdxTag *mpTag = (xbMdxTag *) vpTag; + return *mpTag->sKeyExp; +} + +/***********************************************************************/ +//! @brief Get the key expression for the given tag. +/*! + \param vpTag Tag to retrieve filter expression from tag (if it exists). + \returns Key filter. +*/ + +xbString &xbIxMdx::GetKeyFilter( const void * vpTag ) const{ + + xbMdxTag * mpTag = (xbMdxTag *) vpTag; + + // std::cout << "GetKeyFilter() "; + if( mpTag->sFiltExp ) + return *mpTag->sFiltExp; +// std::cout << " not null\n"; + else + return sNullString; + // std::cout << " null\n"; + +// next line causes seg faults +// return *mpTag->sFiltExp; + +} +/**************************************************************************************************/ +xbInt16 xbIxMdx::GetKeyPtr( void *vpTag, xbInt16 iKeyNo, xbIxNode *np, xbUInt32 &ulKeyPtr ) const { + + xbInt16 iRc = XB_NO_ERROR; + + xbInt16 iErrorStop = 0; + + try{ + + xbMdxTag *mdxTag = (xbMdxTag *) vpTag; + char *p = np->cpBlockData; + xbUInt32 ulKeyCnt = eGetUInt32( p ); + if( iKeyNo < 0 || iKeyNo > (xbInt16) ulKeyCnt ){ + iErrorStop = 100; + iRc = XB_INVALID_KEYNO; + throw iRc; + } + p+=8; // skip past first two four byte numeric fields + p+= (iKeyNo * mdxTag->iKeyItemLen); + ulKeyPtr = eGetUInt32( p ); + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbIxMdx::GetKeyPtr() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return iRc; +} +/***********************************************************************/ +//! @brief Returns key update status. +/*! + \param vpTag Tag to check status on. + \returns XB_UPD_KEY Key updated.
+ XB_DEL_KEY Key deleted.
+ XB_ADD_KEY Key added.
+ 0 No key updates + +*/ +inline xbInt16 xbIxMdx::GetKeySts( void *vpTag ) const{ + + xbMdxTag *mpTag = (xbMdxTag *) vpTag; + return mpTag->iKeySts; +} + +/***********************************************************************/ +char xbIxMdx::GetKeyType( const void *vpTag ) const { + xbMdxTag * mpTag = (xbMdxTag *) vpTag; + return mpTag->cKeyType; +} + +/***********************************************************************/ +//! @brief Get the last key for the given tag. +/*! + \param vpTag Tag to retrieve last key on. + \param iRetrieveSw xbTrue - Retrieve the record on success.
+ xbFalse - Don't retrieve record. + \returns Return Codes +*/ + +xbInt16 xbIxMdx::GetLastKey( void *vpTag, xbInt16 iRetrieveSw ){ + return GetLastKey( 0, vpTag, iRetrieveSw ); +} + +/***********************************************************************/ +//! @brief Get the last key for the given tag and starting node. +/*! + \param ulBlockNo Starting node + \param vpTag Tag to retrieve last key on. + \param iRetrieveSw xbTrue - Retrieve the record if key found.
+ xbFalse - Don't retrieve record. + \returns Return Codes +*/ + +xbInt16 xbIxMdx::GetLastKey( xbUInt32 ulBlockNo, void *vpTag, xbInt16 iRetrieveSw ){ + + // if UlNodeNo is zero, start at head node, otherwise start at ulNodeNo + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + + // convert the tag pointer to mdx tag pointer + xbMdxTag * mpTag = (xbMdxTag *) vpTag; + + try{ + xbUInt32 ulNoOfKeys = 0; + // clear out any history + if( mpTag->npNodeChain ){ + mpTag->npNodeChain = FreeNodeChain( mpTag->npNodeChain ); + mpTag->npCurNode = NULL; + } + // Get the root + if( ulBlockNo == 0 ){ + if(( iRc = LoadTagDetail( 2, mpTag )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + //if(( iRc = GetBlock( vpTag, (mpTag->ulRootPage / (xbUInt32) iBlockFactor), 1 )) != XB_NO_ERROR ){ + if(( iRc = GetBlock( vpTag, PageToBlock( mpTag->ulRootPage ), 1 )) != XB_NO_ERROR ){ + iErrorStop = 110; + throw iRc; + } + + } else { + if(( iRc = GetBlock( vpTag, ulBlockNo, 1 )) != XB_NO_ERROR ){ + iErrorStop = 120; + throw iRc; + } + } + + ulNoOfKeys = eGetUInt32( mpTag->npCurNode->cpBlockData ); + + if( ulNoOfKeys == 0 && IsLeaf( vpTag, mpTag->npCurNode )){ + iRc = XB_EMPTY; + return iRc; + } + + mpTag->npCurNode->iCurKeyNo = ulNoOfKeys; + ulNoOfKeys = eGetUInt32( mpTag->npCurNode->cpBlockData ); + + xbUInt32 ulKeyPtr = 0; + while( !IsLeaf( vpTag, mpTag->npCurNode )){ // go down the chain looking for a leaf node + // std::cout << "Considered an interior node\n"; + if(( iRc = GetKeyPtr( vpTag, ulNoOfKeys, mpTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){ + iErrorStop = 130; + throw iRc; + } + if(( iRc = GetBlock( vpTag, (ulKeyPtr / (xbUInt32) iBlockFactor), 1 )) != XB_NO_ERROR ){ + iErrorStop = 140; + throw iRc; + } + + ulNoOfKeys = eGetUInt32( mpTag->npCurNode->cpBlockData ); + mpTag->npCurNode->iCurKeyNo = ulNoOfKeys; + } + // leaf node has one fewer keys than the interior node + mpTag->npCurNode->iCurKeyNo--; + ulNoOfKeys = eGetUInt32( mpTag->npCurNode->cpBlockData ); + + // retrieve record to data buf + if( iRetrieveSw ){ + if(( iRc = GetKeyPtr( vpTag, (ulNoOfKeys-1), mpTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){ + iErrorStop = 150; + throw iRc; + } + if(( iRc = dbf->GetRecord( ulKeyPtr )) != XB_NO_ERROR ){ + iErrorStop = 160; + throw iRc; + } + } + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbIxMdx::GetLastKey() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return iRc; +} + +/***********************************************************************/ +//! @brief Get the last key for a block number. +/*! + \param vpTag Tag to retrieve first key on. + \param ulBlockNo Block number for key retrieval. + \param cpBuf output buffer for key placement + \returns Return Codes +*/ + +xbInt16 xbIxMdx::GetLastKeyForBlockNo( void *vpTag, xbUInt32 ulBlockNo, char *cpBuf ){ + + // returns the last key for a given block number + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + + xbMdxTag * npTag = (xbMdxTag *) vpTag; + + try{ + xbIxNode * npSaveNodeChain = npTag->npNodeChain; + xbIxNode * npSaveCurNode = npTag->npCurNode; + npTag->npNodeChain = NULL; + + if(( iRc = GetLastKey( ulBlockNo, npTag, 0 )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + memcpy( cpBuf, GetKeyData( npTag->npCurNode, GetKeyCount( npTag->npCurNode ) - 1, npTag->iKeyItemLen ), (size_t) npTag->iKeyLen ); + + // free memory + FreeNodeChain( npTag->npNodeChain ); + npTag->npNodeChain = npSaveNodeChain; + npTag->npCurNode = npSaveCurNode; + + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbIxMdx::GetLastKeyForBlockNo() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( iRc ) ); + } + return iRc; +} +/***********************************************************************/ +//! @brief Get the next key for the given tag. +/*! + \param vpTag Tag to retrieve next key on. + \param iRetrieveSw xbTrue - Retrieve the record on success.
+ xbFalse - Don't retrieve record. + \returns Return Codes +*/ + +xbInt16 xbIxMdx::GetNextKey( void * vpTag, xbInt16 iRetrieveSw ){ + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + + // convert the tag pointer to mdx tag pointer + xbMdxTag * mpTag = (xbMdxTag *) vpTag; + + try{ + if( !mpTag->npCurNode ) + return GetFirstKey( vpTag, iRetrieveSw ); + + // more keys on this node? + xbUInt32 ulKeyPtr; + if( (eGetUInt32( mpTag->npCurNode->cpBlockData ) - 1) > mpTag->npCurNode->iCurKeyNo ){ + mpTag->npCurNode->iCurKeyNo++; + + if(( iRc = GetKeyPtr( vpTag, mpTag->npCurNode->iCurKeyNo, mpTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + if( iRetrieveSw ){ + if(( iRc = dbf->GetRecord( ulKeyPtr )) != XB_NO_ERROR ){ + iErrorStop = 110; + throw iRc; + } else { + return iRc; + } + } else { + return iRc; + } + } + + // if at end head node, then at eof + if( mpTag->npCurNode->ulBlockNo == ( mpTag->ulRootPage / (xbUInt32) iBlockFactor )) + return XB_EOF; + + // This logic assumes that interior nodes have n+1 left node no's where n is the number of keys in the node + xbIxNode * TempIxNode = mpTag->npCurNode; + mpTag->npCurNode = mpTag->npCurNode->npPrev; + TempIxNode = FreeNodeChain( TempIxNode ); + + // While no more right keys && not head node, pop up one node + while( mpTag->npCurNode->iCurKeyNo >= eGetUInt32( mpTag->npCurNode->cpBlockData ) && + mpTag->npCurNode->ulBlockNo != ( mpTag->ulRootPage / (xbUInt32) iBlockFactor) ){ + + TempIxNode = mpTag->npCurNode; + mpTag->npCurNode = mpTag->npCurNode->npPrev; + TempIxNode = FreeNodeChain( TempIxNode ); + } + + // if head node && right most key, return eof + if( mpTag->npCurNode->ulBlockNo == (mpTag->ulRootPage / (xbUInt32) iBlockFactor) && + mpTag->npCurNode->iCurKeyNo == eGetUInt32( mpTag->npCurNode->cpBlockData )) + return XB_EOF; + + // move one to the right + mpTag->npCurNode->iCurKeyNo++; + if(( iRc = GetKeyPtr( vpTag, mpTag->npCurNode->iCurKeyNo, mpTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){ + iErrorStop = 120; + throw iRc; + } + + if(( iRc = GetBlock( vpTag, (ulKeyPtr / (xbUInt32) iBlockFactor), 1 )) != XB_NO_ERROR ){ + iErrorStop = 130; + throw iRc; + } + + // traverse down the left side of the tree + while( !IsLeaf( vpTag, mpTag->npCurNode )) // go down the chain looking for a leaf node + { + if(( iRc = GetKeyPtr( vpTag, 0, mpTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){ + iErrorStop = 140; + throw iRc; + } + if(( iRc = GetBlock( vpTag, (ulKeyPtr / (xbUInt32) iBlockFactor), 1 )) != XB_NO_ERROR ){ + iErrorStop = 150; + throw iRc; + } + } + // retrieve record to data buf + if( iRetrieveSw ){ + if(( iRc = GetKeyPtr( vpTag, 0, mpTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){ + iErrorStop = 160; + throw iRc; + } + if(( iRc = dbf->GetRecord( ulKeyPtr )) != XB_NO_ERROR ){ + iErrorStop = 170; + throw iRc; + } + } else { + return iRc; + } + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbIxMdx::GetNextKey() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return iRc; +} + +/***********************************************************************/ +//! @brief Get the previous key for the given tag. +/*! + \param vpTag Tag to retrieve previous key on. + \param iRetrieveSw xbTrue - Retrieve the record on success.
+ xbFalse - Don't retrieve record. + \returns Return Codes +*/ + +xbInt16 xbIxMdx::GetPrevKey( void * vpTag, xbInt16 iRetrieveSw ){ + + xbString s; + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + + // convert the tag pointer to mdx tag pointer + xbMdxTag * mpTag = (xbMdxTag *) vpTag; + + try{ + if( !mpTag->npCurNode ){ + return GetLastKey( 0, vpTag, iRetrieveSw ); + } + + xbUInt32 ulKeyPtr = 0; + // more keys on this assumed-leaf node? + + if( mpTag->npCurNode->iCurKeyNo > 0 ){ + mpTag->npCurNode->iCurKeyNo--; + + if(( iRc = GetKeyPtr( vpTag, mpTag->npCurNode->iCurKeyNo, mpTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + + if( iRetrieveSw ){ + if(( iRc = dbf->GetRecord( ulKeyPtr )) != XB_NO_ERROR ){ + iErrorStop = 110; + throw iRc; + } else { + return iRc; + } + } else { + return iRc; + } + } + + //if head node = start node, at eof + if( mpTag->npCurNode->ulBlockNo == ( mpTag->ulRootPage / (xbUInt32) iBlockFactor )) + return XB_BOF; + + // This logic assumes that interior nodes have n+1 left node no's where n is the number of keys in the node + xbIxNode * TempIxNode = mpTag->npCurNode; + mpTag->npCurNode = mpTag->npCurNode->npPrev; + TempIxNode = FreeNodeChain( TempIxNode ); + + // While no more left keys && not head node, pop up one node + while( mpTag->npCurNode->iCurKeyNo == 0 && + mpTag->npCurNode->ulBlockNo != ( mpTag->ulRootPage / (xbUInt32) iBlockFactor) ){ + TempIxNode = mpTag->npCurNode; + mpTag->npCurNode = mpTag->npCurNode->npPrev; + TempIxNode = FreeNodeChain( TempIxNode ); + } + + //if head node && left most key, return bof + if( mpTag->npCurNode->ulBlockNo == ( mpTag->ulRootPage / (xbUInt32) iBlockFactor) && mpTag->npCurNode->iCurKeyNo == 0 ) + return XB_BOF; + + // move one to the left + mpTag->npCurNode->iCurKeyNo--; + if(( iRc = GetKeyPtr( vpTag, mpTag->npCurNode->iCurKeyNo, mpTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){ + iErrorStop = 120; + throw iRc; + } + + if(( iRc = GetBlock( vpTag, (ulKeyPtr / (xbUInt32) iBlockFactor), 1 )) != XB_NO_ERROR ){ + iErrorStop = 130; + throw iRc; + } + + // traverse down the right side of the tree + xbUInt32 ulNoOfKeys; + while( !IsLeaf( vpTag, mpTag->npCurNode )) // go down the chain looking for a leaf node + { + ulNoOfKeys = eGetUInt32( mpTag->npCurNode->cpBlockData ); + mpTag->npCurNode->iCurKeyNo = ulNoOfKeys; + + if(( iRc = GetKeyPtr( vpTag, ulNoOfKeys, mpTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){ + iErrorStop = 140; + throw iRc; + } + if(( iRc = GetBlock( vpTag, (ulKeyPtr / (xbUInt32) iBlockFactor), 1 )) != XB_NO_ERROR ){ + iErrorStop = 150; + throw iRc; + } + } + + // ulNoOfKeys = eGetUInt32( mpTag->npCurNode->cpBlockData ); + mpTag->npCurNode->iCurKeyNo = eGetUInt32( mpTag->npCurNode->cpBlockData ) - 1; + + // retrieve record to data buf + if( iRetrieveSw ){ + if(( iRc = GetKeyPtr( vpTag, mpTag->npCurNode->iCurKeyNo, mpTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){ + iErrorStop = 160; + throw iRc; + } + if(( iRc = dbf->GetRecord( ulKeyPtr )) != XB_NO_ERROR ){ + iErrorStop = 170; + throw iRc; + } + } else { + return iRc; + } + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbIxMdx::GetPrevKey() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return iRc; +} + +/***********************************************************************/ +//! @brief GetReuseEmptyNode swuitch setting. +/*! + \returns xbFalse - Do not reuse empty MDX index nodes (Dbase 6. behavior). + xbTrue - Reuse empty MDX index nodes. +*/ + +xbBool xbIxMdx::GetReuseEmptyNodesSw() const { + return bReuseEmptyNodes; +} +/***********************************************************************/ +xbBool xbIxMdx::GetSortOrder( void *vpTag ) const { + + // return true if descending + xbMdxTag *mTag = (xbMdxTag *) vpTag; + if( mTag->cKeyFmt2 & 0x08 ) + return 0x01; + else + return 0x00; +} + +/***********************************************************************/ +//! @brief Get tag for tag number. +/*! + \param iTagNo - Zero based, which tag to retrieve. + \returns Pointer to mdx tag for a given tag number. +*/ + +void * xbIxMdx::GetTag( xbInt16 iTagNo ) const { + + xbMdxTag *tt = mdxTagTbl; + xbInt16 i = 0; + + while( i < iTagNo && tt->next ){ + tt = tt->next; + i++; + } + if( i == iTagNo ) + return (void *) tt; + else + return NULL; +} + +/***********************************************************************/ +//! @brief Get tag for tag name. +/*! + \sTagName - Tag name to retrieve. + \returns Pointer to mdx tag for a given tag number. +*/ + +void * xbIxMdx::GetTag( xbString &sTagName ) const { + + xbMdxTag *tt = mdxTagTbl; + + while( sTagName != tt->cTagName && tt->next ){ + tt = tt->next; + } + + if( sTagName == tt->cTagName ) + return (void *) tt; + else + return NULL; +} +/***********************************************************************/ +xbInt16 xbIxMdx::GetTagCount() const { + return iTagUseCnt; +} + +/***********************************************************************/ +void xbIxMdx::GetTagName( void *vpTag, xbString &sTagName ){ + + xbMdxTag *mpTag = (xbMdxTag *) vpTag; + sTagName = mpTag->sTagName->Str(); +} + +/***********************************************************************/ +//const char *xbIxMdx::GetTagName( void *vpTag, xbInt16 iOpt ) const { + +const char *xbIxMdx::GetTagName( void *vpTag, xbInt16 ) const { + xbMdxTag *mpTag = (xbMdxTag *) vpTag; + return mpTag->cTagName; +} + +/***********************************************************************/ +xbString &xbIxMdx::GetTagName( void *vpTag ) const { + + xbMdxTag *mpTag = (xbMdxTag *) vpTag; + return *mpTag->sTagName; +} + +/***********************************************************************/ +void *xbIxMdx::GetTagTblPtr() const { + return (void *) mdxTagTbl; +} + +/***********************************************************************/ +xbBool xbIxMdx::GetUnique( void *vpTag ) const { +//! @brief Determine unique setting for given tag. +/*! + \param vpTag Tag to retrieve expression from. + \returns xbTrue if unique key. +*/ + xbMdxTag *mTag = (xbMdxTag *) vpTag; + return mTag->cUnique; +} + +/***********************************************************************/ +//! @brief Harvest Empty Node. +/*! + Harvest empty MDX node and add it to the chain of link nodes + + \param mpTag Tag to harvest. + \param iOpt - 0 Don't write the node info to disk, handled elsewhere (don't write it twice) + 1 Write the update into to disk + \returns Return Codes +*/ +xbInt16 xbIxMdx::HarvestEmptyNode( xbMdxTag *mpTag, xbIxNode *npNode, xbInt16 iOpt, xbBool bHarvestRoot ){ + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + xbBool bRootPage = xbFalse; + xbInt32 iOffset = 0; + + try{ + +// std::cout << "xbIxMdx::HarvestEmptyNode() page= " << BlockToPage( npNode->ulBlockNo ); +// std::cout << " block = " << npNode->ulBlockNo << "\n"; + + if( mpTag->ulRootPage == BlockToPage( npNode->ulBlockNo ) && !bHarvestRoot ){ + bRootPage = xbTrue; + +// std::cout << "special root page processing *****************\n"; + } + + memset( npNode->cpBlockData, 0x00, GetBlockSize()); + + char *pTrg = npNode->cpBlockData; + if( !bRootPage ){ + pTrg += 4; + ePutUInt32( pTrg, ulFirstFreePage ); + } + + + if( bRootPage ){ + if( mpTag->cHasKeys ){ + + // std::cout << "setting has keys\n"; + + mpTag->cHasKeys = 0x00; + if(( iRc = xbFseek( ((mpTag->ulTagHdrPageNo * 512) + 246), SEEK_SET )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + if(( iRc = xbFwrite( &mpTag->cHasKeys, 1, 1 )) != XB_NO_ERROR ){ + iErrorStop = 110; + throw iRc; + } + // might need to update left sibling and right sibling here. + // Fields don't seem to be updated consistently by other xbase tools, + // for now, not updating + + } + + } else { + + // update header + // seek to position byte 13 + ulFirstFreePage = BlockToPage( npNode->ulBlockNo ); + if(( iRc = xbFseek( 36, SEEK_SET )) != XB_NO_ERROR ){ + iErrorStop = 120; + throw iRc; + } + // write it + char c4[4]; + ePutUInt32( c4, ulFirstFreePage ); + if(( iRc = xbFwrite( c4, 4, 1 )) != XB_NO_ERROR ){ + iErrorStop = 130; + throw iRc; + } + + } + + if( iOpt == 1 ){ + if(( iRc = xbFseek( (xbInt64) ((npNode->ulBlockNo * GetBlockSize() )) + iOffset, SEEK_SET )) != XB_NO_ERROR ){ + iErrorStop = 140; + throw iRc; + } + // write out the block + if(( iRc = xbFwrite( npNode->cpBlockData, GetBlockSize(), 1 )) != XB_NO_ERROR ){ + iErrorStop = 150; + throw iRc; + } + } + } + + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbIxMdx::HarvestEmptyNodeI() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return iRc; +} + + + +/***********************************************************************/ +//! @brief Harvest Tag Nodes. +/*! + Save all nodes for a given tag into the free node chain. + Used for reindexing or deleting a given tag. + + \param mpTag Tag for harvesting nodes + \param bHarvestRoot Set to True when deleting tag + \returns Return Codes +*/ + +xbInt16 xbIxMdx::HarvestTagNodes( xbMdxTag *mpTag, xbBool bHarvestRoot ){ + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + + xbUInt32 ulBlkNo; + xbLinkListOrd ll; + xbLinkListNode * llN; + xbIxNode * n; + + try{ + + ll.SetDupKeys( xbFalse ); + + // clear out any history + if( mpTag->npNodeChain ){ + mpTag->npNodeChain = FreeNodeChain( mpTag->npNodeChain ); + mpTag->npCurNode = NULL; + } + + while( GetNextKey( mpTag, 0 ) == 0 ){ + n = mpTag->npNodeChain; + while(n){ + ll.InsertKey( n->ulBlockNo, (xbUInt32) n->iCurKeyNo ); + n = n->npNext; + } + } + + if( bHarvestRoot ) + ll.InsertKey( PageToBlock( mpTag->ulTagHdrPageNo ), 0 ); + + llN = ll.GetHeadNode(); + if(( n = xbIx::AllocateIxNode( GetBlockSize())) == NULL ){ + iErrorStop = 100; + iRc = XB_NO_MEMORY; + throw iRc; + } + + while( llN ){ + ulBlkNo = llN->GetKey(); + + // read in a block for the block number + if(( iRc = ReadBlock( ulBlkNo, GetBlockSize(), n->cpBlockData )) != XB_NO_ERROR ){ + iErrorStop = 110; + throw iRc; + } + // harvest it + n->ulBlockNo = ulBlkNo; + if(( iRc = HarvestEmptyNode( mpTag, n, 1, bHarvestRoot )) != XB_NO_ERROR ){ + iErrorStop = 120; + throw iRc; + } + llN = llN->GetNextNode(); + } + n = FreeNodeChain( n ); + mpTag->npNodeChain = FreeNodeChain( mpTag->npNodeChain ); + mpTag->npCurNode = NULL; + + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbIxMdx::HarvestTagNodes() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return iRc; +} + +/***********************************************************************/ +//! @brief Insert key into interior node. +/*! + Insert key into non-full interior node.
+ Assumes valid inputs + + \param vpTag Tag in play. + \param npNode Node for insertion. + \param iSlotNo Slot number to insert key. + \param ulPtr Page number to insert. + \returns Return Codes +*/ + +xbInt16 xbIxMdx::InsertNodeI( void *vpTag, xbIxNode *npNode, xbInt16 iSlotNo, xbUInt32 ulPtr ){ + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + char *pNewKeyPos; + char *pTrg; + char *pLastKey = NULL; + xbMdxTag * npTag; + npTag = (xbMdxTag *) vpTag; + xbInt16 iCopyLen; + xbInt16 iNewKeyPos = 8; + + + try{ + + xbInt32 lKeyCnt = GetKeyCount( npNode ); + iNewKeyPos += (iSlotNo * npTag->iKeyItemLen); + char *pSrc = npNode->cpBlockData; + + if( iSlotNo < lKeyCnt ) + iCopyLen = ((lKeyCnt - iSlotNo) * npTag->iKeyItemLen) + 4; + else + iCopyLen = 0; + + xbUInt32 ulRqdBufSize = (xbUInt32) ((lKeyCnt + 1) * npTag->iKeyItemLen) + 12; + if( ulRqdBufSize > npNode->ulBufSize ){ + npNode->ulBufSize += (xbUInt32) npTag->iKeyItemLen; + npNode->cpBlockData = (char *) realloc( npNode->cpBlockData, (size_t) npNode->ulBufSize ); + if( !npNode->cpBlockData ){ + iErrorStop = 100; + iRc = XB_NO_MEMORY; + throw iRc; + } + } + + // if not appending to the end of the node, make some room, move things to the right + pNewKeyPos = npNode->cpBlockData; + pNewKeyPos += iNewKeyPos; + + if( iSlotNo < lKeyCnt ){ + pTrg = pNewKeyPos; + pTrg += npTag->iKeyItemLen; + memmove( pTrg, pNewKeyPos, (size_t) iCopyLen ); + } + + // get the right most key for the left part of the split node + xbUInt32 ulKeyPtr2; + if(( iRc = GetKeyPtr( vpTag, npNode->iCurKeyNo, npNode, ulKeyPtr2 )) != XB_NO_ERROR ){ + iErrorStop = 110; + throw iRc; + } + + // get the new right key value for the freshly split node + pLastKey = (char *) malloc((size_t) npTag->iKeyLen); + if(( iRc = GetLastKeyForBlockNo( vpTag, PageToBlock( ulKeyPtr2 ), pLastKey )) != XB_NO_ERROR ){ + iRc = 120; + throw iRc; + } + + // write the key values + pTrg = pNewKeyPos; + pTrg += 4; + pSrc = pLastKey; + for( xbInt16 i = 0; i < npTag->iKeyLen; i++ ) + *pTrg++ = *pSrc++; + ePutUInt32( pTrg, ulPtr); + ePutInt32( npNode->cpBlockData, ++lKeyCnt ); + + // write out the updated block to disk + if(( iRc = WriteBlock( npNode->ulBlockNo, GetBlockSize(), npNode->cpBlockData )) != XB_NO_ERROR ){ + iErrorStop = 130; + throw iRc; + } + if( pLastKey ) + free( pLastKey ); + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbIxMdx::InsertNodeI() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + if( pLastKey ) + free( pLastKey ); + } + return iRc; +} + +/***********************************************************************/ +//! @brief Insert key into leaf node. +/*! + Insert key into non-full leaf node.
+ Assumes valid inputs + + \param vpTag Tag in play. + \param npNode Node for insertion. + \param iSlotNo Slot number to insert key. + \param ulPtr Pointer number to insert. + \returns Return Codes +*/ +xbInt16 xbIxMdx::InsertNodeL( void *vpTag, xbIxNode *npNode, xbInt16 iSlotNo, char * cpKeyBuf, xbUInt32 ulPtr ){ + + // format of block data is + // 4 bytes number of keys on block + // 4 bytes - next free block or split block num + // repeating + // 4 bytes record number + // x bytes key data + + // Special processing note: when splitting node, new key is first inserted into full left node before + // the node is split. This routine will make additional room in the buffer for that scenario + + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + char *pNewKeyPos; // pointer to position in record for new key composite + char *pTrg; + + xbInt16 iNewKeyPos = 8; // position in data block where new key begins. + // is the position of the record number, where the fmt is + // [four byte rec number][actual key data] repeats + + xbMdxTag * npTag = (xbMdxTag *) vpTag; + xbInt16 iCopyLen; + + try{ + xbInt32 lKeyCnt = GetKeyCount( npNode ); + +// std::cout << "InsertNodeL Keycount = " << lKeyCnt << "\n"; +// next line is correct, this aligns with db7 +// "4" is the four byte record number stored to the left of the key +// xbInt16 iKeyPos = 4 + iSlotNo * npTag->iKeyItemLen; + + iNewKeyPos += (iSlotNo * npTag->iKeyItemLen); + + // length of number of keys that need to be moved to the right + if( iSlotNo < lKeyCnt ) + iCopyLen = (lKeyCnt - iSlotNo) * npTag->iKeyItemLen; + else + iCopyLen = 0; + + // +8 is to include the first two 4 byte fields in the block + // xbUInt32 ulRqdBufSize = (xbUInt32) (iKeyPos + (npTag->iKeyItemLen * 2) + iCopyLen + 8); + xbUInt32 ulRqdBufSize = (xbUInt32) ((lKeyCnt + 1) * npTag->iKeyItemLen) + 8; + +/* + std::cout << "InsertNodeL CopyLen = " << iCopyLen << "\n"; + std::cout << "InsertNodeL iNewKeyPos = " << iNewKeyPos << "\n"; + std::cout << "InsertNodeL SlotNo = " << iSlotNo << "\n"; + std::cout << "InsertNodeL lKeyCnt = " << lKeyCnt << "\n"; + std::cout << "InsertNodeL node buf size = " << npNode->ulBufSize << "\n"; + std::cout << "InsertNodeL key item len = " << npTag->iKeyItemLen << "\n"; + std::cout << "InsertNodeL key len = " << npTag->iKeyLen << "\n"; + std::cout << "required buf size = " << ulRqdBufSize << "\n"; + std::cout << "InsertNodeL key buf strlen = " << strlen( npTag->cpKeyBuf ) << "\n"; +*/ + + if( ulRqdBufSize > npNode->ulBufSize ){ + npNode->ulBufSize += (xbUInt32) npTag->iKeyItemLen; + npNode->cpBlockData = (char *) realloc( npNode->cpBlockData, (size_t) npNode->ulBufSize ); + if( !npNode->cpBlockData ){ + iErrorStop = 100; + iRc = XB_NO_MEMORY; + throw iRc; + } + } + + // if not appending to end, move things right + + pNewKeyPos = npNode->cpBlockData; + pNewKeyPos += iNewKeyPos; + + if( iSlotNo < lKeyCnt ) { + // pKeyPos = npNode->cpBlockData; + // pKeyPos += iKeyPos; + // pKeyPos += iNewKeyPos; + // pTrg = pKeyPos; + pTrg = pNewKeyPos; + pTrg += npTag->iKeyItemLen; + memmove( pTrg, pNewKeyPos, (size_t) iCopyLen ); + + } + + // write rec number + ePutUInt32( pNewKeyPos, ulPtr ); + + // write the key value + pTrg = pNewKeyPos; + pTrg += 4; + char * pSrc = cpKeyBuf; + for( xbInt16 i = 0; i < npTag->iKeyLen; i++ ) + *pTrg++ = *pSrc++; + + // update number of keys on the node + ePutInt32( npNode->cpBlockData, ++lKeyCnt ); +// std::cout << "lKeyCntA = " << GetKeyCount( npNode ) << "\n"; + + + // determine length of node, zap everything to the right of it + xbUInt32 iStartPos = 8 + ((xbUInt32) lKeyCnt * (xbUInt32) npTag->iKeyItemLen ); + xbUInt32 iClearLen = npNode->ulBufSize - iStartPos; + +// std::cout << "InsertNodeL SP = " << iStartPos << " clear len = " << iClearLen << " ulBufsize = " << npNode->ulBufSize << "\n"; + char *p = npNode->cpBlockData; + p += iStartPos; + memset( p, 0x00, iClearLen ); + + // write out the updated block to disk + if(( iRc = WriteBlock( npNode->ulBlockNo, GetBlockSize(), npNode->cpBlockData )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + +// std::cout << "lKeyCntB = " << GetKeyCount( npNode ) << "\n"; + + + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbIxMdx::InsertNodeL() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return iRc; +} +/***********************************************************************/ +inline xbBool xbIxMdx::IsLeaf( void *vpTag, xbIxNode *npNode ) const{ + + // for performance reasons, does no data checking + // will result in potentially hard to find segfaults if passing invalid npNode + + xbMdxTag *mTag = (xbMdxTag *) vpTag; + char *p = npNode->cpBlockData; + + xbInt32 lNoOfKeys = eGetInt32( p ); + // mdx interior nodes have a sibling number to the right of the right most key in the node + p+=8; + p+= mTag->iKeyItemLen * lNoOfKeys; + + // printf( "IsLeaf p = [%d] b1 = [%x] keylen = [%d]\n", eGetUInt32( p ), *p, mTag->iKeyItemLen ); + + if( eGetUInt32( p ) == 0 ){ + // std::cout << "leaf node\n"; + return true; + } else { + // std::cout << "interior node\n"; + return false; + } +} + +/***********************************************************************/ +xbInt16 xbIxMdx::KeyExists( void * vpTag ) +{ + // this method assumes the key has already been built + + xbMdxTag *mpTag = (xbMdxTag *) vpTag; + xbInt16 iRc = FindKey( vpTag, mpTag->cpKeyBuf, mpTag->iKeyLen, 0 ); + + // iRc == 0 ? return 1 : return 0; + + if( iRc == 0 ) + return 1; + else + return 0; + +} + +/***********************************************************************/ +//! @brief Set position for key add. +/*! + This routine is called by the AddKey() method and is used to position + the node chain to the position the new key should be added to the index. + + \param npTag Pointer to npTag. + \param ulAddRecNo Record number to add. + \returns Return Codes +*/ +xbInt16 xbIxMdx::KeySetPosAdd( xbMdxTag *mpTag, xbUInt32 ulAddRecNo ){ + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + try{ + + iRc = FindKey( mpTag, mpTag->cpKeyBuf, mpTag->iKeyLen, 0 ); + if( iRc == XB_NOT_FOUND || iRc == XB_EMPTY ) + return XB_NO_ERROR; // good position + else if( iRc != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + // get here if key was found, get the right most instance of any non unique key for append, find correct spot for update + if( GetUnique( mpTag ) == 0 ){ + + xbUInt32 ulCurRecNo; + if(( iRc = GetDbfPtr( mpTag, mpTag->npCurNode->iCurKeyNo, mpTag->npCurNode, ulCurRecNo )) != XB_NO_ERROR ){ + iErrorStop = 110; + throw iRc; + } + xbBool bKeysMatch = xbTrue; + + while( bKeysMatch && ulAddRecNo > ulCurRecNo && iRc == XB_NO_ERROR ){ + if(( iRc = GetNextKey( mpTag, 0 )) == XB_NO_ERROR ){ + if( memcmp( GetKeyData( mpTag->npCurNode, mpTag->npCurNode->iCurKeyNo, mpTag->iKeyItemLen ), mpTag->cpKeyBuf, (size_t) mpTag->iKeyLen )) + bKeysMatch = xbFalse; + else{ + if(( iRc = GetDbfPtr( mpTag, mpTag->npCurNode->iCurKeyNo, mpTag->npCurNode, ulCurRecNo )) != XB_NO_ERROR ){ + iErrorStop = 120; + throw iRc; + } + } + } + } + } + if( iRc == XB_EOF ){ // eof condition + if(( iRc = GetLastKey( 0, mpTag, 0 )) != XB_NO_ERROR ){ + iErrorStop = 130; + throw iRc; + } + mpTag->npCurNode->iCurKeyNo++; + return XB_NO_ERROR; + } + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbIxMdx::KeySetPosAdd() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return iRc; +} + +/***********************************************************************/ +//! @brief Set position for key delete. +/*! + This routine is called by the DeleteKey() method and is used to position + the node chain to the position the old key should be deleted from the index. + + \param npTag Pointer to npTag. + \returns Return Codes +*/ + +xbInt16 xbIxMdx::KeySetPosDel( xbMdxTag *npTag ){ + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + xbString sMsg; + + try{ + + iRc = FindKey( npTag, npTag->cpKeyBuf2, npTag->iKeyLen, 0 ); + if( iRc == XB_NOT_FOUND || iRc == XB_EMPTY ) + return XB_NO_ERROR; // good pos ition + else if( iRc != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + + xbUInt32 ulIxRecNo; + if(( iRc = GetDbfPtr( npTag, npTag->npCurNode->iCurKeyNo, npTag->npCurNode, ulIxRecNo )) != XB_NO_ERROR ){ + iErrorStop = 110; + throw iRc; + } + + if( ulIxRecNo == dbf->GetCurRecNo()) + return XB_NO_ERROR; + + if( GetUnique( npTag ) == 1 ){ + iErrorStop = 120; + iRc = XB_NOT_FOUND; + throw iRc; + } + + xbBool bFound = xbFalse; + xbBool bKeysMatch = xbTrue; + while( bKeysMatch && !bFound && iRc == XB_NO_ERROR ){ + + if(( iRc = GetNextKey( npTag, 0 )) != XB_NO_ERROR ){ + iErrorStop = 130; + throw iRc; + } + if( memcmp( GetKeyData( npTag->npCurNode, npTag->npCurNode->iCurKeyNo, npTag->iKeyItemLen ), npTag->cpKeyBuf2, (size_t) npTag->iKeyLen )){ + bKeysMatch = xbFalse; + } else { + if(( iRc = GetDbfPtr( npTag, npTag->npCurNode->iCurKeyNo, npTag->npCurNode, ulIxRecNo )) != XB_NO_ERROR ){ + iErrorStop = 140; + throw iRc; + } + if( ulIxRecNo == dbf->GetCurRecNo()) + bFound = xbTrue; + } + } + + if( bFound ) + return XB_NO_ERROR; + else + return XB_NOT_FOUND; + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbIxMdx::KeySetPosDel() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return iRc; +} + +/***********************************************************************/ +//! @brief Returns key update status. +/*! + \param vpTag Tag to check status on. + \returns xbtrue - Key was updated.
xbFalse - Key not updated. +*/ +/* +inline xbBool xbIxMdx::KeyFiltered( void *vpTag ) const{ + + xbMdxTag *mpTag = (xbMdxTag *) vpTag; + return mpTag->bKeyFiltered; +} +*/ +/***********************************************************************/ +xbInt16 xbIxMdx::LoadTagDetail( xbInt16 iOption, xbMdxTag *tte ){ + + // option 1 - Load the entire tag detail + // option 2 - Load the dynamic variables only + + // std::cout << "LoadTagDetail() iOption = " << iOption << "\n"; + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + size_t iReadSize; + char *buf = NULL; + char *p; + + try{ + // set the read length based on the option + if( iOption == 1 ) + iReadSize = 1024; + + else if( iOption == 2 ) + // iReadSize = 4; + iReadSize = 260; + else{ + iRc = XB_INVALID_OPTION; + iErrorStop = 100; + throw iRc; + } + if(( buf = (char *) calloc( 1, (size_t) iReadSize )) == NULL ){ + iErrorStop = 110; + iRc = XB_NO_MEMORY; + throw iRc; + } + if(( iRc = ReadBlock( tte->ulTagHdrPageNo,(xbUInt32) (GetBlockSize() / (xbUInt16) iBlockFactor), + iReadSize, buf )) != XB_NO_ERROR ){ + free( buf ); + iErrorStop = 30; + throw iRc; + } + + p = buf; + tte->ulRootPage = eGetUInt32( p ); + + if( iOption == 1 ){ + p+=4; + tte->ulTagSize = eGetUInt32( p ); + p+=4; + tte->cKeyFmt2 = *p; + p++; + tte->cKeyType2 = *p; + p+=3; + tte->iKeyLen = eGetInt16( p ); + p+=2; + tte->iKeysPerBlock = *p; + p+=2; + tte->iSecKeyType = eGetInt16( p ); + p+=2; + tte->iKeyItemLen = eGetInt16( p ); + p+=2; + tte->cSerialNo = *p; + p+=3; + tte->cUnique = *p; + p++; + + // next line assumes expression is a null terminated string in the block + tte->sKeyExp = new xbString(); + tte->sKeyExp->Sprintf( "%s", p ); + + p+=221; + tte->cHasFilter = *p; + p+=1; + tte->cHasKeys = *p; + p+=2; + tte->ulLeftChild = eGetUInt32( p ); + p+=4; + tte->ulRightChild = eGetUInt32( p ); + p+=5; + tte->cTagYY = *p; + p++; + tte->cTagMM = *p; + p++; + tte->cTagDD = *p; + // p+=223; + + p+=221; + tte->cKeyFmt3 = *p; + +//for( int i = 0; i < 5; i++ ) +// printf( "%d [%x]\n", i, *p++ ); +// p+=2; + + if( tte->cHasFilter ){ + p+=282; + tte->sFiltExp = new xbString(); + tte->sFiltExp->Sprintf( "%s", p ); + tte->filter = new xbExp( xbase, dbf ); + if(( iRc = tte->filter->ParseExpression( tte->sFiltExp->Str())) != XB_NO_ERROR ){ + iErrorStop = 120; + throw iRc; + } + } + tte->npNodeChain = NULL; + tte->npCurNode = NULL; + tte->cpKeyBuf = (char *) malloc( (size_t) tte->iKeyLen + 1 ); + tte->cpKeyBuf2 = (char *) malloc( (size_t) tte->iKeyLen + 1 ); + tte->exp = new xbExp( xbase, dbf ); + if(( iRc = tte->exp->ParseExpression( tte->sKeyExp->Str() )) != XB_NO_ERROR ){ + iErrorStop = 130; + throw iRc; + } + } else if( iOption == 2 ){ + // refresh the dynamic tag variables + p+=4; + tte->ulTagSize = eGetUInt32( p ); + p+= 16; + tte->cSerialNo = *p; + p+= 226; + tte->cHasKeys = *p; + p+=2; + tte->ulLeftChild = eGetUInt32( p ); + p+=4; + tte->ulRightChild = eGetUInt32( p ); + p+=5; + tte->cTagYY = *p; + p++; + tte->cTagMM = *p; + p++; + tte->cTagDD = *p; + } + if( buf ) + free( buf ); + + } + catch (xbInt16 iRc ){ + if( buf ) + free( buf ); + xbString sMsg; + sMsg.Sprintf( "xbIxMdx::LoadTagDetail() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return iRc; +} + +/***********************************************************************/ +xbInt16 xbIxMdx::LoadTagTable() +{ + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + char * buf = NULL; + + //std::cout << "xbIxMdx::LoadTagTable() tag use cnt = " << iTagUseCnt << "\n"; + + try{ + + if( iTagUseCnt > 46 ){ + iErrorStop = 100; + iRc = XB_INVALID_INDEX; + throw iRc; + } + + xbInt16 iBufSize = (xbInt16) iTagLen * iTagUseCnt; + + if(( buf = (char *) malloc( (size_t) iBufSize )) == NULL ){ + iErrorStop = 110; + iRc = XB_NO_MEMORY; + throw iRc; + } + + if(( iRc = xbFseek( 544, SEEK_SET )) != XB_NO_ERROR ){ + iErrorStop = 120; + throw iRc; + } + + if(( iRc = xbFread( buf, (size_t) iBufSize, 1 )) != XB_NO_ERROR ){ + iErrorStop = 130; + throw iRc; + } + + xbInt16 iPos; + char *p; + xbMdxTag *tte; + xbMdxTag *ttel = NULL; + + for( xbInt16 i = 0; i < iTagUseCnt; i++ ){ + iPos = i * iTagLen; + p = buf + iPos; + + if(( tte = (xbMdxTag *) calloc( 1, (size_t) sizeof( xbMdxTag ))) == NULL ){ + iErrorStop = 140; + iRc = XB_NO_MEMORY; + throw iRc; + } + + // set the current tag to the first tag in the table + if( !vpCurTag ) + xbIx::SetCurTag( (void *) tte ); + + if( mdxTagTbl ) + ttel->next = tte; + else + mdxTagTbl = tte; + // tte->sKeyExp = new xbString(); + + ttel = tte; + tte->next = NULL; + tte->ulTagHdrPageNo = eGetUInt32( p ); + + p += 4; + for( xbUInt32 i = 0; i < 11; i ++ ) + tte->cTagName[i] = *p++; + + tte->cTagName[11] = 0x00; + tte->cKeyFmt = *p++; + tte->cLeftChild = *p++; + tte->cRightChild = *p++; + tte->cParent = *p++; + tte->c2 = *p++; + tte->cKeyType = *p; + tte->sTagName = new xbString(); + tte->sTagName->Set( tte->cTagName ); + tte->sTagName->Trim(); + + if(( iRc = LoadTagDetail( 1, tte )) != XB_NO_ERROR ){ + iErrorStop = 150; + throw iRc; + } + } + if( buf ) + free( buf ); + } + catch (xbInt16 iRc ){ + if( buf ) + free( buf ); + xbString sMsg; + sMsg.Sprintf( "xbIxMdx::LoadTagTable() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + if( iErrorStop == 100 ){ + sMsg.Sprintf( "xbIxMdx::LoadTagTable() Invalid Tag Count: %d", iTagUseCnt ); + xbase->WriteLogMessage( sMsg.Str()); + } + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return iRc; +} + +/**************************************************************************************************************/ +//! @brief Calculate the block number for a given page. +/*! + This routine is called by any function needing to calculate the block number for a given page. + Page numbers are stored internally in the physical file, and the library reads and writes in + blocks of one or more pages. + + Assumes valid data input + + \param ulPageNo Page Number + \returns Calculated block number. +*/ + +inline xbUInt32 xbIxMdx::PageToBlock( xbUInt32 ulPageNo ){ + return ulPageNo / (xbUInt32) iBlockFactor; +} + + + +/**************************************************************************************************************/ +#ifdef XB_DEBUG_SUPPORT +xbInt16 xbIxMdx::PrintKey( void *vpTag, xbIxNode *npNode, xbInt16 iKeyNo, xbInt16 iDepth, char cType, xbInt16 iOutputOpt ){ + + xbString sPre; + sPre.Sprintf( "%c ", cType ); + for( xbInt16 i = 0; i < iDepth; i++ ) + sPre += "|"; + + xbString sPost; + sPost.Sprintf( "\tThisBlock=[%ld] KeyNo=[%d] Depth=[%d]", npNode->ulBlockNo, iKeyNo, iDepth ); + + xbMdxTag * mpTag = (xbMdxTag *) vpTag; + char *p = npNode->cpBlockData + (8 + (iKeyNo * mpTag->iKeyItemLen )); + + xbString sKeyPtr; + xbUInt32 ulNoOfKeys = 0; + if( cType == 'I' ) { // interior + sKeyPtr.Sprintf( " ptr=[%ld]", eGetUInt32( p )); + ulNoOfKeys = eGetUInt32( mpTag->npCurNode->cpBlockData ); + } + else if( cType == 'L' ) // leaf + sKeyPtr.Sprintf( " rec=[%ld]", eGetUInt32( p )); + p += 4; + + xbString s; + if(( cType == 'I' && iKeyNo < (xbInt16) ulNoOfKeys) || cType == 'L' ){ + if( mpTag->cKeyType2 == 'C' ){ //CHAR + for( xbInt32 l = 0; l < (mpTag->iKeyItemLen-4); l++ ) + s += *p++; + + } else if( mpTag->cKeyType2 == 'N' ){ // NUMERIC + xbBcd bcd( p ); + xbString s2; + bcd.ToString( s2 ); + s += s2; + + } else if( mpTag->cKeyType2 == 'D' ){ // DATE + xbInt32 lDate = (xbInt32) eGetDouble( p ); + xbDate d( lDate ); + //xbString s2; + //d.JulToDate8( lDate, s2 ); + s.Sprintf( "%s%s", s.Str(), d.Str()); + } + } else { + s = "Rightmost InteriorNode Pointer"; + } + + xbString sOut( sPre ); + sOut += s; + sOut += sPost; + sOut += sKeyPtr; + + xbase->WriteLogMessage( sOut, iOutputOpt ); + return XB_NO_ERROR; +} +#endif + +/***********************************************************************/ +//! @brief ReadHeadBlock. +/*! + Read values off head block in MDX file + \param iOpt 0 - Read entire block, initialize as needed.
+ 1 - Read in only dynamic section of block
+ \returns Return Codes + +*/ + +xbInt16 xbIxMdx::ReadHeadBlock( xbInt16 iOpt ) +{ + + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + +// std::cout << "ReadHeadBlock() option = " << iOpt << "\n"; + + try{ + if( !FileIsOpen()){ + iRc = XB_NOT_OPEN; + iErrorStop = 100; + throw iRc; + } + char sBuf[48]; + memset( sBuf, 0x00, 48 ); + + if( iOpt == 0 ){ + if(( iRc = xbFseek( 0, SEEK_SET )) != XB_NO_ERROR ){ + iErrorStop = 110; + throw iRc; + } + + if(( iRc = xbFread( sBuf, 47, 1 )) != XB_NO_ERROR ){ + iErrorStop = 120; + throw iRc; + } + } else { + + if(( iRc = xbFseek( 28, SEEK_SET )) != XB_NO_ERROR ){ + iErrorStop = 130; + throw iRc; + } + + if(( iRc = xbFread( sBuf, 19, 1 )) != XB_NO_ERROR ){ + iErrorStop = 140; + throw iRc; + } + } + + char *p = sBuf; + if( iOpt == 0 ){ + cVersion = *p++; + cCreateYY = *p++; + cCreateMM = *p++; + cCreateDD = *p++; + sFileName.Assign( p, 1, 16 ); + p+=16; + iBlockFactor = eGetInt16( p ); + p+=2; + SetBlockSize( (xbUInt32) eGetInt16( p )); + p+=2; + cProdIxFlag = *p++; + cTagEntryCnt = *p++; + iTagLen = *p; + p+=2; + + iTagUseCnt = eGetInt16( p ); + //lTagUseCnt = eGetInt32( p ); + //p+=4; + p+=2; + cNextTag = *p++; + c1B = *p++; + + ulPageCnt = eGetUInt32( p ); + p+=4; + ulFirstFreePage = eGetUInt32( p ); + p+=4; + ulNoOfBlockAvail = eGetUInt32( p ); + p+=4; + cUpdateYY = *p++; + cUpdateMM = *p++; + cUpdateDD = *p; + + if( cNodeBuf ) + free( cNodeBuf ); + + if(( cNodeBuf = (char *) malloc( (size_t) GetBlockSize())) == NULL ){ + iErrorStop = 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.
+ NULL - Reindex all tags
+ \returns Return Codes + + If this method fails, the index is left in an undefined state + +*/ + +xbInt16 xbIxMdx::Reindex( void **vpTag ){ + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + xbMdxTag * mpTag; + #ifdef XB_LOCKING_SUPPORT + 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.
+ R - Right child.
+ P - Parent. + \param iWhich - Which tag to update + \param cVal - Value to set. + \returns void +*/ + +/* +void xbIxMdx::SetTagPtr( char cPtr, xbInt16 iWhich, char cVal ){ + + xbMdxTag *mpTag = (xbMdxTag *) GetTag( iWhich ); + if( mpTag ){ + switch( cPtr ){ + case 'L': + mpTag->cLeftChild = cVal; + break; + case 'R': + mpTag->cRightChild = cVal; + break; + case 'P': + mpTag->cParent = cVal; + break; + } + } +} +*/ + +/***********************************************************************/ +//! @brief Split an interior node +/*! + + This routine splits an interior node into two nodes, divided by dSplitFactor.
+ This behaves differently than V7 Dbase. V7 does not balance the nodes.
+ For V7, if adding a key to the end of a node, it will create a right node + with only one key, and the left node is still full.

+ + Possible performance improvement options.
+ Two modes when splitting:
+ a) Split nodes in the middle - good for random access applications
+ b) Split off right node with only one key - good for applications with + expectation of ascending keys added moving forward.
+ + This routine first inserts the key into the left node in the appropriate location + then splits the node based on the split factor setting. + + \param vpTag Tag in play. + \param npLeft Left node to split. + \param npRight Right node to split. + \param iSlotNo Slot number for split. + \param ulPtr Pointer number to insert. + \returns Return Codes +*/ + +xbInt16 xbIxMdx::SplitNodeI( void *vpTag, xbIxNode * npLeft, xbIxNode *npRight, xbInt16 iSlotNo, xbUInt32 ulPtr ){ + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + xbMdxTag * npTag = (xbMdxTag *) vpTag; + xbDouble dSplitFactor = .5; // split the nodes 50/50 + xbInt16 iLen; + char *pSrc; + char *pTrg; + + + try{ + xbInt32 lKeyCnt = GetKeyCount( npLeft ); + xbInt32 lNewLeftKeyCnt = (xbInt32) ((lKeyCnt + 1) * dSplitFactor); + xbInt32 lNewRightKeyCnt = lKeyCnt - lNewLeftKeyCnt; + if(( iRc = InsertNodeI( vpTag, npLeft, iSlotNo, ulPtr )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + + // move the right half of the left node to the right node + pSrc = npLeft->cpBlockData; + pSrc += 8 + ((lNewLeftKeyCnt+1) * npTag->iKeyItemLen); + pTrg = npRight->cpBlockData; + pTrg += 8; + iLen = (lNewRightKeyCnt * npTag->iKeyItemLen) + 4; + memmove( pTrg, pSrc, (size_t) iLen ); + + // eliminate chattle on the right + iLen = 12 + (lNewLeftKeyCnt * npTag->iKeyItemLen); + pSrc = npLeft->cpBlockData; + pSrc += iLen; + memset( pSrc, 0x00, npLeft->ulBufSize - (xbUInt32) iLen ); + + // write the new key counts into the nodes + pTrg = npLeft->cpBlockData; + ePutInt32( pTrg, lNewLeftKeyCnt ); + pTrg = npRight->cpBlockData; + ePutInt32( pTrg, lNewRightKeyCnt ); + + // write out the block + if(( iRc = WriteBlock( npLeft->ulBlockNo, GetBlockSize(), npLeft->cpBlockData )) != XB_NO_ERROR ){ + iErrorStop = 110; + throw iRc; + } + // write out the block + if(( iRc = WriteBlock( npRight->ulBlockNo, GetBlockSize(), npRight->cpBlockData )) != XB_NO_ERROR ){ + iErrorStop = 120; + throw iRc; + } + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "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.
+ + Possible performance improvement options.
+ Two modes when splitting:
+ a) Split nodes in the middle - good for random access applications
+ b) Split off right node with only one key - good for applications with + expectation of ascending keys added moving forward.
+ + This routine first inserts the key into the left node in the appropriate location + then splits the node based on the split factor setting. + + \param vpTag Tag in play. + \param npLeft Left node to split. + \param npRight Right node to split. + \param iSlotNo Slot number for split. + \param ulPtr Pointer number to insert. + \returns Return Codes +*/ + +xbInt16 xbIxMdx::SplitNodeL( void *vpTag, xbIxNode * npLeft, xbIxNode *npRight, + xbInt16 iSlotNo, char * cpKeyBuf, xbUInt32 ulPtr ){ + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + xbDouble dSplitFactor = .5; // can adjust performance with this number + xbMdxTag *mpTag = (xbMdxTag *) vpTag; + xbString sMsg; + + xbInt16 iLen; + char *pSrc; + char *pTrg; + + // std::cout << "In xbIxMdx::SplitNodeL()\n"; + try{ + xbInt32 lKeyCnt = GetKeyCount( npLeft ); + xbInt32 lNewLeftKeyCnt = (xbInt32) ((lKeyCnt + 1) * dSplitFactor) + 1; + xbInt32 lNewRightKeyCnt = lKeyCnt + 1 - lNewLeftKeyCnt; + + if(( iRc = InsertNodeL( vpTag, npLeft, iSlotNo, cpKeyBuf, ulPtr )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + + // move right half off of left node to the right node + pSrc = npLeft->cpBlockData; + pSrc += 8 + (lNewLeftKeyCnt * mpTag->iKeyItemLen); + pTrg = npRight->cpBlockData; + pTrg += 8; + iLen = lNewRightKeyCnt * mpTag->iKeyItemLen; + memmove( pTrg, pSrc, (size_t) iLen ); + + // write the new key counts into the nodes + pTrg = npLeft->cpBlockData; + ePutInt32( pTrg, lNewLeftKeyCnt ); + pTrg = npRight->cpBlockData; + ePutInt32( pTrg, lNewRightKeyCnt ); + + // zero out the next key number so this node is not confused with interior node + iLen = 8 + (lNewLeftKeyCnt * mpTag->iKeyItemLen); + pSrc = npLeft->cpBlockData; + pSrc += iLen; + memset( pSrc, 0x00, npLeft->ulBufSize - (xbUInt32) iLen ); + + // write out the left block + if(( iRc = WriteBlock( npLeft->ulBlockNo, GetBlockSize(), npLeft->cpBlockData )) != XB_NO_ERROR ){ + iErrorStop = 110; + throw iRc; + } + // write out the right block + if(( iRc = WriteBlock( npRight->ulBlockNo, GetBlockSize(), npRight->cpBlockData )) != XB_NO_ERROR ){ + iErrorStop = 120; + throw iRc; + } + + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbIxMdx::SplitNodeL() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return iRc; +} +/**************************************************************************************************************/ +//! @brief TagSerialNo. +/*! + This routine is used internally for reading or updating the serial number on a given tag when the tag. + + \param iOption 1 - Read tag serial number off disk, save in structure
+ 2 - Write serial number from memory to disk
+ 3 - Read serial number off disk, increment, write updated number to disk
+ mpTag - Pointer to tag for serial number update + \returns Return Codes +*/ + +xbInt16 xbIxMdx::TagSerialNo( xbInt16 iOption, xbMdxTag * mpTag ){ + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + + try{ + + xbInt64 lPos = (mpTag->ulTagHdrPageNo * 512) + 20; + +// std::cout << "UpdateSerialNo offset = " << lPos << " option = " << iOption << "\n"; + + if( iOption != 2 ){ + if(( iRc = xbFseek( lPos, SEEK_SET )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + if(( iRc = xbFgetc( mpTag->cSerialNo )) != XB_NO_ERROR ){ + iErrorStop = 110; + throw iRc; + } + } + + if( iOption == 3 ) + mpTag->cSerialNo++; + + if( iOption != 1 ){ + if(( iRc = xbFseek( lPos, SEEK_SET )) != XB_NO_ERROR ){ + iErrorStop = 120; + throw iRc; + } + if(( iRc = xbFwrite( &mpTag->cSerialNo, 1, 1 )) != XB_NO_ERROR ){ + iErrorStop = 130; + throw iRc; + } + } + + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbIxMdx::UpdateSerialNo() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return iRc; +} + + +/***********************************************************************/ +//! @brief UpdateTagKey +/*! + This routine updates a key or a given tag. + The file header is considered to be the first 2048 bytes in the file. + + \param cAction A - Add a key.
+ D - Delete a key.
+ R - Revise a key.
+ \param vpTg - Pointer to tag.
+ \param ulRecNo - Record number association with the action.
+ \returns Return Codes +*/ + +xbInt16 xbIxMdx::UpdateTagKey( char cAction, void *vpTag, xbUInt32 ulRecNo ){ + + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + xbMdxTag *npTag = (xbMdxTag *) vpTag; + + try{ + // save off any needed fields for updating + xbUInt32 ulTagSizeSave = npTag->ulTagSize; + // std::cout << "old size = " << ulTagSizeSave << " new size = " << npTag->ulTagSize << "\n"; + //std::cout << "UpdateTagKey - tag size was updated need to do something here - test \n"; + + + if( cAction == 'D' || cAction == 'R' ){ + // std::cout << "UpdateTagKey-delete going to DeleteKey \n"; + if(( iRc = DeleteKey( vpTag )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + //std::cout << "UpdateTagKey-delete back from DeleteKey \n"; + } + + if( cAction == 'A' || cAction == 'R' ){ + if(( iRc = AddKey( vpTag, ulRecNo )) != XB_NO_ERROR ){ + iErrorStop = 110; + throw iRc; + } + } + if( ulTagSizeSave != npTag->ulTagSize ){ + if(( iRc = UpdateTagSize( npTag, npTag->ulTagSize )) != XB_NO_ERROR) { + iErrorStop = 120; + throw iRc; + } + } + // update the serial number + if(( iRc = TagSerialNo( 3, npTag )) != XB_NO_ERROR ){ + iErrorStop = 130; + throw iRc; + } + + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbIxMdx::UpdateTagKey() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return iRc; +} + +/**************************************************************************************************************/ +//! @brief Write head block. +/*! + This routine updates the MDX file header and commits changes to disk. + The file header is considered to be the first 2048 bytes in the file. + + \param iOption 0 - Entire 2048 byte header, used for creating a new mdx file.
+ 1 - Bytes 28 through 46, used when adding or deleting a tag.
+ 2 - Bytes 32 through 46, used after updating keys in the file. + \returns Return Codes +*/ + + +xbInt16 xbIxMdx::WriteHeadBlock( xbInt16 iOption ){ + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + + try{ + xbDate d; // default is system date, today + cUpdateYY = (char) d.YearOf() - 1900; + cUpdateMM = (char) d.MonthOf(); + cUpdateDD = (char) d.DayOf( XB_FMT_MONTH ); + + if( iOption > 0 ){ + char buf[48]; + memset( buf, 0x00, 48 ); + xbUInt32 ulStartPos = 0; + xbUInt32 ulLen = 0; + + if( iOption == 1 ){ + ePutInt16( &buf[28], iTagUseCnt ); + buf[30] = cNextTag; + buf[31] = 0x1b; + ulStartPos = 28; + ulLen = 19; + } else { + ulStartPos = 32; + ulLen = 16; + } + + ePutUInt32( &buf[32], ulPageCnt ); + ePutUInt32( &buf[36], ulFirstFreePage ); + ePutUInt32( &buf[40], ulNoOfBlockAvail ); + buf[44] = cUpdateYY; + buf[45] = cUpdateMM; + buf[46] = cUpdateDD; + + if(( iRc = xbFseek( ulStartPos, SEEK_SET )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + + if(( iRc = xbFwrite( &buf[ulStartPos], ulLen, 1 )) != XB_NO_ERROR ){ + iErrorStop = 110; + throw iRc; + } + + } else if( iOption == 0 ){ + char buf[2048]; + memset( buf, 0x00, 2048 ); + + buf[0] = cVersion; + cCreateYY = cUpdateYY; + cCreateMM = cUpdateMM; + cCreateDD = cUpdateDD; + buf[1] = cCreateYY; + buf[2] = cCreateMM; + buf[3] = cCreateDD; + + + for( xbUInt32 l = 0; l < sFileName.Len() && l < 10; l++ ){ + buf[l+4] = sFileName[l+1]; + } + + ePutInt16( &buf[20], iBlockFactor ); + ePutInt16( &buf[22], (xbInt16) GetBlockSize() ); + + buf[24] = cProdIxFlag; + buf[25] = cTagEntryCnt; + ePutInt16 ( &buf[26], iTagLen ); + ePutInt16 ( &buf[28], iTagUseCnt ); + buf[30] = cNextTag; + buf[31] = c1B; + ePutUInt32( &buf[32], ulPageCnt ); + ePutUInt32( &buf[36], ulFirstFreePage ); + ePutUInt32( &buf[40], ulNoOfBlockAvail ); + buf[44] = cUpdateYY; + buf[45] = cUpdateMM; + buf[46] = cUpdateDD; + + // not sure what the following "1" is for in a sea of zeroes.... + // maybe it's current tag or default tag or something along those lines? + buf[529] = 0x01; + + xbRewind(); + if(( iRc = xbFwrite( buf, 2048, 1 )) != XB_NO_ERROR ){ + iErrorStop = 120; + throw iRc; + } + } else { + iErrorStop = 130; + iRc = XB_INVALID_OPTION; + throw iRc; + } + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbIxMdx::WriteHeadBlock() Exception Caught. Error Stop = [%d] iRc = [%d] option = [%d]", iErrorStop, iRc, iOption ); + xbase->WriteLogMessage( sMsg ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return iRc; +} +/***********************************************************************/ +xbInt16 xbIxMdx::UpdateTagSize( xbMdxTag *mpTag, xbUInt32 ulTagSz ){ + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + char buf[4]; + try{ + ePutUInt32( &buf[0], ulTagSz ); + if(( iRc = xbFseek( (xbInt64) ((mpTag->ulTagHdrPageNo *512 )+ 4), SEEK_SET )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + if(( iRc = xbFwrite( &buf[0], 4, 1 )) != XB_NO_ERROR ){ + iErrorStop = 110; + throw iRc; + } + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbIxMdx::UpdateTagSize() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return iRc; +} + + +/***********************************************************************/ +//void xbIxMdx::TestStub( char *s, void *vpTag ){ +void xbIxMdx::TestStub( char *, void * ){ +} +/***********************************************************************/ +} /* namespace */ +#endif /* XB_MDX_SUPPORT */ + diff --git a/src/core/xbixndx.cpp b/src/core/xbixndx.cpp 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 Return Codes +*/ + +xbInt16 xbIxNdx::AddKey( void * vpTag, xbUInt32 ulRecNo ){ + + xbNdxTag * npTag; + vpTag ? npTag = (xbNdxTag *) vpTag : npTag = ndxTag; + if( GetUniqueKeyOpt() == XB_EMULATE_DBASE && npTag->bFoundSts ) + return XB_NO_ERROR; + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + xbInt16 iHeadNodeUpdateOpt = 2; + + + try{ + + if(( iRc = xbIxNdx::KeySetPosAdd( npTag, ulRecNo )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + xbInt32 lKeyCnt = GetKeyCount( npTag->npCurNode ); + if( lKeyCnt < npTag->iKeysPerBlock ){ + // Section A - add key to appropriate position if space available + if(( iRc = InsertNodeL( npTag, npTag->npCurNode, npTag->npCurNode->iCurKeyNo, npTag->cpKeyBuf, ulRecNo )) != XB_NO_ERROR ){ + iErrorStop = 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 Return Codes +*/ +xbInt16 xbIxNdx::AddKeyNewRoot( xbNdxTag *npTag, xbIxNode *npLeft, xbIxNode *npRight ){ + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + xbString sMsg; + char *pLastKey = NULL; + + try{ + xbIxNode *npRoot = AllocateIxNode( GetBlockSize() + (xbUInt32) npTag->iKeyItemLen, 1 ); + if( !npRoot ){ + iErrorStop = 100; + iRc = XB_NO_MEMORY; + throw iRc; + } + npTag->ulRootBlock = npRoot->ulBlockNo; + pLastKey = (char *) malloc( (size_t) ndxTag->iKeyLen ); + if(( iRc = GetLastKeyForBlockNo( npTag, npLeft->ulBlockNo, pLastKey )) != XB_NO_ERROR ){ + iErrorStop = 110; + throw iRc; + } + char * pTrg = npRoot->cpBlockData; + + // set no of keys to 1 + ePutUInt32( pTrg, 1 ); + + // set the left node number + pTrg += 4; + ePutUInt32( pTrg, npLeft->ulBlockNo ); + + // set the key + pTrg+= 8; + memcpy( pTrg, pLastKey, (size_t) npTag->iKeyLen ); + + // set the right node number + pTrg+= (npTag->iKeyItemLen - 8); + ePutUInt32( pTrg, npRight->ulBlockNo ); + + // write out the block + if(( iRc = WriteBlock( npRoot->ulBlockNo, GetBlockSize(), npRoot->cpBlockData )) != XB_NO_ERROR ){ + iErrorStop = 120; + throw iRc; + } + if( pLastKey ) + free( pLastKey ); + NodeFree( npRoot ); + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbIxNdx::AddKeyNewRoot() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + if( pLastKey ) + free( pLastKey ); + } + return iRc; +} +/***********************************************************************/ +//! @brief Append node to node chain. +/*! + Append a node to the current node chain for a given tag. + + \param vpTag Tag to update. + \param npNode Node to add to node chain. + \returns void +*/ +void xbIxNdx::AppendNodeChain( void *vpTag, xbIxNode * npNode ){ + + xbNdxTag * npTag; + vpTag ? npTag = (xbNdxTag *) vpTag : npTag = ndxTag; + + if( npTag->npNodeChain == NULL ){ + npTag->npNodeChain = npNode; + npTag->npCurNode = npNode; + } else { + npNode->npPrev = npTag->npCurNode; + npTag->npCurNode->npNext = npNode; + npTag->npCurNode = npNode; + } + // time stamp the node chain + GetFileMtime( npTag->tNodeChainTs ); +} + +/***********************************************************************/ +//! @brief Allocate a node. +/*! + \param ulBufSize Buffer size. + \param iOpt 0 - Don't update the node block number on the node. + 1 - Set node block number to the next available block number. + \returns Pointer to new node. +*/ + +xbIxNode * xbIxNdx::AllocateIxNode( xbUInt32 ulBufSize, xbInt16 iOpt ){ + xbIxNode *n = xbIx::AllocateIxNode( ulBufSize ); + if( n && iOpt == 1 ) n->ulBlockNo = ndxTag->ulTotalBlocks++; + return n; +} +/***********************************************************************/ +//! @brief Check for duplicate key. +/*! + \param vpTag Tag to check. + \returns XB_KEY_NOT_UNIQUE
XB_NO_ERROR +*/ +xbInt16 xbIxNdx::CheckForDupKey( void *vpTag ) +{ + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + xbNdxTag *npTag = (xbNdxTag *) vpTag; + npTag->bFoundSts = xbFalse; + try{ + if( GetUnique()){ + if( npTag->iKeySts == XB_ADD_KEY || npTag->iKeySts == XB_UPD_KEY ) + if( KeyExists( vpTag )){ + if( GetUniqueKeyOpt() == XB_EMULATE_DBASE ){ + npTag->bFoundSts = xbTrue; + return 0; + } else { + return XB_KEY_NOT_UNIQUE; + } + } + } + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbIxNdx::CheckForDupKey() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return iRc; +} + +/***********************************************************************/ +//! @brief Check tag integrity. +/*! + Check a tag for accuracy. + + \param vpTag Tag to create key for. + \param iOpt Output message destination
+ 0 = stdout
+ 1 = Syslog
+ 2 = Both
+ \returns Return Codes +*/ +xbInt16 xbIxNdx::CheckTagIntegrity( void *vpTag, xbInt16 iOpt ){ + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iRc2; + xbInt16 iRc3; + xbInt16 iErrorStop = 0; + xbUInt32 ulIxCnt = 0; + xbUInt32 ulThisRecNo = 0; + xbUInt32 ulPrevRecNo = 0; + xbBool bDone = false; + xbString sMsg; + char cKeyType; + char *pPrevKeyBuf = NULL; + 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.
+ 1 = Append Mode, Create key for an append, only use rec buf 0, set updated switch.
+ 2 = Update Mode, Create old version and new version keys, check if different, set update switch appropriately. + \returns Return Codes +*/ + +xbInt16 xbIxNdx::CreateKey( void * vpTag, xbInt16 iOpt ){ + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + try{ + xbNdxTag * npTag; + vpTag ? npTag = (xbNdxTag *) vpTag : npTag = ndxTag; + + if(( iRc = npTag->exp->ProcessExpression( 0 )) != XB_NO_ERROR ){ + iErrorStop = 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.
xbFalse - Not unique. + \param iOverLay xbTrue - Overlay if file already exists.
xbFalse - Don't overlay. + \param vpTag Output from method Pointer to vptag pointer. + \returns Return Codes +*/ + +xbInt16 xbIxNdx::CreateTag( const xbString &sName, const xbString &sKey, + const xbString &, xbInt16, xbInt16 iUnique, xbInt16 iOverLay, void **vpTag ){ + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + xbNdxTag *npTag = ndxTag; + *vpTag = ndxTag; + + try{ + //xbString sMsg; + SetFileName( sName ); + + if( FileExists() && !iOverLay ) + return XB_FILE_EXISTS; + + if( FileIsOpen()){ + if(( iRc = xbTruncate(0)) != XB_NO_ERROR ){ + iErrorStop = 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 Return Codes +*/ + +xbInt16 xbIxNdx::DeleteFromNode( void *vpTag, xbIxNode * npNode, xbInt16 iSlotNo ) +{ + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + xbNdxTag * npTag; + vpTag ? npTag = (xbNdxTag *) vpTag : npTag = ndxTag; + + try{ + + xbInt32 lKeyCnt = GetKeyCount( npNode ); + xbInt16 iLen = (lKeyCnt - iSlotNo - 1) * npTag->iKeyItemLen; + if( !IsLeaf( vpTag, npNode )) + iLen += 4; + + if( iLen > 0 ){ + char *pTrg = npNode->cpBlockData; + pTrg += (4 + (npTag->iKeyItemLen * (iSlotNo)) ); //lTrgPos; + char *pSrc = pTrg; + pSrc += npTag->iKeyItemLen; + memmove( pTrg, pSrc, (size_t) iLen ); + } + + // set the new number of keys + ePutUInt32( npNode->cpBlockData, (xbUInt32) lKeyCnt - 1 ); + + // write out the block + if(( iRc = WriteBlock( npNode->ulBlockNo, GetBlockSize(), npNode->cpBlockData )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbIxNdx::DeleteFromNode() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return XB_NO_ERROR; +} + +/***********************************************************************/ +//! @brief Delete a key. +/*! + This routine deletes a key. It assumes the key to delete + is the current key in the node chain. + + \param vpTag Tag to delete key on. + + \returns Return Codes +*/ + +xbInt16 xbIxNdx::DeleteKey( void *vpTag ){ + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + xbNdxTag * npTag; + vpTag ? npTag = (xbNdxTag *) vpTag : npTag = ndxTag; + + // save copy of node chain to reset to after delete completed + xbIxNode *npSaveNodeChain = npTag->npNodeChain; + npTag->npNodeChain = NULL; + xbIxNode * npSaveCurNode = npTag->npCurNode; + + try{ + + xbString sMsg; + + if(( iRc = xbIxNdx::KeySetPosDel( npTag )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + // Delete key needs to handle two scenarios + // 1 - if the delete is on the only key of a leaf node, then traverse up the tree, trimming as needed + // 2 - if the last key on a node is deleted, and the key value is not the same as the prev key value + // go up the tree looking for an interior node needing updated key value + + + xbInt32 lOrigKeyCnt = GetKeyCount( npTag->npCurNode ); + if(( iRc = DeleteFromNode( npTag, npTag->npCurNode, npTag->npCurNode->iCurKeyNo )) != XB_NO_ERROR ){ + iErrorStop = 110; + throw iRc; + } + + if( lOrigKeyCnt == 1 ){ + // scenario 1 + xbBool bDone = xbFalse; + xbBool bIsLeaf = xbFalse; + xbInt32 lKeyCnt; + npTag->npCurNode = npTag->npCurNode->npPrev; + + while( npTag->npCurNode && !bDone ){ + lKeyCnt = GetKeyCount( npTag->npCurNode ); + bIsLeaf = IsLeaf( npTag, npTag->npCurNode ); + if( lKeyCnt > 0 ){ + if(( iRc = DeleteFromNode( npTag, npTag->npCurNode, npTag->npCurNode->iCurKeyNo )) != XB_NO_ERROR ){ + iErrorStop = 120; + throw iRc; + } + } + if( (bIsLeaf && lKeyCnt > 1) || (!bIsLeaf && lKeyCnt > 0) ) + bDone = xbTrue; + else + npTag->npCurNode = npTag->npCurNode->npPrev; + } + } else if( npTag->npCurNode->iCurKeyNo == (xbUInt32) lOrigKeyCnt - 1 ){ + + // scenario 2 + // if last two keys identical, then nothing to do, else go up looking for a key to change + if( memcmp( GetKeyData( npTag->npCurNode, npTag->npCurNode->iCurKeyNo, npTag->iKeyItemLen ), + GetKeyData( npTag->npCurNode, npTag->npCurNode->iCurKeyNo-1, npTag->iKeyItemLen ), + (size_t) npTag->iKeyLen )){ + + xbIxNode *pNode = npTag->npCurNode->npPrev; + char *pSrc = GetKeyData( npTag->npCurNode, npTag->npCurNode->iCurKeyNo-1, npTag->iKeyItemLen ); + + while( pNode && pNode->ulBlockNo != npTag->ulRootBlock && pNode->iCurKeyNo == (xbUInt32) GetKeyCount( pNode ) ) + pNode = pNode->npPrev; + if( pNode ){ + if( pNode->iCurKeyNo < (xbUInt32) GetKeyCount( pNode )){ + char *pTrg = pNode->cpBlockData; + pTrg += 12 + (pNode->iCurKeyNo * (xbUInt32) npTag->iKeyItemLen); + for( xbInt16 i = 0; i < npTag->iKeyLen; i++ ) + *pTrg++ = *pSrc++; + + // write out the block + if(( iRc = WriteBlock( pNode->ulBlockNo, GetBlockSize(), pNode->cpBlockData )) != XB_NO_ERROR ){ + iErrorStop = 130; + throw iRc; + } + } + } + } + } + + // restore node chain to pre delete status (which should be post add status) + npTag->npNodeChain = FreeNodeChain( npTag->npNodeChain ); + npTag->npNodeChain = npSaveNodeChain; + npTag->npCurNode = npSaveCurNode; + + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbIxNdx::DeleteKey() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + if( npSaveNodeChain ){ + npTag->npNodeChain = npSaveNodeChain; + npSaveNodeChain = FreeNodeChain( npSaveNodeChain ); + npTag->npCurNode = npSaveCurNode; + } + } + return iRc; +} + + +/***********************************************************************/ +//! @brief Delete tag. +/*! + In the case of an ndx tag, it deletes the ndx file as it contains + only one tag. + \returns Return Codes +*/ + +xbInt16 xbIxNdx::DeleteTag( void * ){ + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + //xbNdxTag * npTag; + //vpTag ? npTag = (xbNdxTag *) vpTag : npTag = ndxTag; + + try{ + + // if open, close it + if( FileIsOpen()){ + if(( iRc = Close()) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + } + // delete file + if(( iRc = xbRemove()) != XB_NO_ERROR ){ + iErrorStop = 110; + throw iRc; + } + + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbIxNdx::DeleteTag() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return iRc; +} +/***********************************************************************/ +#ifdef XB_DEBUG_SUPPORT + +//! @brief Dump a block for a given tag. +/*! + Dump blocks for given tag for debugging purposes. + \param iOpt Output message destination
+ 0 = stdout
+ 1 = Syslog
+ 2 = Both
+ \param vpTag - Not required for single tag NDX files. + \returns void +*/ + +//xbInt16 xbIxNdx::DumpTagBlocks( xbInt16 iOpt, void *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
+ 0 = stdout
+ 1 = Syslog
+ 2 = Both
+ \returns Return Codes +*/ + +xbInt16 xbIxNdx::DumpHeader( xbInt16 iOpt, xbInt16 ){ + xbString s; + xbInt16 iRc; + + if(( iRc = ReadHeadBlock( 1 )) != XB_NO_ERROR ) + return iRc; + + s.Sprintf( "Index Header Node for %s", GetFileName().Str()); + xbase->WriteLogMessage( s, iOpt ); + s.Sprintf( "--------------------------------" ); + xbase->WriteLogMessage( s, iOpt ); + s.Sprintf( "Root block = %ld", ndxTag->ulRootBlock ); + xbase->WriteLogMessage( s, iOpt ); + s.Sprintf( "Total blocks = %ld", ndxTag->ulTotalBlocks ); + xbase->WriteLogMessage( s, iOpt ); + s.Sprintf( "Key types = %c,%d", ndxTag->cKeyType, ndxTag->iKeyType ); + xbase->WriteLogMessage( s, iOpt ); + s.Sprintf( "Key Length = %d", ndxTag->iKeyLen ); + xbase->WriteLogMessage( s, iOpt ); + s.Sprintf( "Keys Per Block = %d", ndxTag->iKeysPerBlock ); + xbase->WriteLogMessage( s, iOpt ); + s.Sprintf( "Key Item Len = %ld", ndxTag->iKeyItemLen ); + xbase->WriteLogMessage( s, iOpt); + s.Sprintf( "Serial No = %d", ndxTag->cSerNo ); + xbase->WriteLogMessage( s, iOpt); + s.Sprintf( "Unique = %d", ndxTag->iUnique ); + xbase->WriteLogMessage( s, iOpt ); + s.Sprintf( "KeyExpression = %s", ndxTag->sKeyExpression.Str() ); + xbase->WriteLogMessage( s, iOpt ); + return XB_NO_ERROR; +} +/***********************************************************************/ +//! @brief Dump the index for a tag. +/*! + Stub. + \returns XB_NO_ERROR +*/ +xbInt16 xbIxNdx::DumpIxForTag( void *, xbInt16 ) +{ + return XB_NO_ERROR; +} +/***********************************************************************/ +//! @brief Dump the index node chain. +/*! + Dump the index node chain for debugging purposes. + \param vpTag Tag of node chain to dump. + \param iOpt Output message destination
+ 0 = stdout
+ 1 = Syslog
+ 2 = Both
+ \returns void +*/ +void xbIxNdx::DumpIxNodeChain( void *vpTag, xbInt16 iOpt ) const +{ + + xbNdxTag * npTag; + vpTag ? npTag = (xbNdxTag *) vpTag : npTag = ndxTag; + + xbString s( "Dump Node Chain" ); + xbase->WriteLogMessage( s, iOpt ); + + if( npTag->npNodeChain ){ + xbIxNode *n = npTag->npNodeChain; + xbInt16 iCtr = 0; + char cLeaf; + s.Sprintf( "Cnt\tThis Prev Next CurKeyNo BlockNo NoOfKeys Type" ); + xbase->WriteLogMessage( s, iOpt ); + while( n ){ + IsLeaf( vpTag, n ) ? cLeaf = 'L' : cLeaf = 'I'; + s.Sprintf( "%d\t%08x %08x %08x %08d %08d %08d %c", + iCtr++, n, n->npPrev, n->npNext, n->iCurKeyNo, + n->ulBlockNo, eGetUInt32( n->cpBlockData ), cLeaf ); + xbase->WriteLogMessage( s, iOpt ); + n = n->npNext; + } + } else { + s = "Empty Node Chain"; + xbase->WriteLogMessage( s, iOpt ); + } +} +/***********************************************************************/ +//! @brief Dump node. +/*! + Dump a node for debugging purposes. + \param vpTag Tag of node chain to dump. + \param pNode Node to dump. + \param iOpt Output message destination
+ 0 = stdout
+ 1 = Syslog
+ 2 = Both
+ \returns XB_NVALID_OBJECT
XB_NO_ERROR +*/ + +xbInt16 xbIxNdx::DumpNode( void *vpTag, xbIxNode *pNode, xbInt16 iOpt ) const +{ + xbString s; + xbString sKey; + xbUInt32 lLeftBranch; + xbUInt32 lRecNo; + xbDouble d; + + if( !pNode ) + return XB_INVALID_OBJECT; + + xbIx::DumpNode( vpTag, pNode, iOpt ); + + xbNdxTag * npTag; + vpTag ? npTag = (xbNdxTag *) vpTag : npTag = ndxTag; + + xbUInt32 lNoOfKeys = eGetUInt32( pNode->cpBlockData ); + xbBool bIsLeaf = IsLeaf( vpTag, pNode ); + + if( bIsLeaf ) + xbase->WriteLogMessage( "Leaf node", iOpt ); + else + xbase->WriteLogMessage( "Interior node", iOpt ); + + s.Sprintf( "Key type = [%c] No Of Keys =[%d] Prev =[%x] Next =[%x]", npTag->cKeyType, lNoOfKeys, pNode->npPrev, pNode->npNext ); + xbase->WriteLogMessage( s, iOpt ); + + char *p = pNode->cpBlockData; + p += 4; + + xbUInt32 l; + for( l = 0; l < lNoOfKeys; l++ ){ + + lLeftBranch = eGetUInt32( p ); + p+= 4; + lRecNo = eGetUInt32( p ); + p+= 4; + + if( npTag->cKeyType == 'C' ){ + sKey.Assign( p, 1, (xbUInt32) npTag->iKeyLen ); + } else if( npTag->cKeyType == 'D' ){ + xbInt32 lDate = (xbInt32) eGetDouble( p ); + xbDate dt( lDate ); + sKey.Sprintf( "%ld - %s", lDate, dt.Str()); + } else { + d = eGetDouble( p ); + sKey.Sprintf( "%f", d ); + } + p+= (npTag->iKeyItemLen-8); + s.Sprintf( "%3d %9d %9d %s", l+1, lLeftBranch, lRecNo, sKey.Str() ); + xbase->WriteLogMessage( s, iOpt ); + } + if( !bIsLeaf ){ + lLeftBranch = eGetUInt32( p ); + s.Sprintf( "%3d %9d", l+1, lLeftBranch ); + xbase->WriteLogMessage( s.Str(), iOpt ); + } + return XB_NO_ERROR; +} +#endif +/***********************************************************************/ +//! @brief Find key +/*! + \param vpTag Pointer to tag to search. + \param vpKey Void pointer to key data to search on. + \param lSearchKeyLen Length of key to search for. + \param iRetrieveSw xbTrue - Retrieve the record if key found.
+ xbFalse - Don't retrieve record, check for key existence only. + \returns XB_NO_ERROR - Key found.
+ XB_NOT_FOUND - Key not found.
+ Return Codes +*/ +xbInt16 xbIxNdx::FindKey( void *vpTag, const void *vpKey, xbInt32 lSearchKeyLen, + xbInt16 iRetrieveSw ){ + + + xbInt16 iRc = 0; + xbInt16 iErrorStop = 0; + xbString sMsg; + // xbInt16 iFindSts; + try{ + // clean up any previous table updates before moving on + if( iRetrieveSw ){ + if( dbf->GetDbfStatus() == XB_UPDATED ){ + if( dbf->GetAutoCommit() == 1 ){ + if(( iRc = dbf->Commit()) != XB_NO_ERROR ){ + iErrorStop = 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.
+ Return Codes +*/ + +xbInt16 xbIxNdx::FindKeyForCurRec( void * vpTag ) +{ + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + xbNdxTag * npTag; + vpTag ? npTag = (xbNdxTag *) vpTag : npTag = ndxTag; + + try{ + if(( iRc = CreateKey( vpTag, 0 )) < XB_NO_ERROR ){ + iErrorStop = 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 Return Codes +*/ +xbInt16 xbIxNdx::GetDbfPtr( void *vpTag, xbInt16 iKeyNo, xbIxNode *np, xbUInt32 &ulDbfPtr ) const { + + xbInt16 iRc = 0; + xbInt16 iErrorStop = 0; + + try{ + #ifdef XB_DEBUG_SUPPORT + // turn this off in production mode for better performance + xbUInt32 ulNoOfKeys = eGetUInt32 ( np->cpBlockData ); + if( iKeyNo < 0 || iKeyNo > (xbInt16) --ulNoOfKeys ){ + iErrorStop = 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.
+ xbFalse - Don't retrieve record. + \returns Return Codes +*/ + +xbInt16 xbIxNdx::GetFirstKey( void *vpTag, xbInt16 iRetrieveSw ){ + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + xbNdxTag * npTag; + vpTag ? npTag = (xbNdxTag *) vpTag : npTag = ndxTag; + + try{ + // clear out any history + if( npTag->npNodeChain ){ + npTag->npNodeChain = FreeNodeChain( npTag->npNodeChain ); + npTag->npCurNode = NULL; + } + if(( iRc = ReadHeadBlock( 1 )) != XB_NO_ERROR ){ + iErrorStop = 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 Return Codes +*/ + +xbInt16 xbIxNdx::GetKeyPtr( void *vpTag, xbInt16 iKeyNo, xbIxNode *np, xbUInt32 &ulKeyPtr ) const { + + xbInt16 iRc = 0; + xbInt16 iErrorStop = 0; + try{ + #ifdef XB_DEBUG_SUPPORT + // turn this off in production mode for better performance + xbUInt32 ulNoOfKeys = eGetUInt32 ( np->cpBlockData ); + if( iKeyNo < 0 || iKeyNo > (xbInt16) ulNoOfKeys ){ + iErrorStop = 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.
+ XB_DEL_KEY Key deleted.
+ XB_ADD_KEY Key added.
+ 0 No key updates + +*/ +xbInt16 xbIxNdx::GetKeySts( void *vpTag ) const{ + xbNdxTag * npTag; + vpTag ? npTag = (xbNdxTag *) vpTag : npTag = ndxTag; + return npTag->iKeySts; +} +/***********************************************************************/ +//! @brief Get character key type for given tag. +/*! + \param vpTag Tag to retrieve key type for. + \returns Char key type. +*/ + +char xbIxNdx::GetKeyType( const void * vpTag ) const{ + xbNdxTag * npTag; + vpTag ? npTag = (xbNdxTag *) vpTag : npTag = ndxTag; + return npTag->cKeyType; +} + +/***********************************************************************/ +//! @brief Get numeric key type for given tag. +/*! + \param vpTag Tag to retrieve first key for. + \returns Numeric key type. +*/ +xbInt16 xbIxNdx::GetKeyTypeN( const void * vpTag ) const{ + xbNdxTag * npTag; + vpTag ? npTag = (xbNdxTag *) vpTag : npTag = ndxTag; + return npTag->iKeyType; +} +/***********************************************************************/ +//! @brief Get the last key for the given tag. +/*! + \param vpTag Tag to retrieve last key on. + \param iRetrieveSw xbTrue - Retrieve the record on success.
+ xbFalse - Don't retrieve record. + \returns Return Codes +*/ +xbInt16 xbIxNdx::GetLastKey( void *vpTag, xbInt16 iRetrieveSw ){ + return GetLastKey( 0, vpTag, iRetrieveSw ); +// return GetLastKey( 0, vpTag, 1 ); + +} +/***********************************************************************/ +//! @brief Get the last key for the given tag and starting node. +/*! + \param ulNodeNo Starting node + \param vpTag Tag to retrieve last key on. + \param iRetrieveSw xbTrue - Retrieve the record if key found.
+ xbFalse - Don't retrieve record. + \returns Return Codes +*/ +xbInt16 xbIxNdx::GetLastKey( xbUInt32 ulNodeNo, void *vpTag, xbInt16 iRetrieveSw ){ + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + xbUInt32 ulKeyPtr = 0; + xbUInt32 ulNoOfKeys = 0; + xbNdxTag * npTag; + vpTag ? npTag = (xbNdxTag *) vpTag : npTag = ndxTag; + + try{ + // clear out any history + if( npTag->npNodeChain ){ + npTag->npNodeChain = FreeNodeChain( npTag->npNodeChain ); + npTag->npCurNode = NULL; + } + if( ulNodeNo == 0 ){ + if(( iRc = ReadHeadBlock( 1 )) != XB_NO_ERROR ){ + iErrorStop = 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 Return Codes +*/ + +xbInt16 xbIxNdx::GetLastKeyForBlockNo( void *vpTag, xbUInt32 ulBlockNo, char *cpBuf ){ + + // returns the last key for a given block number + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + + xbNdxTag * npTag; + vpTag ? npTag = (xbNdxTag *) vpTag : npTag = ndxTag; + + try{ + xbIxNode * npSaveNodeChain = npTag->npNodeChain; + xbIxNode * npSaveCurNode = npTag->npCurNode; + npTag->npNodeChain = NULL; + + if(( iRc = GetLastKey( ulBlockNo, npTag, 0 )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + // set the key + memcpy( cpBuf, GetKeyData( npTag->npCurNode, GetKeyCount( npTag->npCurNode ) - 1, npTag->iKeyItemLen ), (size_t) npTag->iKeyLen ); + + // free memory + npTag->npNodeChain = FreeNodeChain( npTag->npNodeChain ); + npTag->npNodeChain = npSaveNodeChain; + npTag->npCurNode = npSaveCurNode; + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbIxNdx::GetLastKeyForBlockNo() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( iRc ) ); + } + return iRc; +} +/***********************************************************************/ +//! @brief Get the next key for the given tag. +/*! + \param vpTag Tag to retrieve next key on. + \param iRetrieveSw xbTrue - Retrieve the record on success.
+ xbFalse - Don't retrieve record. + \returns Return Codes +*/ + +xbInt16 xbIxNdx::GetNextKey( void * vpTag, xbInt16 iRetrieveSw ){ + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + xbNdxTag * npTag; + vpTag ? npTag = (xbNdxTag *) vpTag : npTag = ndxTag; + + try{ + if( !npTag->npNodeChain ) + return GetFirstKey( vpTag, iRetrieveSw ); + + // more keys on this node? if yes, get the next one to the right + xbUInt32 ulKeyPtr; + if((eGetUInt32( npTag->npCurNode->cpBlockData ) -1) > npTag->npCurNode->iCurKeyNo ){ + npTag->npCurNode->iCurKeyNo++; + if(( iRc = GetDbfPtr( npTag, npTag->npCurNode->iCurKeyNo, npTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){ + iErrorStop = 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.
+ xbFalse - Don't retrieve record. + \returns Return Codes +*/ +xbInt16 xbIxNdx::GetPrevKey( void * vpTag, xbInt16 iRetrieveSw ){ + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + xbNdxTag * npTag; + vpTag ? npTag = (xbNdxTag *) vpTag : npTag = ndxTag; + + // This method assumes last index call landed on a valid key. + // If last call resulted in an error, this method will returns XB_BOF + + try{ + if( !npTag->npNodeChain ) + return GetLastKey( 0, vpTag, iRetrieveSw ); + + xbUInt32 ulKeyPtr; + if( npTag->npCurNode->iCurKeyNo > 0 ){ + npTag->npCurNode->iCurKeyNo--; + + if(( iRc = GetDbfPtr( npTag, npTag->npCurNode->iCurKeyNo, npTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){ + iErrorStop = 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.
xbFalse - Not unique index. +*/ +xbBool xbIxNdx::GetUnique( void * vpTag ) const{ + xbNdxTag * npTag; + vpTag ? npTag = (xbNdxTag *) vpTag : npTag = ndxTag; + return npTag->iUnique; +} + +/***********************************************************************/ +//! @brief Insert key into interior node. +/*! + Insert key into non-full interior node.
+ Assumes valid inputs + + \param vpTag Tag in play. + \param npNode Node for insertion. + \param iSlotNo Slot number to insert key. + \param ulPtr Pointer number to insert. + \returns Return Codes +*/ + +xbInt16 xbIxNdx::InsertNodeI( void *vpTag, xbIxNode *npNode, xbInt16 iSlotNo, xbUInt32 ulPtr ){ + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + char *pTrg; + xbInt16 iSrcPos; + char *pLastKey = NULL; + xbNdxTag * npTag; + vpTag ? npTag = (xbNdxTag *) vpTag : npTag = ndxTag; + + try{ + // update number of keys on the node + xbInt32 lKeyCnt = GetKeyCount( npNode ); + iSrcPos = 12 + (iSlotNo * npTag->iKeyItemLen); + + char *pSrc = npNode->cpBlockData; + pSrc += iSrcPos; + + // if not appending to the end of the node, make some room, move things to the right + if( iSlotNo < lKeyCnt ) { + xbInt16 iCopyLen = ((lKeyCnt - iSlotNo) * npTag->iKeyItemLen) - 4; + pTrg = pSrc; + pTrg += npTag->iKeyItemLen; + memmove( pTrg, pSrc, (size_t) iCopyLen ); + } + + // get the right most key for the left part of the split node + xbUInt32 ulKeyPtr2; + if(( iRc = GetKeyPtr( vpTag, npNode->iCurKeyNo, npNode, ulKeyPtr2 )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + // get the new right key value for the freshly split node + pLastKey = (char *) malloc((size_t) ndxTag->iKeyLen); + if(( iRc = GetLastKeyForBlockNo( vpTag, ulKeyPtr2, pLastKey )) != XB_NO_ERROR ){ + iRc = 110; + throw iRc; + } + // write the key value + pTrg = pSrc; + char *pTrg2 = pSrc; + pSrc = pLastKey; + for( xbInt16 i = 0; i < npTag->iKeyLen; i++ ) + *pTrg++ = *pSrc++; + + pTrg2 += (npTag->iKeyItemLen - 8); + ePutUInt32( pTrg2, ulPtr ); + ePutInt32( npNode->cpBlockData, ++lKeyCnt ); + + // write out the updated block to disk + if(( iRc = WriteBlock( npNode->ulBlockNo, GetBlockSize(), npNode->cpBlockData )) != XB_NO_ERROR ){ + iErrorStop = 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.
+ Assumes valid inputs + + \param vpTag Tag in play. + \param npNode Node for insertion. + \param iSlotNo Slot number to insert key. + \param ulPtr Pointer number to insert. + \returns Return Codes +*/ +xbInt16 xbIxNdx::InsertNodeL( void *vpTag, xbIxNode *npNode, xbInt16 iSlotNo, + char * cpKeyBuf, xbUInt32 ulPtr ){ + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + char *pSrc; + char *pTrg; + char *pKeyPos; + xbString sMsg; + xbNdxTag * npTag; + vpTag ? npTag = (xbNdxTag *) vpTag : npTag = ndxTag; + + try{ + xbInt32 lKeyCnt = GetKeyCount( npNode ); + xbInt16 iKeyPos = 4 + iSlotNo * npTag->iKeyItemLen; + pKeyPos = npNode->cpBlockData; + pKeyPos += iKeyPos; + + // if not appending to end, make space, move things right + if( iSlotNo < lKeyCnt ) { + xbInt16 iCopyLen = (lKeyCnt - iSlotNo) * npTag->iKeyItemLen; + pTrg = pKeyPos; + pTrg += npTag->iKeyItemLen; + memmove( pTrg, pKeyPos, (size_t) iCopyLen ); + } + // if leaf, write rec number + pTrg = pKeyPos; + memset( pTrg, 0x00, 4 ); + pTrg += 4; + ePutUInt32( pTrg, ulPtr ); + pTrg += 4; + + // write the key value + pSrc = cpKeyBuf; + for( xbInt16 i = 0; i < npTag->iKeyLen; i++ ) + *pTrg++ = *pSrc++; + + // update number of keys on the node + ePutInt32( npNode->cpBlockData, ++lKeyCnt ); + + // write out the updated block to disk + if(( iRc = WriteBlock( npNode->ulBlockNo, GetBlockSize(), npNode->cpBlockData )) != XB_NO_ERROR ){ + iErrorStop = 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.
xbFalse - Interior node. +*/ +xbBool xbIxNdx::IsLeaf( void *, xbIxNode *npNode ) const { + xbUInt32 ulBlock = eGetUInt32 ( npNode->cpBlockData+4 ); + if( ulBlock > 0 ) // if the second four bytes are a number, it's an interior node + return false; + else + return true; +} +/***********************************************************************/ +//! @brief Determine if key exists. +/*! + This method assumes the key has already been built and is in either + cpKeyBuf or dKey. + + \param vpTag - Pointer to tag. + \returns xbTrue - Key exists.
xbFalse - Key does not exist. +*/ +xbInt16 xbIxNdx::KeyExists( void * vpTag ){ + + xbNdxTag * npTag; + vpTag ? npTag = (xbNdxTag *) vpTag : npTag = ndxTag; + + xbInt16 iRc = FindKey( vpTag, npTag->cpKeyBuf, npTag->iKeyLen, 0 ); + if( iRc == 0 ) + return 1; + else + return 0; +} + +/***********************************************************************/ +//! @brief Set position for key add. +/*! + This routine is called by the AddKey() method and is used to position + the node chain to the position the new key should be added to the index. + + \param npTag Pointer to npTag. + \param ulAddRecNo Record number to add. + \returns Return Codes +*/ +xbInt16 xbIxNdx::KeySetPosAdd( xbNdxTag *npTag, xbUInt32 ulAddRecNo ){ + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + try{ + + iRc = FindKey( npTag, npTag->cpKeyBuf, npTag->iKeyLen, 0 ); + if( iRc == XB_NOT_FOUND || iRc == XB_EMPTY ) + return XB_NO_ERROR; // good position + + else if( iRc != XB_NO_ERROR ){ + iErrorStop = 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 Return Codes +*/ + +xbInt16 xbIxNdx::KeySetPosDel( xbNdxTag *npTag ){ + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + xbString sMsg; + + try{ + iRc = FindKey( NULL, npTag->cpKeyBuf2, npTag->iKeyLen, 0 ); + if( iRc == XB_NOT_FOUND || iRc == XB_EMPTY ) + return XB_NO_ERROR; // good position + else if( iRc != XB_NO_ERROR ){ + iErrorStop = 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.
xbFalse - Key not updated. + + Always true for NDX style indices. +*/ +//inline xbBool xbIxNdx::KeyFiltered( void *vpTag ) const{ +// return xbTrue; +//} + +/***********************************************************************/ +//! @brief Read head block of index file. +/*! + \param iOpt 0 - Read in entire block + 1 - Read in only dynamic section of block + \returns Return Codes +*/ +xbInt16 xbIxNdx::ReadHeadBlock( xbInt16 iOpt = 0 ) { + + xbInt16 iRc = 0; + xbInt16 iErrorStop = 0; + try{ + if( !FileIsOpen()){ + iRc = XB_NOT_OPEN; + iErrorStop = 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 Return Codes +*/ + +xbInt16 xbIxNdx::Reindex( void **vpTag ){ + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + //xbNdxTag * npTag; + //vpTag ? npTag = (xbNdxTag *) vpTag : npTag = ndxTag; + + try{ + xbString sFileName = GetFqFileName(); + xbString sKey; // = GetKeyExpression( vpTag ); + sKey.Set( GetKeyExpression( *vpTag )); + xbInt16 iUnique = GetUnique( *vpTag ); + xbString sFilter = ""; + + void *vpTag2; + if(( iRc = CreateTag( sFileName, sKey, sFilter, 0, iUnique, xbTrue, &vpTag2 )) != XB_NO_ERROR ){ + iErrorStop = 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.
+ This behaves differently than V7 Dbase. V7 does not balance the nodes.
+ For V7, if adding a key to the end of a node, it will create a right node + with only one key, and the left node is still full.

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

+ + Possible performance improvement options.
+ Two modes when splitting:
+ a) Split nodes in the middle - good for random access applications
+ b) Split off right node with only one key - good for applications with + expectation of ascending keys added moving forward.
+ + This routine first inserts the key into the left node in the appropriate location + then splits the node based on the split factor setting. + + \param vpTag Tag in play. + \param npLeft Left node to split. + \param npRight Right node to split. + \param iSlotNo Slot number for split. + \param ulPtr Pointer number to insert. + \returns Return Codes +*/ + +xbInt16 xbIxNdx::SplitNodeL( void *vpTag, xbIxNode * npLeft, xbIxNode *npRight, + xbInt16 iSlotNo, char * cpKeyBuf, xbUInt32 ulPtr ){ + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + xbDouble dSplitFactor = .5; + xbNdxTag *npTag; + vpTag ? npTag = (xbNdxTag *) vpTag : npTag = ndxTag; + + xbString sMsg; + try{ + xbInt32 lKeyCnt = GetKeyCount( npLeft ); + xbInt32 lNewLeftKeyCnt = (xbInt32) ((lKeyCnt + 1) * dSplitFactor) + 1; + xbInt32 lNewRightKeyCnt = lKeyCnt + 1 - lNewLeftKeyCnt; + + // xbInt16 iSrcPos; + xbInt16 iLen; + char *pSrc = npLeft->cpBlockData; + char *pTrg; + + if(( iRc = InsertNodeL( vpTag, npLeft, iSlotNo, cpKeyBuf, ulPtr )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + + // move right half off of left node to the right node + pSrc = npLeft->cpBlockData; + pSrc += ((lNewLeftKeyCnt * npTag->iKeyItemLen)+4); + pTrg = npRight->cpBlockData; + pTrg += 4; + iLen = lNewRightKeyCnt * npTag->iKeyItemLen; + memmove( pTrg, pSrc, (size_t) iLen ); + + // write the new key counts into the nodes + pTrg = npLeft->cpBlockData; + ePutInt32( pTrg, lNewLeftKeyCnt ); + pTrg = npRight->cpBlockData; + ePutInt32( pTrg, lNewRightKeyCnt ); + + // write out the 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.
+ D - Delete a key.
+ R - Revise a key.
+ \param vpTg - Pointer to tag.
+ \param ulRecNo - Record number association with the action.
+ \returns Return Codes +*/ + +xbInt16 xbIxNdx::UpdateTagKey( char cAction, void *vpTag, xbUInt32 ulRecNo ){ + + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + // ..xbNdxTag *npTag = (xbMdxTag *) vpTag; + + try{ + // save off any needed fileds for updating + // xbUInt32 ulTagSizeSave = mpTag->ulTagSize; + //xbUInt32 ulLeftChildSave = mpTag->ulLeftChild; + //xbUInt32 ulRightChildSave = mpTag->ulRightChild; + + + if( cAction == 'D' || cAction == 'R' ){ +// std::cout << "UpdateTagKey delete\n"; + if(( iRc = DeleteKey( vpTag )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + } + + if( cAction == 'A' || cAction == 'R' ){ +// 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.
+ 1 - Update root block, number of blocks and seq number.
+ 2 - Update sequence number only
+ \returns +*/ + +xbInt16 xbIxNdx::WriteHeadBlock( xbInt16 iOpt ){ + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + + try{ + if( iOpt == 2 ){ + + // increment the serial number + if( ndxTag->cSerNo >= 0 && ndxTag->cSerNo < 127 ) + ndxTag->cSerNo++; + else + ndxTag->cSerNo = 0; + + if(( iRc = xbFseek( 20, SEEK_SET )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + if(( iRc = xbFputc( ndxTag->cSerNo )) != XB_NO_ERROR ){ + iErrorStop = 110; + throw iRc; + } + } else if( iOpt == 1 ){ + xbRewind(); + char buf[8]; + ePutUInt32( &buf[0], ndxTag->ulRootBlock ); + ePutUInt32( &buf[4], ndxTag->ulTotalBlocks ); + if(( iRc = xbFwrite( buf, 8, 1 )) != XB_NO_ERROR ){ + iErrorStop = 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 + +#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.
xbFalse - Logging turned off. +*/ +xbBool xbLog::LogGetStatus(){ + return bLoggingStatus; +} +/******************************************************************************/ +//! @brief Close the logfile. +/*! + \returns
Return Codes +*/ +xbInt16 xbLog::LogClose(){ + return xbFclose(); +} +/******************************************************************************/ +//! @brief Set maximum log file size. +/*! + \param lSize - New maximum log file size. + \returns void +*/ +void xbLog::LogSetLogSize( size_t lSize ){ + lLogSize = lSize; +} +/******************************************************************************/ +//! @brief Set log status. +/*! + \param bStatus xbTrue - Turn logging on.
xbFalse - Turn logging off. + \returns void +*/ +void xbLog::LogSetStatus( xbBool bStatus ){ + bLoggingStatus = bStatus; +} +/******************************************************************************/ +//! @brief Open the logfile. +/*! + \returns Return Codes +*/ +xbInt16 xbLog::LogOpen(){ + xbInt16 rc; + if(( rc = xbFopen( "a", iShareMode )) != XB_NO_ERROR ) + return rc; + xbFTurnOffFileBuffering(); + return XB_NO_ERROR; +} +/******************************************************************************/ +//! @brief Write a logfile message. +/*! + \param sLogEntryData - Message to write to the logfile. + \param iOutputOption 0 - Write to logfile.
+ 1 - Write to stdout.
+ 2 - Write to both logfile and stdout. + \returns Return Codes +*/ + +xbInt16 xbLog::LogWrite( const xbString &sLogEntryData, xbInt16 iOutputOption ){ + + if( bLoggingStatus == xbFalse ){ // logging turned off + return XB_NO_ERROR; + } + xbInt16 rc = 0; + if( iOutputOption != 1 && !FileIsOpen() ){ + if(( rc = LogOpen()) != XB_NO_ERROR ){ + fprintf( stderr, "Error - cant write to logfile\n" ); + return rc; + } + } + if( iOutputOption != 1 && lLogSize < xbFtell()){ + xbFputs( "Swapping to next log file" ); + xbFclose(); + xbString sBackupName; + sBackupName.Sprintf( "%s.bak", GetFqFileName().Str()); + if( FileExists( sBackupName )) + xbRemove( sBackupName ); + + xbRename( GetFqFileName(), sBackupName ); + xbFopen( "a", iShareMode ); + } + xbString sTimeStamp; + xbString sFled; // formatted log entry data + + if( iOutputOption != 1 ){ + #ifdef HAVE__LOCALTIME64_S_F + __time64_t timer; + struct tm tb; + _time64( &timer ); + _localtime64_s( &tb, &timer ); + tb.tm_year += 1900; + tb.tm_mon++; + sTimeStamp.Sprintf( "%4d-%02d-%02d %02d:%02d:%02d", tb.tm_year, tb.tm_mon, tb.tm_mday, tb.tm_hour, tb.tm_min, tb.tm_sec ); + #else + time_t timer; + struct tm *tb; + timer = time( NULL ); + tb = localtime( &timer ); + tb->tm_year += 1900; + tb->tm_mon++; + sTimeStamp.Sprintf( "%4d-%02d-%02d %02d:%02d:%02d", tb->tm_year, tb->tm_mon, tb->tm_mday, tb->tm_hour, tb->tm_min, tb->tm_sec ); + #endif + sFled.Sprintf( "%s - %s\n", sTimeStamp.Str(), sLogEntryData.Str() ); + } + + switch( iOutputOption ){ + case 0: + xbFputs( sFled ); + break; + case 1: + std::cout << sLogEntryData << std::endl; + break; + case 2: + xbFputs( sFled ); + std::cout << sLogEntryData << std::endl; + break; + } + return XB_NO_ERROR; +} +/******************************************************************************/ +//! @brief Write bytes to logfile. +/*! + \param ulByteCnt - Number of bytes to write to logfile. + \param p - Pointer to data to write to logfile. + \returns XB_NO_ERROR +*/ + +xbInt16 xbLog::LogWriteBytes( xbUInt32 ulByteCnt, const char *p ){ + + if( bLoggingStatus == xbFalse ) // logging turned off + return XB_NO_ERROR; + const char *p2 = p; + xbFputc( '[' ); + for( xbUInt32 l = 0; l < ulByteCnt; l++ ) + xbFputc( *p2++ ); + xbFputc( ']' ); + return XB_NO_ERROR; +} +/******************************************************************************/ +} // namespace +#endif // XB_LOGGING_ON + + + + diff --git a/src/core/xbmemo.cpp b/src/core/xbmemo.cpp 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 Return Codes +*/ +xbInt16 xbMemo::CalcLastDataBlock( xbUInt32 & ulLastDataBlock ){ + + xbInt16 iRc = XB_NO_ERROR; + if(( iRc = xbFseek( 0, SEEK_END )) != XB_NO_ERROR ) + return iRc; + + ulLastDataBlock = (xbUInt32) xbFtell() / (xbUInt32) GetBlockSize(); + return XB_NO_ERROR; +} +/***********************************************************************/ +//! @brief Close the memo file. +/*! + \returns Return Codes +*/ +xbInt16 xbMemo::CloseMemoFile(){ + + if( mbb ){ + free( mbb ); + mbb = NULL; + } + return xbFclose(); +} + + +/***********************************************************************/ +//! @brief Get memo file type. +/*! + \returns 3 - Version 3 memo file.
+ 4 - Version 4 memo file. +*/ +xbInt16 xbMemo::GetMemoFileType(){ + return iMemoFileType; +} + +/***********************************************************************/ +//! @brief Get next block available from file header. +/*! + \param ulBlockNo Output - Next block number for appending data to memo file. + \returns Return Codes +*/ +xbInt16 xbMemo::GetHdrNextBlock( xbUInt32 & ulBlockNo ){ + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + try{ + if(( iRc = ReadDbtHeader( 0 )) != XB_NO_ERROR ){ + iErrorStop = 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
XB_UNLOCK + \returns Return Codes +*/ + +xbInt16 xbMemo::LockMemo( xbInt16 iLockFunction ){ + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + + try{ + if( iLockFunction == XB_LOCK ){ + + if( bFileLocked ) // already locked + return XB_NO_ERROR; + + if( dbf->GetLockFlavor() == LK_DBASE ){ + iRc = xbLock( XB_LOCK, LK4026531838, 1 ); + if( iRc != XB_NO_ERROR ){ + if( iRc == XB_LOCK_FAILED ) + return iRc; + else { + iErrorStop = 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.
+ xbFalse - Memo file is not locked. +*/ +xbBool xbMemo::GetMemoLocked() const { + return bFileLocked; +} +#endif + +/***********************************************************************/ +//! @brief Update Next Node number in file header +/*! + \returns Return Codes +*/ +xbInt16 xbMemo::UpdateHeadNextNode(){ + + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + try{ + char buf[4]; + ePutUInt32( buf, ulHdrNextBlock ); + if(( iRc = xbFseek( 0, SEEK_SET )) != XB_NO_ERROR ){ + iErrorStop = 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 Return Codes +*/ + +xbInt16 xbMemoDbt3::CreateMemoFile(){ + + xbInt16 rc = XB_NO_ERROR; + char cBuf[4]; + if(( rc = xbFopen( "w+b", dbf->GetShareMode())) != XB_NO_ERROR ) + return rc; + ulHdrNextBlock = 1L; + ePutUInt32( cBuf, ulHdrNextBlock ); + if(( rc = xbFwrite( cBuf, 4, 1 ))!= XB_NO_ERROR ){ + xbFclose(); + return rc; + } + for(int i = 0; i < 12; i++ ) + xbFputc( 0x00 ); + xbFputc( 0x03 ); + for(int i = 0; i < 495; i++ ) + xbFputc( 0x00 ); + if(( mbb = (void *) malloc( 512 )) == NULL ){ + xbFclose(); + return XB_NO_MEMORY; + } + return XB_NO_ERROR; +} +/***********************************************************************/ +#ifdef XB_DEBUG_SUPPORT +//! @brief Dump memo file header. +/*! + \returns XB_NO_ERROR +*/ +xbInt16 xbMemoDbt3::DumpMemoFreeChain() { + std::cout << "Xbase version 3 file - no free block chain" << std::endl; + return XB_NO_ERROR; +} + +//! @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 Return Codes +*/ + +xbInt16 xbMemoDbt3::GetMemoField( xbInt16 iFieldNo, xbString & sMemoData ){ + + xbInt16 iErrorStop = 0; + xbInt16 rc = XB_NO_ERROR; + xbUInt32 ulScnt; + char *sp, *spp; + xbUInt32 ulBlockNo; + xbBool bDone = xbFalse; + sMemoData = ""; + try{ + if(( rc = dbf->GetULongField( iFieldNo, ulBlockNo )) < XB_NO_ERROR ){ + iErrorStop = 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 Return Codes +*/ +xbInt16 xbMemoDbt3::GetMemoFieldLen( xbInt16 iFieldNo, xbUInt32 & ulFieldLen ){ + + xbInt16 iErrorStop = 0; + xbInt16 rc = XB_NO_ERROR; + xbInt16 iScnt; + char *sp, *spp; + xbUInt32 ulBlockNo; + xbInt16 iNotDone; + try{ + if(( rc = dbf->GetULongField( iFieldNo, ulBlockNo )) < XB_NO_ERROR ){ + iErrorStop = 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 Return Codes +*/ + +xbInt16 xbMemoDbt3::OpenMemoFile() { + xbInt16 rc = XB_NO_ERROR; + if(( rc = xbFopen( dbf->GetOpenMode(), dbf->GetShareMode())) != XB_NO_ERROR ) + return rc; + if(( mbb = (void *) malloc( 512 )) == NULL ){ + xbFclose(); + return XB_NO_MEMORY; + } + return XB_NO_ERROR; +} +/***********************************************************************/ +//! @brief Pack memo file. +/*! + This routine frees up any unused blocks in the file resulting from field updates. + Version 3 memo files do not reclaim unused space (Version 4 files do). + This routine cleans up the unused space. + \returns Return Codes +*/ +xbInt16 xbMemoDbt3::PackMemo( void (*memoStatusFunc ) ( xbUInt32 ulItemNum, xbUInt32 ulNumItems )) +{ + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + char * cBlock = NULL; + + #ifdef XB_LOCKING_SUPPORT + 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
+ 1 --> read the entire thing + \returns Return Codes +*/ + +xbInt16 xbMemoDbt3::ReadDbtHeader( xbInt16 iOption ){ + char *p; + char MemoBlock[20]; + xbInt16 rc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + xbUInt32 ulReadSize; + + try{ + if( !FileIsOpen() ){ + iErrorStop = 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 Return Codes +*/ + +xbInt16 xbMemoDbt3::UpdateMemoField( xbInt16 iFieldNo, const xbString & sMemoData ) { + + xbInt16 iErrorStop = 0; + xbInt16 rc = XB_NO_ERROR; + try{ + if( sMemoData == "" ){ + if(( rc = dbf->PutField( iFieldNo, "" )) != XB_NO_ERROR ){ + iErrorStop = 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 Return Codes +*/ +xbInt16 xbMemoDbt3::Zap(){ + + xbInt16 iRc = 0; + xbInt16 iErrorStop = 0; + char cBuf[4]; + + try{ + ulHdrNextBlock = 1L; + ePutUInt32( cBuf, ulHdrNextBlock ); + + if(( iRc != xbFseek( 0, SEEK_SET )) != XB_NO_ERROR ){ + iErrorStop = 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 Return Codes +*/ + +xbInt16 xbMemoDbt4::CreateMemoFile(){ + + xbInt16 iErrorStop = 0; + xbInt16 rc = XB_NO_ERROR; + char cBuf[4]; + + try{ + if(( rc = xbFopen( "w+b", dbf->GetShareMode() )) != XB_NO_ERROR ){ + iErrorStop = 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 *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.
+ 0 if header node + \param bFound Output xbFalse - Spot not found in chain.
+ xbTrue - Spot found in chain. + \returns Return Codes +*/ +xbInt16 xbMemoDbt4::FindBlockSetInChain( xbUInt32 ulBlocksNeeded, + xbUInt32 &ulLastDataBlock, xbUInt32 &ulLocation, xbUInt32 &ulPrevNode, xbBool &bFound ){ + xbInt16 rc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + xbUInt32 ulCurNode; + + try{ + ulPrevNode = 0L; + if( ulLastDataBlock == 0 ){ + /* Determine last good data block */ + if(( rc = CalcLastDataBlock( ulLastDataBlock )) != XB_NO_ERROR ){ + iErrorStop = 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 Return Codes +*/ + +xbInt16 xbMemoDbt4::FreeMemoBlockChain( xbUInt32 ulBlockNo ){ + xbUInt32 ulLastDataBlock; + return FreeMemoBlockChain( ulBlockNo, ulLastDataBlock ); +} + +/***********************************************************************/ +//! @brief Free a block. +/*! + \param ulBlockNo The block number being deleted. + \param ulLastDataBlock Output - Last free block number,prior to this block. + \returns Return Codes +*/ +xbInt16 xbMemoDbt4::FreeMemoBlockChain( xbUInt32 ulBlockNo, xbUInt32 &ulLastDataBlock ) +{ + xbInt16 rc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + xbUInt32 ulNoOfFreedBlocks; + xbUInt32 ulLastFreeBlock = 0; + xbUInt32 ulLastFreeBlockCnt = 0; + xbUInt32 ulSaveNextFreeBlock; + + // iFieldNo - The field no m\bing deleted + // iBlockNo - The block number being deleted + // iNoOfFreeBlocks - The number of blocks being freed with this delete + // iLastDataBlock - The next block number to allocate if more blocks needed + // iHdrNextBlock - The head pointer in the main header block + // iNextFreeBlock - The block that is immediately following the current free block to be added + // iLastFreeBlock - Last free block number,prior to this block + // iLastFreeBlockCnt - Last free block number of blocks + + try{ + + if( ulBlockNo <= 0 ){ + iErrorStop = 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 Return Codes +*/ + + +xbInt16 xbMemoDbt4::GetBlockSetFromChain( xbUInt32 ulBlocksNeeded, + xbUInt32 ulLocation, xbUInt32 ulPrevNode ) + +{ + + xbInt16 rc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + xbUInt32 ulNextFreeBlock2; + xbUInt32 ulNewFreeBlocks; + xbUInt32 ulSaveNextFreeBlock; + + try{ + if(( rc = ReadBlockHeader( ulLocation, 2 )) != XB_NO_ERROR ){ + iErrorStop = 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 Return Codes +*/ + +xbInt16 xbMemoDbt4::GetMemoField( xbInt16 iFieldNo, xbString & sMemoData ){ + + xbUInt32 ulBlockNo; + xbUInt32 ulMemoFieldLen; + xbUInt32 ulMemoFieldDataLen; + xbInt16 rc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + char *p = NULL; + + try{ + + if(( rc = GetMemoFieldLen( iFieldNo, ulMemoFieldLen, ulBlockNo )) != XB_NO_ERROR ){ + iErrorStop = 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 Return Codes +*/ +xbInt16 xbMemoDbt4::GetMemoFieldLen( xbInt16 iFieldNo, xbUInt32 &ulMemoFieldLen ){ + xbUInt32 ulBlockNo; + return GetMemoFieldLen( iFieldNo, ulMemoFieldLen, ulBlockNo ); +} + +/***********************************************************************/ +//! @brief Get a memo field length for a given field number. +/*! + \param iFieldNo Field number to retrieve data for. + \param ulMemoFieldLen Output - length of memo field data. + \param ulBlockNo Output - Starting block number. + \returns Return Codes +*/ + +xbInt16 xbMemoDbt4::GetMemoFieldLen( xbInt16 iFieldNo, xbUInt32 &ulMemoFieldLen, xbUInt32 &ulBlockNo ){ + + xbInt16 rc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + char cFieldType; + + try{ + + if(( rc = dbf->GetFieldType( iFieldNo, cFieldType )) != XB_NO_ERROR ){ + iErrorStop = 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 Return Codes +*/ + +xbInt16 xbMemoDbt4::OpenMemoFile() { + + xbInt16 iErrorStop = 0; + xbInt16 rc = XB_NO_ERROR; + + try{ + if(( rc = xbFopen( dbf->GetOpenMode(), dbf->GetShareMode())) != XB_NO_ERROR ){ + iErrorStop = 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 Return Codes +*/ + +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 Return Codes +*/ + +xbInt16 xbMemoDbt4::ReadBlockHeader( xbUInt32 ulBlockNo, xbInt16 iOption ) { + + xbInt16 rc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + + try{ + + if(( rc = ReadBlock( ulBlockNo, 8, mbb )) != XB_NO_ERROR ){ + iErrorStop = 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
+ 1 --> read the entire thing + \returns Return Codes +*/ + +xbInt16 xbMemoDbt4::ReadDbtHeader( xbInt16 iOption ) { + + xbInt16 iErrorStop = 0; + xbInt16 rc = XB_NO_ERROR; + xbInt16 iReadLen = 0; + char *p; + char MemoBlock[22]; + + try{ + if( !FileIsOpen() ){ + iErrorStop = 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 Return Codes +*/ + +xbInt16 xbMemoDbt4::UpdateMemoField( xbInt16 iFieldNo, const xbString & sMemoData ) { + + xbInt16 iErrorStop = 0; + xbInt16 rc = XB_NO_ERROR; + xbUInt32 ulBlockNo; + + try{ + + if(( rc = dbf->GetULongField( iFieldNo, ulBlockNo )) < XB_NO_ERROR ){ + iErrorStop = 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 Return Codes +*/ + +xbInt16 xbMemoDbt4::WriteBlockHeader( xbUInt32 ulBlockNo, xbInt16 iOption ) { + + xbInt16 rc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + + try{ + if( iOption == 1 ){ + ePutInt16 ((char *) mbb, iField1 ); + ePutInt16 ((char *) mbb+2, iStartPos ); + ePutUInt32((char *) mbb+4, ulFieldLen ); + } + else if( iOption == 2 ){ + ePutUInt32((char *) mbb, ulNextFreeBlock ); + ePutUInt32((char *) mbb+4, ulFreeBlockCnt ); + } + else{ + iErrorStop = 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 Return Codes +*/ +xbInt16 xbMemoDbt4::Zap(){ + + xbInt16 iRc = 0; + xbInt16 iErrorStop = 0; + char cBuf[4]; + try{ + ulHdrNextBlock = 1L; + ePutUInt32( cBuf, ulHdrNextBlock ); + + if(( iRc != xbFseek( 0, SEEK_SET )) != XB_NO_ERROR ){ + iErrorStop = 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
+ xbFalse Bit is not set +*/ +xbBool xbSsv::BitSet( unsigned char c, xbInt16 iBitNo ) const { + return c & 1 << iBitNo; +} + +/*************************************************************************/ +//! @brief Display error message on console for a given error number. +/*! + \param iErrorCode Error number to reference +*/ +void xbSsv::DisplayError( xbInt16 iErrorCode ) const { + std::cout << (const char *) GetErrorMessage( iErrorCode ) << std::endl; +} +/*************************************************************************/ +//! @brief Get the default auto commit setting. +/*! + + When auto commit is enabled, the library will automatically post any updates + when moving off an updated record or closing files. + If auto commit is disabled, the application program will need to explicitly + update the tables using using dbf->Put() and dbf->AppendRecord(). + + \returns xbTrue if auto commit is turned on
+ xbFalse is auto commit is turned off +*/ + +xbBool xbSsv::GetDefaultAutoCommit() const { + return bDefaultAutoCommit; +} + +/*************************************************************************/ +//! @brief Get the current data directory. +/*! + \returns xbString containing the current data directory + where the database files are stored. +*/ + +xbString &xbSsv::GetDataDirectory() const { + return sDataDirectory; +} +/*************************************************************************/ +//! @brief Get the default date format. +/*! + \returns xbString containing the default date format. +*/ + +xbString & xbSsv::GetDefaultDateFormat() const { + return sDefaultDateFormat; +} + +/*************************************************************************/ +// @brief Get the default file format version. +/* + \returns 3 - Dbase III file format
+ 4 - Dbase IV file format
+*/ + +/* +xbInt16 xbSsv::GetDefaultFileVersion() const { + return iDefaultFileVersion; +} +*/ +/*************************************************************************/ +//! @brief Get the Endian type. +/*! + \returns B - Big endian
+ L - Little endian
+*/ +xbInt16 xbSsv::GetEndianType() const { + return iEndianType; +} + +/*************************************************************************/ +//! @brief Get an error message. +/*! + \param iErrorCode ErrorCode is the error number of description to be returned. + \returns Returns a pointer to a string containing a text description for the error code. +*/ + +const char * xbSsv::GetErrorMessage( xbInt16 iErrorCode ) const{ + + if( iErrorCode > 0 || iErrorCode <= XB_MAX_ERROR_NO ) + return ""; + + xbBool bFound = xbFalse; + xbInt16 iCtr = 0; + while( !bFound ){ + if( xbErrorMessages[iCtr].iErrorNo == XB_MAX_ERROR_NO ) + return "Unknown Error"; + if( xbErrorMessages[iCtr].iErrorNo == iErrorCode ) + return xbErrorMessages[iCtr].sErrorText; + iCtr++; + } + return ""; +} + +/*************************************************************************/ +//! @brief Set the data directory. +/*! + \param sDataDirectory Set the data direcroty. +*/ + +void xbSsv::SetDataDirectory( const xbString &sDataDirectory ){ + this->sDataDirectory = sDataDirectory; + + + #ifdef WIN32 + this->sDataDirectory.SwapChars( '/', '\\' ); + #else + this->sDataDirectory.SwapChars( '\\', '/' ); + #endif + +} + +/*************************************************************************/ +//! @brief Set the default date format. +/*! + \param sDefaultDateFormat Set the default date format. +*/ + +void xbSsv::SetDefaultDateFormat( const xbString &sDefaultDateFormat ) { + this->sDefaultDateFormat = sDefaultDateFormat; +} + +/*************************************************************************/ +//! @brief Set the default auto commit. +/*! + + Disabling auto commit requires the application execute explicit updates + using dbf->Put() and dbf->AppendRecord(). With auto commit on, the library + posts updates automatically when moving off the current record or closing + a file. + + \param bDefaultAutoCommit xbTrue - Enable default auto commit.
+ xbFalse - Disable default auto commit.
+*/ + +void xbSsv::SetDefaultAutoCommit( xbBool bDefaultAutoCommit ) { + this->bDefaultAutoCommit = bDefaultAutoCommit; +} +/*************************************************************************/ +//! @brief Set the endian type +/*! + This routine determines the Endian-ness at run time instead of + compile time as some processers (ie; Sparc,ARM) can be switched either way. + This routine is called automatically by the library at startup and does not + need to be called in an application program. + +*/ + +void xbSsv::SetEndianType() { + xbInt16 e = 1; + iEndianType = *(char *) &e; + if( iEndianType ) + iEndianType = 'L'; + else + iEndianType = 'B'; + return; +} + +/*************************************************************************/ +#ifdef XB_LOGGING_SUPPORT + +//! @brief Get the default log file name. +/*! + \returns Returns the log file name. +*/ +xbString & xbSsv::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
+ xbFalse - Turn autolocking off
+*/ +void xbSsv::SetDefaultAutoLock( xbBool bDefaultAutoLock ) { + this->bDefaultAutoLock = bDefaultAutoLock; +} + +//! @brief Enable default auto locking. +/*! + When auto locking is turned on, the library automatically locks and unlocks + files and indices as needed in a multi user environment. +*/ +void xbSsv::EnableDefaultAutoLock() { + this->bDefaultAutoLock = xbTrue; +} + +//! @brief Disable defalt auto locking. +/*! + When auto locking is turned off, the library does not automatically lock + and unlock files and indices as needed in a multi user environment. + Locking is not needed in a single user environment. + +*/ +void xbSsv::DisableDefaultAutoLock() { + this->bDefaultAutoLock = xbFalse; +} +/***************************************************************************/ +//! @brief Get default lock flavor +/*! + Currently one flavor. This routine is part of the structure to support + future additional locking scenarios for Clipper and Foxpro. + \returns 1 +*/ +xbInt16 xbSsv::GetDefaultLockFlavor() const { + return iDefaultLockFlavor; +} + +/***************************************************************************/ +//! @brief Set default lock flavor +/*! + Currently one flavor. This routine is part of the structure to support + future additional locking scenarios for Clipper and Foxpro. +*/ +void xbSsv::SetDefaultLockFlavor( xbInt16 iDefaultLockFlavor ) { + this->iDefaultLockFlavor = iDefaultLockFlavor; +} + +/***************************************************************************/ +//! @brief Set default lock wait +/*! + \param lLockWait Set default lock wait in milliseconds. +*/ +void xbSsv::SetDefaultLockWait( xbInt32 lLockWait ) { + this->lDefaultLockWait = lLockWait; +} + +/***************************************************************************/ +//! @brief Get default lock wait +/*! + \returns Lock wait time in milliseconds. + +*/ +xbInt32 xbSsv::GetDefaultLockWait() const { + return lDefaultLockWait; +} +#endif + +/***************************************************************************/ +//! @brief Get the multi user setting. +/*! + \returns xbTrue - Multi user mode turned on.
+ xbFalse - Multi user mode turned off.
+*/ +xbBool xbSsv::GetMultiUser() const { + return bMultiUser; +} + +//! @brief Get the multi user setting. +/*! + \param bMultiUser xbTrue - Turn on Multi user mode.
+ xbFalse - Turn off Multi user mode.
+*/ +void xbSsv::SetMultiUser( xbBool bMultiUser ) { + this->bMultiUser = bMultiUser; +} + + + +/************************************************************************/ +#ifdef XB_MDX_SUPPORT +//! @brief Get the mdx file block size used when creating a memo file. +/*! + \returns system default setting for MDX block size. +*/ +xbInt16 xbSsv::GetCreateMdxBlockSize() const { + return iCreateMdxBlockSize; +} + +/************************************************************************/ +//! @brief Create mdx block size. +/*! + This routine sets the mdx file block size at the system level. This value is + used when the mdx index file is initially created so if you want to change it, + this must be called before creating the table. + + \param iBlockSize - Block size, must be evenly divisible by 512 and <= 16384 + \returns XB_INVALID_BLOCK_SIZE
XB_NO_ERROR +*/ + +xbInt16 xbSsv::SetCreateMdxBlockSize( xbInt16 iBlockSize ){ + + if( iBlockSize < 512 || iBlockSize > 16384 || iBlockSize % 512 ) + return XB_INVALID_BLOCK_SIZE; + else + iCreateMdxBlockSize = iBlockSize; + + return XB_NO_ERROR; +} +#endif + +/************************************************************************/ +#if defined (XB_NDX_SUPPORT) || defined (XB_MDX_SUPPORT) +//! @brief Get Unique Key Opt +/*! + This routine returns the Unique Key Processing Option which is one of: + XB_HALT_ON_DUPKEY + XB_EMULATE_DBASE +*/ + +xbInt16 xbSsv::GetUniqueKeyOpt() const { + return iUniqueKeyOpt; +} + +//! @brief Set Unique Key Opt +/*! @brief Set Unique Key Opt + This routine Sets the Unique Key Processing Option which is one of: + XB_HALT_ON_DUPKEY + XB_EMULATE_DBASE +*/ +xbInt16 xbSsv::SetUniqueKeyOpt( xbInt16 iOpt ){ + if( iOpt == XB_HALT_ON_DUPKEY || iOpt == XB_EMULATE_DBASE ){ + iUniqueKeyOpt = iOpt; + return XB_NO_ERROR; + } else { + return XB_INVALID_OPTION; + } +} +#endif +/************************************************************************/ + + + + +} /* 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.
+ zbFalse - Strings don't match.
+ +*/ +xbBool xbString::operator==( const xbString &s ) const { + + if( data == NULL || data[0] == 0 ) { + if( s.data == NULL || s.data[0] == 0 ) + return true; + return false; + } else { + if( s.data == NULL || s.data[0] == 0 ) + return false; + return( strcmp(data,s.data) == 0 ? xbTrue : xbFalse ); + } +} +/************************************************************************/ +//! @brief operator == +/*! + \param s String to compare + \returns xbTrue - Strings match.
+ zbFalse - Strings don't match.
+*/ +xbBool xbString::operator==( const char *s ) const { + + if (s == NULL) { + if ( data == NULL) + return true; + return false; + } + if ((s[0] == 0) && data == NULL) + return true; + if ( data == NULL) + return false; + return( strcmp( data, s) == 0 ? xbTrue : xbFalse ); +} +/************************************************************************/ +//! @brief operator != +/*! + \param s String to compare + \returns xbTrue - Strings don't match.
+ xbFalse - Strings match.
+*/ +xbBool xbString::operator!=( const xbString &s ) const { + if( data == NULL || data[0] == 0 ) { + if( s.data == NULL || s.data[0] == 0 ) + return xbFalse; // NULL != NULL + return xbTrue; // NULL != !NULL + } else { + if( s.data == NULL || s.data[0] == 0 ) + return xbTrue; // !NULL != NULL + return( strcmp( data, s.data ) != 0 ? xbTrue : xbFalse ); // !NULL != !NULL + } +} +/************************************************************************/ +//! @brief operator != +/*! + \param s String to compare + \returns xbTrue - Strings don't match.
+ zbFalse - Strings match.
+*/ +xbBool xbString::operator!=( const char *s ) const { + if( s == NULL || s[0] == 0 ) { + if( data == NULL || data[0] == 0 ) + return xbFalse; // NULL != NULL + return xbTrue; // NULL != !NULL + } else { + if( s == NULL || s[0] == 0 ) + return xbTrue; // !NULL != NULL + return( strcmp( data, s ) != 0 ? xbTrue : xbFalse ); // !NULL != !NULL + } +} +/************************************************************************/ +//! @brief operator < +/*! + \param s String to compare + \returns xbTrue - Left string is less than the right string.
+ zbFalse - Left string is not less than the right string.
+*/ +xbBool xbString::operator< ( const xbString &s ) const { + + if( data == NULL || data[0] == 0 ) { + if( s.data == NULL || s.data[0] == 0 ) + return false; + return true; + } else { + if( s.data == NULL || s.data[0] == 0 ) + return false; + return ( strcmp(data, s.data) < 0 ? xbTrue : xbFalse ); + } +} +/************************************************************************/ +//! @brief operator > +/*! + \param s String to compare + \returns xbTrue - Left string is greater than the right string.
+ zbFalse - Left string is not greater than the right string.
+*/ +xbBool xbString::operator> ( const xbString &s ) const { + if( data == NULL || data[0] == 0 ) { + if( s.data == NULL || s.data[0] == 0 ) + return false; + return false; + } else { + if( s.data == NULL || s.data[0] == 0 ) + return true; + return( strcmp(data,s.data) > 0 ? xbTrue : xbFalse ); + } +} +/************************************************************************/ +//! @brief operator <= +/*! + \param s String to compare + \returns xbTrue - Left string is less than or equal to the right string.
+ zbFalse - Left string is not less than or equal to the right string.
+*/ +xbBool xbString::operator<=( const xbString &s ) const { + if( data == NULL || data[0] == 0 ) { + if( s.data == NULL || s.data[0] == 0 ) + return true; + return true; + } else { + if( s.data == NULL || s.data[0] == 0 ) + return false; + return( strcmp(data,s.data) <= 0 ? xbTrue : xbFalse ); + } +} +/************************************************************************/ +//! @brief operator >= +/*! + \param s String to compare + \returns xbTrue - Left string is greater than or equal to the right string.
+ zbFalse - Left string is not greater than or equal to the right string.
+*/ +xbBool xbString::operator>=( const xbString &s ) const { + if( data == NULL || data[0] == 0 ) { + if( s.data == NULL || s.data[0] == 0 ) + return true; + return false; + } else { + if( s.data == NULL || s.data[0] == 0 ) + return true; + return( strcmp(data, s.data) >= 0 ? xbTrue : xbFalse ); + } +} + +/************************************************************************/ +//! @brief Add a prefixing back slash to specified characters in the string. +/*! + \param c Character to prefix with a backslash. + \returns Reference to this string. +*/ +xbString &xbString::AddBackSlash( char c ) { + + xbUInt32 lCnt = CountChar( c ); + if( lCnt == 0 ) + return *this; + char *p; + if(( p = (char *)calloc( 1, size + lCnt )) == NULL ) + return *this; + + char *p2 = p; + for( xbUInt32 lS = 0; lS < size; lS++ ){ + if( data[lS] == c ) + *p2++ = '\\'; + *p2++ = data[lS]; + } + if( data ) + free( data ); + data = p; + size += lCnt; + return *this; +} +/************************************************************************/ +//! @brief Append data to string. +/*! + \param s String data to append. + \returns Reference to this string. +*/ +xbString &xbString::Append( const xbString &s ) { + *this += s; + return *this; +} + +/************************************************************************/ +//! @brief Append data to string. +/*! + \param s String data to append. + \returns Reference to this string. +*/ +xbString &xbString::Append( const char *s ) { + *this += s; + return *this; +} +/************************************************************************/ +//! @brief Append data to string. +/*! + \param c String data to append. + \returns Reference to this string. +*/ +xbString &xbString::Append( char c ) { + *this += c; + return *this; +} +/************************************************************************/ +//! @brief Append data to string. +/*! + \param s String data to append. + \param ulByteCount Maximum number of bytes to append. + \returns Reference to this string. +*/ +xbString &xbString::Append( const char *s, xbUInt32 ulByteCount ) { + + if (s == NULL) return (*this); + xbUInt32 oldLen = this->Len(); + xbUInt32 newLen = ulByteCount + oldLen; + + data = (char *)realloc(data, newLen+1); + + if(oldLen == 0) + data[0] = 0; + + for( xbUInt32 i = 0; i < ulByteCount; i++ ) + data[i+oldLen] = s[i]; + + data[newLen] = '\0'; + size += ulByteCount; + return (*this); +} + +/************************************************************************/ +//! @brief Assign portion of string. +/*! + \param sStr - Source string for copy operation. sStr needs to be a Null terminated string. + \param ulStartPos - Starting position within source string. + \param ulCopyLen - Length of data to copy. + \returns Reference to this string. +*/ +xbString &xbString::Assign(const char * sStr, xbUInt32 ulStartPos, xbUInt32 ulCopyLen){ + if(data){ + free(data); + data = 0; + size = 0; + } + xbUInt32 lLen = (xbUInt32) strlen( sStr ); + if( ulStartPos > lLen ){ + size = 0; + return( *this ); + } + if((( ulCopyLen - 1) + ulStartPos ) > lLen ) + ulCopyLen = lLen - ulStartPos + 1; + data = (char *)calloc(1, ulCopyLen + 1); + size = ulCopyLen; + for( xbUInt32 i = 0; i < ulCopyLen; i++ ) + data[i] = sStr[i + ulStartPos - ((xbUInt32) 1)]; + data[ulCopyLen] = '\0'; + return (*this); +} +/************************************************************************/ +//! @brief Assign portion of string. +/*! + \param sStr - Source string for copy operation. sStr needs to be a Null terminated string. + \param ulStartPos - Starting position within source string. + \returns Reference to this string. +*/ +xbString &xbString::Assign(const char * sStr, xbUInt32 ulStartPos){ + if(data){ + free(data); + data = 0; + size = 0; + } + xbUInt32 ulSrcLen = (xbUInt32) strlen( sStr ); + if( ulStartPos > ulSrcLen ){ + size = 0; + return( *this ); + } + xbUInt32 ulCopyLen; + ulCopyLen = ulSrcLen - ulStartPos + 1; + data = (char *)calloc(1, ulCopyLen + 1); + size = ulCopyLen; + for( xbUInt32 i = 0; i < ulCopyLen; i++ ) + data[i] = sStr[i + ulStartPos - ((xbUInt32) 1)]; + data[ulCopyLen] = '\0'; + return (*this); +} + +/************************************************************************/ +//! @brief Assign portion of string. +/*! + \param sStr - Source string for copy operation. sStr needs to be a Null terminated string. + \param ulStartPos - Starting position within source string. + \param ulCopyLen - Length of data to copy. + \returns Reference to this string. +*/ +xbString &xbString::Assign(const xbString& sStr, xbUInt32 ulStartPos, xbUInt32 ulCopyLen){ + if(data){ + free(data); + data = 0; + size = 0; + } + xbUInt32 ulSrcLen = sStr.Len(); + if( ulStartPos > ulSrcLen ){ + size = 0; + return( *this ); + } + if((( ulCopyLen - 1) + ulStartPos ) > ulSrcLen ) + ulCopyLen = ulSrcLen - ulStartPos + 1; + data = (char *)calloc(1, ulCopyLen + 1); + size = ulCopyLen; + for( xbUInt32 i = 0; i < ulCopyLen; i++ ) + data[i] = sStr[i + ulStartPos]; + data[ulCopyLen] = '\0'; + return (*this); +} +/************************************************************************/ +//! @brief Assign portion of string. +/*! + \param sStr - Source string for copy operation. sStr needs to be a Null terminated string. + \param ulStartPos - Starting position within source string. + \returns Reference to this string. +*/ +xbString &xbString::Assign(const xbString& sStr, xbUInt32 ulStartPos){ + if(data){ + free(data); + data = 0; + size = 0; + } + xbUInt32 ulSrcLen = sStr.Len(); + if( ulStartPos > ulSrcLen ){ + size = 0; + return( *this ); + } + xbUInt32 ulCopyLen; + ulCopyLen = ulSrcLen - ulStartPos + 1; + data = (char *)calloc(1, ulCopyLen + 1); + size = ulCopyLen; + for( xbUInt32 i = 0; i < ulCopyLen; i++ ) + data[i] = sStr[i + ulStartPos]; + data[ulCopyLen] = '\0'; + return (*this); +} +/************************************************************************/ +//! @brief Copy a string +/*! + \returns xbString. +*/ +xbString xbString::Copy() const { + return( *this ); +} +/************************************************************************/ +//! @brief Count the number of characters in the string. +/*! + \param c Character to count. + \param iOpt 0 - Count the number of characters.
+ 1 - Count the number of characters not between single or double quotes. + \returns The number of characters. +*/ +xbUInt32 xbString::CountChar( char c, xbInt16 iOpt ) const { + if( iOpt == 0 ) + return CountChar( c ); + else{ + xbBool bSingleQuote = xbFalse; + xbBool bDoubleQuote = xbFalse; + char cPrevChar = 0x00; + xbUInt32 i,j; + for( i = 0, j = 0; i < size; i++ ){ + if( bSingleQuote && data[i] == '\'' && cPrevChar != '\\' ){ + bSingleQuote = xbFalse; + } + else if( bDoubleQuote && data[i] == '"' && cPrevChar != '\\' ){ + bDoubleQuote = xbFalse; + } + else if( data[i] == '\'' && cPrevChar != '\\' && !bDoubleQuote ){ + bSingleQuote = xbTrue; + } + else if( data[i] == '"' && cPrevChar != '\\' && !bSingleQuote ){ + bDoubleQuote = xbTrue; + } + else if( !bDoubleQuote && !bSingleQuote && data[i] == c ){ + j++; + } + cPrevChar = data[i]; + } + return j; + } +} +/************************************************************************/ +//! @brief Count the number of characters in the string. +/*! + \param c Character to count. + \returns The number of characters. +*/ +xbUInt32 xbString::CountChar( char c ) const { + xbUInt32 i,j; + for( i = 0,j = 0; i < size; i++ ) + if( data[i] == c ) + j++; + return j; +} +/************************************************************************/ +void xbString::ctor( const char *s ) { + + // this routine assumes it was called by one of the constructors. + + if (s == NULL) { + data = NULL; + size = 0; + return; + } + + size = (xbUInt32) (strlen(s) + 1); + data = (char *) calloc( 1, size); + + xb_strcpy(data, s); +} +/************************************************************************/ +//! @brief Convert hex character to string. +/*! + This routine converts a four byte string in the format of 0x00 to a one byte char value. + The first four bytes of the string must be in the format 0x00. + Anything past the first four bytes is disregarded. + + \param cOut Output character. + \returns XB_INVALID_PARM on error
+ XB_NO_ERROR on success. +*/ +xbInt16 xbString::CvtHexChar( char &cOut ){ + + int j, k; + char c; + + if( Len() < 4 || data[0] != '0' || (data[1]!='X' && data[1]!='x' )) + return XB_INVALID_PARM; + + c = (char) toupper( data[2] ); + j = ( c > '9' ? c - 'A' + 10 : c - '0' ); + c = (char)toupper( data[3] ); + k = ( c > '9' ? c - 'A' + 10 : c - '0' ); + j = ( j << 4 ) + k; + + cOut = ( char ) j; + return XB_NO_ERROR; +} +/************************************************************************/ +//! @brief Convert string of hex characters to string. +/*! + + This routine converts a string of four byte format of 0x00 to a string of one byte chars. + + \param sOut Output string of converted characters. + \returns XB_INVALID_PARM on error
+ XB_NO_ERROR on success. +*/ +xbInt16 xbString::CvtHexString( xbString &sOut ){ + char c; + xbString ws; + ws = data; + sOut = ""; + xbInt16 iRc; + while( ws.Len()){ + if(( iRc= ws.CvtHexChar( c )) != XB_NO_ERROR ) + return iRc; + sOut += c; + ws.Ltrunc( 4 ); + } + return XB_NO_ERROR; +} +/************************************************************************/ +//! @brief Convert string to xbUInt64 number +/*! + \param ullOut - output unsigned long long. + \returns XB_NO_ERROR +*/ +xbInt16 xbString::CvtULongLong( xbUInt64 &ullOut ){ + + // version 1 - fast, but no data checking + ullOut = 0; + char *s = data; + int i = 0; + while( *s ){ + ullOut *= 10; + ullOut += (xbUInt64) *s - '0'; + s++; + i++; + } + return XB_NO_ERROR; +} +/************************************************************************/ +//! @brief Convert string to xbInt64 number +/*! + \param llOut - output long long. + \returns XB_NO_ERROR +*/ +xbInt16 xbString::CvtLongLong( xbInt64 &llOut ){ + + // version 1 - fast, but no data checking + llOut = 0; + char *s = data; + int i = 0; + while( *s ){ + llOut *= 10; + llOut += (xbInt64) *s - '0'; + s++; + i++; + } + return XB_NO_ERROR; +} + +/************************************************************************/ +#ifdef XB_DEBUG_SUPPORT +void xbString::Dump( const char * title, xbInt16 iHexOption ) const { + fprintf(stdout, "%s StringSize[%d] DataLen=[%d] data=[%s]\n", title, size, Len(), data ); + if( iHexOption ){ + std::cout << "Hex values" << std::endl; + for( xbUInt32 i = 0; i < strlen( data ); i++ ) + printf( " %x", data[i] ); + std::cout << std::endl; + } +} +void xbString::Dump( const char * title ) const { + Dump( title, 0 ); +} + +void xbString::DumpHex( const char * title ) const { + Dump( title, 1 ); +} +#endif + +/************************************************************************/ +//! @brief Extract an element out of a delimited string. +/*! + \param pSrc Source string. + \param cDelim Delimiter. + \param lSkipCnt Number of delimiters to skip. + \param iOpt 0 - ignore single and double quotes.
+ 1 - ignore delimiters between single or double quotes. + \returns Reference to string extracted from element. +*/ +xbString &xbString::ExtractElement( const char *pSrc, char cDelim, xbUInt32 lSkipCnt, xbInt16 iOpt ) +{ + /* opt values + 0 - ignore single and double quotes + 1 - ignore delimiters between single or double quotes + */ + + xbUInt32 lLen; + xbUInt32 lCurCnt = 0; + xbBool bInSingleQuotes = xbFalse; + xbBool bInDoubleQuotes = xbFalse; + char cPrevChar = 0x00; + const char *s = pSrc; + const char *pAnchor; + + /* skip past skipcnt delimiters */ + while( *s && lCurCnt < (lSkipCnt-1) ){ + if( iOpt == 0 ){ + if( *s == cDelim ) + lCurCnt++; + } else { + if( *s == cDelim && !bInSingleQuotes && !bInDoubleQuotes ){ + lCurCnt++; + } else if( *s == '\'' && !bInDoubleQuotes && cPrevChar != '\\' ){ + if( bInSingleQuotes == xbTrue ) + bInSingleQuotes = xbFalse; + else + bInSingleQuotes = xbTrue; + } else if( *s == '"' && !bInSingleQuotes && cPrevChar != '\\' ){ + if( bInDoubleQuotes == xbTrue ) + bInDoubleQuotes = xbFalse; + else + bInDoubleQuotes = xbTrue; + } + } + cPrevChar = *s; + s++; + } + + /* at the beginning of the field */ + pAnchor = s; + xbBool bDone = xbFalse; + while( *s && !bDone ){ + if( iOpt == 0 ){ + if( *s == cDelim ) + bDone = xbTrue; + } else { + if( *s == cDelim && !bInSingleQuotes && !bInDoubleQuotes ){ + bDone = xbTrue; + } else if( *s == '\'' && !bInDoubleQuotes && cPrevChar != '\\' ){ + if( bInSingleQuotes == xbTrue ) + bInSingleQuotes = xbFalse; + else + bInSingleQuotes = xbTrue; + } else if( *s == '"' && !bInSingleQuotes && cPrevChar != '\\' ){ + if( bInDoubleQuotes == xbTrue ) + bInDoubleQuotes = xbFalse; + else + bInDoubleQuotes = xbTrue; + } + } + cPrevChar = *s; + s++; + } + + // if at end of string, go back one and drop the delimiter + if( *s ) s--; + + lLen = (xbUInt32)(s - pAnchor); + + /* copy data */ + data = (char *) realloc( data, lLen+1 ); + memcpy( data, pAnchor, lLen ); + data[lLen] = 0; + this->size = lLen+1; + return *this; +} + +/************************************************************************/ +//! @brief Get a character by position +/*! + \param n - Position in string to extract. First position is 1 (not 0). + \returns Character from position n, or null. +*/ +char xbString::GetCharacter( xbUInt32 n ) const { + if( n > 0 && n <= size ) + return data[n-1]; + else + return 0x00; +} +/************************************************************************/ +//! @brief Get the position of the last occurrence of a given character. +/*! + \param c - Character to search for. + \returns Last position of character in the string. +*/ +xbUInt32 xbString::GetLastPos(char c) const { + + if (data == NULL) + return 0; + + char *p = data; + xbUInt32 iPos = 0; + xbUInt32 hPos = 0; + while( *p && iPos++ < ( size - 1 )){ + if( *p == c ) + hPos = iPos; + p++; + } + if( hPos ) + return hPos; + else + return 0; +} +/************************************************************************/ +//! @brief Get the position of the last occurrence of a given string. +/*! + \param s - String to search for. + \returns Last position of character in the string. +*/ +xbUInt32 xbString::GetLastPos(const char* s) const{ + + if (data == NULL) + return 0; + + char *p = data; + char *saveP = NULL; + while( p ){ + p = strstr( p, s); + if( p ){ + saveP = p; + p++; + } + } + if (saveP == NULL) + return 0; + return (xbUInt32)(saveP - data) + 1; +} +/************************************************************************/ +//! @brief Get the path separator out of the string. +/*! + \returns Char value containing either / or \ depending on OS. +*/ +char xbString::GetPathSeparator() const { + + if (data == NULL) + return 0x00; + char *p = data; + while( *p ){ + if( *p == '\\' || *p == '/' ) + return *p; + else + p++; + } + return 0x00; +} + +/************************************************************************/ +//! @brief Retrieve the size of the string buffer. +/*! + \returns Size of string buffer including the null terminating byte. +*/ +xbUInt32 xbString::GetSize() const { + return size; +} + +/************************************************************************/ +//! @brief Determine if the string has any alpha characters +/*! + \returns xbTrue - String contains one or more aloha characters.
+ xbFalse - String contains no alpha characters. +*/ +xbBool xbString::HasAlphaChars() const { + for( xbUInt32 i = 0; i < size; i++ ) + if( isalpha( data[i] )) + return xbTrue; + return xbFalse; +} + + +/************************************************************************/ +//! @brief Determine if string is empty +/*! + \returns xbTrue if string is empty.
+ xbFalse if string is not empty. +*/ +xbBool xbString::IsEmpty() const { + if( data == NULL ) + return true; + if( data[0] == 0 ) + return xbTrue; + return xbFalse; +} + +/************************************************************************/ +//! @brief Determine if string is NULL +/*! + \returns xbTrue if string is NULL.
+ xbFalse if string is not NULL. +*/ +xbBool xbString::IsNull() const { + return( data == NULL ); +} + + +/************************************************************************/ +//! @brief Retain left part of string, drop rightmost characters. +/*! + \param ulLen New string length, truncate rightmost excess. + \returns Reference to string. +*/ +xbString &xbString::Left( xbUInt32 ulLen ) { + return Mid( 1, ulLen ); +} + +/************************************************************************/ +//! @brief Retrieve length of current string. +/*! + \returns String length, excluding the terminating null byte. +*/ +// return length of string +xbUInt32 xbString::Len() const { + return( data ? (xbUInt32) strlen(data) : 0 ); +} + +/************************************************************************/ +//! @brief Left trim white space from string. +/*! + \returns Reference to this string. +*/ +xbString &xbString::Ltrim(){ + + if( !data ) + return *this; + + char *p = data; + if( !*p || (*p && *p != ' ') ) + return *this; /* nothing to do */ + + xbUInt32 s = 0; + while( *p && *p == ' ' ){ + p++; + s++; + size--; + } + xbUInt32 i; + for( i = 0; i < size; i++ ) + data[i] = data[i+s]; + data[i] = 0x00; + data = (char *) realloc( data, size ); + return *this; +} + +/************************************************************************/ +//! @brief Left truncate string +/*! + \param ulCnt Number of bytes to remove from the left. + \returns Reference to this string. +*/ +xbString &xbString::Ltrunc( xbUInt32 ulCnt ){ + // left truncate cnt bytes + + char * ndata; + char * p; + if( ulCnt >= size ){ + if( size > 0 ){ + free( data ); + data = NULL; + size = 0; + } + return *this; + } + ndata = (char *) calloc( 1, size - ulCnt ); + p = data; + p += ulCnt; + xb_strcpy( ndata, p ); + free( data ); + data = ndata; + size = size - ulCnt; + return *this; +} + +/************************************************************************/ +//! @brief Extract portion of data from string +/*! + \param ulStartPos Starting position + \param ulTargLen Length + \returns Reference to string +*/ +xbString &xbString::Mid( xbUInt32 ulStartPos, xbUInt32 ulTargLen ){ + + // this is a 1 based routine + if( ulStartPos == 0 ) + return *this; + + if( data == NULL ) + return( *this ); + if( data[0] == 0 ) + return( *this ); + if( ulStartPos > Len() ) + return( *this ); + char *pTarg = data; + char *pSrc = data + ulStartPos - 1; + for( xbUInt32 l = 0; l < ulTargLen; l++ ) + *pTarg++ = *pSrc++; + *pTarg = 0x00; + Resize( ulTargLen + 1 ); + return *this; +} + +/************************************************************************/ +//! @brief Left pad string +/*! + \param c Padding character. + \param ulNewLen New string length. + \returns Reference to this string. +*/ +xbString &xbString::PadLeft( char c, xbUInt32 ulNewLen ){ + + xbUInt32 srcLen; + if( data ) + srcLen = (xbUInt32) strlen( data ); + else + srcLen = 0; + + if( srcLen >= ulNewLen ) + return *this; + + char * newData = (char *) calloc( 1, ulNewLen + 1 ); + xbUInt32 i; + for( i = 0; i < ulNewLen - srcLen; i++ ) + newData[i] = c; + + char *targ = &newData[i]; + xb_strcpy( targ, data ); + free( data ); + data = newData; + size = ulNewLen + 1; + return *this; +} + +/************************************************************************/ +//! @brief Right pad string +/*! + \param c Padding character. + \param ulNewLen New string length. + \returns Reference to this string. +*/ +xbString &xbString::PadRight( char c, xbUInt32 ulNewLen ){ + xbUInt32 srcLen = (xbUInt32) strlen( data ); + if( srcLen >= ulNewLen ) + return *this; + data = (char *) realloc( data, ulNewLen + 1 ); + xbUInt32 i; + for( i = srcLen; i < ulNewLen; i++ ) + data[i] = c; + data[i] = 0x00; + size = ulNewLen + 1; + return *this; +} + +/************************************************************************/ +//! @brief Determine position of a given character +/*! + \param c Seek character + \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; iLen(); + for (xbUInt32 i=0;i + + \returns xbTrue if the data is valid logical data.
+ xbFalse if not valid logical data. +*/ + +xbBool xbString::ValidLogicalValue() const { + if( Len() == 1 ) + if( data[0] == 'T' || data[0] == 'F' || data[0] == 'Y' || data[0] == 'N' || data[0] == '?' ) + return xbTrue; + return xbFalse; +} +/************************************************************************/ +//! @brief This function returns true if the data is valid numeric data +/*! + \returns xbTrue if valid numeric data.
+ xbFalse if not valid numeric daata. +*/ + +xbBool xbString::ValidNumericValue() const { + const char *p; + p = data; + while( *p ){ + if( *p != '+' && *p != '-' && *p != '.' && *p != '0' && *p != '1' && + *p != '2' && *p != '3' && *p != '4' && *p != '5' && *p != '6' && + *p != '7' && *p != '8' && *p != '9' ) + return xbFalse; + else + p++; + } + return xbTrue; +} + + +/************************************************************************/ +//! @brief Remove every instance of a character from a string. +/*! + \param c character to remove from string. + \returns Reference to this stirng.void +*/ + +xbString &xbString::ZapChar( char c ){ + + if( data == NULL ) + return *this; + if( data[0] == 0 ) + return *this; + + char *s; + char *t; + + s = data; + t = data; + while( *s ){ + if( *s == c ){ + s++; + size--; + } else { + *t++ = *s++; + } + } + *t = 0x00; + Resize( size ); + return *this; +} + +/************************************************************************/ +//! @brief Remove leading character from a string. +/*! + \param c character to remove from beginning of string. + \returns Reference to this string. +*/ +xbString &xbString::ZapLeadingChar( char c ){ + /* left truncate all of character c */ + xbUInt32 iLen = 0; + char *p; + p = data; + while( *p && *p == c ){ + iLen++; + p++; + } + if( iLen ) + Ltrunc( iLen ); + return *this; +} + + +/************************************************************************/ +//! @brief Remove trailing character from a string. +/*! + \param c character to remove from ending of string. + \returns Reference to this string. +*/ +xbString &xbString::ZapTrailingChar( char c ){ + + xbUInt32 l = Len(); + if( l == 0 ) + return *this; + + l--; + for(;;) { + if( data[l] != c ) + break; + data[l] = 0; + size--; + if( l == 0 ) + break; + l--; + } + return *this; +} + +} /* namespace */ \ No newline at end of file diff --git a/src/core/xbtag.cpp b/src/core/xbtag.cpp 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.
xbFalse - Index is not unique. + \param bSort MDX only
xbTrue - Descending.
xbFalse - Ascending. +*/ + +xbTag::xbTag( xbIx *pIx, void *vpTag, xbString &sType, xbString &sTagName, xbString &sExpression, xbString &sFilter, +xbBool bUnique, xbBool bSort ) +{ + this->pIx = pIx; + this->vpTag = vpTag; + this->sType = sType; + this->sTagName = sTagName; + this->sExpression = sExpression; + this->sFilter = sFilter; + this->bUnique = bUnique; + this->bSort = bSort; +} + + +/************************************************************************/ +//! @brief Get tag key expression. +/*! + \returns Tag key expression. +*/ +const xbString &xbTag::GetExpression() const { + return sExpression; +} +/************************************************************************/ +//! @brief Get tag filter expression. +/*! + \returns Tag filter expression (mdx only). +*/ + +const xbString &xbTag::GetFilter() const { + return sFilter; +} +/************************************************************************/ +//! @brief Get index file pointer. +/*! + \returns Pointer to index file instance. +*/ +xbIx *xbTag::GetIx() const { + return pIx; +} +/************************************************************************/ +//! @brief Get tag ascending setting. +/*! + \returns Tag sort setting - MDX only.
xbTrue - Descending.
xbFalse - Ascending. +*/ +xbBool xbTag::GetSort() const { + return bSort; +} +/************************************************************************/ +//! @brief Get tag name. +/*! + \returns Tag name. +*/ + +const xbString &xbTag::GetTagName() const { + return sTagName; +} +/************************************************************************/ +//! @brief Get tag type. +/*! + \returns Tag type. +*/ +const xbString &xbTag::GetType() const { + return sType; +} + +/************************************************************************/ +//! @brief Get tag unique setting. +/*! + \returns Tag unique setting.
xbTrue - Unique.
xbFalse - Not unique. +*/ + +xbBool xbTag::GetUnique() const { + return bUnique; +} +/************************************************************************/ +//! @brief Get tag pointer for tag within index file. +/*! + \returns Pointer to tag within index file instance. +*/ +void *xbTag::GetVpTag() const { + return vpTag; +} +/************************************************************************/ +} /* namespace */ +#endif /* XB_INDEX_SUPPORT */ \ No newline at end of file diff --git a/src/core/xbtblmgr.cpp b/src/core/xbtblmgr.cpp new file mode 100755 index 0000000..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 +//#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 *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 Return Codes +*/ +xbInt16 xbXBase::CloseAllTables(){ + + xbInt16 iRc = 0; + xbInt16 iErrorStop = 0; + xbInt16 iOpenTableCnt = GetOpenTableCount(); + try{ + xbDbf *d; + for( xbInt16 i = 0; i < iOpenTableCnt; i++ ){ + d = (xbDbf *) GetDbfPtr( 1 ); + if( d ){ + if(( iRc = d->Close()) != XB_NO_ERROR ){ + iErrorStop = 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.
xbFalse - Logging disables. +*/ +xbBool xbXBase::GetLogStatus() const { + return xLog->LogGetStatus(); +} + +//! @brief Set the log file name. +/*! + \param sLogFileName - Log File Name. + \return void +*/ +void xbXBase::SetLogFileName( const xbString & sLogFileName ){ + xLog->SetFileName( sLogFileName ); +} + +//! @brief Set the log directory. +/*! + \param sLogDirectory - Log File Directory. + \return void +*/ +void xbXBase::SetLogDirectory( const xbString & sLogDirectory ){ + xLog->SetDirectory( sLogDirectory ); +} + +//! @brief Set the logfile size. +/*! + \param lSize - Log File Size. + \return void +*/ +void xbXBase::SetLogSize( size_t lSize ) { + xLog->LogSetLogSize( lSize ); +} + + +//! @brief Write message to logfile. +/*! + \param sLogMessage - Message to write. + \param iOpt 0 = stdout
+ 1 = Syslog
+ 2 = Both
+ \returns Return Codes +*/ + +xbInt16 xbXBase::WriteLogMessage( const xbString & sLogMessage, xbInt16 iOpt ){ + return xLog->LogWrite( sLogMessage, iOpt ); +} + +//! @brief Write message to logfile. +/*! + \param lCnt - Number of bytes to write. + \param p - Pointer to bytes to write to log file. + \returns Return Codes +*/ +xbInt16 xbXBase::WriteLogBytes( xbUInt32 lCnt, const char *p ){ + return xLog->LogWriteBytes( lCnt, p ); +} + +//! @brief Enable message logging. +void xbXBase::EnableMsgLogging() { + xLog->LogSetStatus( xbTrue ); +} + +//! @brief Disable message logging. +void xbXBase::DisableMsgLogging() { + xLog->LogSetStatus( xbFalse ); +} + +//! @brief Flush log file updates to disk. +xbInt16 xbXBase::FlushLog() { + return xLog->xbFflush(); +} +#else + +// if logging not compiled in, these stubs are called with no effect +const xbString & xbXBase::GetLogFqFileName() const { + return sNullString; +} +const xbString & xbXBase::GetLogFileName() const { + return sNullString; +} +const xbString & xbXBase::GetLogDirectory() const { + return sNullString; +} +void xbXBase::SetLogFileName( const xbString & sLogFileName ){ + return; +} +void xbXBase::SetLogDirectory( const xbString & sLogDirectory ){ + return; +} +xbBool xbXBase::GetLogStatus() const { + return xbFalse; +} +xbInt16 xbXBase::WriteLogMessage( const xbString & sLogMessage ){ + 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
+ 1 = use value specified in lReturnLenVal
+ 2 = use length of operand specified in col 4
+ 3 = use valued of numeric operand specified in col 4
+ 4 = length of parm 1 * numeric value parm
+ 5 = larger length of parm 2 or length of parm 3
+ 6 = if two or more parms, use numeric value from second parm, otherwise use col4 value + \param lReturnLenVal Used in combination with iReturnLenCalc. + \returns Return Codes +*/ + +xbInt16 xbXBase::GetFunctionInfo( const xbString &sExpLine, char &cReturnType, xbInt16 &iCalc, xbInt32 &lReturnLenVal ) const{ + + xbUInt32 iLen; + const char *s; + if( sExpLine.Len() == 0 ) + return XB_INVALID_FUNCTION; + + s = sExpLine; + iLen = 0; + while( *s && *s != '(' && *s != ' ' ) { s++; iLen++; } + xbString sFunction( sExpLine, iLen ); + cReturnType = 0x00; + char cFunc1 = sFunction[1]; + + if( cFunc1 < 'L' ){ + // std::cout << "less than L\n"; + if( cFunc1 < 'D' ){ + // std::cout << "less than D\n"; + if( sFunction == "ABS" ){ + // { "ABS", 'N', 1, 4 }, + cReturnType = 'N'; + iCalc = 1; + lReturnLenVal = 4; + } else if( sFunction == "ALLTRIM" ){ + // { "ALLTRIM", 'C', 2, 1 }, + cReturnType = 'C'; + iCalc = 2; + lReturnLenVal = 1; + } else if( sFunction == "ASC" ){ + // { "ASC", 'N', 1, 4 }, + cReturnType = 'N'; + iCalc = 1; + lReturnLenVal = 4; + } else if( sFunction == "AT" ){ + // { "AT", 'N', 1, 4 }, + cReturnType = 'N'; + iCalc = 1; + lReturnLenVal = 4; + } else if( sFunction == "CDOW" ){ + // { "CDOW", 'C', 1, 9 }, + cReturnType = 'C'; + iCalc = 1; + lReturnLenVal = 9; + } else if( sFunction == "CHR" ){ + // { "CHR", 'C', 1, 1 }, + cReturnType = 'C'; + iCalc = 1; + lReturnLenVal = 1; + } else if( sFunction == "CMONTH" ){ + // { "CMONTH", 'C', 1, 9 }, + cReturnType = 'C'; + iCalc = 1; + lReturnLenVal = 9; + } else if( sFunction == "CTOD" ){ + // { "CTOD", 'D', 1, 8 }, + cReturnType = 'D'; + iCalc = 1; + lReturnLenVal = 8; + } + } else { + // std::cout << ">= D\n"; + if( sFunction == "DATE" ){ + // { "DATE", 'D', 1, 8 }, + cReturnType = 'D'; + iCalc = 1; + lReturnLenVal = 8; + } else if( sFunction == "DAY" ){ + // { "DAY", 'N', 1, 4 }, + cReturnType = 'N'; + iCalc = 1; + lReturnLenVal = 4; + } else if( sFunction == "DEL" ){ + // { "DEL", 'C', 1, 1 }, + cReturnType = 'C'; + iCalc = 1; + lReturnLenVal = 1; + } else if( sFunction == "DELETED" ){ + // { "DELETED", 'L', 1, 1 }, + cReturnType = 'L'; + iCalc = 1; + lReturnLenVal = 1; + } else if( sFunction == "DESCEND" ){ + // { "DESCEND", '1', 2, 1 }, + cReturnType = '1'; + iCalc = 2; + lReturnLenVal = 1; + } else if( sFunction == "DOW" ){ + // { "DOW", 'N', 1, 4 }, + cReturnType = 'N'; + iCalc = 1; + lReturnLenVal = 4; + } else if( sFunction == "DTOC" ){ + // { "DTOC", 'C', 1, 8 }, + cReturnType = 'C'; + iCalc = 1; + lReturnLenVal = 8; + } else if( sFunction == "DTOS" ){ + // { "DTOS", 'C', 1, 8 }, + cReturnType = 'C'; + iCalc = 1; + lReturnLenVal = 8; + } else if( sFunction == "EXP" ){ + // { "EXP", 'N', 1, 4 }, + cReturnType = 'N'; + iCalc = 1; + lReturnLenVal = 4; + } else if( sFunction == "IIF" ){ + // { "IIF", 'C', 5, 0 }, + cReturnType = 'C'; + iCalc = 5; + lReturnLenVal = 0; + } else if( sFunction == "INT" ){ + // { "INT", 'N', 1, 4 }, + cReturnType = 'N'; + iCalc = 1; + lReturnLenVal = 4; + } else if( sFunction == "ISALPHA" ){ + // { "ISALPHA", 'L', 1, 1 }, + cReturnType = 'L'; + iCalc = 1; + lReturnLenVal = 1; + } else if( sFunction == "ISLOWER" ){ + // { "ISLOWER", 'L', 1, 1 }, + cReturnType = 'L'; + iCalc = 1; + lReturnLenVal = 1; + } else if( sFunction == "ISUPPER" ){ + // { "ISUPPER", 'L', 1, 1 }, + cReturnType = 'L'; + iCalc = 1; + lReturnLenVal = 1; + } + } + } else { + // std::cout << ">= L\n"; + if( cFunc1 < 'R' ) { + // std::cout << " < R\n"; + if( sFunction == "LEFT" ){ + // { "LEFT", 'C', 3, 2 }, + cReturnType = 'C'; + iCalc = 3; + lReturnLenVal = 2; + } else if( sFunction == "LEN" ){ + // { "LEN", 'N', 1, 4 }, + cReturnType = 'N'; + iCalc = 1; + lReturnLenVal = 3; + } else if( sFunction == "LOG" ){ + // { "LOG", 'N', 1, 4 }, + cReturnType = 'N'; + iCalc = 1; + lReturnLenVal = 4; + } else if( sFunction == "LOWER" ){ + // { "LOWER", 'C', 2, 1 }, + cReturnType = 'C'; + iCalc = 2; + lReturnLenVal = 1; + } else if( sFunction == "LTRIM" ){ + // { "LTRIM", 'C', 2, 1 }, + cReturnType = 'C'; + iCalc = 2; + lReturnLenVal = 1; + } else if( sFunction == "MAX" ){ + // { "MAX", 'N', 1, 4 }, + cReturnType = 'N'; + iCalc = 1; + lReturnLenVal = 4; + } else if( sFunction == "MIN" ){ + // { "MIN", 'N', 1, 4 }, + cReturnType = 'N'; + iCalc = 1; + lReturnLenVal = 4; + } else if( sFunction == "MONTH" ){ + // { "MONTH", 'N', 1, 4 }, + cReturnType = 'N'; + iCalc = 1; + lReturnLenVal = 4; + } + } else if( cFunc1 == 'R' ){ + // std::cout << "==R\n"; + if( sFunction == "RECNO" ){ + // { "RECNO", 'N', 1, 4 }, + cReturnType = 'N'; + iCalc = 1; + lReturnLenVal = 4; + } else if( sFunction == "RECCOUNT" ){ + // { "RECCOUNT", 'N', 1, 4 }, + cReturnType = 'N'; + iCalc = 1; + lReturnLenVal = 4; + } else if( sFunction == "REPLICATE" ){ + // { "REPLICATE", 'C', 4, 0 }, + cReturnType = 'C'; + iCalc = 4; + lReturnLenVal = 0; + } else if( sFunction == "RIGHT" ){ + // { "RIGHT", 'C', 3, 2 }, + cReturnType = 'C'; + iCalc = 3; + lReturnLenVal = 2; + } else if( sFunction == "RTRIM" ){ + // { "RTRIM", 'C', 2, 1 }, + cReturnType = 'C'; + iCalc = 2; + lReturnLenVal = 1; + } + } else if( cFunc1 == 'S' ){ + // std::cout << "==S\n"; + if( sFunction == "SPACE" ){ + // { "SPACE", 'C', 3, 1 }, + cReturnType = 'C'; + iCalc = 3; + lReturnLenVal = 1; + } else if( sFunction == "SQRT" ){ + // { "SQRT", 'N', 1, 4 }, + cReturnType = 'N'; + iCalc = 1; + lReturnLenVal = 4; + } else if( sFunction == "STOD" ){ + // { "STOD", 'D', 1, 8 }, + cReturnType = 'D'; + iCalc = 1; + lReturnLenVal = 8; + } else if( sFunction == "STR" ){ + // { "STR", 'C', 6, 10 }, + cReturnType = 'C'; + iCalc = 6; + lReturnLenVal = 10; + } else if( sFunction == "STRZERO" ){ + // { "STRZERO", 'C', 3, 2 }, + cReturnType = 'C'; + iCalc = 3; + lReturnLenVal = 2; + } else if( sFunction == "SUBSTR" ){ + // { "SUBSTR", 'C', 3, 3 }, + cReturnType = 'C'; + iCalc = 3; + lReturnLenVal = 3; + } + } else { + // std::cout << ">S\n"; + if( sFunction == "TRIM" ){ + // { "TRIM", 'C', 2, 1 }, + cReturnType = 'C'; + iCalc = 2; + lReturnLenVal = 1; + } else if( sFunction == "UPPER" ){ + // { "UPPER", 'C', 2, 1 }, + cReturnType = 'C'; + iCalc = 2; + lReturnLenVal = 1; + } else if( sFunction == "VAL" ){ + // { "VAL", 'N', 1, 4 }, + cReturnType = 'N'; + iCalc = 1; + lReturnLenVal = 3; + } else if( sFunction == "YEAR" ){ + // { "YEAR", 'N', 1, 4 }, + cReturnType = 'N'; + iCalc = 1; + lReturnLenVal = 4; + } + } + } + if( cReturnType == 0x00 ) + return XB_INVALID_FUNCTION; + else + return XB_NO_ERROR; +} +#endif +/*************************************************************************/ +//! @brief Cross platform sleep function. +/*! + \param lMillisecs Milliseconds to sleep. +*/ +void xbXBase::xbSleep( xbInt32 lMillisecs ){ + #ifdef WIN32 + Sleep( lMillisecs ); + #else + usleep( (xbInt64) lMillisecs * 1000 ); + #endif + +} +/***********************************************************************/ +//! @brief Cross memcmp function. +/*! + \param s1 Left operand to compare. + \param s2 Right operand to compare. + \param n Number of bytes to compare. + \returns 1 s1 > s2
+ 0 s1 == s2
+ -1 s1 < s2 +*/ +xbInt16 xbXBase::xbMemcmp( const unsigned char *s1, const unsigned char *s2, size_t n ){ + // The standard memcmp function was found not to behave the same across all platforms + for( size_t i = 0; i < n; i++ ){ + if( s1[i] > s2[i] ) + return 1; + else if( s1[i] < s2[i] ) + return -1; + } + return 0; +} + +/***********************************************************************/ +//! @brief Open highest qualified class available for dbf file. +/*! + This routine opens the highest available version of the dbf file. + Defaults to XB_READ_WRITE and XB_MULTI_USER mode. + \returns param dbf - Output pointer to dbf file opened or null if error +*/ +xbDbf* xbXBase::Open( const xbString &sTableName, xbInt16 &iRc ){ + return Open( sTableName, "", XB_READ_WRITE, XB_MULTI_USER, 0, iRc ); +} +/***********************************************************************/ +//! @brief Open highest qualified class available for dbf file. +/*! + This routine can open various versions of the dbf file dependent on the iVersion field + + \param sTableName - Table name to open. + \param sAlias - Optional alias name. + \param iOpenMode - XB_READ_WRITE or XB_READ + \param iShareMode - XB_SINGLE_USER or XB_MULTI_USER + \param iRequestVersion 0 - Highest available + 4 - Version four dbf + 3 - Version three dbf + \param iRc - Return code from open request + \returns param dbf - Output pointer to dbf file opened or null if error +*/ + + +xbDbf* xbXBase::Open( const xbString &sTableName, const xbString &sAlias, xbInt16 iOpenMode, + xbInt16 iShareMode, xbInt16 iRequestVersion, xbInt16 &iRc ){ + + xbInt16 iErrorStop = 0; + xbDbf * pDbf = 0; + iRc = 0; + xbString sFqFileName; + + try{ + + if( sTableName.Len() == 0 ){ + iErrorStop = 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 */ diff --git a/src/examples/xb_ex_date.cpp b/src/examples/xb_ex_date.cpp new file mode 100755 index 0000000..fc038b5 --- /dev/null +++ b/src/examples/xb_ex_date.cpp @@ -0,0 +1,210 @@ +/* xb_ex_date.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 + +This program demonstrates using the xbDate class + +*/ + + +#include "xbase.h" + +using namespace xb; + +int main() +{ + + xbString StringDate( "19601007" ); /* oct 7 1960 */ + char CharDate[9] = "19611109"; /* nov 9 1961 */ + + xbDate d1; /* today is default */ + xbDate d2( StringDate ); /* from string data */ + xbDate d3( CharDate ); /* from char data */ + xbDate d4; /* another date class */ + xbString s; + + std::cout << "This program demonstrates usage of the xbDate class" << std::endl; + std::cout << "Date 1 (Sysdate) is " << d1.Str() << std::endl; + std::cout << "Date 2 (StringDate) is " << d2.Str() << std::endl; + std::cout << "Date 3 (CharDate) is " << d3.Str() << std::endl; + + std::cout << "This year is " << d1.YearOf() << std::endl; + std::cout << "Year of xbString Date is " << d2.YearOf() + << std::endl; + std::cout << "Year of char Date is " << d3.YearOf() + << std::endl; + + std::cout << "This Month is " << d1.MonthOf() << std::endl; + std::cout << "Month of xbString Date is " << d2.MonthOf() + << std::endl; + std::cout << "Month of char Date is " << d3.MonthOf() + << std::endl; + + std::cout << "Today is day " << d1.DayOf( XB_FMT_WEEK ) << " of the week" << std::endl; + std::cout << "StringDate is day " << d2.DayOf( XB_FMT_MONTH ) << " of the month" << std::endl; + std::cout << "CharDate is day " << d3.DayOf( XB_FMT_YEAR ) << " of the year" << std::endl; + + if( d1.IsLeapYear()) + std::cout << "This is a leap year" << std::endl; + else + std::cout << "This is not a leap year." << std::endl; + + if( d2.IsLeapYear()) + std::cout << "StringDate is a leap year" << std::endl; + else + std::cout << "StringDate is not a leap year." << std::endl; + + if( d3.IsLeapYear()) + std::cout << "CharDate is a leap year" << std::endl; + else + std::cout << "CharDate is not a leap year." << std::endl; + + std::cout << "Today is " << d1.Sysdate() << std::endl; + + if( d1.DateIsValid( "19951301" )) + std::cout << "19951301 is a valid date" << std::endl; + else + std::cout << "19951301 is not a valid date" << std::endl; + + if( d1.DateIsValid( "19920229" )) + std::cout << "19920229 is a valid date" << std::endl; + else + std::cout << "19920229 is not a valid date" << std::endl; + + if( d2.DateIsValid( StringDate )) + std::cout << StringDate.Str() << " is a valid date" << std::endl; + else + std::cout << StringDate.Str() << " is not a valid date" << std::endl; + + std::cout << "Today's Julian date " << d1.JulianDays() << std::endl; + std::cout << "Julian date of Jan 01, 1970 " << d2.JulianDays() << std::endl; + std::cout << "StringDate Julian date " << d2.JulianDays() << std::endl; + + std::cout << "There are " << (d1.JulianDays() - d2.JulianDays()) << " days between " << d1.Str() << " and " << d2.Str() << std::endl; + + std::cout << "Todays Julian date (Number of days since Jan 1 0100):" << d1.JulianDays() << std::endl; + + d4 = d1; // set d4 class = to sysdate + std::cout << "Object d4 initialized to " << d4.Str() << std::endl; + std::cout << "This should be todays date: " + << d4.JulToDate8(d4.JulianDays()) << std::endl; + std::cout << "In 7 days it will be " + << d4.JulToDate8(d4.JulianDays() + 7L ) << std::endl; + + d1.CharDayOf( s ); + std::cout << "Today is " << s.Str() << std::endl; + + d2.CharDayOf( s ); + std::cout << "StringDate day is " << s.Str() << std::endl; + d1.CharMonthOf( s ); + std::cout << "This month is " << s.Str() << std::endl; + d2.CharMonthOf( s ); + std::cout << "StringDate month is " << s.Str() << std::endl; + + + /* various format routines using different formats, strings and chars */ + xbString sOutDate; + + d1.FormatDate( "YYDDD", sOutDate ); + std::cout << "Format (YYDDD) = " << sOutDate.Str() << std::endl; + + d1.FormatDate( "MM/DD/YY", sOutDate ); + std::cout << "Format (MM/DD/YY) = " << sOutDate.Str() << std::endl; + + d1.FormatDate( "YY-MM-DD", sOutDate ); + std::cout << "Format (YY-MM-DD) = " << sOutDate.Str() << std::endl; + + d1.FormatDate( "DDDDDDDDD, MMMMMMMMMM DD YYYY", sOutDate ); + std::cout << "Format (DDDDDDDDD, MMMMMMMMMM DD YYYY) = " << sOutDate.Str() << std::endl; + + std::cout << "Last day this month " << d1.LastDayOfMonth() << std::endl; + std::cout << "Last day of month for StringDate is " << d2.LastDayOfMonth() << std::endl; + + std::cout << "Overloaded operators test..." << std::endl; + + if( d1 == d2 ) + std::cout << d1.Str() << " is equal to " << d2.Str() + << std::endl; + else + std::cout << d1.Str() << " is not equal to " << d2.Str() + << std::endl; + + if( d1 != d3 ) + std::cout << d1.Str() << " is not equal to " << d3.Str() + << std::endl; + else + std::cout << d1.Str() << " is equal to " << d3.Str() + << std::endl; + + if( d1 < d2 ) + std::cout << d1.Str() << " is less than " << d2.Str() + << std::endl; + else + std::cout << d1.Str() << " is not less than " << d2.Str() + << std::endl; + + if( d1 > d2 ) + std::cout << d1.Str() << " is greater than " << d2.Str() + << std::endl; + else + std::cout << d1.Str() << " is not greater than " << d2.Str() + << std::endl; + + if( d1 <= d2 ) + std::cout << d1.Str() << " is less than or equal to " << d2.Str() + << std::endl; + else + std::cout << d1.Str() << " is not less than or equal to " + << d2.Str() << std::endl; + + if( d1 >= d2 ) + std::cout << d1.Str() << " is greater than or equal to " + << d2.Str() << std::endl; + else + std::cout << d1.Str() << " is not greater than or equal to " + << d2.Str() << std::endl; + + d1.Sysdate(); + d1++; + std::cout << "Tomorrow is " << d1.Str() << std::endl; + d1-=2; + std::cout << "Yesterday was " << d1.Str() << std::endl; + std::cout << "There are " << d1 - d2 << " days between " << d1.Str() + << " and " << d2.Str() << std::endl; + + d1="20140701"; + std::cout << "Operator = example " << d1.Str() << std::endl; + + d1+=5; + std::cout << "Operator += 5 example " << d1.Str() << std::endl; + + d1--; + std::cout << "Operator -- example " << d1.Str() << std::endl; + + d1-4; + std::cout << "Operator -4 example " << d1.Str() << std::endl; + + d1+10; + std::cout << "Operator +10 example " << d1.Str() << std::endl; + std::cout << "CenturyOf() " << d1.CenturyOf() << std::endl; + + xbString sWorkDate; + d1.CTOD( "10/07/60" ); + std::cout << "CTOD( '10/07/60' ) " << d1.Str() << std::endl; + + d1.Set( "19590118" ); + std::cout << "Set( '19590118' ) " << d1.Str() << std::endl; + + std::cout << "CalcRollingCenturyForYear( 95 ) = " << d1.CalcRollingCenturyForYear( 95 ) << std::endl; + return 0; +} diff --git a/src/examples/xb_ex_sql.cpp b/src/examples/xb_ex_sql.cpp new file mode 100755 index 0000000..695baa2 --- /dev/null +++ b/src/examples/xb_ex_sql.cpp @@ -0,0 +1,111 @@ +/* xb_ex_sql.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 example demonstrates the implementation of initial sql functions into the library + +*/ + +#include +using namespace xb; + +int main( int argCnt, char **av ) +{ + xbInt16 iRc; + xbXBase x; /* initialize xbase */ + +#ifdef XB_LOGGING_SUPPORT + x.EnableMsgLogging(); + std::cout << "Logfile is [" << x.GetLogFqFileName().Str() << "]" << std::endl; + xbString sMsg; + sMsg.Sprintf( "Program [%s] initializing...", av[0] ); + x.WriteLogMessage( sMsg ); +#endif + + xbSql sql( &x ); + xbString sSql; + + + sSql.Sprintf( "USE %s", PROJECT_DATA_DIR); + iRc = sql.ExecuteNonQuery( sSql ); + if( iRc != XB_NO_ERROR ){ + std::cout << "USE PROJECT_DATA_DIR error" << std::endl; + x.DisplayError( iRc ); + return 1; + } else { + std::cout << sSql << " successful" << std::endl; + } + + sSql = "DROP TABLE IF EXISTS TESTTBL.DBF"; + iRc = sql.ExecuteNonQuery( sSql ); + if( iRc != XB_NO_ERROR ){ + std::cout << "DROP TABLE error" << std::endl; + x.DisplayError( iRc ); + return 1; + } else { + std::cout << "DROP TABLE successful" << std::endl; + } + + std::cout << "Drop table completed\n"; + + sSql = "CREATE TABLE TESTTBL.DBF( CHRFLD CHAR(60), DTFLD DATE, INTFLD INTEGER, SMINTFLD SMALLINT, NUMFLD NUMERIC(9,4), DECFLD DECIMAL(9,4), FLTFLD FLOAT(9,4) VCHARFLD VARCHAR, LGFLD LOGICAL)"; + iRc = sql.ExecuteNonQuery( sSql ); + if( iRc != XB_NO_ERROR ){ + std::cout << "CREATE TABLE error" << std::endl; + x.DisplayError( iRc ); + return 1; + } else { + std::cout << "CREATE TABLE successful" << std::endl; + } + + sSql = "CREATE INDEX TAG1 ON TESTTBL.DBF( CHRFLD )"; + iRc = sql.ExecuteNonQuery( sSql ); + if( iRc != XB_NO_ERROR ){ + std::cout << "CREATE INDEX error" << std::endl; + x.DisplayError( iRc ); + return 1; + } else { + std::cout << "CREATE INDEX successful" << std::endl; + } + + + sSql = "INSERT INTO TESTTBL.DBF ( CHRFLD ) VALUES ( 'z' )"; + iRc = sql.ExecuteNonQuery( sSql ); + if( iRc != XB_NO_ERROR ){ + std::cout << "INSERT error" << std::endl; + x.DisplayError( iRc ); + return 1; + } else { + std::cout << "INSERT successful" << std::endl; + } + + char c; + xbString s; + for( xbUInt16 i = 0; i < 3 && iRc == XB_NO_ERROR; i++ ){ + for( xbUInt16 j = 0; j < 5 && iRc == XB_NO_ERROR; j++ ){ + c = j + 65; + s = c; + s.PadRight( c, (xbUInt32) i + 1 ); + sSql.Sprintf( "INSERT INTO TESTTBL.DBF ( CHRFLD ) VALUES ( '%s' )", s.Str()); + std::cout << sSql.Str() << std::endl; + iRc = sql.ExecuteNonQuery( sSql ); + if( iRc != XB_NO_ERROR ){ + std::cout << "INSERT error" << std::endl; + x.DisplayError( iRc ); + return 1; + } + } + } + + x.CloseAllTables(); + return 0; +} diff --git a/src/examples/xb_ex_string.cpp b/src/examples/xb_ex_string.cpp new file mode 100755 index 0000000..3d582a1 --- /dev/null +++ b/src/examples/xb_ex_string.cpp @@ -0,0 +1,385 @@ +/* xb_ex_string.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 + +*/ + +// This demonstrates the string class + + +#include "xbase.h" + +using namespace xb; + +int main() +{ + + // create a string, assign a value, print it + xbString s1; + s1 = "Test String 1"; + fprintf( stdout, "s1 = [%s]\n", s1.Str()); + + // create another string, copy the value from s1 into it, print it + xbString s2; + s2 = s1; + std::cout << "s2 = [" << s2.Str() << "]" << std::endl; + + // create another string with and print it + xbString s3( 'X' ); + std::cout << "s3 = [" << s3.Str() << "]" << std::endl; + + // create another string with and print it and print it out yet another way + xbString s4( "Class constructor test 4" ); + printf( "s4 = [%s]\n", s4.Str() ); + + // create another string with a size limit and print it out + xbString s5( "Class constructor test 4", 7 ); + printf( "s5 = [%s]\n", s5.Str() ); + + // create another string from a string + xbString s6( s5 ); + printf( "s6 = [%s]\n", s6.Str() ); + + // create 100 byte string with nothing in it + xbString s7( (xbUInt32) 100 ); + printf( "s7 = [%s]\n", s7.Str() ); + + // Extract character from a particular position in the string + printf( "[] test -- Position 7 (starts from 1) from String 1 = [%c]\n", s1[7] ); + // or use the getCharacter method + printf( "getCharacter() test -- Position 7 (starts from 1) from String 1 = [%c]\n", s1.GetCharacter(7) ); + + // set string 7 to a character + s7 = 'Z'; + printf( "updated s7 = [%s]\n", s7.Str() ); + + // trim methods + s3 = " abc "; + s3.Ltrim(); + s3.Dump( "LTrim test" ); + + + s3 = " abc "; + s3.Rtrim(); + s3.Dump( "RTrim test" ); + + s3.Trim(); + s3.Dump( "Trim test" ); + printf( "s3 Len = [%d]\n", s3.Len() ); + + // Concatenation tests - I + s1 = "Concatenation test1 part 1 "; + s1 += "Concatenation test1 part 2 "; + s2 = " s2 data "; + s1 += s2; + s1 += 'z'; + + // Concatenation tests - II + s1 = "Concatenation test1 part 1 "; + s1 -= "Concatenation test1 part 2 "; + s1 -= 'X'; + s1 -= s2; + s1.Dump( "Concatenation test 2 " ); + + + // Concatenation tests - III + s1 = "s1data "; + s2 = "s2data "; + + s3 = s1 - s2; + s3.Dump( "Concatenation test 3a" ); + + s3 = s1 + s2; + s3.Dump( "Concatenation test 3b" ); + + s3 = s1 + " char * data "; + s3.Dump( "Concatenation test 3c" ); + + // s3 = " char * data " + s2; + // s3.Dump( "Concatenation test 3d" ); + + s3 = s1 + 'Z'; + s3.Dump( "Concatenation test 3e" ); + +// s3 = 'A' + s1; + s3 = 'A'; + + std::cout << s3.Str() << std::endl; + s3 += s1; + + std::cout << s3.Str() << std::endl; + + + s3 = 'A' + s1; + + + return 0; + + // s3.Dump( "Concatenation test 3f" ); + + std::cout << std::endl << "== operator tests" << std::endl; + if( s1 == s2 ) + std::cout << s1.Str() << " == " << s2.Str() << std::endl; + else + std::cout << s1.Str() << " != " << s2.Str() << std::endl; + + s1 = s2; + if( s1 == s2 ) + std::cout << s1.Str() << " == " << s2.Str() << std::endl; + else + std::cout << s1.Str() << " != " << s2.Str() << std::endl; + + if( s1 == "sometestdata" ) + std::cout << s1.Str() << " == sometestdata" << s2.Str() << std::endl; + else + std::cout << s1.Str() << " != sometestdata" << s2.Str() << std::endl; + + std::cout << std::endl << "!= operator tests" << std::endl; + s2 = "abc123"; + s1.Dump( "s1" ); + s2.Dump( "s2" ); + if( s1 != s2 ) + std::cout << s1.Str() << " != " << s2.Str() << std::endl; + else + std::cout << s1.Str() << " == " << s2.Str() << std::endl; + + s1 = s2; + if( s1 != s2 ) + std::cout << s1.Str() << " != " << s2.Str() << std::endl; + else + std::cout << s1.Str() << " == " << s2.Str() << std::endl; + + if( s1 != "sometestdata" ) + std::cout << s1.Str() << " != [sometestdata]" << std::endl; + else + std::cout << s1.Str() << " == [sometestdata]" << std::endl; + + std::cout << std::endl << "< operator tests" << std::endl; + s1 = "AAA"; + s2 = "BBB"; + + if( s1 < s2 ) + std::cout << s1.Str() << " < " << s2.Str() << std::endl; + else + std::cout << s1.Str() << " >= " << s2.Str() << std::endl; + + s1 = "BBB"; + if( s1 < s2 ) + std::cout << s1.Str() << " < " << s2.Str() << std::endl; + else + std::cout << s1.Str() << " >= " << s2.Str() << std::endl; + + s1 = "CCC"; + if( s1 < s2 ) + std::cout << s1.Str() << " < " << s2.Str() << std::endl; + else + std::cout << s1.Str() << " >= " << s2.Str() << std::endl; + + std::cout << std::endl << "> operator tests" << std::endl; + s1 = "AAA"; + s2 = "BBB"; + + if( s1 > s2 ) + std::cout << s1.Str() << " > " << s2.Str() << std::endl; + else + std::cout << s1.Str() << " <= " << s2.Str() << std::endl; + + s1 = "BBB"; + if( s1 > s2 ) + std::cout << s1.Str() << " > " << s2.Str() << std::endl; + else + std::cout << s1.Str() << " <= " << s2.Str() << std::endl; + + s1 = "CCC"; + if( s1 > s2 ) + std::cout << s1.Str() << " > " << s2.Str() << std::endl; + else + std::cout << s1.Str() << " <= " << s2.Str() << std::endl; + + std::cout << std::endl << "<= operator tests" << std::endl; + s1 = "AAA"; + s2 = "BBB"; + + if( s1 <= s2 ) + std::cout << s1.Str() << " <= " << s2.Str() << std::endl; + else + std::cout << s1.Str() << " > " << s2.Str() << std::endl; + + s1 = "BBB"; + if( s1 <= s2 ) + std::cout << s1.Str() << " <= " << s2.Str() << std::endl; + else + std::cout << s1.Str() << " > " << s2.Str() << std::endl; + + s1 = "CCC"; + if( s1 <= s2 ) + std::cout << s1.Str() << " <= " << s2.Str() << std::endl; + else + std::cout << s1.Str() << " > " << s2.Str() << std::endl; + + std::cout << std::endl << ">= operator tests" << std::endl; + s1 = "AAA"; + s2 = "BBB"; + + if( s1 >= s2 ) + std::cout << s1.Str() << " >= " << s2.Str() << std::endl; + else + std::cout << s1.Str() << " < " << s2.Str() << std::endl; + + s1 = "BBB"; + if( s1 >= s2 ) + std::cout << s1.Str() << " >= " << s2.Str() << std::endl; + else + std::cout << s1.Str() << " < " << s2.Str() << std::endl; + + s1 = "CCC"; + if( s1 >= s2 ) + std::cout << s1.Str() << " >= " << s2.Str() << std::endl; + else + std::cout << s1.Str() << " < " << s2.Str() << std::endl; + + std::cout << "(const char *) " << (const char *) s2.Str() << std::endl; + + std::cout << std::endl << "CountChar() test" << std::endl; + s1 = "ABADFDSGA"; + xbUInt32 i = s1.CountChar( 'A' ); + std::cout << "There are " << i << " 'A's in " << s1.Str() << std::endl; + + s1.Ltrunc( 4 ); + std::cout << "lTunc(4) test s1 = [" << s1.Str() << "]" << std::endl; + + std::cout << std::endl << "PutAt() test" << std::endl; + s1.PutAt( 3, 'Z' ); + std::cout << "Third char should be a 'Z' = " << s1.Str() << std::endl; + + std::cout << std::endl << "AddBackSlash() test" << std::endl; + s1.AddBackSlash( 'Z' ); + std::cout << "Should be a backslash before the 'Z' = " << s1.Str() << std::endl; + + std::cout << std::endl << "Assign() test" << std::endl; + s2 = "1234567890"; + std::cout << "s2 = " << s2.Str() << std::endl; + s1.Assign( s2, 4, 5 ); + std::cout << "assign( s2, 4, 5 ) results = " << s1.Str() << std::endl; + s1.Assign( s2, 4, 15 ); + std::cout << "assign( s2, 4, 15 ) results = " << s1.Str() << std::endl; + + s1.Assign( s2, 5 ); + std::cout << "Assign( s2, 5 ) results = " << s1.Str() << std::endl; + s1.Assign( s2, 15 ); + std::cout << "Assign( s2, 15 ) results = " << s1.Str() << std::endl; + + std::cout << std::endl << "s1.copy() test" << std::endl; + s1 = "ABC"; + std::cout << "s1 = " << s1.Str() << std::endl; + std::cout << "s2 = " << s2.Str() << std::endl; + + s1 = s2.Copy(); + s1.Dump( "s1.Copy() results" ); + + s1 = "0x35"; + char hexChar; + s1.CvtHexChar( hexChar ); + std::cout << "CvtHexChar test [" << s1.Str() << "] converts to [" << hexChar << "]" << std::endl; + s1 = "0x65"; + s1.CvtHexChar( hexChar ); + std::cout << "cvHexChar test [" << s1.Str() << "] converts to [" << hexChar << "]" << std::endl; + + s1 = "0x610x620x630x640x65"; + s1.CvtHexString( s2 ); + std::cout << "CvtHexString [" << s1.Str() << "] converts to [" << s2.Str() << "]" << std::endl; + + s1.ExtractElement( "aaaa|bbbb|cccc|dddd", '|', 2, 0 ); + s1.Dump( "ExtractElement() " ); + + s1 = "123"; + s2 = "ABC"; + std::cout << "HasAlphaChars( " << s1.Str() << " ) = " << s1.HasAlphaChars() << std::endl; + std::cout << "HasAlphaChars( " << s2.Str() << " ) = " << s2.HasAlphaChars() << std::endl; + + s2 = ""; + std::cout << "IsEmpty( " << s1.Str() << " ) = " << s1.IsEmpty() << std::endl; + std::cout << "IsEmpty( " << s2.Str() << " ) = " << s2.IsEmpty() << std::endl; + + s1 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + std::cout << s1.Str() << " s1.Mid( 3,5 ) = [" << s1.Mid( 3, 5 ).Str() << "]" << std::endl; + std::cout << s1.Str() << " s1.Mid( 25, 10 ) = [" << s1.Mid( 25, 10 ).Str() << "]" << std::endl; + + std::cout << s1.Str() << " s1.Pos('G') = " << s1.Pos( 'G' ) << std::endl; + std::cout << s1.Str() << " s1.Pos(\"JKL\") = " << s1.Pos( "JKL" ) << std::endl; + + s1.Dump( "Remove( 3, 5 ) before " ); + std::cout << s1.Str() << " s1.Remove( 3, 5 ) = [" << s1.Remove( 3, 5 ).Str() << "]" << std::endl; + s1.Dump( "Remove( 3, 5 ) after " ); + s1 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + + std::cout << "s1.Remove( 20, 10 ) = [" << s1.Remove( 20, 10 ).Str() << "]" << std::endl; + s1.Dump( "Remove( 20, 10 ) " ); + + s1.Sprintf( "%d", 12345 ); + s1.Dump( "Sprintf( %d, 12345 ) " ); + + s1.SetNum( (long) 123456 ); + std::cout << "s1.SetNum( 123456 ) = " << s1.Str() << std::endl; + + s1.Set( "Yet another way to set a string value" ); + s1.Dump( "Set" ); + + s1 = "ABCABCABZ"; + s1.Dump( "SwapChars( 'A', '9' ) before" ); + s1.SwapChars( 'A', '9' ); + s1.Dump( "SwapChars( 'A', '9' ) after"); + + s1.ToLowerCase(); + s1.Dump( "ToLowerCase" ); + + s1.ToUpperCase(); + s1.Dump( "ToUpperCase" ); + + s1.ZapChar( '9' ); + s1.Dump( "ZapChar( '9' )" ); + + s1.ZapLeadingChar( 'B' ); + s1.Dump( "ZapLeadingChar( 'B' )" ); + + s1.ZapTrailingChar( 'Z' ); + s1.Dump( "ZapLeadingChar( 'Z' )" ); + + s1 = "123"; + s1.PadLeft( '0', 9 ); + s1.Dump( "s1.PadLeft('0', 9 ) "); + + s1 = "abc"; + s1.PadRight( 'Z', 9 ); + s1.Dump( "s1.PadRight('Z', 9 ) "); + + xbString sNullString; + if( sNullString.IsNull()) + std::cout << "sNullString is null" << std::endl; + else + std::cout << "sNullString is not null" << std::endl; + + xbString tstS( "ZZZZZZZZZ" ); + tstS = s1.Left( 5 ); + + std::cout << "tstS = " << tstS.Str() << "\n"; + std::cout << "s1 = " << s1.Str() << "\n"; + + tstS = "1234567890"; + std::cout << "mid result = " << tstS.Mid( 3, 3 ).Str() << std::endl; + + tstS = "1234567890"; + std::cout << "left result = " << tstS.Left( 3 ).Str() << std::endl; + + + return 0; +} diff --git a/src/examples/xb_ex_v3_create_dbf.cpp b/src/examples/xb_ex_v3_create_dbf.cpp new file mode 100755 index 0000000..ecfcd72 --- /dev/null +++ b/src/examples/xb_ex_v3_create_dbf.cpp @@ -0,0 +1,102 @@ +/* xb_ex_v3_create_dbf.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 example demonstrates the creation of a Version III file and and indices + +*/ + +#include + +using namespace xb; + +int main() +{ + +#ifdef XB_DBF3_SUPPORT + + xbSchema MyRecord[] = + { + { "FIRSTNAME", XB_CHAR_FLD, 15, 0 }, + { "LASTNAME", XB_CHAR_FLD, 20, 0 }, + { "BIRTHDATE", XB_DATE_FLD, 8, 0 }, + { "AMOUNT", XB_NUMERIC_FLD, 9, 2 }, + { "RETIRED?", XB_LOGICAL_FLD, 1, 0 }, + { "ZIPCODE", XB_NUMERIC_FLD, 5, 0 }, +#ifdef XB_MEMO_SUPPORT + { "MEMO1", XB_MEMO_FLD, 10, 0 }, +#endif + { "",0,0,0 } + }; + + /* define the classes */ + xbXBase x; /* initialize xbase */ + x.SetDataDirectory( PROJECT_DATA_DIR ); /* where all the tables live */ + + xbDbf *MyDbfFile; /* Pointer to dbf class */ + MyDbfFile = new xbDbf3(&x); /* Create Version 3 instance */ + + +// Create Dbase3 NDX style indices if support compiled in +#ifdef XB_NDX_SUPPORT + xbIxNdx MyIndex1( MyDbfFile ); /* class for index 1 */ + xbIxNdx MyIndex2( MyDbfFile ); /* class for index 2 */ + xbIxNdx MyIndex3( MyDbfFile ); /* class for index 3 */ +#endif + + +// fixme +// Create Clipper NTX style indices if support compiled in - bring this back to life in a future release +#ifdef XB_INDEX_NTX + xbNtx MyIndex4( &MyDbfFile ); /* class for index 4 */ + xbNtx MyIndex5( &MyDbfFile ); /* class for index 5 */ +#endif + + xbInt16 rc; + + if(( rc = MyDbfFile->CreateTable( "MyV3Table1", "MyV3ExampleTableAlias", MyRecord, XB_OVERLAY, XB_MULTI_USER )) != XB_NO_ERROR ) + x.DisplayError( rc ); + else + { + +#ifdef XB_NDX_SUPPORT + + xbIx *pIx; + void *pTag; + + /* + Create a few index tags + CreateTag( const xbString &sIxType, const xbString &sName, const xbString &sKey, const xbString &sFilter, + xbInt16 iDescending, xbInt16 iUnique, xbInt16 iOverLay, xbIx **xbIxOut, void **vpTagOut ); + */ + + /* define a simple index */ + if(( rc = MyDbfFile->CreateTag ( "NDX", "MYINDEX1.NDX", "LASTNAME", "", 0, 1, XB_OVERLAY, &pIx, &pTag )) != XB_NO_ERROR ) + x.DisplayError( rc ); + + /* define a multi-field index "LASTNAME FIRSTNAME" */ + if(( rc = MyDbfFile->CreateTag( "NDX", "MYINDEX2.NDX", "LASTNAME+FIRSTNAME", "", 0, 1, XB_OVERLAY, &pIx, &pTag )) != XB_NO_ERROR ) + x.DisplayError( rc ); + + /* define a numeric index "ZIPCODE" */ + if(( rc = MyDbfFile->CreateTag( "NDX", "MYINDEX3.NDX", "ZIPCODE", "", 0, 0, XB_OVERLAY, &pIx, &pTag )) != XB_NO_ERROR ) + x.DisplayError( rc ); + +#endif + + } + + MyDbfFile->Close(); /* Close database and associated indexes */ + +#endif // XB_DBF3_SUPPORT + return 0; +} diff --git a/src/examples/xb_ex_v3_upd_dbf.cpp b/src/examples/xb_ex_v3_upd_dbf.cpp new file mode 100755 index 0000000..aa6ec49 --- /dev/null +++ b/src/examples/xb_ex_v3_upd_dbf.cpp @@ -0,0 +1,282 @@ +/* xb_ex_v3_upd_dbf.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 + + This example demonstrates how to open the DBase III table created by xb_ex_v3_create_dbf + and apply various updates to the table. + +*/ + +#include + +using namespace xb; + +int main() +{ + +#ifdef XB_DBF3_SUPPORT + + xbInt16 fld_FIRSTNAME; + xbInt16 fld_LASTNAME; + xbInt16 fld_BIRTHDATE; + xbInt16 fld_AMOUNT; + xbInt16 fld_RETIRED; + xbInt16 fld_ZIPCODE; + + #ifdef XB_MEMO_SUPPORT + xbInt16 fld_MEMO1; + xbString sMemoData; + #endif + + + /* define the classes */ + xbXBase x; /* initialize xbase */ + x.SetDataDirectory( PROJECT_DATA_DIR ); /* where all the tables live */ + x.EnableMsgLogging(); + x.WriteLogMessage( "Program [xb_ex_v3_upd_dbf] initializing..." ); + + + xbDbf * MyTable = new xbDbf3( &x ); /* class for V3 table */ + + xbInt16 iRc = 0; + xbInt16 iErrorStop = 0; + + try{ + + if(( iRc = MyTable->Open( "MyV3Table1.DBF" )) != XB_NO_ERROR ){ + iErrorStop = 1; + throw iRc; + } + + #ifdef XB_NDX_SUPPORT + // V3 NDX style indices can be opened manually (vs production MDX index files opened automatically) + if(( iRc = MyTable->OpenIndex( "NDX", "MYINDEX1.NDX")) != XB_NO_ERROR ){ + iErrorStop = 2; + throw iRc; + } + if(( iRc = MyTable->OpenIndex( "NDX", "MYINDEX2.NDX" )) != XB_NO_ERROR ){ + iErrorStop = 3; + throw iRc; + } + if(( iRc = MyTable->OpenIndex( "NDX", "MYINDEX3.NDX" )) != XB_NO_ERROR ){ + iErrorStop = 4; + throw iRc; + } + std::cout << "Current tag = [" << MyTable->GetCurTagName().Str() << "]\n"; + #endif + + + + // get the field numbers for all the fields in the table + fld_FIRSTNAME = MyTable->GetFieldNo( "FIRSTNAME" ); + fld_LASTNAME = MyTable->GetFieldNo( "LASTNAME" ); + fld_BIRTHDATE = MyTable->GetFieldNo( "BIRTHDATE" ); + fld_AMOUNT = MyTable->GetFieldNo( "AMOUNT" ); + fld_RETIRED = MyTable->GetFieldNo( "RETIRED?" ); + fld_ZIPCODE = MyTable->GetFieldNo( "ZIPCODE" ); + + #ifdef XB_MEMO_SUPPORT + fld_MEMO1 = MyTable->GetFieldNo( "MEMO1" ); + #endif + + + // Blank the record buffer + if(( iRc = MyTable->BlankRecord()) != XB_NO_ERROR ){ + iErrorStop = 7; + throw iRc; + } + + // put field examples - using field numbers + if(( iRc = MyTable->PutField( fld_LASTNAME, "JONES" )) != XB_NO_ERROR ){ + iErrorStop = 8; + throw iRc; + } + + if(( iRc = MyTable->PutField( fld_FIRSTNAME, "JERRY" )) != XB_NO_ERROR ){ + iErrorStop = 9; + throw iRc; + } + + if(( iRc = MyTable->PutField( fld_AMOUNT, "12.35" )) != XB_NO_ERROR ){ + iErrorStop = 10; + throw iRc; + } + + if(( iRc = MyTable->PutField( fld_BIRTHDATE, "19880208" )) != XB_NO_ERROR ){ + iErrorStop = 10; + throw iRc; + } + + if(( iRc = MyTable->PutLogicalField( fld_RETIRED, "Y" )) != XB_NO_ERROR ){ + iErrorStop = 11; + throw iRc; + } + + if(( iRc = MyTable->PutLongField( fld_ZIPCODE, 12345 )) != XB_NO_ERROR ){ + iErrorStop = 12; + throw iRc; + } + + #ifdef XB_MEMO_SUPPORT + sMemoData = "Memo data record 1"; + if(( iRc = MyTable->UpdateMemoField( fld_MEMO1, sMemoData )) != XB_NO_ERROR ){ + iErrorStop = 13; + throw iRc; + } + #endif + + // Append the first record + if(( iRc = MyTable->AppendRecord()) != XB_NO_ERROR ){ + iErrorStop = 15; + throw iRc; + } + + // put field to the record buffer using field name (slightly less efficient than using field numbers) + + // Blank the record buffer + if(( iRc = MyTable->BlankRecord()) != XB_NO_ERROR ){ + iErrorStop = 20; + throw iRc; + } + + if(( iRc = MyTable->PutField( "LASTNAME", "EINSTIEN" )) != XB_NO_ERROR ){ + iErrorStop = 21; + throw iRc; + } + + if(( iRc = MyTable->PutField( "FIRSTNAME", "ALBERT" )) != XB_NO_ERROR ){ + iErrorStop = 22; + throw iRc; + } + + if(( iRc = MyTable->PutField( "AMOUNT", "987.55" )) != XB_NO_ERROR ){ + iErrorStop = 23; + throw iRc; + } + + if(( iRc = MyTable->PutField( fld_BIRTHDATE, "19890209" )) != XB_NO_ERROR ){ + iErrorStop = 24; + throw iRc; + } + + if(( iRc = MyTable->PutLogicalField( "RETIRED?", "N" )) != XB_NO_ERROR ){ + iErrorStop = 25; + throw iRc; + } + + if(( iRc = MyTable->PutLongField( "ZIPCODE", 44256 )) != XB_NO_ERROR ){ + iErrorStop = 26; + throw iRc; + } + + #ifdef XB_MEMO_SUPPORT + sMemoData = "Memo data record 2"; + if(( iRc = MyTable->UpdateMemoField( fld_MEMO1, sMemoData )) != XB_NO_ERROR ){ + iErrorStop = 27; + throw iRc; + } + #endif + + // Append the second record + if(( iRc = MyTable->AppendRecord()) != XB_NO_ERROR ){ + iErrorStop = 30; + throw iRc; + } + + // get a field with a field number + xbString FirstName; + if(( iRc = MyTable->GetField( fld_FIRSTNAME, FirstName )) < 0 ){ + iErrorStop = 40; + throw iRc; + } + std::cout << "First Name is [" << FirstName.Str() << "]" << std::endl; + + xbString LastName; + if(( iRc = MyTable->GetField( "LASTNAME", LastName )) < 0 ){ + iErrorStop = 41; + throw iRc; + } + std::cout << "Last Name is [" << LastName.Str() << "]" << std::endl; + + xbInt16 iNoOfDecimals; + if(( iRc = MyTable->GetFieldDecimal( "AMOUNT", iNoOfDecimals )) != XB_NO_ERROR ){ + iErrorStop = 42; + throw iRc; + } + std::cout << "There are " << iNoOfDecimals << " decimals in the AMOUNT field" << std::endl; + + xbString FieldName; + if(( iRc = MyTable->GetFieldName( 4, FieldName )) != XB_NO_ERROR ){ + iErrorStop = 43; + throw iRc; + } + std::cout << "Field #4 name is " << FieldName.Str() << std::endl; + + xbString sRetired; + if(( iRc = MyTable->GetLogicalField( "RETIRED?", sRetired )) < 0 ){ + iErrorStop = 45; + throw iRc; + } + std::cout << "Switch value = [" << sRetired.Str() << "]" << std::endl; + + xbInt32 lZip; + if(( iRc = MyTable->GetLongField( "ZIPCODE", lZip )) < 0 ){ + iErrorStop = 46; + throw iRc; + } + std::cout << "Long value = [" << lZip << "]" << std::endl; + + // Initialize the record buffer in preparation for another record + if(( iRc = MyTable->BlankRecord()) != XB_NO_ERROR ){ + iErrorStop = 48; + throw iRc; + } + + // Append another record (it will be blank) + if(( iRc = MyTable->AppendRecord()) != XB_NO_ERROR ){ + iErrorStop = 49; + throw iRc; + }; + + // mark current record for deletion + if(( iRc = MyTable->DeleteRecord()) != XB_NO_ERROR ){ + iErrorStop = 50; + throw iRc; + }; + + // save current record + if(( iRc = MyTable->PutRecord()) != XB_NO_ERROR ){ + iErrorStop = 51; + throw iRc; + } + // pack the table with no options + if(( iRc = MyTable->Pack()) != XB_NO_ERROR ){ + iErrorStop = 52; + throw iRc; + } + + /* Close database and associated indexes */ + if(( iRc = MyTable->Close()) != XB_NO_ERROR ){ + iErrorStop = 53; + throw iRc; + } + + } + catch( xbInt16 rc ){ + std::cout << "xb_ex_v3_upd_dbf error. Error stop point = [" << iErrorStop << "] iRc = [" << rc << "]" << std::endl; + std::cout << x.GetErrorMessage( rc ) << std::endl; + } + +#endif // XB_DBF3_SUPPORT + + return 0; +} diff --git a/src/examples/xb_ex_v4_create_dbf.cpp b/src/examples/xb_ex_v4_create_dbf.cpp new file mode 100755 index 0000000..a2aff74 --- /dev/null +++ b/src/examples/xb_ex_v4_create_dbf.cpp @@ -0,0 +1,81 @@ +/* xb_ex_v4_create_dbf.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 example demonstrates the creation of a Version IV file and and indices + +*/ + +#include + +using namespace xb; + +int main() +{ + +#ifdef XB_DBF4_SUPPORT + + xbSchema MyRecord[] = + { + { "FIRSTNAME", XB_CHAR_FLD, 15, 0 }, + { "LASTNAME", XB_CHAR_FLD, 20, 0 }, + { "BIRTHDATE", XB_DATE_FLD, 8, 0 }, + { "AMOUNT", XB_NUMERIC_FLD, 9, 2 }, + { "RETIRED?", XB_LOGICAL_FLD, 1, 0 }, + { "ZIPCODE", XB_NUMERIC_FLD, 5, 0 }, + { "NUMFLD1", XB_FLOAT_FLD, 12, 2 }, + { "NUMFLD2", XB_FLOAT_FLD, 14, 2 }, + #ifdef XB_MEMO_SUPPORT + { "MEMO1", XB_MEMO_FLD, 10, 0 }, + #endif + { "",0,0,0 } + }; + + /* define the classes */ + xbXBase x; /* initialize xbase */ + x.SetDataDirectory( PROJECT_DATA_DIR ); /* where all the tables/files live */ + + xbInt16 iRc; + xbDbf * MyDbfFile; + xbIx *pIx; + void *pTag; + MyDbfFile = new xbDbf4( &x ); + + if(( iRc = MyDbfFile->CreateTable( "MyV4Table1", "MyV4TableAlias", MyRecord, XB_OVERLAY, XB_MULTI_USER )) != XB_NO_ERROR ) + x.DisplayError( iRc ); + else + { + + #ifdef XB_MDX_SUPPORT + + /* + Create a few index tags + CreateTag( const xbString &sIxType, const xbString &sName, const xbString &sKey, const xbString &sFilter, + xbInt16 iDescending, xbInt16 iUnique, xbInt16 iOverLay, xbIx **xbIxOut, void **vpTagOut ); + */ + + // std::cout << "Creating three index tags\n"; + if(( iRc = MyDbfFile->CreateTag( "MDX", "NAME_TAG", "LASTNAME+FIRSTNAME", ".NOT. DELETED()", 0, 0, XB_OVERLAY, &pIx, &pTag )) != XB_NO_ERROR ) + x.DisplayError( iRc ); + if(( iRc = MyDbfFile->CreateTag( "MDX", "BDDATE_TAG", "BIRTHDATE", "", 0, 0, XB_OVERLAY, &pIx, &pTag )) != XB_NO_ERROR ) + x.DisplayError( iRc ); + if(( iRc = MyDbfFile->CreateTag( "MDX", "ZIP_TAG", "ZIPCODE", "", 0, 0, XB_OVERLAY, &pIx, &pTag )) != XB_NO_ERROR ) + x.DisplayError( iRc ); + + #endif // XB_MDX_SUPPORT + } + + MyDbfFile->Close(); /* Close database and associated indexes */ + +#endif // XB_DBF4_SUPPORT + return 0; +} diff --git a/src/examples/xb_ex_v4_upd_dbf.cpp b/src/examples/xb_ex_v4_upd_dbf.cpp new file mode 100755 index 0000000..6f68c73 --- /dev/null +++ b/src/examples/xb_ex_v4_upd_dbf.cpp @@ -0,0 +1,283 @@ +/* xb_ex_v4_upd_dbf.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 example demonstrates how to open the DBase III table created by xb_ex_v4_create_dbf + and apply various updates to the table. + +*/ + +#include + +using namespace xb; + +#undef XB_MEMO_FIELDS + +int main() +{ + +#ifdef XB_DBF4_SUPPORT + + xbInt16 fld_FIRSTNAME; + xbInt16 fld_LASTNAME; + xbInt16 fld_BIRTHDATE; + xbInt16 fld_AMOUNT; + xbInt16 fld_RETIRED; + xbInt16 fld_ZIPCODE; + xbInt16 fld_NUMFLD1; + xbInt16 fld_NUMFLD2; + + + #ifdef XB_MEMO_FIELDS + xbInt16 fld_MEMO1; + #endif + + + /* define the classes */ + xbXBase x; /* initialize xbase */ + x.SetDataDirectory( PROJECT_DATA_DIR ); /* where all the tables live */ + x.EnableMsgLogging(); + x.WriteLogMessage( "Program [xb_ex_v4_upd_dbf] initializing..." ); + + + xbDbf *MyTable = new xbDbf4( &x ); /* class for table */ + +#ifdef XB_INDEX_NDX + xbNdx MyIndex1( &MyTable ); /* class for index 1 */ +#endif + + xbInt16 iRc = 0; + xbInt16 iErrorStop = 0; + + try{ + + if(( iRc = MyTable->Open( "MyV4Table1.DBF" )) != XB_NO_ERROR ){ + iErrorStop = 1; + throw iRc; + } + + /* get the field numbers for all the fields in the table */ + + + fld_FIRSTNAME = MyTable->GetFieldNo( "FIRSTNAME" ); + fld_LASTNAME = MyTable->GetFieldNo( "LASTNAME" ); + fld_BIRTHDATE = MyTable->GetFieldNo( "BIRTHDATE" ); + fld_AMOUNT = MyTable->GetFieldNo( "AMOUNT" ); + fld_RETIRED = MyTable->GetFieldNo( "RETIRED?" ); + fld_ZIPCODE = MyTable->GetFieldNo( "ZIPCODE" ); + fld_NUMFLD1 = MyTable->GetFieldNo( "NUMFLD1" ); + fld_NUMFLD2 = MyTable->GetFieldNo( "NUMFLD2" ); + + #ifdef XB_MEMO_FIELDS + fld_MEMO1 = MyTable->GetFieldNo( "MEMO1" ); + #endif + + + #ifdef XB_MDX_SUPPPORT + std::cout << "Current tag = [" << MyTable->GetCurTagName() << "]\n"; + #endif + + // Blank the record buffer + if(( iRc = MyTable->BlankRecord()) != XB_NO_ERROR ){ + iErrorStop = 7; + throw iRc; + } + + // put field examples - using field numbers + if(( iRc = MyTable->PutField( fld_LASTNAME, "JONES" )) != XB_NO_ERROR ){ + iErrorStop = 8; + throw iRc; + } + + if(( iRc = MyTable->PutField( fld_FIRSTNAME, "JERRY" )) != XB_NO_ERROR ){ + iErrorStop = 9; + throw iRc; + } + + if(( iRc = MyTable->PutField( fld_AMOUNT, "12.35" )) != XB_NO_ERROR ){ + iErrorStop = 10; + throw iRc; + } + + if(( iRc = MyTable->PutLogicalField( fld_RETIRED, "Y" )) != XB_NO_ERROR ){ + iErrorStop = 11; + throw iRc; + } + + if(( iRc = MyTable->PutField( fld_BIRTHDATE, "19880209" )) != XB_NO_ERROR ){ + iErrorStop = 12; + throw iRc; + } + + if(( iRc = MyTable->PutLongField( fld_ZIPCODE, 12345 )) != XB_NO_ERROR ){ + iErrorStop = 13; + throw iRc; + } + + // Append the first record + if(( iRc = MyTable->AppendRecord()) != XB_NO_ERROR ){ + iErrorStop = 15; + throw iRc; + } + + // Blank the record buffer + if(( iRc = MyTable->BlankRecord()) != XB_NO_ERROR ){ + iErrorStop = 16; + throw iRc; + } + + // put field to the record buffer using field name (slightly less efficient than using field numbers) + if(( iRc = MyTable->PutField( "LASTNAME", "FUCKPUTIN" )) != XB_NO_ERROR ){ + iErrorStop = 20; + throw iRc; + } + + if(( iRc = MyTable->PutField( "FIRSTNAME", "ALBERT" )) != XB_NO_ERROR ){ + iErrorStop = 21; + throw iRc; + } + + if(( iRc = MyTable->PutField( "AMOUNT", "987.55" )) != XB_NO_ERROR ){ + iErrorStop = 22; + throw iRc; + } + + if(( iRc = MyTable->PutLogicalField( "RETIRED?", "N" )) != XB_NO_ERROR ){ + iErrorStop = 23; + throw iRc; + } + + if(( iRc = MyTable->PutLongField( "ZIPCODE", 44256 )) != XB_NO_ERROR ){ + iErrorStop = 24; + throw iRc; + } + + xbFloat f = (xbFloat) 12345.67; + std::cout << f << std::endl; + + if(( iRc = MyTable->PutFloatField( fld_NUMFLD1, f )) != XB_NO_ERROR ){ + iErrorStop = 13; + throw iRc; + } + xbDouble d = 76543.21; + if(( iRc = MyTable->PutDoubleField( fld_NUMFLD2, d )) != XB_NO_ERROR ){ + iErrorStop = 14; + throw iRc; + } + + // Append the second record + if(( iRc = MyTable->AppendRecord()) != XB_NO_ERROR ){ + iErrorStop = 25; + throw iRc; + } + + // get a field with a field number + xbString FirstName; + if(( iRc = MyTable->GetField( fld_FIRSTNAME, FirstName )) < 0 ){ + iErrorStop = 30; + throw iRc; + } + std::cout << "First Name is [" << FirstName.Str() << "]" << std::endl; + + xbString LastName; + if(( iRc = MyTable->GetField( "LASTNAME", LastName )) < 0 ){ + iErrorStop = 31; + throw iRc; + } + std::cout << "Last Name is [" << LastName.Str() << "]" << std::endl; + + xbInt16 iNoOfDecimals; + if(( iRc = MyTable->GetFieldDecimal( "AMOUNT", iNoOfDecimals )) != XB_NO_ERROR ){ + iErrorStop = 32; + throw iRc; + } + std::cout << "There are " << iNoOfDecimals << " decimals in the AMOUNT field" << std::endl; + + xbString FieldName; + if(( iRc = MyTable->GetFieldName( 4, FieldName )) != XB_NO_ERROR ){ + iErrorStop = 36; + throw iRc; + } + std::cout << "Field #4 name is " << FieldName.Str() << std::endl; + + xbString sRetired; + if(( iRc = MyTable->GetLogicalField( "RETIRED?", sRetired )) < 0 ){ + iErrorStop = 38; + throw iRc; + } + std::cout << "Switch value = [" << sRetired.Str() << "]" << std::endl; + + xbInt32 lZip; + if(( iRc = MyTable->GetLongField( "ZIPCODE", lZip )) < 0 ){ + iErrorStop = 39; + throw iRc; + } + std::cout << "Long value = [" << lZip << "]" << std::endl; + + if(( iRc = MyTable->GetFloatField( fld_NUMFLD1, f )) < 0 ){ + iErrorStop = 41; + throw iRc; + } + printf( "Field NUMFLD1 %8.2f\n", f ); + + + if(( iRc = MyTable->GetDoubleField( fld_NUMFLD2, d )) < 0 ){ + iErrorStop = 42; + throw iRc; + } + printf( "Field NUMFLD2 %8.2f\n", d ); + + + // Initialize the record buffer in preparation for another record + if(( iRc = MyTable->BlankRecord()) != XB_NO_ERROR ){ + iErrorStop = 44; + throw iRc; + } + + // Append another record (it will be blank) + if(( iRc = MyTable->AppendRecord()) != XB_NO_ERROR ){ + iErrorStop = 45; + throw iRc; + }; + + // mark current record for deletion + if(( iRc = MyTable->DeleteRecord()) != XB_NO_ERROR ){ + iErrorStop = 50; + throw iRc; + }; + + // save current record + if(( iRc = MyTable->PutRecord()) != XB_NO_ERROR ){ + iErrorStop = 51; + throw iRc; + } + + if(( iRc = MyTable->Commit()) != XB_NO_ERROR ){ + iErrorStop = 52; + throw iRc; + } + + /* Close database and associated indexes */ + if(( iRc = MyTable->Close()) != XB_NO_ERROR ){ + iErrorStop = 53; + throw iRc; + } + + } + catch( xbInt16 iRc ){ + std::cout << "xb_ex_v4_upd_dbf error. Error stop point = [" << iErrorStop << "] iRc = [" << iRc << "]" << std::endl; + std::cout << x.GetErrorMessage( iRc ) << std::endl; + } + +#endif // XB_DBF4_SUPPORT + return 0; +} diff --git a/src/include/xbase.h b/src/include/xbase.h new file mode 100755 index 0000000..33d2dc9 --- /dev/null +++ b/src/include/xbase.h @@ -0,0 +1,93 @@ +/* xbase.h + + +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 +#include +#include +#include +#include + +#ifdef HAVE_INTTYPES_H +#include +#endif + +#ifdef HAVE_STRING_H +#include +#endif + +#ifdef HAVE_STRINGS_H +#include +#endif + +// #ifdef STDC_HEADERS +#ifdef HAVE_STDARGS_H +#include +#endif + +#ifdef HAVE_CTYPE_H +#include +#endif + +#ifdef HAVE_STDARG_H +#include +#endif + +#ifdef HAVE_STAT_H +#include +#endif + +#ifdef HAVE_FCNTL_H +#include +#endif + +#ifdef HAVE_UNISTD_H +#include +#endif + +#ifdef HAVE_VARARGS_H +#include +#endif + +#ifdef HAVE_WINDOWS_H +#include +#include +#endif + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include /* dbf base class */ +#include /* index base class */ +#include +#include diff --git a/src/include/xbbcd.h b/src/include/xbbcd.h new file mode 100755 index 0000000..f94ed01 --- /dev/null +++ b/src/include/xbbcd.h @@ -0,0 +1,95 @@ +/* xbbcd.h + +XBase64 Software Library + +Copyright (c) 1997,2003,2014, 2018, 2019, 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 + +*/ + +#ifndef __XB_BCD_H__ +#define __XB_BCD_H__ +#ifdef XB_INDEX_SUPPORT + +///@cond DOXYOFF + +namespace xb{ + +// structure for bcd value +// next structure superceeded. Different compilers allocate different +// sizes to structures with bit fields, can't count on the size + +/* +struct XBDLLEXPORT xbBcdStrucOld { + unsigned SigDigits : 8; // significant digit count + unsigned Filler : 2; // always set to 1 + unsigned EncDigits : 5; // encoded digit count + unsigned Sign : 1; // +/- sign + unsigned char Data[10]; +}; +*/ + +struct XBDLLEXPORT xbBcdStruct { + unsigned char cSigDigits; // significant digit count + unsigned char cEncDigits; // encoded digit count + unsigned char cData[10]; // data +}; + +struct XBDLLEXPORT xbByteSplit { + unsigned c1 : 4; + unsigned c2 : 4; +}; + +class XBDLLEXPORT xbBcd { + public: + + xbBcd( const xbString &sIn ); + xbBcd( xbDouble dIn ); + xbBcd( const void *vBcd12In ); + void Set( xbDouble dIn ); + void Set( const void *cBcd12In ); + void Set( const xbString &sIn ); + + + void ToString( xbString &sOut ); + void ToDouble( xbDouble &dOut ); + void ToBcd( xbBcdStruct &bcdOut ); + void ToChar( char *cOut ); + + // const unsigned char * GetBcd() const; + void StringToBcd( const xbString &sStringIn ); + +// void StringToBcdOld( const xbString &sStringIn ); + + xbInt16 Compare( const xbBcd &bcdIn ); + xbInt16 Compare( xbDouble d ); + + const unsigned char *GetData() const; + const void * GetBcd() const; + + + private: + void ctor(); + unsigned char GetEncDigitsNoSign() const; + unsigned GetSign() const; + unsigned GetSigDigits() const; + unsigned GetActualSigDigits() const; + + +// xbBcdStruc bcdOld; + xbBcdStruct bcd; +}; + +} /* namespace xb */ + + +///@endcond DOXYOFF + +#endif /* XB_INDEX_SUPPORT */ +#endif /* __XB_BCD_H__ */ diff --git a/src/include/xbconfig.h.in b/src/include/xbconfig.h.in new file mode 100755 index 0000000..b749ff2 --- /dev/null +++ b/src/include/xbconfig.h.in @@ -0,0 +1,122 @@ +// the configured options and settings for xbase + +#define xbase_VERSION_MAJOR @xbase_VERSION_MAJOR@ +#define xbase_VERSION_MINOR @xbase_VERSION_MINOR@ +#define xbase_VERSION_PATCH @xbase_VERSION_PATCH@ + +#define PROJECT_PARENT_DIR "@PROJECT_PARENT_DIR@" +#define PROJECT_BINARY_DIR "@PROJECT_BINARY_DIR@" + +#define PROJECT_SOURCE_DIR "@PROJECT_SOURCE_DIR@" +#define PROJECT_RUNTIME_DIR "@PROJECT_RUNTIME_DIR@" +#define PROJECT_DATA_DIR "@PROJECT_DATA_DIR@" +#define PROJECT_LOG_DIR "@PROJECT_LOG_DIR@" +#define PROJECT_DFLT_LOGFILE "@CMAKE_SYSTEM_NAME@_@XB_PLATFORM@.xbLog.txt" +#define EXTRA_LIBS "@EXTRA_LIBS@" +#define CMAKE_RUNTIME_OUTPUT_DIRECTORY "@CMAKE_RUNTIME_OUTPUT_DIRECTORY@" + + +#define CMAKE_HOME_DIRECTORY "@CMAKE_HOME_DIRECTORY@" +#define CMAKE_PROJECT_NAME "@CMAKE_PROJECT_NAME@" +#define BUILD_SHARED_LIBS "@BUILD_SHARED_LIBS@" +#define CMAKE_SYSTEM_NAME "@CMAKE_SYSTEM_NAME@" +#define CMAKE_SYSTEM_PROCESSOR "@CMAKE_SYSTEM_PROCESSOR@" +#define CMAKE_SYSTEM_VERSION "@CMAKE_SYSTEM_VERSION@" +#define CMAKE_BUILD_TYPE "@CMAKE_BUILD_TYPE@" +#define CMAKE_C_FLAGS "@CMAKE_C_FLAGS@" +#define CMAKE_C_FLAGS_DEBUG "@CMAKE_C_FLAGS_DEBUG@" +#define CMAKE_C_FLAGS_RELEASE "@CMAKE_C_FLAGS_RELEASE@" + + +#define XB_PLATFORM "@XB_PLATFORM@" +#define Mylibrary_Exports "@Mylibrary_Exports@" +#define MYLIB_EXPORT "@MYLIB_EXPORT@" +#define CMAKE_SIZEOF_VOID_P "@CMAKE_SIZEOF_VOID_P@" + +#define CMAKE_COMPILER "@CMAKE_CXX_COMPILER_ID@" + +// integer definitions +#define xbInt16 @xbInt16@ +#define xbUInt16 @xbUInt16@ +#define xbInt32 @xbInt32@ +#define xbUInt32 @xbUInt32@ +#define xbInt64 @xbInt64@ +#define xbUInt64 @xbUInt64@ + +#cmakedefine UNIX + +#cmakedefine CMAKE_COMPILER_IS_GNUCC +#cmakedefine HAVE_CTYPE_H +#cmakedefine HAVE_DIRENT_H +#cmakedefine HAVE_DOS_H +#cmakedefine HAVE_FCNTL_H +#cmakedefine HAVE_INTTYPES_H +#cmakedefine HAVE_STDARG_H +#cmakedefine HAVE_STDARGS_H +#cmakedefine HAVE_STRING_H +#cmakedefine HAVE_STRINGS_H +#cmakedefine HAVE_STAT_H +#cmakedefine HAVE_UNISTD_H +#cmakedefine HAVE_VARARGS_H +#cmakedefine HAVE_WINDOWS_H + +#cmakedefine HAVE__CLOSE_F +#cmakedefine HAVE_CREATEPROCESSW_F +#cmakedefine HAVE_CREATEPROCESSW_F2 +#cmakedefine HAVE_FCNTL_F +#cmakedefine HAVE__FDOPEN_F +#cmakedefine HAVE__FILENO_F +#cmakedefine HAVE_FOPEN_S_F +#cmakedefine HAVE__FSOPEN_F + +#cmakedefine HAVE_FORK_F +#cmakedefine HAVE__FSEEKI64_F +#cmakedefine HAVE_FSEEKO_F +#cmakedefine HAVE_FTRUNCATE_F +#cmakedefine HAVE__LOCALTIME64_S_F +#cmakedefine HAVE_LOCKFILE_F +#cmakedefine HAVE_LOCKING_F +#cmakedefine HAVE__LOCKING_F +#cmakedefine HAVE__OPEN_F +#cmakedefine HAVE_SETENDOFFILE_F +#cmakedefine HAVE_VSNPRINTF_F +#cmakedefine HAVE__VSNPRINTF_S_F +#cmakedefine HAVE_VSPRINTF_S_F + +#cmakedefine XB_PLATFORM_32 +#cmakedefine XB_PLATFORM_64 +#cmakedefine XB_DEBUG_SUPPORT +#cmakedefine XB_LINKLIST_SUPPORT +#cmakedefine XB_MEMO_SUPPORT +#cmakedefine XB_LOGGING_SUPPORT +#cmakedefine XB_DBF3_SUPPORT +#cmakedefine XB_DBF4_SUPPORT +#cmakedefine XB_LOCKING_SUPPORT +#cmakedefine XB_FUNCTION_SUPPORT +#cmakedefine XB_EXPRESSION_SUPPORT +#cmakedefine XB_INDEX_SUPPORT +#cmakedefine XB_NDX_SUPPORT +#cmakedefine XB_MDX_SUPPORT +#cmakedefine XB_SQL_SUPPORT +#cmakedefine XB_NDXINF_SUPPORT +#cmakedefine XB_FILTER_SUPPORT + +#cmakedefine XB_UTILS_SUPPORT +#cmakedefine XB_EXAMPLES_SUPPORT + +#cmakedefine HAVE_LONG_LONG + +#if defined (WIN32) + #if defined ( Mylibrary_Exports ) + #define XBDLLEXPORT __declspec(dllexport) + #else + #define XBDLLEXPORT __declspec(dllimport) + #endif +#else + #define XBDLLEXPORT +#endif + +#if defined (UNIX) && defined (XB_PLATFORM_32) + #define _FILE_OFFSET_BITS 64 +#endif + diff --git a/src/include/xbdate.h b/src/include/xbdate.h new file mode 100755 index 0000000..5406cbb --- /dev/null +++ b/src/include/xbdate.h @@ -0,0 +1,123 @@ +/* xbdate.h + +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 + +*/ + + +#ifndef __XB_XBDATE_H__ +#define __XB_XBDATE_H__ + +#ifdef CMAKE_COMPILER_IS_GNUCC +#pragma interface +#endif + + +namespace xb{ + +#define XB_FMT_WEEK 1 +#define XB_FMT_MONTH 2 +#define XB_FMT_YEAR 3 + +// next value is added to the date calculation to match dbase calculation +// first valid dbase date is 01/01/0001 +#define JUL_OFFSET 1721425L + + +//! @brief xbDate class. +/*! +This xbDate class handles two type of date values:
+ +1) Gregorian dates with a format of CCYYMMDD. This is how dates are stored in dbf files.
+2) Julian dates calculated as the number of days since 1901-01-01 + 1721425.
+ +Leap Years: The routines in the class support both leap years (one every four +years) and leap centuries (one every four hundred years.) + +A leap year is a year having 366 days, which can be evenly +divisible by 4 and not by 100.
+Leap centuries are years which are evenly divisible by 400.
+ +From a programming perspective, Julian dates are useful for date +arithmetic, determining the difference between two dates or calculating +a future or past date.
+ +To determine the difference between two dates, convert both dates to a +Julian date and subtract one from the other.
+ +To calculate a future or past date, convert the base date to a Julian date, +add (or subtract) the number of days necessary to (from) it and convert the +julian date back to a Gregorian date. +*/ + +class XBDLLEXPORT xbDate : public xbSsv { + public: + + xbDate(); + xbDate( const char * Date8 ); + xbDate( const xbString &Date8 ); + xbDate( xbInt32 lJulDate ); + ~xbDate(); + + void operator=( const xbDate &d ); + void operator+=( xbInt32 i ); + void operator-=( xbInt32 i ); + void operator++( xbInt32 i ); + void operator--( xbInt32 i ); + + xbInt32 operator-( const xbDate & ) const; + const char * operator-( xbInt32 i ); + const char * operator+( xbInt32 i ); + + xbBool operator==( const xbDate & ) const; + xbBool operator!=( const xbDate & ) const; + xbBool operator< ( const xbDate & ) const; + xbBool operator> ( const xbDate & ) const; + xbBool operator<=( const xbDate & ) const; + xbBool operator>=( const xbDate & ) const; + + xbInt16 CalcRollingCenturyForYear( xbInt16 year ) const; + xbInt16 CenturyOf() const; + xbInt16 CharDayOf( xbString &sOutCharDay ); + xbInt16 CharMonthOf( xbString &sOutCharMonth ); + xbBool DateIsValid ( const xbString &sDate8 ) const; + xbInt16 DayOf( xbInt16 iFormat ) const; + xbInt16 CTOD( const xbString &sCtodInDate ); +// xbInt16 FormatDate( const char *sFmtIn, xbString &sFmtOut ); + xbInt16 FormatDate( const xbString &sFmtIn, xbString &sFmtOut ); + const char *Str() const; + xbBool IsLeapYear( xbInt16 iYear ) const; + xbBool IsLeapYear() const; + xbInt32 JulianDays() const; + xbInt16 JulToDate8( xbInt32 lJulDate ); + xbInt16 LastDayOfMonth(); + xbInt16 MonthOf() const; + xbInt16 Set( const xbString &Date8 ); + xbInt16 Sysdate(); + xbInt16 YearOf() const; + +#ifdef XB_DEBUG_SUPPORT + void Dump( const char * title ); + void DumpDateTables(); +#endif + + private: + void SetDateTables(); + xbString sDate8; /* CCYYMMDD date format ie; 20140718 */ + + static int iAggregatedDaysInMonths[2][13]; + static int iDaysInMonths[2][13]; +}; + +} /* namespace */ +#endif /*__XB_XBDATE_H__ */ + diff --git a/src/include/xbdbf.h b/src/include/xbdbf.h new file mode 100755 index 0000000..710dd14 --- /dev/null +++ b/src/include/xbdbf.h @@ -0,0 +1,538 @@ +/* xbdbf.h + +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 + +*/ + + +#ifndef __XB_XBDBF_H__ +#define __XB_XBDBF_H__ + +//#ifdef CMAKE_COMPILER_IS_GNUCC +//#pragma interface +//#endif + +namespace xb{ + +/*****************************/ +/* Field Types */ + +#define XB_CHAR_FLD 'C' +#define XB_LOGICAL_FLD 'L' +#define XB_NUMERIC_FLD 'N' +#define XB_DATE_FLD 'D' +#define XB_MEMO_FLD 'M' +#define XB_FLOAT_FLD 'F' + +/*****************************/ +/* File Status Codes */ + +#define XB_CLOSED 0 +#define XB_OPEN 1 +#define XB_UPDATED 2 + + +/*****************************/ +/* File Access Modes */ +// #define XB_SINGLE_USER 0 // file buffering on +// #define XB_MULTI_USER 1 // file buffering off + +/*****************************/ +/* File Lock Functions */ +#define XB_LOCK 0 +#define XB_UNLOCK 1 + +#define LK_DBASE 1 +#define LK_CLIPPER 2 // future +#define LK_FOX 3 // future +#define LK_XBASE64 9 // future + +/*****************************/ +/* Record retrieve options */ +#define XB_ALL_RECS 0 +#define XB_ACTIVE_RECS 1 +#define XB_DELETED_RECS 2 + + +/*****************************/ +/* Other defines */ + +#define XB_OVERLAY 1 +#define XB_DONTOVERLAY 0 +#define XB_CHAREOF '\x1A' /* end of DBF */ +#define XB_CHARHDR '\x0D' /* header terminator */ + + +//! @brief Schema used for defining tables with CreateTable methods. +/*! + See program xb_ex_v3_create_dbf.cpp or xb_ex_v4_create_dbf.cpp + as examples on how to use. + +\code +xbSchema MyRecord[] = { +// FieldName, Type, Len, Dec + { "FIRSTNAME", XB_CHAR_FLD, 15, 0 }, + { "LASTNAME", XB_CHAR_FLD, 20, 0 }, + { "BIRTHDATE", XB_DATE_FLD, 8, 0 }, + { "AMOUNT", XB_NUMERIC_FLD, 9, 2 }, + { "RETIRED?", XB_LOGICAL_FLD, 1, 0 }, + { "ZIPCODE", XB_NUMERIC_FLD, 5, 0 }, + { "NUMFLD1", XB_FLOAT_FLD, 12, 2 }, + { "NUMFLD2", XB_FLOAT_FLD, 14, 2 }, + { "MEMO1", XB_MEMO_FLD, 10, 0 }, + { "",0,0,0 }}; +\endcode +*/ +struct XBDLLEXPORT xbSchema { + char cFieldName[11]; + char cType; + xbInt16 iFieldLen; /* fields are stored as one byte on record*/ + xbInt16 iNoOfDecs; +}; + +///@cond DOXYOFF +struct XBDLLEXPORT xbSchemaRec { + char cFieldName[11]; /* ASCIIZ field name */ + char cType; /* field type */ + char *pAddress; /* pointer to field in record buffer 1 */ + unsigned char cFieldLen; /* fields are stored as one byte on record */ + unsigned char cNoOfDecs; + char *pAddress2; /* pointer to field in record buffer 2 */ + xbInt16 iLongFieldLen; /* to handle long field lengths */ + unsigned char cIxFlag; /* DBase IV Index field flag */ +}; +///@endcond DOXYOFF + + + +///@cond DOXYOFF +#ifdef XB_INDEX_SUPPORT +class XBDLLEXPORT xbIx; +// structure for file list, each open DBF file can have one or more index files +struct XBDLLEXPORT xbIxList { + xbIxList *next; + xbIx *ix; + xbString *sFmt; +}; +#endif // XB_INDEX_SUPPORT +///@endcond DOXYOFF + + +//! @brief Base class for handling dbf files/tables. +/*! +The xbDbf class is used as a base class for accessing dbf files. +In line with relational theory, a each dbf file can be considered as a table. +The documentation uses the terms dbf and table interchangeably.
+ +This module handles methods for accessing and updating dbf files. + +
+The class is designed to support additional file layouts with a minimal amount of effort. +If you are wanting to update the library to support a new dbf file type not currently supported +by the library, create a derived class using xbDbf as a base class and modify methods needed +to support the new dbf file version.
+See the following for examples on how to start on this:
+xbDbf3 is a derived class from xbDbf and supports the original Dbase III+ file version.
+xbDbf4 is a derived class from xbDbf and supports the original Dbase IV file version.
+ +*/ + + +class XBDLLEXPORT xbDbf : public xbFile { + + public: + xbDbf( xbXBase *x ); + virtual ~xbDbf(); + + virtual xbInt16 Abort(); + virtual xbInt16 AppendRecord(); + virtual xbInt16 BlankRecord(); + virtual xbInt16 Commit(); + virtual xbInt16 Close(); + virtual xbInt16 CopyDbfStructure( xbDbf *dNewTable, const xbString &sTableName, const xbString &sTableAlias, xbInt16 iOverlay, xbInt16 iShareMode ); + virtual xbInt16 CreateTable ( const xbString &sTableName, const xbString &sAlias, xbSchema *pSchema, xbInt16 iOverlay, xbInt16 iShareMode ) = 0; + virtual xbInt16 DeleteTable (); + virtual xbInt16 DeleteAll ( xbInt16 iOption ); + virtual xbInt16 DeleteAllRecords (); + virtual xbInt16 DeleteRecord (); + virtual xbInt16 DumpHeader ( xbInt16 iOption ) const; + virtual xbInt16 DumpRecord ( xbUInt32 ulRecNo, xbInt16 iOutputDest = 0, xbInt16 iOutputFmt = 0 ); + virtual xbInt16 GetAutoCommit () const; + virtual xbInt16 GetAutoCommit ( xbInt16 iOption ) const; + virtual xbUInt32 GetCurRecNo () const; + virtual xbInt16 GetDbfStatus () const; + virtual xbInt32 GetFieldCnt () const; + virtual xbInt16 GetFirstRecord (); + virtual xbInt16 GetFirstRecord ( xbInt16 iOption ); + virtual xbInt16 GetLastRecord (); + virtual xbInt16 GetLastRecord ( xbInt16 iOption ); + + virtual xbInt16 GetNextRecord (); + virtual xbInt16 GetNextRecord ( xbInt16 iOption ); + virtual xbInt16 GetNextRecord ( xbInt16 iOption, xbUInt32 ulStartRec ); + virtual xbInt32 GetPhysicalIxCnt () const; + virtual xbInt16 GetPrevRecord (); + virtual xbInt16 GetPrevRecord ( xbInt16 iOption ); + virtual xbInt16 GetRecord ( xbUInt32 ulRecNo ); + virtual xbInt16 GetRecordCnt ( xbUInt32 & ulRecCnt ); + virtual char * GetRecordBuf ( xbInt16 iOpt = 0 ) const; + virtual xbUInt16 GetRecordLen () const; + virtual const xbString &GetTblAlias() const; + virtual xbInt16 GetVersion () const = 0; + virtual xbXBase *GetXbasePtr () const; // return xbase pointer + + virtual xbBool MemoFieldsExist () const; + + virtual xbInt16 Open ( const xbString &sTableName ); + virtual xbInt16 Open ( const xbString &sTableName, const xbString &sAlias ); + virtual xbInt16 Open ( const xbString &sTableName, const xbString &sAlias, xbInt16 iOpenMode, xbInt16 iShareMode ) = 0; + virtual xbInt16 Pack (); + virtual xbInt16 Pack ( xbUInt32 &ulDeletedRecCnt ); + + + virtual xbInt16 PutRecord (); // Put record to current location + virtual xbInt16 PutRecord ( xbUInt32 ulRecNo ); + virtual xbInt16 ReadHeader ( xbInt16 iFilePositionOption, xbInt16 iReadOption ); + virtual xbInt16 RecordDeleted ( xbInt16 iOpt = 0 ) const; + + virtual xbInt16 SetAutoCommit ( xbInt16 iAutoCommit ); + + virtual xbInt16 UndeleteAllRecords(); + virtual xbInt16 UndeleteRecord (); + virtual xbInt16 Zap (); + + + /* field methods */ + //virtual xbInt16 GetRawField( xbInt16 iFieldNo, char *Buf, xbUInt32 BufSize, xbInt16 iRecBufSw ) const; + virtual xbInt16 GetField(xbInt16 iFieldNo, xbString &sFieldValue, xbInt16 iRecBufSw ) const; + virtual xbInt16 GetField(xbInt16 iFieldNo, xbString &sFieldValue) const; + virtual xbInt16 GetField(const xbString &sFieldName, xbString &sFieldValue) const; + virtual xbInt16 GetFieldDecimal( xbInt16 iFieldNo, xbInt16 &iFieldDecimal ) const; + virtual xbInt16 GetFieldDecimal( const xbString &sFieldName, xbInt16 &iFieldDecimal ) const; + virtual xbInt16 GetFieldLen( xbInt16 iFieldNo, xbInt16 &iFieldLen ) const; + virtual xbInt16 GetFieldLen( const xbString &sFieldName, xbInt16 &iFieldLen ) const; + virtual xbInt16 GetFieldName( xbInt16 iFieldNo, xbString &sFieldName ) const; + + virtual xbInt16 GetFieldNo( const xbString &sFieldName, xbInt16 &iFieldNo ) const; + virtual xbInt16 GetFieldNo( const xbString &sFieldName ) const; + + virtual xbInt16 GetFieldType( xbInt16 iFieldNo, char &cFieldType ) const; + virtual xbInt16 GetFieldType( const xbString &sFieldName, char &cFieldType ) const; + virtual xbInt16 PutField( const xbString &sFieldName, const xbString &sFieldData ); + virtual xbInt16 PutField( xbInt16 iFieldNo, const xbString &sFieldData ); + + virtual xbInt16 PutLogicalField( xbInt16 iFieldNo, const xbString &sFieldData ); + virtual xbInt16 PutLogicalField( const xbString &sFieldName, const xbString &sFieldData ); + virtual xbInt16 GetLogicalField( xbInt16 iFieldNo, xbString &sFieldData ) const; + virtual xbInt16 GetLogicalField( const xbString &sFieldName, xbString &sFieldData) const; + + virtual xbInt16 PutLogicalField( xbInt16 iFieldNo, xbBool bFieldData ); + virtual xbInt16 PutLogicalField( const xbString &sFieldName, xbBool bFieldData ); + virtual xbInt16 GetLogicalField( xbInt16 iFieldNo, xbBool &bFieldData ) const; + virtual xbInt16 GetLogicalField( xbInt16 iFieldNo, xbBool &bFieldData, xbInt16 iRecBufSw ) const; + virtual xbInt16 GetLogicalField( const xbString &sFieldName, xbBool &bFieldData) const; + + virtual xbInt16 GetLongField( xbInt16 iFieldNo, xbInt32 &lFieldValue ) const; + virtual xbInt16 GetLongField( const xbString &sFieldName, xbInt32 &lFieldValue ) const; + virtual xbInt16 PutLongField( xbInt16 iFieldNo, xbInt32 lFieldValue ); + virtual xbInt16 PutLongField( const xbString &sFieldNo, xbInt32 lFieldValue ); + + virtual xbInt16 GetULongField( xbInt16 iFieldNo, xbUInt32 &lFieldValue ) const; + virtual xbInt16 GetULongField( const xbString &sFieldName, xbUInt32 &lFieldValue ) const; + virtual xbInt16 PutULongField( xbInt16 iFieldNo, xbUInt32 lFieldValue ); + virtual xbInt16 PutULongField( const xbString &sFieldNo, xbUInt32 lFieldValue ); + + virtual xbInt16 GetDoubleField( xbInt16 FieldNo, xbDouble &dFieldValue ) const; + virtual xbInt16 GetDoubleField( xbInt16 FieldNo, xbDouble &dFieldValue, xbInt16 iRecBufSw ) const; + virtual xbInt16 GetDoubleField( const xbString &sFieldName, xbDouble &dFieldValue ) const; + virtual xbInt16 PutDoubleField( xbInt16 FieldNo, xbDouble dFieldValue ); + virtual xbInt16 PutDoubleField( const xbString &FieldName, xbDouble dFieldValue ); + + virtual xbInt16 GetFloatField( xbInt16 iFieldNo, xbFloat &fFieldValue ) const; + virtual xbInt16 GetFloatField( const xbString &sFieldName, xbFloat &fFieldValue ) const; + virtual xbInt16 PutFloatField( xbInt16 iFieldNo, xbFloat fFieldValue ); + virtual xbInt16 PutFloatField( const xbString &sFieldName, xbFloat fFieldValue ); + + virtual xbInt16 GetDateField( xbInt16 iFieldNo, xbDate &dt ) const; + virtual xbInt16 GetDateField( const xbString &sFieldName, xbDate &dt ) const; + virtual xbInt16 PutDateField( xbInt16 iFieldNo, const xbDate &dt ); + virtual xbInt16 PutDateField( const xbString &sFieldName, const xbDate &dt ); + + + + #ifdef XB_MEMO_SUPPORT + virtual xbInt16 GetMemoFieldCnt () const; + virtual xbMemo *GetMemoPtr (); + virtual xbUInt32 GetCreateMemoBlockSize() const; + virtual xbInt16 GetMemoField ( xbInt16 iFldNo, xbString &sMemoData ); + virtual xbInt16 GetMemoField ( const xbString & sFldName, xbString &sMemoData ); + virtual xbInt16 GetMemoFieldLen ( xbInt16 iFldNo, xbUInt32 &ullMemoFieldLen ); + virtual xbInt16 GetMemoFieldLen ( const xbString & sFldName, xbUInt32 &ulMemoFieldLen ); + virtual xbBool MemoFieldExists ( xbInt16 iFieldNo ) const; + virtual xbBool MemoFieldExists ( const xbString &sFieldName ) const; + virtual xbInt16 SetCreateMemoBlockSize( xbUInt32 ulBlockSize ) = 0; + virtual xbInt16 UpdateMemoField ( xbInt16 iFldNo, const xbString &sMemoData ); + virtual xbInt16 UpdateMemoField ( const xbString & sFldName, const xbString &sMemoData ); + #endif // XB_MEMO_SUPPORT + + + #ifdef XB_LOCKING_SUPPORT + virtual xbInt16 LockTable ( xbInt16 iLockFunction ); + virtual xbInt16 LockRecord ( xbInt16 iLockFunction, xbUInt32 ulRecNo ); + virtual xbInt16 LockAppend ( xbInt16 iLockFunction ); + virtual xbInt16 LockHeader ( xbInt16 iLockFunction ); + + #ifdef XB_MEMO_SUPPORT + virtual xbInt16 LockMemo ( xbInt16 iLockFunction ); + virtual xbBool GetMemoLocked () const; + #endif // XB_MEMO_LOCK + + #ifdef XB_INDEX_SUPPORT + virtual xbInt16 LockIndices ( xbInt16 iLockFunction ); + #endif // XB_INDEX_SUPPORT + + xbInt16 GetAutoLock () const; + void SetAutoLock ( xbInt16 iAutoLock ); + xbInt16 GetLockFlavor () const; + void SetLockFlavor ( xbInt16 iLockFlavor ); + xbBool GetTableLocked () const; + xbBool GetHeaderLocked () const; + xbUInt32 GetAppendLocked () const; + xbLinkListNode * GetFirstRecLock () const; + + #ifdef XB_DEBUG_SUPPORT + void DumpTableLockStatus() const; + #endif // XB_DEBUG_SUPPORT + #endif // XB_LOCKING_SUPPORT + + + #ifdef XB_INDEX_SUPPORT + virtual xbInt16 CloseIndexFile( xbIx *pIx ); + virtual xbInt16 CreateTag( const xbString &sIxType, const xbString &sName, const xbString &sKey, const xbString &sFilter, + xbInt16 iDescending, xbInt16 iUnique, xbInt16 iOverLay, xbIx **xbIxOut, void **vpTagOut ); + virtual xbInt16 DeleteTag( const xbString &sIxType, const xbString &sName ); + virtual xbInt16 DeleteAllIndexFiles(); + virtual xbInt16 Find( xbString &sKey ); + virtual xbInt16 Find( xbDate &dtKey ); + virtual xbInt16 Find( xbDouble &dKey ); + virtual xbIx *GetCurIx() const; + virtual void *GetCurTag() const; + + virtual xbInt16 GetFirstKey(); + virtual xbInt16 GetNextKey(); + virtual xbInt16 GetPrevKey(); + virtual xbInt16 GetLastKey(); + + virtual const xbString &GetCurIxType() const; + virtual const xbString &GetCurTagName() const; + virtual xbIxList *GetIxList() const; + xbLinkListNode *GetTagList () const; + virtual xbInt16 OpenIndex( const xbString &sIxType, const xbString &sIndexName ); + virtual xbInt16 SetCurTag( const xbString &sTagName ); + virtual void SetCurTag( const xbString &sIxType, xbIx *pIx, void *vpTag ); + + #ifdef XB_DEBUG_SUPPORT + virtual xbInt16 CheckTagIntegrity( xbInt16 iTagOpt, xbInt16 iOutputOpt ); + virtual xbInt16 Reindex( xbInt16 iTagOpt ); + #endif // XB_DEBUG_SUPPORT + #endif // XB_INDEX_SUPPORT + + #ifdef XB_NDXINF_SUPPORT + virtual xbInt16 AssociateIndex( const xbString &sType, const xbString &sName, xbInt16 iOption ); + xbLinkListNode *GetNdxInfList() const; + #endif // XB_NDXINF_SUPPORT + + //#ifdef XB_MDX_SUPPORT + //virtual xbInt16 GetCreateMdxBlockSize() const; + //virtual xbInt16 SetCreateMdxBlockSize( xbInt16 ulBlockSize ); + //#endif + + + + protected: + #ifdef XB_INDEX_SUPPORT + friend class xbIx; + friend class xbIxMdx; + + xbInt16 AddIndex( xbIx *ix, const xbString &sFmt ); + void ClearTagList(); + virtual xbInt16 GetNdxInfFileName( xbString &sNdxIdxFileName ); + xbInt16 RemoveIndex( xbIx * ix ); + void UpdateSchemaIxFlag( xbInt16 iFldNo, unsigned char cVal ); + virtual xbInt16 UpdateTagList (); + #endif + + + + #ifdef XB_LOCKING_SUPPORT + void SetHeaderLocked ( xbBool bTableLocked ); + void SetTableLocked ( xbBool bTableLocked ); + #endif // XB_LOCKING_SUPORT + + #ifdef XB_MEMO_SUPPORT + xbInt16 iMemoFieldCnt; // Memo field cnt in the table + xbMemo *Memo; // Pointer to memo class + xbUInt32 ulCreateMemoBlockSize; // blocksize to use when creating dbt file + #endif + + #ifdef XB_NDXINF_SUPPORT + virtual xbInt16 DeleteNdxInfData(); + virtual xbInt16 LoadNdxInfData(); + virtual xbInt16 SaveNdxInfData(); + #endif // XB_NDXINF_SUPPORT + +/* + #ifdef XB_MDX_SUPPORT + xbInt16 iCreateMdxBlockSize; + #endif // XB_MDX_SUPPORT +*/ + + virtual xbInt16 GetRawField( xbInt16 iFieldNo, char *Buf, xbUInt32 BufSize, xbInt16 iRecBufSw ) const; + virtual void InitVars(); + virtual xbInt16 SetVersion() = 0; + virtual xbInt16 ValidateSchema( xbSchema * s ) = 0; + xbInt16 WriteHeader( xbInt16 iPositionOption, xbInt16 iWriteOption ); + + + xbUInt32 ulCurRec; // Current record or zero + xbInt16 iAutoCommit; // Auto commit updates if not explicitly performed before moving off record? + // -1 --> Use DBMS default + // 0 --> No auto update on this table, regardless of DBMS setting + // 1 --> Auto update on this table, regardless of DBMS setting + + xbInt16 iFileVersion; // xBase file version - which class is in play + + + xbString sAlias; // table alias + xbInt16 iNoOfFields; + xbInt16 iDbfStatus; // 0 = closed + // 1 = open + // 2 = updates pending + + xbSchemaRec *SchemaPtr; // Pointer to field data + char *RecBuf; // Pointer to record buffer + char *RecBuf2; // Pointer to original rec buf + + +/* Next several variables are database header fields, up through dbase V */ + unsigned char cVersion; + char cUpdateYY; + char cUpdateMM; + char cUpdateDD; + xbUInt32 ulNoOfRecs; + xbUInt16 uiHeaderLen; + xbUInt16 uiRecordLen; + char cTransactionFlag; + char cEncryptionFlag; + char cIndexFlag; + char cLangDriver; // dbase 7 sets to 1B + + + + private: + void ResetNoOfRecords(); + + +#ifdef XB_LOCKING_SUPPORT + xbInt16 iAutoLock; // 0 - autolock off, 1 - autolock on + xbInt16 iLockFlavor; + xbBool bTableLocked; // is the table locked + xbBool bHeaderLocked; // is the header locked + xbUInt32 ulAppendLocked; // record number of the new record for the append lock operation + xbLinkListOrd lloRecLocks; // ordered link list of locked records +#endif + + +#ifdef XB_INDEX_SUPPORT + xbIxList *ixList; // pointer to a list of indices associated with the table +// #ifdef XB_MDX_SUPPORT +// xbIx *ixMdx; // pointer to production multi-tag index file +// #endif + xbIx *pCurIx; // Pointer to current index class + void *vpCurIxTag; // Pointer to current tag + xbString sCurIxType; // Current index type + xbLinkList llTags; // linked list of open tags + +#endif // XB_INDEX_SUPPORT + +#ifdef XB_NDXINF_SUPPORT + xbLinkList llNdxInfData; // linked list of strings containing ndx file entries +#endif // XB_NDXINF_SUPPORT + + +}; + + +#ifdef XB_DBF3_SUPPORT +//! @brief Derived class for handling dbf version III files/tables. +/*! +The xbDbf class is used as a base class for accessing dbf files. +The xbDbf3 class is derived from the xbDbf class and is designed to handle the +orginal version 3 type files. +*/ + +class XBDLLEXPORT xbDbf3 : public xbDbf { + public: + xbDbf3(xbXBase *x); + ~xbDbf3(); + virtual xbInt16 CreateTable ( const xbString &sTableName, const xbString &sAlias, xbSchema *, xbInt16 iOverlay, xbInt16 iShareMode ); + virtual xbInt16 GetVersion () const; + virtual xbInt16 Open ( const xbString &sTableName, const xbString &sAlias, xbInt16 iOpenMode, xbInt16 iShareMode ); + + #ifdef XB_MEMO_SUPPORT + virtual xbInt16 SetCreateMemoBlockSize( xbUInt32 iBlockSize ); + #endif + + protected: + xbInt16 SetVersion (); + virtual xbInt16 ValidateSchema( xbSchema *s ); + + private: + +}; +#endif /* XB_DBF3_SUPPORT */ + + +#ifdef XB_DBF4_SUPPORT +//! @brief Derived class for handling dbf version IV files/tables. +/*! +The xbDbf class is used as a base class for accessing dbf files. +The xbDbf4 class is derived from the xbDbf class and is designed to handle the +orginal version 4 type files. +*/ +class XBDLLEXPORT xbDbf4 : public xbDbf { + public: + xbDbf4( xbXBase *x ); + + ~xbDbf4(); + + virtual xbInt16 CreateTable ( const xbString &sTableName, const xbString &sAlias, xbSchema *, xbInt16 iOverlay, xbInt16 iShareMode ); + virtual xbInt16 GetVersion () const; + virtual xbInt16 Open ( const xbString &sTableName, const xbString &sAlias, xbInt16 iOpenMode, xbInt16 iShareMode ); + + #ifdef XB_MEMO_SUPPORT + virtual xbInt16 SetCreateMemoBlockSize( xbUInt32 iBlockSize ); + #endif + + protected: + // void InitVars (); + xbInt16 SetVersion(); + virtual xbInt16 ValidateSchema ( xbSchema *s ); + + private: + +}; + +#endif /* XB_DBF4_SUPPORT */ + +} /* namespace xb */ +#endif /* __XB_DBF_H__ */ diff --git a/src/include/xbexp.h b/src/include/xbexp.h new file mode 100755 index 0000000..96413ac --- /dev/null +++ b/src/include/xbexp.h @@ -0,0 +1,215 @@ +/* xbexp.h + +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 + +*/ + + +#ifndef __XB_EXP_H__ +#define __XB_EXP_H__ + + +// #ifdef CMAKE_COMPILER_IS_GNUCC +// #pragma interface +// #endif + + + +#ifdef XB_FUNCTION_SUPPORT +#define XB_EXP_CHAR 'C' +#define XB_EXP_DATE 'D' +#define XB_EXP_LOGICAL 'L' +#define XB_EXP_NUMERIC 'N' +#define XB_EXP_UNKNOWN 'U' +#endif + +#ifdef XB_EXPRESSION_SUPPORT + +#define XB_EXP_CONSTANT 'C' +#define XB_EXP_FUNCTION 'F' +#define XB_EXP_FIELD 'D' +#define XB_EXP_OPERATOR 'O' +#define XB_EXP_NOTROOT 'N' // not root node, needs further parsing +#define XB_EXP_PRE_OPERATOR 'B' // (B)efore) pre increment, pre decrement +#define XB_EXP_POST_OPERATOR 'A' // (A)fter) post increment, pre decrement + +#define XB_END_OF_EXPRESSION -100 + +// #define XB_UNBALANCED_PARENS -101 +// #define XB_UNBALANCED_QUOTES -102 + + +namespace xb{ + +///@cond DOXYOFF +struct XBDLLEXPORT xbExpToken { + xbString sExpression; // in - expression to pull next token from + // out - remainder of the expression after token removed + xbString sToken; // next token pulled from the expression + char cNodeType; // one of XB_EXP_CONSTANT, XB_EXP_FUNCTION, XB_EXP_FIELD, XB_EXP_OPERATOR, XB_EXP_NOTROOT + char cReturnType; // one of XB_EXP_CHAR, XB_EXP_DATE, XB_EXP_LOGICAL, XB_EXP_NUMERIC, XB_EXP_UNKNOWN + xbInt16 iSts; // return status after retrieving or attempting next token from expression + // 0 = no error + // XB_END_OF_EXPRESSION + // XB_UNBALANCED_PARENS + // XB_UNBALANCED_QUOTES + char cPrevNodeType; // previous node type + char cPrevReturnType; // previous return type + + // constructor + xbExpToken() { cNodeType = 0; cReturnType = 0; iSts = 0; cPrevNodeType = 0; cPrevReturnType = 0; } +}; +///@endcond DOXYOFF + + +/************************************************************************/ + +//! @brief Class for handling expressions. +/*! +The xbExp class is used for parsing and evaluating expression. + +The Xbase64 library includes an expression parsing module which assists +application programmers by providing a high level data manipulation tool and +also allows for building complex index keys. + +The functions included were derived from dBASE III Plus, Dbase IV and Clipper.

+ +

Internal fuctioning

+The expression module works in two phases. Firstly, method +ParseExpression is called and builds an expression tree from +all the components of the expression. The tree is made up of individual +nodes. The expression is checked for valid field names, literals, +operands and functions. Any field references are resolved. If fields +are used in an expression and the database name for the field is not +included in the name with the -> operand, the routines assume the +associated database has been successfully opened. +
+Secondly, method ProcessExpression is called to process the +expression tree created by ParseExpression(). The routine parses each +node in the expression tree, executing functions, processing operands +and manipulating data to produce the desired result.
+ +If an expression will be processed repeatedly, it is best to pre-parse the +tree using ParseExpression, then for each new call to the expression, +execute method ProcessExpression which processes the tree.

+ +

Expression Return Types

+Expressions will return a type of CHAR, NUMERIC, DATE or LOGICAL.
+ +An expression return type can be determined with method +GetExpressionResultType after parsing it.
+ +Expressions returning a return type of CHAR are limited to a 200 byte internal +buffer. There is also a 100 byte limit for NDX index key support. If +the 200 byte limit is not large enough for your application, adjust field +enum { WorkBufMaxLen = 200 }; in file exp.h.

+ +

Expression Functions

+Each expression function also has a corresponding C++ function. It is +slightly more efficient to call the C++ functions directly, rather than +execute the expression parsing routines.

+ +

Expression Components

+Expressions are made up of one or more tokens. A token is one of literal, +database field, operand or function. Literals are either numeric or character. +Character literals are enclosed in 'single' or "double" quotes. numeric +literals are a series of one or more contiguous numerals, ".", "+" or "-'". +

+A field is simply a field name in the default database, or is in the form +of database->fieldname. + +*/ + +class XBDLLEXPORT xbExp{ + + public: + xbExp( xbXBase * ); + xbExp( xbXBase *, xbDbf * ); + virtual ~xbExp(); + void ClearTreeHandle(); + + + #ifdef XB_DEBUG_SUPPORT + void DumpTree( xbInt16 iOption ); + void DumpToken( xbExpToken &t, xbInt16 iOption = 0 ); + #endif + + xbInt16 GetResultLen() const; + char GetReturnType() const; + xbInt16 GetBoolResult( xbBool &bResult ); + xbInt16 GetDateResult( xbDate &dtResult ); + xbInt16 GetNumericResult( xbDouble &dResult ); + xbInt16 GetStringResult( xbString &sResult ); + xbInt16 GetStringResult( char * vpResult, xbUInt32 ulLen ); + xbExpNode *GetTreeHandle(); + xbInt16 ParseExpression( const xbString &sExpression ); + xbInt16 ParseExpression( xbDbf *dbf, const xbString &sExpression ); + xbInt16 ProcessExpression(); + xbInt16 ProcessExpression( xbInt16 iRecBufSw ); + + + protected: + xbInt16 GetNextToken( xbExpToken &t ); + xbInt16 OperatorWeight( const xbString &sOperator ); + xbExpNode *GetNextNode( xbExpNode * n ) const; // traverses the tree from bottom left node, right, then up + + private: // methods + + // xbInt16 CalcExpressionResultLen(); + xbInt16 CalcFunctionResultLen( xbExpNode *n ) const; + xbInt16 CalcCharNodeLen( xbExpNode *n ); + xbInt16 CheckParensAndQuotes( const xbString &sExpression ); + xbInt16 GetExpressionResultLen() const; + + xbInt16 GetTokenCharConstant ( xbExpToken &t ); + xbInt16 GetTokenDatabaseField ( xbExpToken &t ); + xbInt16 GetTokenDateConstant ( xbExpToken &t ); + xbInt16 GetTokenFunction ( xbExpToken &t ); + xbInt16 GetTokenLogicalConstant( xbExpToken &t ); + xbInt16 GetTokenNumericConstant( xbExpToken &t ); + xbInt16 GetTokenOperator ( xbExpToken &t ); + xbInt16 GetTokenParen ( xbExpToken &t ); + + xbBool IsFunction ( const xbString &sExp, char &cReturnType ); + xbBool IsLogicalConstant ( const xbString &sExp ); + xbBool IsNumericConstant ( const xbString &sExp, char cPrevNodeType ); + xbBool IsOperator ( const xbString &sExp ); + char IsTokenSeparator ( char c ); + xbBool IsWhiteSpace ( char c ); + + xbInt16 ParseExpression ( const xbString &sExpression, xbInt16 iWeight ); + xbInt16 ParseExpressionConstant( xbExpToken &t, xbExpNode *n ); + xbInt16 ParseExpressionFunction( xbExpToken &t, xbExpNode *n, xbInt16 iWeight ); + xbInt16 ParseExpressionFunctionParms( const xbString &sParms, xbLinkList &llParms ); + xbInt16 ParseExpressionField ( xbExpToken &t, xbExpNode *n ); + xbInt16 ParseExpressionOperator( xbExpToken &t, xbExpNode *n, xbInt16 iWeight ); + + xbInt16 ProcessExpressionFunction( xbExpNode *n, xbInt16 iRecBufSw = 0 ); + xbInt16 ProcessExpressionOperator( xbExpNode *n ); + + private: // fields + xbXBase *xbase; + xbDbf *dbf; + xbExpNode *nTree; // pointer to tree of expNodes + // xbInt16 iExpLen; // size of expression result + + +}; + +/* Expression handler */ + + +}; +#endif // XB_EXPRESSION_SUPPORT +#endif // __XB_EXP_H__ + + diff --git a/src/include/xbexpnode.h b/src/include/xbexpnode.h new file mode 100755 index 0000000..f50d9eb --- /dev/null +++ b/src/include/xbexpnode.h @@ -0,0 +1,120 @@ +/* xbexpnode.h + +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 + +*/ + + +#ifndef __XB_EXPNODE_H__ +#define __XB_EXPNODE_H__ + +// #ifdef CMAKE_COMPILER_IS_GNUCC +// #pragma interface +// #endif + +#ifdef XB_EXPRESSION_SUPPORT + + +namespace xb{ + +//class XBDLLEXPORT xbDbf; + +/************************************************************************/ +//! @brief Expression node class for handling expression nodes. +/*! +The xbExpNode class is used by the expression logic for handling specific +nodes within a tree of nodes.
+ +Expressions are parsed into nodes and a logical tree of nodes is created +out of all the individual components within the expression.
+ +This class is used for handling a specific node within a tree.
+ +An application program will typically not need to be concerned with +this class.
+ +*/ +class XBDLLEXPORT xbExpNode { + public: + xbExpNode(); + xbExpNode( xbString &sNodeText, char cReturnType, char cNodeType ); + xbExpNode( xbString &sNodeText, char cNodeType ); + xbExpNode( char cNodeType ); + + virtual ~xbExpNode(); + xbInt16 AddChild( xbExpNode *n ); + xbExpNode *GetChild( xbUInt32 ulChildNo ) const; + xbUInt32 GetChildCnt() const; + xbUInt32 GetChildNo() const; + xbDbf *GetDbf() const; + xbInt16 GetFieldNo() const; + xbExpNode *GetFirstNode(); + xbExpNode *GetNextNode() const; + void GetNodeText( xbString &sNodeText ) const; + char GetNodeType() const; + xbExpNode *GetParent() const; + xbString &GetStringResult(); + xbBool GetBoolResult() const; + xbDouble GetNumericResult() const; + xbUInt32 GetResultLen() const; + xbExpNode *GetRightSibling() const; + char GetReturnType() const; + xbInt16 GetWeight() const; + xbBool HasRightSibling() const; + xbBool IsOperator() const; + xbBool IsUnaryOperator() const; + void RemoveLastChild(); + void SetResultLen( xbUInt32 ulResultLen ); + void SetDbfInfo( xbDbf *dbf ); + void SetDbfInfo( xbDbf *dbf, xbInt16 iFieldNo ); + void SetNodeText( xbString &sNodeText ); + void SetNodeType( char cNodeType ); + void SetParent( xbExpNode *n ); + void SetResult( xbString &sResult ); + void SetResult( xbDate &dtResult ); + void SetResult( xbBool bResult ); + void SetResult( xbDouble dResult ); + void SetReturnType( char cReturnType ); + void SetWeight( xbInt16 iWeight ); + + #ifdef XB_DEBUG_SUPPORT + void DumpNode( xbInt16 iOption ) const; // 0 = no children, 1 = and children + #endif + + private: + xbString sNodeText; // expression text + char cReturnType; // one of: XB_EXP_CHAR, XB_EXP_DATE, XB_EXP_LOGICAL, XB_EXP_NUMERIC + char cNodeType; // one of: XB_EXP_CONSTANT, XB_EXP_FUNCTION, XB_EXP_FIELD, XB_EXP_OPERATOR + xbString sResult; // char result, and operator + xbDouble dResult; // numeric and bool results + xbExpNode * nParent; // pointer to parent + xbLinkList llChildren; // linked list of descendent nodes + xbDbf *dbf; // pointer to dbf, used for field, RECNO() and RECCOUNT() + xbInt16 iFieldNo; // field no if DBF field + xbUInt32 ulResultLen; // for string results, accumulated length of character operations + // includes the sum of all nodes under this + this + // date = 8, numeric = 4, logical = 1 + xbInt16 iWeight; // used for buildign the tree of nodes, assigned to operators + // the higher the number, the lower it goes on the tree + +}; + +/************************************************************************/ + +/* Expression handler */ + + +}; +#endif // XB_EXPRESSION_SUPPORT +#endif // __XB_EXP_H__ + + diff --git a/src/include/xbfile.h b/src/include/xbfile.h new file mode 100755 index 0000000..a636632 --- /dev/null +++ b/src/include/xbfile.h @@ -0,0 +1,194 @@ +/* xbfile.h + +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 + +*/ + +#ifndef __XB_FILE_H__ +#define __XB_FILE_H__ + +#ifdef CMAKE_COMPILER_IS_GNUCC +#pragma interface +#endif + +/*****************************/ +/* File Open Modes */ +#define XB_READ 0 +#define XB_READ_WRITE 1 +#define XB_WRITE 2 + +/*****************************/ +/* File Access Modes */ +#define XB_SINGLE_USER 0 // file buffering on +#define XB_MULTI_USER 1 // file buffering off + +namespace xb{ + +/*! @brief Class for handling low level file I/O. +*/ + +/*! +The xbFile class is used as an abstraction layer to isolate the library from the nuances +of different operating systems and machine architectures. + +This module handles low level file I/O and is a base class +for the (dbf) table, (dbt) memo and (ndx,mdx) index classes. +Files are opened and manipulated via methods in the xbFile class.
+This class handles:
+1) Big and little endian reads/writes
+2) 32 or 64 bit system calls, depending on OS
+3) Various different c/c++ calls to open/close/read/write based on OS and compiler version
+
+If you are wanting to port this library to a new platform, start with this class. +This class could be used if you want to write a platform independent program that needs R/W file access. +*/ + + +class XBDLLEXPORT xbFile : public xbSsv { + + public: + // xbFile(); + xbFile( xbXBase * x ); + + ~xbFile(); + xbInt16 CreateUniqueFileName( const xbString &sDirIn, const xbString &sExtIn, xbString &sFqnOut ); + xbInt16 CreateUniqueFileName( const xbString &sDirIn, const xbString &sExtIn, xbString &sFqnOut, xbInt16 iOption ); + + const xbString& GetDirectory() const; + const xbString& GetFileName() const; + const xbString& GetFqFileName() const; + + void SetDirectory ( const xbString &sDirectory); + void SetFileName ( const xbString &sFileName ); + void SetFqFileName( const xbString &sFqName ); + + xbUInt32 GetBlockSize () const; + xbInt16 SetBlockSize ( xbUInt32 ulBlockSize ); + + xbInt16 GetOpenMode () const; + xbInt16 GetShareMode () const; + + xbInt16 GetFileDirPart ( xbString &sFileDirPartOut ) const; + xbInt16 GetFileDirPart ( const xbString &sCompleteFileNameIn, xbString &sFileDirPartOut ) const; + xbInt16 GetFileExtPart ( xbString &sFileExtPartOut ) const; + xbInt16 GetFileExtPart ( const xbString &sCompleteFileNameIn, xbString &sFileExtPartOut ) const; + xbInt16 GetFileNamePart( xbString &sFileNamePartOut ) const; + xbInt16 GetFileNamePart( const xbString &sCompleteFileNameIn, xbString &sFileNamePartOut ) const; + xbInt16 GetFileType ( xbString &sFileType ) const; + + xbInt16 GetXbaseFileTypeByte( const xbString &sFileName, xbInt16 &iVersion ); + xbInt16 GetXbaseFileTypeByte( const xbString &sFileName, unsigned char &cFileTypeByte ); + xbInt16 GetXbaseFileTypeByte( const xbString &sFileName, unsigned char &cFileTypeByte, xbInt16 &iVersion ); + xbInt16 DetermineXbaseTableVersion( unsigned char cFileTypeByte ) const; + xbInt16 DetermineXbaseMemoVersion( unsigned char cFileTypeByte ) const; + + xbBool FileExists () const; + xbBool FileExists ( xbInt16 iOption ) const; + xbBool FileExists ( const xbString &sFileName ) const; + xbBool FileExists ( const xbString &sFileName, xbInt16 iOption ) const; + + xbBool FileIsOpen () const; + + xbInt16 ReadBlock ( xbUInt32 ulBlockNo, size_t readSize, void *buf ); + xbInt16 ReadBlock ( xbUInt32 ulBlockNo, xbUInt32 ulBlockSize, size_t readSize, void *buf ); + xbInt16 WriteBlock( xbUInt32 ulBlockNo, size_t writeSize, void *buf ); + + xbInt16 GetFileSize( xbUInt64 &ullFileSize ); + xbInt16 GetFileMtime( time_t &mtime ); + + xbDouble eGetDouble( const char *p ) const; + xbInt32 eGetInt32 ( const char *p ) const; + xbUInt32 eGetUInt32( const char *p ) const; + xbInt16 eGetInt16 ( const char *p ) const; + xbUInt16 eGetUInt16( const char *p ) const; + void ePutDouble( char *p, xbDouble d ); + void ePutInt32 ( char *p, xbInt32 l ); + void ePutUInt32( char *p, xbUInt32 ul ); + void ePutInt16 ( char *p, xbInt16 s ); + void ePutUInt16( char *p, xbUInt16 s ); + + xbInt16 xbFclose (); + xbInt16 xbFeof (); + xbInt16 xbFflush (); + xbInt16 xbFgetc ( xbInt32 &c ); + xbInt16 xbFgetc ( char &c ); + + #ifdef XB_LOCKING_SUPPORT + xbInt16 xbLock ( xbInt16 iFunction, xbInt64 llOffset, size_t stLen ); + xbInt16 GetLockRetryCount() const; + void SetLockRetryCount( xbInt16 iLockRetries ); + #endif + + xbInt16 xbFopen ( xbInt16 iOpenMode ); + xbInt16 xbFopen ( const xbString &sOpenMode, xbInt16 iShareMode ); + xbInt16 xbFopen ( xbInt16 iOpenMode, xbInt16 iShareMode ); + xbInt16 xbFopen ( const xbString &sMode, const xbString &sFileName, xbInt16 iShareMode ); + + xbInt16 xbFputc ( xbInt32 c ); + xbInt16 xbFputc ( xbInt32 c, xbInt32 iNoTimes ); + xbInt16 xbFputs ( const xbString &s ); + xbInt16 xbFread ( void *ptr, size_t size, size_t nmemb ); + xbInt16 xbFgets ( size_t lSize, xbString &sLine ); + + size_t xbFtell (); + xbInt16 xbFseek ( xbInt64 llOffset, xbInt32 whence ); + + void xbFTurnOffFileBuffering(); + xbInt16 xbFwrite ( const void *ptr, size_t lSize, size_t lNmemb ); + + xbInt16 xbReadUntil ( const char cDelim, xbString &sOut ); + xbInt16 xbRemove ( const xbString &sFileName, xbInt16 iOption ); + xbInt16 xbRemove ( const xbString &sFileName ); + xbInt16 xbRemove (); + + xbInt16 xbRename ( const xbString &sOldName, const xbString &sNewName ); + xbInt16 xbRename ( const xbString &sOldName, const xbString &sNewName, xbInt16 iOption ); + void xbRewind (); + + xbInt16 xbTruncate ( xbInt64 llSize ); + xbInt16 NameSuffixMissing( const xbString &sFileName, xbInt16 iOption ) const; + + #ifdef XB_DEBUG_SUPPORT + xbInt16 DumpBlockToDisk( xbUInt32 ulBlockNo, size_t lBlockSize ); + #endif + + + protected: + + xbXBase *xbase; /* pointer to the main structures */ + + private: + FILE *fp; + xbString sFqFileName; /* fully qualified file name */ + xbString sFileName; /* file name */ + xbString sDirectory; /* directory, ends with / or \ */ + xbBool bFileOpen; /* true if file is open */ + xbUInt32 ulBlockSize; /* used for memo and index files */ + + xbInt16 iOpenMode; /* XB_READ || XB_READ_WRITE || XB_WRITE */ + xbInt16 iShareMode; /* XB_SINGLE_USER || XB_MULTI_USER - set file buffering */ + xbInt32 iFileNo; /* Library File No */ + + #ifdef XB_LOCKING_SUPPORT + xbInt16 iLockRetries; /* file override number of lock attempts */ + #endif + + #ifdef HAVE_SETENDOFFILE_F + HANDLE fHandle; + #endif + + +}; + +} /* namespace */ +#endif /* __XBFILE_H__ */ + diff --git a/src/include/xbfilter.h b/src/include/xbfilter.h new file mode 100755 index 0000000..8722256 --- /dev/null +++ b/src/include/xbfilter.h @@ -0,0 +1,78 @@ +/* xbfilter.h + +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 manages the user data area (UDA) + +*/ + + +#ifndef __XB_XBFILTER_H__ +#define __XB_XBFILTER_H__ + +#ifdef CMAKE_COMPILER_IS_GNUCC +#pragma interface +#endif + + +#ifdef XB_FILTER_SUPPORT + + +namespace xb{ + +//#ifdef XB_INDEX_SUPPORT +//class XBDLLEXPORT xbIx; +//#endif // XB_INDEX_SUPPORT + + +class XBDLLEXPORT xbFilter { + + public: + xbFilter( xbXBase *xbase, xbDbf *dbf ); + ~xbFilter(); + xbInt16 Set( xbString &sFilterExpression ); + xbInt16 GetFirstRecord( xbInt16 iOpt = 1 ); + xbInt16 GetNextRecord ( xbInt16 iOpt = 1 ); + xbInt16 GetPrevRecord ( xbInt16 iOpt = 1 ); + xbInt16 GetLastRecord ( xbInt16 iOpt = 1 ); + void SetLimit( xbInt32 ulLimit ); + xbInt32 GetLimit() const; + void ResetQryCnt(); + xbInt32 GetQryCnt() const; + + #ifdef XB_INDEX_SUPPORT +// void Set( xbIx *pIx, void *vpTag ); + xbInt16 GetFirstRecordIx( xbInt16 iOpt = 1 ); + xbInt16 GetNextRecordIx ( xbInt16 iOpt = 1 ); + xbInt16 GetPrevRecordIx ( xbInt16 iOpt = 1 ); + xbInt16 GetLastRecordIx ( xbInt16 iOpt = 1 ); + #endif // XB_INDEX_SUPPORT + + + private: + xbXBase *xbase; + xbDbf *dbf; + xbExp *exp; + + xbInt32 lLimit; // max number rows returned + xbInt32 lCurQryCnt; // current count of rows returned, neg# is moving from bottom to top + // pos# is moving from top to bottom + #ifdef XB_INDEX_SUPPORT + xbIx *pIx; // if index is set, the class uses the index tag, otherwise table + void *vpTag; + #endif // XB_INDEX_SUPPORT + + +}; +} /* namespace */ +#endif /* XB_FILTER_SUPPORT */ +#endif /* __XBFILTER_H__ */ \ No newline at end of file diff --git a/src/include/xbindex.h b/src/include/xbindex.h new file mode 100755 index 0000000..b42f76e --- /dev/null +++ b/src/include/xbindex.h @@ -0,0 +1,605 @@ +/* xbindex.h + +XBase64 Software Library + +Copyright (c) 1997,2003,2014, 2018, 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 + +*/ + + +#ifndef __XB_INDEX_H__ +#define __XB_INDEX_H__ + +#ifdef XB_INDEX_SUPPORT + + +#define XB_ADD_KEY 1 +#define XB_UPD_KEY 2 +#define XB_DEL_KEY 3 + +namespace xb{ + + + +///@cond DOXYOFF +// structure for index nodes, each node contains information regarding one block +struct XBDLLEXPORT xbIxNode { + xbIxNode *npPrev; // pointer to previous node in chain + xbIxNode *npNext; // pointer to next node in chain + xbUInt32 iCurKeyNo; // current key number in the node, 0 offset + xbUInt32 ulBlockNo; // this block number + xbUInt32 ulBufSize; // size of cpBlockData + char *cpBlockData; // pointer to memory version of block data stored in file +}; +///@endcond DOXYOFF + + +//! @brief Base class for handling dbf indices. +/*! + +The xbIx class is used as a base class for accessing index files. +Each index file can have one or more tags. + +Calls to the index routines to perform index updates are handled automatically by the dbf class. +The application program does not need to be concerned with index updates. + +If there is a production MDX index, it is opened automatically when the dbf file is opened. +If there is an ndx file, that has been associated with the dbf file with the metadata routines, +it will be opened automatically when the dbf file is opened. +If there are non prod ndx indices that are not associated with the dbf file, the application +program will need to open as appropriate. +The meta data association logic is specific to the Xbase64 library and is not applicable to +other available tools that handle ndx indices. +All index files are automatically closed when the dbf file is closed. + + +
+The class is designed to support the addition of additional indices with a minimal amount of effort +needed to integrate into the library. +If you are looking at adding an new index type to the library, create a derived class using xbIx as a +base class and modify methods needed to support the new index file version. +The xbDbf class (and derived classes) perform the needed calls to the index routines for updates.
+See the following for examples on how to start on this:
+xbIxNdx is a derived class and supports a single tag.
+xbIxMdx is a derived class and supports multiple tags.
+ + + +
+How data fields are stored in index files: + + +
Field TypeStored in DBF asStored in NDX asStored in MDX as
Ccharcharchar +
Ftext numbersxbDoublexbBcd +
Ntext numbersxbDoublexbBcd +
Dtext CCYYMMDDxbDouble - julianxbDouble - julian +
+ + +
+Pages Vs Blocks +
+For purposes of the Xbase index classes, a page is considered to be 512 bytes of data +and a block is made up of one or more 512 byte pages. +
Default block sixe of NDX indices is one 512 byte page. +
Default block size of MDX indices is two 512 byte pages or 1024 bytes. + +
The WriteBlock and GetBlock functions calculate the physical position in the +file based on a combination of Block Number and Block Size. Block size is set at +time of index file creation, default is 1024 or two pages. + +
Page numbers are stored in the physical file, but block reads and writes +are performed. + +
+Duplicate Keys +
+With the original DBase unique indexing option, if a table has multiple records with the +same key value, DBase would allow multiple records in the table, but only the first +record would be found in the index. +
+XBase64 can be configured to support the original DBase duplicate key implementation, +or can be configured to halt with a DUPLICATE_KEY error on the insertion of a record +with a duplicate key. +
+ + + + +
OptionDescription
XB_HALT_ON_DUPKEYReturn error XB_KEY_NOT_UNIQUE when attempting to append record with duplicate key
XB_EMULATE_DBASEEmulate DBase, allow duplicate records with the same key, only the first record is indexed
+*/ + + +class XBDLLEXPORT xbIx : public xbFile { + public: + xbIx( xbDbf * d ); + virtual ~xbIx(); + + virtual xbInt16 CheckTagIntegrity( void *vpTag, xbInt16 iOpt ) = 0; + virtual xbInt16 Close(); + virtual xbInt16 CreateTag( const xbString &sName, const xbString &sKey, const xbString &sFilter, xbInt16 iDescending, xbInt16 iUnique, xbInt16 iOverlay, void **vpTag ) = 0; + virtual xbInt16 FindKey( void *vpTag, const xbString &sKey, xbInt16 iRetrieveSw ); + virtual xbInt16 FindKey( void *vpTag, const char * cKey, xbInt32 lKeyLen, xbInt16 iRetrieveSw ); + virtual xbInt16 FindKey( void *vpTag, const xbBcd &bcd, xbInt16 iRetrieveSw ); + virtual xbInt16 FindKey( void *vpTag, const xbDate &dtKey, xbInt16 iRetrieveSw ); + virtual xbInt16 FindKey( void *vpTag, xbDouble dKey, xbInt16 iRetrieveSw ); + virtual xbInt16 FindKey( void *vpTag, const void *vKey, xbInt32 lKeyLen, xbInt16 iRetrieveSw ) = 0; + virtual void *GetCurTag() const; + virtual xbDbf *GetDbf() const; + + virtual xbString &GetKeyExpression( const void *vpTag ) const = 0; + virtual xbString &GetKeyFilter( const void *vpTag ) const = 0; + virtual char GetKeyType( const void *vpTag ) const = 0; + virtual xbBool GetLocked() const; + + virtual xbInt16 GetFirstKey( void *vpTag, xbInt16 iRetrieveSw ) = 0; + virtual xbInt16 GetFirstKey( void *vpTag ); + virtual xbInt16 GetFirstKey(); + + virtual xbInt16 GetLastKey( void *vpTag, xbInt16 lRetrieveSw ) = 0; + virtual xbInt16 GetLastKey( void *vpTag ); + virtual xbInt16 GetLastKey(); + + virtual xbInt16 GetNextKey( void *vpTag, xbInt16 iRetrieveSw ) = 0; + virtual xbInt16 GetNextKey( void *vpTag ); + virtual xbInt16 GetNextKey(); + + virtual xbInt16 GetPrevKey( void *vpTag, xbInt16 iRetrieveSw ) = 0; + virtual xbInt16 GetPrevKey( void *vpTag ); + virtual xbInt16 GetPrevKey(); + + virtual void *GetTag( xbInt16 iTagNo ) const = 0; + virtual void *GetTag( xbString &sTagName ) const = 0; + virtual xbInt16 GetTagCount() const = 0; + + virtual xbString &GetTagName( void *vpTag ) const = 0; + virtual const char * GetTagName( void *vpTag, xbInt16 iOpt ) const = 0; + virtual void GetTagName( void *vpTag, xbString &sTagName ) {}; + + virtual xbBool GetUnique( void *vpTag ) const = 0; + virtual xbBool GetSortOrder( void *vpTag ) const = 0; + + virtual xbInt16 Open( const xbString &sFileName ); + virtual xbInt16 Reindex( void **vpTag ) = 0; + virtual xbInt16 SetCurTag( xbInt16 iTagNo ) = 0; + virtual xbInt16 SetCurTag( xbString &sTagName ) = 0; + virtual void SetCurTag( void * vpCurTag ); + virtual void SetLocked( xbBool bLocked ); + + virtual void TestStub( char *s, void *vpTag ) {}; + + + #ifdef XB_DEBUG_SUPPORT + virtual xbInt16 DumpFreeBlocks( xbInt16 iOpt = 0 ) { return XB_NO_ERROR; } + virtual xbInt16 DumpHeader( xbInt16 iDestOpt = 0, xbInt16 iFmtOpt = 0 ) = 0; + virtual xbInt16 DumpIxForTag( void *vpTag, xbInt16 iOutputOpt ) = 0; + virtual void DumpIxNodeChain( void *vpTag, xbInt16 iOutputOpt ) const = 0; + virtual xbInt16 DumpNode( void * vpTag, xbIxNode * pNode, xbInt16 iOption ) const; + virtual xbInt16 DumpTagBlocks( xbInt16 iOpt = 1, void *vpTag = NULL ) = 0; + + #endif + + protected: + friend class xbDbf; + + virtual xbInt16 AddKey( void *vpTag, xbUInt32 ulRecNo ) = 0; + virtual xbInt16 AddKeys( xbUInt32 ulRecNo ); + virtual xbIxNode *AllocateIxNode( xbUInt32 ulBufSize = 0, xbInt16 iOption = 0 ); + virtual xbInt16 BSearchBlock( char cKeyType, xbIxNode *npNode, xbInt32 lKeyLen, const void *vpKey, xbInt32 lSearchKeyLen, xbInt16 &iCompRc, xbBool bDescending = xbFalse ) const; + virtual xbInt16 CheckForDupKeys(); + virtual xbInt16 CheckForDupKey( void *vpTag ) = 0; + virtual xbInt16 CompareKey( char cKeyType, const void *v1, const void *v2, size_t lKeyLen ) const; + virtual xbInt16 CreateKeys( xbInt16 iOpt ); + virtual xbInt16 CreateKey( void * vpTag, xbInt16 iOpt ) = 0; + virtual xbInt16 DeleteFromNode( void *vpTag, xbIxNode * npNode, xbInt16 iSlotNo ) = 0; +// virtual xbInt16 DeleteKeys( xbUInt32 ulRecNo ); + virtual xbInt16 DeleteKeys(); + virtual xbInt16 DeleteKey( void *vpTag ) = 0; + + virtual xbInt16 DeleteTag( void *vpTag ) = 0; + + + virtual xbInt16 FindKeyForCurRec( void *vpTag ) = 0; + virtual xbIxNode *FreeNodeChain( xbIxNode *np ); + virtual xbInt16 GetBlock( void *vpTag, xbUInt32 ulBlockNo, xbInt16 iOpt, xbUInt32 ulAddlBuf = 0 ); +// virtual xbBool GetIndexUpdated() const = 0; + virtual xbInt32 GetKeyCount( xbIxNode *npNode ) const; + virtual char *GetKeyData( xbIxNode *npNode, xbInt16 iKeyNo, xbInt16 iKeyItemLen ) const; + virtual xbInt16 GetKeySts( void *vpTag ) const = 0; + virtual xbInt16 GetLastKey( xbUInt32 ulNodeNo, void *vpTag, xbInt16 lRetrieveSw ) = 0; + virtual xbInt16 InsertNodeL( void *vpTag, xbIxNode * npNode, xbInt16 iSlotNo, char * cpKeyBuf, xbUInt32 uiPtr ) = 0; + virtual xbInt16 InsertNodeI( void *vpTag, xbIxNode * npNode, xbInt16 iSlotNo, xbUInt32 uiPtr ) = 0; + virtual xbInt16 KeyExists( void * ) = 0; +// virtual xbInt16 KeyUpdated( void *vpTag ) const = 0; + virtual void NodeFree( xbIxNode * ixNode ); + virtual xbInt16 ReadHeadBlock( xbInt16 iOpt = 0 ) = 0; + virtual void SetDbf( xbDbf *dbf ); + virtual xbInt16 SplitNodeL( void *vpTag, xbIxNode * npLeft, xbIxNode *npRight, xbInt16 iSlotNo, char *cpKeyBuf, xbUInt32 uiPtr ) = 0; + virtual xbInt16 SplitNodeI( void *vpTag, xbIxNode * npLeft, xbIxNode *npRight, xbInt16 iSlotNo, xbUInt32 uiPtr ) = 0; + virtual xbInt16 UpdateTagKey( char cAction, void *vpTag, xbUInt32 ulRecNo = 0 ) = 0; + virtual xbInt16 WriteHeadBlock( xbInt16 iOption ) = 0; + + xbDbf *dbf; + char *cNodeBuf; // pointer to memory for processing in a block of index data + void *vpCurTag; // pointer to active tag. Single index files have only one tag + + private: + virtual void AppendNodeChain( void *vpTag, xbIxNode *npNode ) = 0; + virtual xbInt16 GetKeyPtr( void *vpTag, xbInt16 iKeyNo, xbIxNode *npNode, xbUInt32 &ulKeyPtr ) const = 0; + virtual xbBool IsLeaf( void *vpTag, xbIxNode *npNode ) const = 0; + // virtual void SetCurNode( void *vpTag, xbIxNode *npNode ) = 0; + + xbBool bLocked; // index file locked? +}; + +#ifdef XB_NDX_SUPPORT + +#define XB_NDX_BLOCK_SIZE 512 + + +///@cond DOXYOFF +struct XBDLLEXPORT xbNdxTag { + + // NDX File Header Fields + xbUInt32 ulRootBlock; // header node is 0 + xbUInt32 ulTotalBlocks; // includes header node + char cKeyType; // C = Char, F = Numeric, D = Date + xbInt16 iKeyLen; // length of key data + xbInt16 iKeysPerBlock; // max number keys per block <=100 + xbInt16 iKeyType; // 00 = Char, 01 = Numeric + xbInt16 iKeyItemLen; // KeyLen + 8 bytes + char cSerNo; // rolling incrementing serial number +1 on each index update + xbInt16 iUnique; // True if unique + xbString sKeyExpression; // index expression + // end of NDX Header field + + xbExp *exp; // pointer to expression for expression keys + time_t tNodeChainTs; // node chain time stamp + xbIxNode *npNodeChain; + xbIxNode *npCurNode; + char *cpKeyBuf; // key buffer, for searches and adds + char *cpKeyBuf2; // key buffer, for deletes + xbString sTagName; // tag name - is the file name without the extension +// xbInt16 iKeyUpdated; // key updated? set in method KeyUpdated, checked in AddKey and DeleteKey routines + xbBool bFoundSts; // key found? used to determine if new key should be added in XB_EMULATE_DBASE mode in AddKey + + + xbInt16 iKeySts; // key updated? set in method CreateKey, checked in AddKey and DeleteKey routines + // old key filtered new key filtered iKeySts + // Y Y XB_UPD_KEY 2 - update key if changed (delete and add) + // Y N XB_DEL_KEY 3 - delete key + // N Y XB_ADD_KEY 1 - add key + // N N 0 - no update + +}; +///@endcond DOXYOFF + +//! @brief Class for handling NDX single tag indices. +/*! + +The xbIxNdx class is derived from the xbIx base class and is specific to handling NDX single tag index files. +Each NDX index file can have only one tag, but the methods are set up to take an argument for a specific tag. +This was done in order to provide a consistant interface across index types. + +Calls to the ndx index routines to perform index updates are handled automatically be the dbf class after +the ndx file has been opened. + +Xbase64 provides a mechanism to automatically open ndx files when a dbf file is opened. +If the ndx file has been associated with the dbf file with the metadata routines, +it will be opened automatically when the dbf file is opened. +If there are non prod ndx indices that are not associated with the dbf file, the application +program will need to open as appropriate. +The meta data association logic is specific to the Xbase64 library and is not applicable to +other available tools that handle ndx indices. + +*/ + +class XBDLLEXPORT xbIxNdx : public xbIx { + public: + xbIxNdx( xbDbf * d ); + ~xbIxNdx(); + xbInt16 CheckTagIntegrity( void *vpTag, xbInt16 iOpt ); + xbInt16 CreateTag( const xbString &sName, const xbString &sKey, const xbString &sFilter, xbInt16 iDescending, xbInt16 iUnique, xbInt16 iOverlay, void **vpTag ); +// xbInt16 DeleteTag( void *vpTag ); + xbInt16 FindKey( void *vpTag, const void *vpKey, xbInt32 lKeyLen, xbInt16 iRetrieveSw ); + xbInt16 GetFirstKey( void *vpTag, xbInt16 iRetrieveSw ); + + xbInt16 GetLastKey( void *vpTag, xbInt16 iRetrieveSw = 1 ); + xbInt16 GetNextKey( void *vpTag, xbInt16 iRetrieveSw = 1 ); + xbInt16 GetPrevKey( void *vpTag, xbInt16 iRetrieveSw = 1 ); + xbInt32 GetKeyLen ( const void *vpTag ) const; + char GetKeyType ( const void *vpTag ) const; + xbString &GetKeyExpression( const void *vpTag ) const; + xbString &GetKeyFilter( const void *vpTag ) const; + void *GetTag( xbInt16 iTagNo ) const; + void *GetTag( xbString &sTagName ) const; + xbString &GetTagName( void *vpTag ) const; + const char * GetTagName( void *vpTag, xbInt16 iOpt ) const; + + xbInt16 GetTagCount() const; + xbBool GetUnique( void *vpTag = NULL ) const; + xbBool GetSortOrder( void *vpTag ) const; + xbInt16 Reindex( void **vpTag ); + xbInt16 SetCurTag( xbInt16 iTagNo ); + xbInt16 SetCurTag( xbString &sTagName ); + + + + #ifdef XB_DEBUG_SUPPORT + xbInt16 DumpTagBlocks( xbInt16 iOpt = 1, void *vpTag = NULL ); + xbInt16 DumpHeader( xbInt16 iOpt = 0, xbInt16 iFmt = 0 ); + xbInt16 DumpIxForTag( void *vpTag, xbInt16 iOutputOpt ); + void DumpIxNodeChain( void *vpTag, xbInt16 iOutputOpt ) const; + xbInt16 DumpNode( void * vpTag, xbIxNode * pNode, xbInt16 iOption ) const; + #endif + + protected: + friend class xbDbf; + xbInt16 AddKey( void *vpTag, xbUInt32 ulRecNo ); + xbIxNode *AllocateIxNode( xbUInt32 ulBufSize = 0, xbInt16 iOption = 0 ); + xbInt16 CheckForDupKey( void *vpTag ); + xbIxNode *CreateIxNode( xbUInt32 ulBufSize ); + xbInt16 CreateKey( void * vpTag, xbInt16 iOpt ); + xbInt16 DeleteFromNode( void *vpTag, xbIxNode * npNode, xbInt16 iSlotNo ); + xbInt16 DeleteKey( void *vpTag ); + + xbInt16 DeleteTag( void *vpTag ); + + xbInt16 FindKeyForCurRec( void *vpTag ); +// xbBool GetIndexUpdated() const; + xbInt16 GetKeyTypeN( const void *vpTag ) const; + xbInt16 GetKeySts( void *vpTag ) const; + xbInt16 GetLastKey( xbUInt32 ulNodeNo, void *vpTag, xbInt16 iRetrieveSw = 1 ); + xbInt16 InsertNodeI( void *vpTag, xbIxNode * npNode, xbInt16 iSlotNo, xbUInt32 uiPtr ); + xbInt16 InsertNodeL( void *vpTag, xbIxNode * npNode, xbInt16 iSlotNo, char * cpKeyBuf, xbUInt32 uiPtr ); + xbInt16 KeyExists( void *vpTag = NULL ); +// xbBool KeyFiltered( void *vpTag ) const; +// xbInt16 KeyUpdated( void *vpTag ) const; + xbInt16 ReadHeadBlock(xbInt16 iOpt); // read the header node of the disk NDX file + xbInt16 SplitNodeI( void *vpTag, xbIxNode * npLeft, xbIxNode *npRight, xbInt16 iSlotNo, xbUInt32 uiPtr ); + xbInt16 SplitNodeL( void *vpTag, xbIxNode * npLeft, xbIxNode *npRight, xbInt16 iSlotNo, char *cpKeyBuf, xbUInt32 uiPtr ); + xbInt16 UpdateTagKey( char cAction, void *vpTag, xbUInt32 ulRecNo = 0 ); + xbInt16 WriteHeadBlock( xbInt16 iOption ); + + private: + xbInt16 AddKeyNewRoot( xbNdxTag *npTag, xbIxNode *npLeft, xbIxNode *npRight ); + void AppendNodeChain( void *vpTag, xbIxNode *npNode ); + xbInt16 GetDbfPtr( void *vpTag, xbInt16 iKeyNo, xbIxNode *npNode, xbUInt32 &ulDbfPtr ) const; + xbInt16 GetKeyPtr( void *vpTag, xbInt16 iKeyNo, xbIxNode *npNode, xbUInt32 &ulKeyPtr ) const; + xbInt16 GetLastKeyForBlockNo( void *vpTag, xbUInt32 ulBlockNo, char *cpKeyBuf ); + xbBool IsLeaf( void *vpTag, xbIxNode *npNode ) const; + xbInt16 KeySetPosAdd( xbNdxTag *npTag, xbUInt32 ulAddKeyRecNo ); + xbInt16 KeySetPosDel( xbNdxTag *npTag ); + // void SetCurNode( void *vpTag, xbIxNode *np ); + xbNdxTag *ndxTag; +}; + +#endif /* XB_NDX_SUPPORT */ + + +#ifdef XB_MDX_SUPPORT +//#define XB_MDX_BLOCK_SIZE 1024 + +struct XBDLLEXPORT xbMdxTag { + + // next 7 fields comprise the tag table entry + xbUInt32 ulTagHdrPageNo; // 512 byte page number, NOT block number + char cTagName[11]; + char cKeyFmt; // always 0x10 w/ DBase V7 + char cLeftChild; // cFwdTagThread + char cRightChild; // cFwdTagThread2 + char cParent; // cBwdTagThread + char c2; + char cKeyType; // C,D,N + + xbUInt32 ulRootPage; // 512 byte page number, NOT block number + xbUInt32 ulTagSize; // Number of 512 byte pages allocated to the tag. Tag size of two is a single 1024 block + + char cKeyFmt2; // 0x10 - base + // 0x08 - descending + // 0x40 - unique + char cKeyType2; + // one unused byte fits here + + char cTag11; // dbase sets to 0x1B + + xbInt16 iKeyLen; + xbInt16 iKeysPerBlock; + xbInt16 iSecKeyType; + xbInt16 iKeyItemLen; // iKeyLen + 4 + + char cSerialNo; // Increments +1 for each tag update + char cUnique; + xbString *sKeyExp; // Key expression + + char cHasFilter; // 0x00 or 0x01 + char cHasKeys; // 0x00 or 0x01 + + xbUInt32 ulLeftChild; // dbase 7 sets this to the root page on tag creation + xbUInt32 ulRightChild; // dbase 7 sets this to the root page on tag creation + + char cTagYY; + char cTagMM; + char cTagDD; + + char cKeyFmt3; // dbase 7 sets this 0x01 if discreet field or 0x00 if calculated or combination field key expression on tag creation + + xbString *sFiltExp; // Filter expression + + time_t tNodeChainTs; + xbIxNode *npNodeChain; + xbIxNode *npCurNode; + xbExp *exp; // pointer to expression for expression based tags + xbExp *filter; // pointer to expression for index filter + + char *cpKeyBuf; // key buffer + char *cpKeyBuf2; // key buffer +// xbBool iKeyUpdated; // key updated? set in method CreateKey, checked in AddKey and DeleteKey routines + // 0 - no update + // 1 - Add + // 2 - Update + // 3 - Delete +// xbBool bKeyFiltered; // key filtered? True if included key, False if excluded key. Set in method CreateKey, checked in AddKey and DeleteKey routines + xbString *sTagName; // string tag name + + + xbMdxTag *next; + xbBool bFoundSts; // key found? used to determine if new key should be added in XB_EMULATE_DBASE mode in AddKey + + + xbInt16 iKeySts; // key updated? set in method CreateKey, checked in AddKey and DeleteKey routines + // old key filtered new key filtered iKeySts + // Y Y XB_UPD_KEY 2 - update key if changed (delete and add) + // Y N XB_DEL_KEY 3 - delete key + // N Y XB_ADD_KEY 1 - add key + // N N 0 - no update + + +}; + + +class XBDLLEXPORT xbIxMdx : public xbIx { + public: + xbIxMdx( xbDbf * d ); + ~xbIxMdx(); + xbInt16 CheckTagIntegrity( void *vpTag, xbInt16 iOpt ); + xbInt16 CreateTag( const xbString &sName, const xbString &sKey, const xbString &sFilter, xbInt16 iDescending, xbInt16 iUnique, xbInt16 iOverlay, void **vpTag ); +// xbInt16 DeleteTag( void *vpTag ); + xbInt16 FindKey( void *vpTag, const void *vKey, xbInt32 lKeyLen, xbInt16 iRetrieveSw ); + xbInt16 FindKey( void *vpTag, xbDouble dKey, xbInt16 iRetrieveSw ); + xbInt16 GetFirstKey( void *vpTag, xbInt16 lRetrieveSw ); + xbString &GetKeyExpression( const void *vpTag ) const; + xbString &GetKeyFilter( const void *vpTag ) const; + char GetKeyType( const void *vpTag ) const; + xbInt16 GetLastKey( void *vpTag, xbInt16 lRetrieveSw ); + xbInt16 GetNextKey( void *vpTag, xbInt16 lRetrieveSw ); + xbInt16 GetPrevKey( void *vpTag, xbInt16 lRetrieveSw ); + xbBool GetReuseEmptyNodesSw() const; + xbBool GetSortOrder( void *vpTag ) const; + void *GetTag( xbInt16 iTagNo ) const; + void *GetTag( xbString &sTagName ) const; + xbInt16 GetTagCount() const; + + xbString &GetTagName( void *vpTag ) const; + const char * GetTagName( void *vpTag, xbInt16 iOpt ) const; + void GetTagName( void *vpTag, xbString &sTagName ); + + xbInt16 GetUnique( void *vpTag ) const; + xbInt16 Reindex( void **vpTag ); + xbInt16 SetCurTag( xbInt16 iTagNo ); + xbInt16 SetCurTag( xbString &sTagName ); + void SetReuseEmptyNodesSw( xbBool bReuse ); + + void TestStub( char *s, void *vpTag ); + + protected: + friend class xbDbf; + + xbInt16 AddKey( void *vpTag, xbUInt32 ulRecNo ); + xbIxNode *AllocateIxNode( xbMdxTag * mpTag, xbUInt32 ulBufSize, xbUInt32 ulBlock2 ); + xbInt16 CheckForDupKey( void *vpTag ); + xbInt16 Close(); + xbInt16 CreateKey( void * vpTag, xbInt16 iOpt ); + xbInt16 DeleteFromNode( void *vpTag, xbIxNode * npNode, xbInt16 iSlotNo ); + xbInt16 DeleteKey( void *vpTag ); + + xbInt16 DeleteTag( void *vpTag ); + + xbInt16 FindKeyForCurRec( void *vpTag ); + xbInt16 GetKeySts( void *vpTag ) const; + xbInt16 GetLastKey( xbUInt32 ulBlockNo, void *vpTag, xbInt16 lRetrieveSw ); + void *GetTagTblPtr() const; + void Init( xbInt16 iOpt = 0 ); + xbInt16 InsertNodeI( void *vpTag, xbIxNode *npNode, xbInt16 iSlotNo, xbUInt32 uiPtr ); + xbInt16 InsertNodeL( void *vpTag, xbIxNode *npNode, xbInt16 iSlotNo, char *cpKeyBuf, xbUInt32 uiPtr ); + xbInt16 KeyExists( void * ); + xbInt16 LoadTagTable(); + xbInt16 ReadHeadBlock(xbInt16 iOpt); // read the header node of the disk file + xbInt16 SplitNodeI( void *vpTag, xbIxNode * npLeft, xbIxNode *npRight, xbInt16 iSlotNo, xbUInt32 uiPtr ); + xbInt16 SplitNodeL( void *vpTag, xbIxNode * npLeft, xbIxNode *npRight, xbInt16 iSlotNo, char *cpKeyBuf, xbUInt32 uiPtr ); + xbInt16 UpdateTagKey( char cAction, void *vpTag, xbUInt32 ulRecNo = 0 ); + xbInt16 WriteHeadBlock( xbInt16 iOption ); + + #ifdef XB_DEBUG_SUPPORT + xbInt16 DumpTagBlocks( xbInt16 iOpt = 1, void *vpTag = NULL ); + xbInt16 DumpFreeBlocks( xbInt16 iOpt = 0 ); + xbInt16 DumpHeader( xbInt16 iOpt = 0, xbInt16 iFmtOpt = 0 ); + xbInt16 DumpIxForTag( void *vpTag, xbInt16 iOutputOpt ); + void DumpIxNodeChain( void *vpTag, xbInt16 iOutputOpt ) const; + #endif + + private: + xbInt16 AddKeyNewRoot( xbMdxTag *npTag, xbIxNode *npLeft, xbIxNode *npRight ); + void AppendNodeChain( void *vpTag, xbIxNode *npNode ); + xbUInt32 BlockToPage( xbUInt32 ulBlockNo ); + void CalcBtreePointers(); + char CalcTagKeyFmt( xbExp &exp ); + char CalcTagFwdThread1(); + char CalcTagFwdThread2(); + xbMdxTag *ClearTagTable(); + xbInt16 DumpBlock( xbInt16 iOpt, xbUInt32 ulBlockNo, xbMdxTag * mpTag ); + xbInt16 GetDbfPtr( void *vpTag, xbInt16 iKeyNo, xbIxNode *npNode, xbUInt32 &ulDbfPtr ) const; + xbInt16 GetKeyPtr( void *vpTag, xbInt16 iKeyNo, xbIxNode *npNode, xbUInt32 &ulKeyPtr ) const; + xbInt16 GetLastKeyForBlockNo( void *vpTag, xbUInt32 ulBlockNo, char *cpKeyBuf ); + xbInt16 HarvestEmptyNode( xbMdxTag *mpTag, xbIxNode *npNode, xbInt16 iOpt, xbBool bHarvestRoot = xbFalse ); + xbInt16 HarvestTagNodes( xbMdxTag *mpTag, xbBool bRecycleRoot = xbFalse ); + xbBool IsLeaf( void *vpTag, xbIxNode *npNode ) const; + xbInt16 KeySetPosAdd( xbMdxTag *mpTag, xbUInt32 ulAddKeyRecNo ); + xbInt16 KeySetPosDel( xbMdxTag *mpTag ); + xbInt16 LoadTagDetail( xbInt16 iOption, xbMdxTag *tte ); + xbUInt32 PageToBlock( xbUInt32 ulPageNo ); + xbInt16 TagSerialNo( xbInt16 iOption, xbMdxTag *mpTag ); + xbInt16 UpdateTagSize( xbMdxTag *mpTag, xbUInt32 ulTagSz ); + + #ifdef XB_DEBUG_SUPPORT + xbInt16 PrintKey( void *vpTag, xbIxNode *npNode, xbInt16 iKeyNo, xbInt16 iDepth, char cType, xbInt16 iOutputOpt ); + #endif + + // MDX File Header Fields + char cVersion; + char cCreateYY; + char cCreateMM; + char cCreateDD; + xbString sFileName; + xbInt16 iBlockFactor; // 1-32 #of 512 byte segments in a block + + // use file version + // xbInt16 iBlockSize; // Stored at the xbFile level + + char cProdIxFlag; + char cTagEntryCnt; + xbInt16 iTagLen; + xbInt16 iTagUseCnt; + char cNextTag; // byte 28 +1 + char c1B; // always 0x1B + xbUInt32 ulPageCnt; // number of 512 byte pages in the mdx file + xbUInt32 ulFirstFreePage; // page number corresponding to the next free block + xbUInt32 ulNoOfBlockAvail; // might be improperly named?? not sure how it is used + char cUpdateYY; + char cUpdateMM; + char cUpdateDD; + // end of MDX Header fields + + + + xbMdxTag *mdxTagTbl; + + xbBool bReuseEmptyNodes; // Reuese empty MDX nodes when all keys deleted? + // DBase 7.x and MS ODBC drivers do not reuse empty nodes, leaves them stranded in the file + // Codebase 6.x reuses empty nodes. + // Setting this to True will reuse empty nodes in the same manner Codebase 6.x reuses them. + +}; +#endif /* XB_MDX_SUPPORT */ + + +} /* namespace xb */ +#endif /* XB_INDEX_SUPPORT */ +#endif /* __XB_INDEX_H__ */ diff --git a/src/include/xblnklst.h b/src/include/xblnklst.h new file mode 100755 index 0000000..eaec587 --- /dev/null +++ b/src/include/xblnklst.h @@ -0,0 +1,257 @@ +/* xblnklst.h + +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 + +*/ + + +#ifndef __XB_XBLNKLST_H__ +#define __XB_XBLNKLST_H__ + +#ifdef XB_LINKLIST_SUPPORT + +namespace xb{ + + +template +class XBDLLEXPORT xbLinkList { + public: + xbLinkList(); + ~xbLinkList(); + + xbLinkListNode *GetHeadNode() const; + xbLinkListNode *GetEndNode() const; + xbLinkListNode *GetNodeForNo( xbUInt32 ulNodeNo ) const; + + void Clear(); + xbUInt32 GetNodeCnt () const; + xbInt16 InsertAtEnd ( const xbNodeType &xbLLN ); + xbInt16 InsertAtFront ( const xbNodeType &xbLLN ); + xbInt16 RemoveByVal ( const xbNodeType &xbLLN ); + xbInt16 RemoveFromEnd (); + xbInt16 RemoveFromEnd ( xbNodeType &xbLLN ); + xbInt16 RemoveFromFront( xbNodeType &xbLLN ); + xbInt16 SearchFor ( const xbNodeType &xbLLN ); + + private: + xbUInt32 ulNodeCnt; + xbLinkListNode *llStartPtr; + xbLinkListNode *llEndPtr; +}; + +template +xbLinkList::xbLinkList(){ + ulNodeCnt = 0; + llStartPtr = NULL; + llEndPtr = NULL; +} + +template +xbLinkList::~xbLinkList(){ + Clear(); +} + +template +void xbLinkList::Clear(){ + xbLinkListNode *cPtr = llStartPtr, *tPtr; + for( xbUInt32 i = 0; i < ulNodeCnt; i++ ){ + tPtr = cPtr; + cPtr = cPtr->GetNextNode(); + delete tPtr; + } + ulNodeCnt = 0; + llStartPtr = NULL; + llEndPtr = NULL; +} + + +template +xbLinkListNode *xbLinkList::GetHeadNode() const{ + return llStartPtr; +} + +template +xbLinkListNode *xbLinkList::GetEndNode() const{ + return llEndPtr; +} + + +template +xbLinkListNode *xbLinkList::GetNodeForNo( xbUInt32 ulNo ) const{ + + xbLinkListNode *cPtr = llStartPtr; + xbUInt32 i; + for( i = 0; i < ulNo && i < ulNodeCnt; i++ ) + cPtr = cPtr->GetNextNode(); + + if( i == ulNo ) + return cPtr; + else + return 0; +} + + + +template +xbUInt32 xbLinkList::GetNodeCnt() const{ + return ulNodeCnt; +} + +template +xbInt16 xbLinkList::InsertAtFront( const xbNodeType & ntKey ){ + + xbLinkListNode *p = new xbLinkListNode( ntKey ); + if( p == 0 ) + return XB_NO_MEMORY; + + if( ulNodeCnt > 0 ){ + llStartPtr->SetPrevNode( p ); + p->SetNextNode( llStartPtr ); + } else { + llEndPtr = p; + } + + llStartPtr = p; + ulNodeCnt++; + return XB_NO_ERROR; +} + +template +xbInt16 xbLinkList::InsertAtEnd( const xbNodeType & ntKey ){ + + xbLinkListNode *p = new xbLinkListNode( ntKey ); + if( p == 0 ) + return XB_NO_MEMORY; + + if( ulNodeCnt > 0 ){ + llEndPtr->SetNextNode( p ); + p->SetPrevNode( llEndPtr ); + llEndPtr = p; + } else { + llStartPtr = p; + } + + llEndPtr = p; + ulNodeCnt++; + return XB_NO_ERROR; +} + + + +template +xbInt16 xbLinkList::RemoveByVal( const xbNodeType & ntKey ){ + // Remove the first instance of ntKey from the node chain + xbLinkListNode *currPtr = llStartPtr; + xbLinkListNode *prevPtr = NULL; + + for( xbUInt32 i = 0; i < ulNodeCnt; i++ ){ + if( currPtr->GetKey() == ntKey ){ + if( prevPtr == NULL ){ //then this is the first node + llStartPtr = currPtr->GetNextNode(); + if( llStartPtr ) // if more than one link in the linked list + llStartPtr->SetPrevNode( NULL ); + delete currPtr; + ulNodeCnt--; + return i + 1; + } + else { + prevPtr->SetNextNode( currPtr->GetNextNode()); + if( currPtr->GetNextNode()) + currPtr->GetNextNode()->SetPrevNode( prevPtr ); + delete currPtr; + ulNodeCnt--; + return i + 1; + } + } + prevPtr = currPtr; + currPtr = currPtr->GetNextNode(); + } + return XB_NOT_FOUND; +} + + +template +xbInt16 xbLinkList::RemoveFromFront( xbNodeType & ntKey ){ + + if( ulNodeCnt <= 0 ) + return XB_INVALID_NODELINK; + xbLinkListNode *p = llStartPtr; + llStartPtr = p->GetNextNode(); + if( llStartPtr ) + llStartPtr->SetPrevNode( NULL ); + ntKey = p->GetKey(); + delete p; + ulNodeCnt--; + return XB_NO_ERROR; +} + + +template +xbInt16 xbLinkList::RemoveFromEnd( xbNodeType & ntKey ){ + + if( ulNodeCnt <= 0 ) + return XB_INVALID_NODELINK; + xbLinkListNode *p = llEndPtr; + if( p->GetPrevNode()){ + llEndPtr = p->GetPrevNode(); + llEndPtr->SetNextNode( NULL ); + } else { + // there are no more nodes + llStartPtr = NULL; + llEndPtr = NULL; + } + ntKey = p->GetKey(); + delete p; + ulNodeCnt--; + return XB_NO_ERROR; +} + + +template +xbInt16 xbLinkList::RemoveFromEnd(){ + + if( ulNodeCnt <= 0 ) + return XB_INVALID_NODELINK; + xbLinkListNode *p = llEndPtr; + if( p->GetPrevNode()){ + llEndPtr = p->GetPrevNode(); + llEndPtr->SetNextNode( NULL ); + } else { + // there are no more nodes + llStartPtr = NULL; + llEndPtr = NULL; + } + delete p->GetKey(); + delete p; + ulNodeCnt--; + return XB_NO_ERROR; +} + + +template +xbInt16 xbLinkList::SearchFor( const xbNodeType & ntKey ){ + + xbLinkListNode *cPtr = llStartPtr; + for( xbUInt32 i = 0; i < ulNodeCnt; i++ ){ + if( cPtr->GetKey() == ntKey ) + return i+1; + cPtr = cPtr->GetNextNode(); + } + return XB_NO_ERROR; + +} +} // namespace + +#endif // XB_LINKLIST_SUPPORT +#endif // XB_XBLNKLST_H__ + + diff --git a/src/include/xblnklstord.h b/src/include/xblnklstord.h new file mode 100755 index 0000000..d319c31 --- /dev/null +++ b/src/include/xblnklstord.h @@ -0,0 +1,367 @@ +/* xblnklstord.h + +XBase64 Software Library + +Copyright (c) 1997,2003,2014,2019,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 + +*/ + + +// Ordered link list + + + +#ifndef __XB_XBLNKLSTORD_H__ +#define __XB_XBLNKLSTORD_H__ + +#ifdef XB_LINKLIST_SUPPORT + + +namespace xb{ + + +template +class XBDLLEXPORT xbLinkListOrd { + public: + xbLinkListOrd(); + ~xbLinkListOrd(); + + void Clear(); + xbLinkListNode *GetHeadNode() const; + xbLinkListNode *GetEndNode() const; + xbLinkListNode *GetNodeForKey( const xbString &sKey ) const; + + xbInt16 GetDataForKey ( const xbNodeType &ntKey, xbString &sData ); + + xbBool GetDupKeys (); + + xbUInt32 GetNodeCnt () const; + xbUInt32 GetNodeCnt ( const xbString &sNodeKey ) const; + xbInt16 InsertKey ( const xbNodeType &ntKey ); + xbInt16 InsertKey ( const xbNodeType &ntKey, const xbString &sData ); + xbInt16 InsertKey ( const xbNodeType &ntKey, xbUInt32 ulData ); + + xbBool KeyExists ( const xbNodeType &ntKey ) const; + xbInt16 RemoveKey ( const xbNodeType &ntKey ); + xbInt16 RemoveFromEnd ( xbNodeType &ntKey ); + xbInt16 RemoveFromFront( xbNodeType &ntKey ); + xbInt16 RemoveFromFront(); + void SetDupKeys ( xbBool bAllowDupKeys ); + xbInt16 UpdateForKey ( const xbNodeType &ntKey, const xbString &sData ); + + + private: + xbUInt32 ulNodeCnt; + xbBool bAllowDupKeys; + xbLinkListNode *llStartPtr; + xbLinkListNode *llEndPtr; + +}; + + +template +xbLinkListOrd::xbLinkListOrd(){ + bAllowDupKeys = xbTrue; // default setting - allow duplicate keys + ulNodeCnt = 0; + llStartPtr = NULL; + llEndPtr = NULL; +} + +template +xbLinkListOrd::~xbLinkListOrd(){ + Clear(); +} + +template +void xbLinkListOrd::Clear(){ + xbLinkListNode *cPtr = llStartPtr, *tPtr; + for( xbUInt32 i = 0; i < ulNodeCnt; i++ ){ + tPtr = cPtr; + cPtr = cPtr->GetNextNode(); + + // next line might cause seg faults + // delete tPtr->GetData(); + + delete tPtr; + } + ulNodeCnt = 0; + llStartPtr = NULL; + llEndPtr = NULL; +} + +template +xbLinkListNode * xbLinkListOrd::GetHeadNode() const{ + return llStartPtr; +} + +template +xbLinkListNode * xbLinkListOrd::GetEndNode() const{ + return llEndPtr; +} + +template +xbUInt32 xbLinkListOrd::GetNodeCnt() const{ + return ulNodeCnt; +} + +template +xbUInt32 xbLinkListOrd::GetNodeCnt( const xbString &sNodeKey ) const{ + + // won't work if nodekey is not a string + xbLinkListNode *currPtr = llStartPtr; + // skip to sNodeKey + while( currPtr && ( sNodeKey > currPtr->GetKey())) { + currPtr = currPtr->GetNextNode(); + } + // count entries for sNodeKey + xbInt16 iKeyCnt = 0; + while( currPtr && ( sNodeKey == currPtr->GetKey())) { + iKeyCnt++; + currPtr = currPtr->GetNextNode(); + } + return iKeyCnt; +} + + +template +xbInt16 xbLinkListOrd::InsertKey( const xbNodeType &ntKey ){ + xbString s; + return InsertKey( ntKey, s ); +} + + + + +template +xbInt16 xbLinkListOrd::InsertKey( const xbNodeType &ntKey, xbUInt32 ul ){ + + xbString s; + s.Sprintf( "%ld", ul ); + return InsertKey( ntKey, s ); +} + + +template +xbInt16 xbLinkListOrd::InsertKey( const xbNodeType &ntKey, const xbString &sData ){ + + xbLinkListNode *p = new xbLinkListNode( ntKey, sData ); + if( p == 0 ) + return XB_NO_MEMORY; + + if( ulNodeCnt > 0 ){ + xbLinkListNode *currPtr = llStartPtr; + xbLinkListNode *prevPtr = NULL; + + // find location in the chain + while( currPtr && ntKey > currPtr->GetKey() ){ + prevPtr = currPtr; + currPtr = currPtr->GetNextNode(); + } + if( currPtr && ntKey == currPtr->GetKey() && bAllowDupKeys == 0 ){ + delete p; + return XB_KEY_NOT_UNIQUE; + } + + if( currPtr == NULL ){ + // std::cout << "at the end of the chain\n"; + llEndPtr = p; + prevPtr->SetNextNode( p ); + p->SetPrevNode( prevPtr ); + + } else if( currPtr->GetPrevNode() == NULL ){ + // std::cout << "at the beginning of the chain\n"; + p->SetNextNode( llStartPtr ); + llStartPtr->SetPrevNode( p ); + llStartPtr = p; + + } else { + // std::cout << "in the middle of the chain\n"; + p->SetNextNode( currPtr ); + p->SetPrevNode( currPtr->GetPrevNode()); + currPtr->SetPrevNode( p ); + prevPtr->SetNextNode( p ); + } + } else { + // std::cout << "first addition to the chain\n"; + llStartPtr = p; + llEndPtr = p; + } + ulNodeCnt++; + return XB_NO_ERROR; +} + +template +xbInt16 xbLinkListOrd::RemoveKey( const xbNodeType &ntKey ){ + // Remove the first instance of ntKey from the node chain + xbLinkListNode *currPtr = llStartPtr; + xbLinkListNode *prevPtr = NULL; + + while( currPtr && ntKey > currPtr->GetKey() ){ + prevPtr = currPtr; + currPtr = currPtr->GetNextNode(); + } + + if( currPtr && ntKey == currPtr->GetKey()){ +// ntKey = currPtr->GetKey(); + if( prevPtr == NULL ){ // this is the first node + llStartPtr = currPtr->GetNextNode(); + // next line fails + if( llStartPtr ){ + llStartPtr->SetPrevNode( NULL ); + } + delete currPtr; + ulNodeCnt--; + return XB_NO_ERROR; + } else if( currPtr->GetNextNode() == NULL ){ // this is the last node + llEndPtr = prevPtr; + prevPtr->SetNextNode( NULL ); + delete currPtr; + ulNodeCnt--; + return XB_NO_ERROR; + } else { + + prevPtr->SetNextNode( currPtr->GetNextNode()); + currPtr->GetNextNode()->SetPrevNode( prevPtr ); + delete currPtr; + ulNodeCnt--; + return XB_NO_ERROR; + } + } else { + return XB_NOT_FOUND; + } +} + +template +xbInt16 xbLinkListOrd::RemoveFromFront( xbNodeType &ntKey ){ + + if( ulNodeCnt <= 0 ) + return XB_INVALID_NODELINK; + xbLinkListNode *p = llStartPtr; + llStartPtr = p->GetNextNode(); + if( llStartPtr ) + llStartPtr->SetPrevNode( NULL ); + ntKey = p->GetKey(); + delete p; + ulNodeCnt--; + return XB_NO_ERROR; +} + +template +xbInt16 xbLinkListOrd::RemoveFromFront(){ + + if( ulNodeCnt <= 0 ) + return XB_INVALID_NODELINK; + xbLinkListNode *p = llStartPtr; + llStartPtr = p->GetNextNode(); + if( llStartPtr ) + llStartPtr->SetPrevNode( NULL ); + + if( p->GetKey()) + delete p->GetKey(); + + delete p; + ulNodeCnt--; + + return XB_NO_ERROR; +} + + +template +xbInt16 xbLinkListOrd::RemoveFromEnd( xbNodeType &ntKey ){ + + if( ulNodeCnt <= 0 ) + return XB_INVALID_NODELINK; + xbLinkListNode *p = llEndPtr; + llEndPtr = p->GetPrevNode(); + llEndPtr->SetNextNode( NULL ); + ntKey = p->GetKey(); + delete p; + ulNodeCnt--; + return XB_NO_ERROR; +} + +template +xbBool xbLinkListOrd::GetDupKeys(){ + return bAllowDupKeys; +} + +template +void xbLinkListOrd::SetDupKeys( xbBool bAllowDupKeys ){ + this->bAllowDupKeys = bAllowDupKeys; +} + + +template +xbBool xbLinkListOrd::KeyExists( const xbNodeType &ntKey ) const { + + xbLinkListNode *currPtr = llStartPtr; + while( currPtr && ntKey > currPtr->GetKey() ){ + currPtr = currPtr->GetNextNode(); + } + if( currPtr && ntKey == currPtr->GetKey()){ + return xbTrue; + } else { + return xbFalse; + } +} + + +template +xbInt16 xbLinkListOrd::GetDataForKey( const xbNodeType &ntKey, xbString &sData ){ + + xbLinkListNode *currPtr = llStartPtr; + while( currPtr && ntKey > currPtr->GetKey() ){ + currPtr = currPtr->GetNextNode(); + } + + if( currPtr && ntKey == currPtr->GetKey()){ + sData = currPtr->GetData(); + return XB_NO_ERROR; + } else { + return XB_NOT_FOUND; + } +} + + +template +xbInt16 xbLinkListOrd::UpdateForKey( const xbNodeType &ntKey, const xbString &sData ){ + + if( ulNodeCnt == 0 ) + return InsertKey( ntKey, sData ); + xbLinkListNode * currPtr = llStartPtr; + xbLinkListNode * prevPtr = NULL; + while( currPtr && ntKey > currPtr->GetKey() ) { + prevPtr = currPtr; + currPtr = currPtr->GetNextNode(); + } + + if( currPtr && ntKey == currPtr->GetKey() ) { + xbLinkListNode *p = new xbLinkListNode( ntKey, sData ); + if( prevPtr ) + prevPtr->SetNextNode( p ); + else + llStartPtr = p; + p->SetNextNode( currPtr->GetNextNode() ); + p->SetPrevNode( currPtr->GetPrevNode() ); + delete currPtr; + return XB_NO_ERROR; + } + + return InsertKey( ntKey, sData ); + +// return 0; +} + +} // namespace + +#endif // XB_LINKLIST_SUPPORT +#endif // XB_XBLNKLSTORD_H__ + + diff --git a/src/include/xblnknod.h b/src/include/xblnknod.h new file mode 100755 index 0000000..ef45be8 --- /dev/null +++ b/src/include/xblnknod.h @@ -0,0 +1,94 @@ +/* xblnknod.h + +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 + +*/ + + +#ifndef __XB_XBLNKNOD_H__ +#define __XB_XBLNKNOD_H__ + + + +#ifdef XB_LINKLIST_SUPPORT + +namespace xb{ + +template +class XBDLLEXPORT xbLinkListNode { + public: + xbLinkListNode( const xbNodeType & ); + xbLinkListNode( const xbNodeType &, const xbString & ); + xbNodeType GetKey() const; + xbString &GetData(); + xbLinkListNode *GetNextNode() const; + xbLinkListNode *GetPrevNode() const; + void SetNextNode( xbLinkListNode *llNext ); + void SetPrevNode( xbLinkListNode *llPrev ); + + private: + xbNodeType ntKey; + xbString sData; + xbLinkListNode *llNext; + xbLinkListNode *llPrev; +}; + + template + xbLinkListNode::xbLinkListNode( const xbNodeType &key ){ + ntKey = key; + llNext = NULL; + llPrev = NULL; + } + + template + xbLinkListNode::xbLinkListNode( const xbNodeType &key, const xbString &s ){ + ntKey = key; + sData = s; + llNext = NULL; + llPrev = NULL; + } + + template + xbNodeType xbLinkListNode::GetKey() const { + return ntKey; + } + + template + xbString &xbLinkListNode::GetData(){ + return sData; + } + + template + xbLinkListNode *xbLinkListNode::GetNextNode() const { + return llNext; + } + + template + xbLinkListNode *xbLinkListNode::GetPrevNode() const { + return llPrev; + } + + template + void xbLinkListNode::SetNextNode( xbLinkListNode *lln ){ + llNext = lln; + } + + template + void xbLinkListNode::SetPrevNode( xbLinkListNode *llp ){ + llPrev = llp; + } + +} // namespace +#endif // XB_LINKLIST_SUPPORT +#endif // XB_XBLNKNOD_H__ + + diff --git a/src/include/xblog.h b/src/include/xblog.h new file mode 100755 index 0000000..e78f476 --- /dev/null +++ b/src/include/xblog.h @@ -0,0 +1,66 @@ +/* xblog.h + +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 + +*/ + +#ifndef __XB_XBLOG_H__ +#define __XB_XBLOG_H__ + +#ifdef CMAKE_COMPILER_IS_GNUCC +#pragma interface +#endif + +namespace xb{ + +#ifdef XB_LOGGING_SUPPORT + +//! @brief xbLog class. +/*! +The xbLog class handles message logging functions. + +If logging is enabled in an application, error messages are printed +in the log file. + +Once a logfile reaches a predefined size, the logfile is closed, renamed +and opened. + +The logging functions can be accessed through the xbXBase class. +Each application has one of those. +*/ + +class XBDLLEXPORT xbLog : public xbFile { + public: + xbLog(); + xbLog( const xbString &sLogFileName ); + ~xbLog(); + + xbInt16 LogClose (); + xbInt16 LogGetStatus (); + xbInt16 LogOpen (); + void LogSetStatus ( xbBool bLogStatus ); + void LogSetLogSize( size_t lSize ); // { LogSize = size; } + xbInt16 LogWrite ( const xbString &LogEntry, xbInt16 iOutputOption = 0 ); + xbInt16 LogWriteBytes( xbUInt32 lByteCnt, const char *p ); + + private: + xbBool bLoggingStatus; // false = logging off + // true = logging on + size_t lLogSize; + xbInt16 iShareMode; +}; + +#endif // XB_LOGGING_SUPPORT +} // namespace +#endif // XB_XBLOG_H__ + + diff --git a/src/include/xbmemo.h b/src/include/xbmemo.h new file mode 100755 index 0000000..e86a65c --- /dev/null +++ b/src/include/xbmemo.h @@ -0,0 +1,223 @@ +/* xbmemo.h + +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 + +*/ + + +#ifndef __XB_XBMEMO_H__ +#define __XB_XBMEMO_H__ + + +// dont use "#pragma interface" with abstract classes +// #ifdef CMAKE_COMPILER_IS_GNUCC +// #pragma interface +// #endif + +#ifdef XB_MEMO_SUPPORT + + + + +namespace xb{ + +//! @brief Base class for memo files. +/*! + +The xbMemo class is used as a base class for accessing memo files. + +Any DBF file which has any memo fields, will also have a DBT file with the same name. +Memo fields are stored in the DBT file in 512 or 1024 byte sized blocks. + +Calls to the memo routines to read and access memo fields are supported from within +the dbf class via the GetMemoField and UpdateMemoField routines. +From an application program point of view, the application program does not need to +be concerned with the memo classes.
+ +The main difference between the version 3 and version 4 memo files is that +the version 4 files will reclaim and reuse blocks if available. The version 3 +memo file logic does not.
+ +If you are adding a new style memo table to the logic, you can create a new class +modeled after xbMemoDbt3 or xbDbtMemo4, using the xbMemo class as a base class. + +*/ + +class XBDLLEXPORT xbMemo : public xbFile { + public: + + xbMemo( xbDbf *dbf, xbString const &sFileName ); + + /* virtual methods */ + virtual ~xbMemo(); + virtual xbInt16 Abort () = 0; + virtual xbInt16 CloseMemoFile (); + virtual xbInt16 Commit () = 0; + + #ifdef XB_LOCKING_SUPPORT + virtual xbInt16 LockMemo ( xbInt16 iLockFunction ); + virtual xbBool GetMemoLocked () const; + #endif + + /* pure virtual methods */ + virtual xbInt16 CreateMemoFile () = 0; + + virtual xbInt16 GetMemoField ( xbInt16 iFieldNo, xbString &sMemoData ) = 0; + virtual xbInt16 GetMemoFieldLen( xbInt16 iFieldNo, xbUInt32 &ulMemoFieldLen ) = 0; + virtual xbInt16 OpenMemoFile () = 0; + virtual xbInt16 GetMemoFileType(); + virtual xbInt16 PackMemo ( void (*memoStatusFunc)(xbUInt32 ulItemNum, xbUInt32 ulNumItems)) = 0; + virtual xbInt16 UpdateMemoField( xbInt16 iFieldNo, const xbString &sMemoData ) = 0; + + #ifdef XB_DEBUG_SUPPORT + virtual xbInt16 DumpMemoFreeChain() = 0; + virtual xbInt16 DumpMemoHeader () = 0; + #endif + + // *********** FIXME ************* + // next two methods should be protected but are called in the xb_test_xxxx programs testing routines + // so for now, they are kept as public + virtual xbInt16 CalcLastDataBlock ( xbUInt32 &lLastDataBlock ); + virtual xbInt16 GetHdrNextBlock ( xbUInt32 &ulBlockNo ); + + protected: + friend class xbDbf; + + virtual xbInt16 ReadDbtHeader ( xbInt16 iOption ) = 0; + virtual xbInt16 UpdateHeaderName () = 0; + virtual xbInt16 UpdateHeadNextNode(); + virtual xbInt16 Zap () = 0; + + xbDbf *dbf; /* pointer to related dbf instance */ + char cVersion; /* byte 16 off the header block, 0x03 for V3, version IV - ? */ + xbInt16 iMemoFileType; /* 3 = version III, 4 = version IV */ + xbUInt32 ulHdrNextBlock; /* next available block , bytes 0-3 of the memo header */ + void * mbb; /* memo block buffer */ + + + private: + + #ifdef XB_LOCKING_SUPPORT + xbBool bFileLocked; /* memo file locked */ + #endif + +}; + + + +#ifdef XB_DBF3_SUPPORT +//! @brief Derived class for version 3 memo files. +/*! + +The xbMemoDbt3 class is derived from the xbMome class and is used for accessing the +older Version 3 memo files. + +The main difference between the version 3 and version 4 memo files is that +the version 4 files will reclaim and reuse blocks if available. The version 3 +memo file logic does not.
+ +*/ + + +class XBDLLEXPORT xbMemoDbt3 : public xbMemo { + public: + xbMemoDbt3( xbDbf *dbf, xbString const &sFileName ); + ~xbMemoDbt3(); + virtual xbInt16 Abort (); + virtual xbInt16 Commit (); + virtual xbInt16 CreateMemoFile (); + virtual xbInt16 GetMemoField ( xbInt16 iFieldNo, xbString &sMemoData ); + virtual xbInt16 GetMemoFieldLen( xbInt16 iFieldNo, xbUInt32 &ulMemoFieldLen ); + virtual xbInt16 OpenMemoFile (); + virtual xbInt16 PackMemo ( void (*memoStatusFunc) (xbUInt32 ulItemNum, xbUInt32 ulNumItems)); + virtual xbInt16 UpdateMemoField( xbInt16 iFieldNo, const xbString &sMemoData ); + + #ifdef XB_DEBUG_SUPPORT + virtual xbInt16 DumpMemoFreeChain(); + virtual xbInt16 DumpMemoHeader (); + #endif + + protected: + virtual xbInt16 ReadDbtHeader( xbInt16 iOption ); + virtual xbInt16 UpdateHeaderName(); + virtual xbInt16 Zap(); + + private: +}; +#endif + + +#ifdef XB_DBF4_SUPPORT + +//! @brief Derived class for version 4 memo files. +/*! + +The xbMemoDbt4 class is derived from the xbMeme class and is used for accessing the +current Version 4 memo files. + +The main difference between the version 3 and version 4 memo files is that +the version 4 files will reclaim and reuse blocks if available. The version 3 +memo file logic does not.
+*/ + +class XBDLLEXPORT xbMemoDbt4 : public xbMemo { + public: + xbMemoDbt4( xbDbf *dbf, xbString const &sFileName ); + ~xbMemoDbt4(); + virtual xbInt16 Abort (); + virtual xbInt16 Commit (); + virtual xbInt16 CreateMemoFile (); + + virtual xbInt16 GetMemoField ( xbInt16 iFieldNo, xbString &sMemoData ); + virtual xbInt16 GetMemoFieldLen ( xbInt16 iFieldNo, xbUInt32 &ulMemoFieldLen ); + virtual xbInt16 GetMemoFieldLen ( xbInt16 iFieldNo, xbUInt32 &ulMemoFieldLen, xbUInt32 &lBlockNo ); + virtual xbInt16 OpenMemoFile (); + virtual xbInt16 PackMemo ( void (*memoStatusFunc) (xbUInt32 ulItemNum, xbUInt32 ulNumItems)); + virtual xbInt16 UpdateMemoField ( xbInt16 iFieldNo, const xbString &sMemoData ); + + #ifdef XB_DEBUG_SUPPORT + virtual xbInt16 DumpMemoFreeChain (); + virtual xbInt16 DumpMemoHeader (); + virtual xbInt16 DumpMemoInternals (); + virtual xbInt16 ReadFreeBlockHeader( xbUInt32 ulBlockNo, xbUInt32 &ulNextBlock, xbUInt32 &ulFreeBlockCnt ); + #endif + + protected: + virtual xbInt16 FindBlockSetInChain( xbUInt32 ulBlocksNeeded, xbUInt32 &ulLastDataBlock, xbUInt32 &ulLocation, xbUInt32 &ulPrevNode, xbBool &bFound ); + virtual xbInt16 FreeMemoBlockChain( xbUInt32 ulBlockNo ); + virtual xbInt16 FreeMemoBlockChain( xbUInt32 ulBlockNo, xbUInt32 &ulLastDataBlock ); + virtual xbInt16 GetBlockSetFromChain( xbUInt32 ulBlocksNeeded, xbUInt32 ulLocation, xbUInt32 ulPrevNode ); + virtual xbInt16 ReadDbtHeader( xbInt16 iOption ); + virtual xbInt16 ReadBlockHeader( xbUInt32 ulBlockNo, xbInt16 iOption ); + virtual xbInt16 UpdateHeaderName (); + virtual xbInt16 WriteBlockHeader( xbUInt32 ulBlockNo, xbInt16 iOption ); + virtual xbInt16 Zap(); + + private: + xbString sDbfFileNameWoExt; + xbUInt32 ulNextFreeBlock; + xbUInt32 ulFreeBlockCnt; + xbInt16 iField1; + xbInt16 iStartPos; + xbUInt32 ulFieldLen; + + xbLinkList llOldBlocks; // list of previously used memo blocks for field, used by Commit() / Abort() + xbLinkList llNewBlocks; // list of newly updated memo blocks for field, used by Commit() / Abort() + +}; +#endif + + +} /* namespace xb */ +#endif /* XB_MEMO_SUPPORT */ +#endif /* __XB_MEMO_H__ */ + diff --git a/src/include/xbretcod.h b/src/include/xbretcod.h new file mode 100755 index 0000000..6b73527 --- /dev/null +++ b/src/include/xbretcod.h @@ -0,0 +1,94 @@ +/* xbretcod.h + +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 + +*/ + +#ifndef __XB_RETCODES_H__ +#define __XB_RETCODES_H__ + +/***********************************************/ +/* Return Codes and Error Messages */ + +namespace xb{ + + #define XB_NO_ERROR 0 // general + #define XB_NO_MEMORY -100 // general + #define XB_INVALID_OPTION -101 // general + #define XB_DUP_TABLE_OR_ALIAS -110 // table manager + #define XB_INVALID_NODELINK -120 // linklist + #define XB_KEY_NOT_UNIQUE -121 // linklist + + #define XB_FILE_EXISTS -200 // file + #define XB_ALREADY_OPEN -201 // file + #define XB_DBF_FILE_NOT_OPEN -202 // file + #define XB_FILE_NOT_FOUND -203 // file + #define XB_FILE_TYPE_NOT_SUPPORTED -204 // file + #define XB_RENAME_ERROR -205 // file + #define XB_INVALID_OBJECT -206 // file + #define XB_NOT_OPEN -207 // file + #define XB_NOT_FOUND -208 // file + #define XB_OPEN_ERROR -209 // file + #define XB_CLOSE_ERROR -210 // file + #define XB_SEEK_ERROR -211 // file + #define XB_READ_ERROR -212 // file + #define XB_WRITE_ERROR -213 // file + #define XB_EOF -214 // file + #define XB_BOF -215 // file + #define XB_INVALID_BLOCK_SIZE -216 // file + #define XB_INVALID_BLOCK_NO -217 // file + #define XB_INVALID_RECORD -218 // file + #define XB_DELETE_FAILED -219 // file + #define XB_INVALID_TABLE_NAME -220 // file + #define XB_EMPTY -221 // file + #define XB_LIMIT_REACHED -222 // file + + #define XB_INVALID_FIELD_TYPE -300 // field + #define XB_INVALID_FIELD_NO -301 // field + #define XB_INVALID_DATA -302 // field + #define XB_INVALID_FIELD_NAME -303 // field + #define XB_INVALID_MEMO_FIELD -304 // field + #define XB_INVALID_FIELD -305 // field + #define XB_INVALID_FIELD_LEN -306 // field + #define XB_INVALID_DATE -307 // date field + + #define XB_INVALID_LOCK_OPTION -400 // lock + #define XB_LOCK_FAILED -401 // lock + #define XB_TABLE_NOT_LOCKED -402 // lock - need table locked for operation + + #define XB_PARSE_ERROR -500 // expression + #define XB_INVALID_FUNCTION -501 // expression + #define XB_INVALID_PARM -502 // expression + #define XB_INCONSISTENT_PARM_LENS -503 // expression + #define XB_INCOMPATIBLE_OPERANDS -504 // expression + #define XB_UNBALANCED_PARENS -505 // expression + #define XB_UNBALANCED_QUOTES -506 // expression + #define XB_INVALID_EXPRESSION -507 // expression + + #define XB_INVALID_KEYNO -600 // index + #define XB_INVALID_INDEX -601 // index file error + #define XB_INVALID_TAG -602 // invalid index tag name, must be <= 10 bytes + #define XB_INVALID_PAGE -603 // invalid index page + + + #define XB_SYNTAX_ERROR -700 // sql syntax error + + + #define XB_MAX_ERROR_NO -999 + + +/* when updating this table, also need to update messages in xbssv.cpp */ + + +} /* namespace */ +#endif /* __XB_RETCODES_H__ */ + diff --git a/src/include/xbsql.h b/src/include/xbsql.h new file mode 100755 index 0000000..3869995 --- /dev/null +++ b/src/include/xbsql.h @@ -0,0 +1,69 @@ +/* xbsql.h + +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 manages a list of open tables, open indices are connected to the open tables + +*/ + + +#ifndef __XB_XBSQL_H__ +#define __XB_XBSQL_H__ + +#ifdef CMAKE_COMPILER_IS_GNUCC +#pragma interface +#endif + + +#ifdef XB_SQL_SUPPORT + +namespace xb{ + + +class XBDLLEXPORT xbSql : public xbSsv { + public: + // xbSql(); + xbSql( xbXBase *x ); + ~xbSql(); + + xbInt16 ExecuteNonQuery( const xbString &sCmdLine ); + xbXBase *GetXbasePtr() const; + + protected: + + private: + + xbInt16 SqlCreateTable( const xbString &sCmdLine ); + xbInt16 SqlCreateIndex( const xbString &sCmdLine ); + + xbInt16 SqlDropIndex( const xbString &sCmdLine ); + xbInt16 SqlDropTable( const xbString &sCmdLine ); + + +// xbInt16 SqlCreateView( const xbString &sCmdLine ); +// xbInt16 SqlDropView( const xbString &sCmdLine ); +// xbInt16 SqlUpdate( const xbString &sCmdLine ); +// xbInt16 SqlSelect( const xbString &sCmdLine ); + + void SqlHelp() const; + xbInt16 SqlInsert( const xbString &sCmLine ); + xbInt16 SqlSet( const xbString &sCmdLine ); + xbInt16 SqlUse( const xbString &sCmdLine ); + + xbXBase *xbase; + xbUda uda; + +}; + +} /* namespace xb */ +#endif /* XB_SQL_SUPPORT */ +#endif /* __XB_XBSQL_H__ */ \ No newline at end of file diff --git a/src/include/xbssv.h b/src/include/xbssv.h new file mode 100755 index 0000000..80da2d0 --- /dev/null +++ b/src/include/xbssv.h @@ -0,0 +1,177 @@ +/* xbssv.h + +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 + +*/ + + +#ifndef __XB_XBSSV_H__ +#define __XB_XBSSV_H__ + +#ifdef CMAKE_COMPILER_IS_GNUCC +#pragma interface +#endif + + +namespace xb{ + +class XBDLLEXPORT xbXBase; + +///@cond DOXYOFF +struct XBDLLEXPORT xbErrorMessage{ + xbInt16 iErrorNo; + const char *sErrorText; +}; +///@endcond DOXYOFF + + +//! @brief Class for handling shared system variables. +/*! +This class defines static variables which are referenced across class instances. + +This class is designed to have the variables set when the xbXBase class constructor is called +(xbXBase is a derived class). The xbXbase class is designed to be called first in an application +programs, and it is only called once. The static values in this class are typically initialized at +program startup and don't require additional updates. + +*/ + + +// By design, DBase allows mutliple records in a table all having the same key, but only one entry in a unique index +// XB_HALT_ON_DUP_KEY tells the library to not allow appending records which generate duplicate keys in a unique index +// +#if defined (XB_NDX_SUPPORT) || defined (XB_MDX_SUPPORT) +#define XB_HALT_ON_DUPKEY 0 +#define XB_EMULATE_DBASE 1 +#endif + + + +class XBDLLEXPORT xbSsv{ + public: + xbSsv(); + const static char *ErrorCodeText[]; + + void DisplayError ( xbInt16 ErrorCode ) const; + xbString& GetDefaultDateFormat () const; + xbString& GetDataDirectory () const; + xbInt16 GetEndianType () const; + + const char *GetErrorMessage ( xbInt16 ErrorCode ) const; + void SetDataDirectory ( const xbString &sDataDirectory ); + void SetDefaultDateFormat ( const xbString &sDefaultDateFormat ); + + + xbBool BitSet ( unsigned char c, xbInt16 iBitNo ) const; + #ifdef XB_DEBUG_SUPPORT + void BitDump ( unsigned char c ) const; + void BitDump ( char c ) const; + #endif + + xbBool GetDefaultAutoCommit () const; + void SetDefaultAutoCommit ( xbBool bDefaultAutoCommit ); + + xbString& GetDefaultLogDirectory () const; + xbString& GetDefaultLogFileName () const; + void SetDefaultLogDirectory ( const xbString &sDefaultLogDirectory ); + + xbBool GetMultiUser () const; + void SetMultiUser ( xbBool bMultiUser ); + + #if defined (XB_NDX_SUPPORT) || defined (XB_MDX_SUPPORT) + xbInt16 GetUniqueKeyOpt () const; + xbInt16 SetUniqueKeyOpt ( xbInt16 iUniqueKeyOpt ); + #endif + + #ifdef XB_LOCKING_SUPPORT + xbInt16 GetDefaultLockRetries () const; + void SetDefaultLockRetries ( xbInt16 iRetryCount ); + xbInt32 GetDefaultLockWait () const; + void SetDefaultLockWait ( xbInt32 lRetryWait ); + xbInt16 GetDefaultLockFlavor () const; + void SetDefaultLockFlavor ( xbInt16 iLockFlavor ); + xbBool GetDefaultAutoLock () const; + void SetDefaultAutoLock ( xbBool bAutoLock ); + void EnableDefaultAutoLock (); + void DisableDefaultAutoLock (); + #endif + + #ifdef XB_MDX_SUPPORT + xbInt16 GetCreateMdxBlockSize() const; + xbInt16 SetCreateMdxBlockSize( xbInt16 ulBlockSize ); + #endif + + protected: + + void SetEndianType (); + + static xbInt16 iEndianType; // B=Big Endian L=Little Endian + static xbString sNullString; // Null String + + + private: + + static xbString sDefaultDateFormat; + static xbString sDataDirectory; //Data file directory + + #ifdef XB_LOGGING_SUPPORT + static xbString sDefaultLogDirectory; //Default location to store log files + static xbString sDefaultLogFileName; //Default LogFileName + #endif + + static xbInt16 iDefaultFileVersion; // 3 = DBase 3 + // 4 = DBase 4 + // default version used in CreateTable command + // can be over ridden at the Xbase level, or table level + // Different versions can be open simultaneously + + static xbBool bDefaultAutoCommit; // Default dbf auto commit switch + + static xbBool bMultiUser; // True if multi user mode is turned on + // Turn this off for better performance in single user mode + // This needs to be turned on or off before any data tables are opened + // turning this on after tables are opened, can result in out of date + // file buffers if multiple users are sharing the files + +#ifdef XB_LOCKING_SUPPORT + static xbInt32 lDefaultLockWait; // Number of milliseconds between lock retries + static xbInt16 iDefaultLockRetries; // Number of times to retry a lock before conceding + static xbInt16 bDefaultAutoLock; // Autolocking enabled? + static xbInt16 iDefaultLockFlavor; // 1 = DBase + // 2 = Clipper - not developed yet + // 3 = FoxPro - not developed yet + // 9 = Xbase64 - not developed yet +#endif + + + +#if defined (XB_NDX_SUPPORT) || defined (XB_MDX_SUPPORT) + + static xbInt16 iUniqueKeyOpt; + + // is one of: + // XB_HALT_ON_DUPKEY + // XB_EMULATE_DBASE + +#endif + + +#ifdef XB_MDX_SUPPORT + static xbInt16 iCreateMdxBlockSize; // System level Mdx Block Size +#endif // XB_MDX_SUPPORT + + + +}; + +} /* namespace xb */ +#endif /* __XB_XBSSV_H__ */ \ No newline at end of file diff --git a/src/include/xbstring.h b/src/include/xbstring.h new file mode 100755 index 0000000..c2f15cd --- /dev/null +++ b/src/include/xbstring.h @@ -0,0 +1,183 @@ +/* xbstring.h + +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 + +*/ + +#ifndef __XBSTRING_H__ +#define __XBSTRING_H__ + +#ifdef CMAKE_COMPILER_IS_GNUCC +#pragma interface +#endif + +#include +#include + +namespace xb{ + + +//! @brief Class for handling string data. + +/*! +This class defines a basic string class with all the functions one would expect in a string class. + +For purposes of the xbString class, a string is defined as a variable sized array of one byte +characters terminated with a null (0x00 or \0) byte.

+ +This version of the xbString class does not support wide (wchar_t) characters. Perhaps you would +be interested in creating a class for supporting wide characters.

+ +This string class handle strings in a 1-based (not 0 based) fashion. +Any string routines taking an offset use a 1-based value. That is, the first position of +the string is position 1, not 0.

+ +Position 1 (not 0) is considered the first position in a string. +A return of 0 would indicate a not found condition. A return of 1, would be the +first byte. +*/ + + +class XBDLLEXPORT xbString { + + public: + //Various constructors + xbString(xbUInt32 size); + xbString(char c); + xbString(const char *s, xbUInt32 lMaxLen); + xbString(const xbString &s); + xbString(const char * = ""); + xbString( xbDouble d ); + ~xbString(); + + //operators + xbString &operator= (const xbString &s); + xbString &operator= (const char *s); + operator const char *() const; + char &operator[](xbUInt32 n) const; + char &operator[](xbInt32 n) const; + + xbString &operator+=(const xbString &s); + xbString &operator+=(const char *s); + xbString &operator+=(char c); + xbString &operator-=(const xbString &s); + xbString &operator-=(const char *s); + xbString &operator-=(char c); + + xbBool operator == ( const xbString& ) const; + xbBool operator == ( const char * ) const; + xbBool operator != ( const xbString& ) const; + xbBool operator != ( const char * ) const; + + xbBool operator < ( const xbString& ) const; + xbBool operator > ( const xbString& ) const; + xbBool operator <= ( const xbString& ) const; + xbBool operator >= ( const xbString& ) const; + + xbString operator-( const xbString &s ); + xbString operator+( const char *s ); + xbString operator+( const xbString &s ); + xbString operator+( const char c ); + + xbString &AddBackSlash( char c ); + xbString &Append(const xbString &s); + xbString &Append(const char *s); + xbString &Append(const char *s, xbUInt32 iByteCount ); + xbString &Append(char c); + xbString &Assign(const char *srcStr, xbUInt32 lStartPos, xbUInt32 lCopyLen ); + xbString &Assign(const char *srcStr, xbUInt32 lStartPos ); + xbString &Assign(const xbString &s, xbUInt32 pos, xbUInt32 n ); + xbString &Assign(const xbString &s, xbUInt32 n ); + + xbString Copy() const; + xbUInt32 CountChar( char c ) const; + xbUInt32 CountChar( char c, xbInt16 iOpt ) const; + xbInt16 CvtHexChar( char &cOut ); + xbInt16 CvtHexString( xbString &sOut ); + xbInt16 CvtULongLong( xbUInt64 &ullOut ); + xbInt16 CvtLongLong( xbInt64 &llOut ); + + #ifdef XB_DEBUG_SUPPORT + void Dump( const char *title ) const; + void Dump( const char *title, xbInt16 iOption ) const; + void DumpHex( const char *title ) const; + #endif + + xbString &ExtractElement(const char *src, char delim, xbUInt32 iCnt, xbInt16 iOpt = 0 ); + char GetCharacter( xbUInt32 lPos ) const; + xbUInt32 GetLastPos(char c) const; + xbUInt32 GetLastPos(const char *s) const; + char GetPathSeparator() const; + xbUInt32 GetSize() const; + + xbBool HasAlphaChars() const; + xbBool IsEmpty() const; + xbBool IsNull() const; + + xbString &Left( xbUInt32 ulLen ); + xbUInt32 Len() const; // returns the length of the string + xbString &Ltrim(); + xbString &Ltrunc( xbUInt32 ulCnt ); + + xbString &Mid(xbUInt32 ulPos, xbUInt32 lLen ); + xbString &PadLeft( char c, xbUInt32 ulLen ); + xbString &PadRight( char c, xbUInt32 ulLen ); + xbUInt32 Pos(char c) const; + xbUInt32 Pos(const char *s) const; + xbString &PutAt(xbUInt32 ulPos, char c); + + xbString &Remove( xbUInt32 ulPos, xbUInt32 ulN ); + xbString &Resize( xbUInt32 lSize ); + xbString &Rtrim(); + + xbString &Set( const char *s ); + xbString &Set( const xbString &s ); + xbString &Set( const char *s, xbUInt32 ulSize ); + xbString &SetNum( xbInt32 lNum ); + xbString &Sprintf(const char *format, ...); + + const char *Str() const; + char *strncpy( char * cDest, xbUInt32 n ) const; + xbString &SwapChars( char from, char to ); + + xbString &ToLowerCase(); + xbString &ToUpperCase(); + xbString &Trim(); + + xbBool ValidLogicalValue() const; + xbBool ValidNumericValue() const; + xbString &ZapChar( char c ); + xbString &ZapLeadingChar( char c ); + xbString &ZapTrailingChar( char c ); + + friend std::ostream& operator<< ( std::ostream& os, const xbString& s ); + + private: + + static const char * NullString; + static char cJunkBuf; + + char *data; // pointer to actual string data + xbUInt32 size; // size of string plus null terminating byte + + void ctor(const char *s); +// xbUInt32 CalcSprintfBufSize(const char *format, ...); + +// char * xb_realloc( char *pIn, xbUInt32 iLen ); + + // next routine could result in buffer over runs if used with improperly sized buffers + char * xb_strcpy ( char *target, const char *source); + +}; + +} /* namespace */ +#endif /* __XBSTRING_H__ */ diff --git a/src/include/xbtag.h b/src/include/xbtag.h new file mode 100755 index 0000000..9b518a3 --- /dev/null +++ b/src/include/xbtag.h @@ -0,0 +1,74 @@ +/* xbtag.h + +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 + +*/ + + +#ifndef __XB_XBTAG_H__ +#define __XB_XBTAG_H__ + +//#ifdef CMAKE_COMPILER_IS_GNUCC +//#pragma interface +//#endif + +namespace xb{ + + +#ifdef XB_INDEX_SUPPORT + +class XBDLLEXPORT xbIx; + + + +//! @brief xbTag is used for linking index tags to a table (aka dbf file). +/*! + + Each dbf file (or table) can have zero, one or many tags. + Each tag is maintained in a linked list of xbTags. + + NDX index files contain only one index tag.
+ MDX index files can contain one to 47 index tags. The production MDX file is opened automatically + and the tags are added to the tag list. + +*/ + +class XBDLLEXPORT xbTag { + + public: + xbTag( xbIx *pIx, void *vpTag, xbString &sType, xbString &sTagName, xbString &sExpression, xbString &sFilter, xbBool bUnique, xbBool bSort ); + + xbIx *GetIx() const; + void *GetVpTag() const; + const xbString &GetType() const; + const xbString &GetTagName() const; + const xbString &GetExpression() const; + const xbString &GetFilter() const; + xbBool GetUnique() const; + xbBool GetSort() const; + + private: + xbIx *pIx; + void *vpTag; + xbString sType; + xbString sTagName; + xbString sExpression; + xbString sFilter; + xbBool bUnique; + xbBool bSort; // 0 = Ascending, 1 = Descending +}; + +#endif // XB_INDEX_SUPPORT + + +} /* namespace xb */ +#endif /* __XB_TAG_H__ */ diff --git a/src/include/xbtblmgr.h b/src/include/xbtblmgr.h new file mode 100755 index 0000000..d551002 --- /dev/null +++ b/src/include/xbtblmgr.h @@ -0,0 +1,61 @@ +/* xbtblmgr.h + +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 manages a list of open tables, open indices are connected to the open tables + +*/ + + +#ifndef __XB_XBMGR_H__ +#define __XB_XBMGR_H__ + +#ifdef CMAKE_COMPILER_IS_GNUCC +#pragma interface +#endif + +namespace xb{ + +class XBDLLEXPORT xbDbf; + +/* this structure is a linked list of open tables */ +struct XBDLLEXPORT xbTblList{ + xbTblList *pNext; + xbString *psTblName; /* Name of table - same table can be opened multiple times, but must have unique alias */ + xbString *psTblAlias; /* TblAliasName must be unique */ + xbDbf *pDbf; +}; + +class XBDLLEXPORT xbTblMgr : public xbSsv { + public: + xbTblMgr(); + ~xbTblMgr(); + xbInt16 AddTblToTblList ( xbDbf *d, const xbString &sTblName ); + xbInt16 AddTblToTblList ( xbDbf *d, const xbString &sTblName, const xbString &sTblAlias ); + xbInt16 DisplayTableList () const; + xbDbf * GetDbfPtr ( const xbString &sTblAlias ) const; + xbDbf * GetDbfPtr ( xbInt16 sItemNo ) const; + xbInt16 GetOpenTableCount () const; + xbInt16 RemoveTblFromTblList ( const xbString &sTblAlias ); + xbInt16 RemoveTblFromTblList ( xbDbf *d ); + + protected: + + private: + xbTblList * TblList; // List of open database tables + xbInt16 iOpenTableCount; // Number of open tables + +}; + +} /* namespace xb */ + +#endif /* __XB_XBMGR_H__ */ \ No newline at end of file diff --git a/src/include/xbtypes.h b/src/include/xbtypes.h new file mode 100755 index 0000000..99b6c22 --- /dev/null +++ b/src/include/xbtypes.h @@ -0,0 +1,55 @@ +/* xbtypes.h + +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 + +*/ + +#ifndef __XB_XTYPES_H__ +#define __XB_XTYPES_H__ + +namespace xb{ + + +typedef float xbFloat; +typedef double xbDouble; +typedef short int xbBool; +typedef xbUInt64 xbOffT; + + +#define xbTrue 1 +#define xbFalse 0 + + +#ifdef HAVE__FILENO_F + #define xbFileNo _fileno +#else + #define xbFileNo fileno +#endif + +#ifdef XB_LOCKING_SUPPORT + #if defined( HAVE_LONG_LONG ) + #define LK4026531839 4026531839LL + #define LK4026531838 4026531838LL + #define LK3026531838 3026531838LL + #define LK1000000000 1000000000LL + #else + #define LK4026531839 4026531839L + #define LK4026531838 4026531838L + #define LK3026531838 3026531838L + #define LK1000000000 1000000000L + #endif +#endif + + + +} /* namespace */ +#endif /* __XB_XTYPES_H__ */ diff --git a/src/include/xbuda.h b/src/include/xbuda.h new file mode 100755 index 0000000..84eccee --- /dev/null +++ b/src/include/xbuda.h @@ -0,0 +1,52 @@ +/* xbuda.h + +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 manages the user data area (UDA) + +*/ + + +#ifndef __XB_XBUDA_H__ +#define __XB_XBUDA_H__ + +#ifdef CMAKE_COMPILER_IS_GNUCC +#pragma interface +#endif + + +#ifdef XB_EXPRESSION_SUPPORT + + +namespace xb{ + +class XBDLLEXPORT xbUda { + + public: + xbUda(); + ~xbUda(); + + xbInt16 AddTokenForKey( const xbString &sKey, const xbString &sToken ); + void Clear(); + xbInt16 DelTokenForKey( const xbString &sKey ); + xbInt16 GetTokenCnt() const; + xbInt16 GetTokenForKey( const xbString &sKey, xbString &sData ); + xbInt16 UpdTokenForKey( const xbString &sKey, const xbString &sToken ); + void DumpUda() const; + + private: + xbLinkListOrd llOrd; + +}; +} /* namespace */ +#endif /* XB_EXPRESSION_SUPPORT */ +#endif /* __XBUDA_H__ */ \ No newline at end of file diff --git a/src/include/xbxbase.h b/src/include/xbxbase.h new file mode 100755 index 0000000..204e744 --- /dev/null +++ b/src/include/xbxbase.h @@ -0,0 +1,222 @@ +/* xbxbase.h + +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 + +*/ + + +#ifndef __XB_XBDBLIST_H__ +#define __XB_XBDBLIST_H__ + +#ifdef CMAKE_COMPILER_IS_GNUCC +#pragma interface +#endif + +namespace xb{ + +class XBDLLEXPORT xbLog; + +/************************************************************************/ +/* + Xbase functions + + Min Parm Count is the minimum number of input parms needed by the function + + Return Type + C Char or string + D Date + L Logical + N Numeric + 1 Varies - pull return type from first sibling + 2 Varies - pull return type from second sibling +*/ + +#ifdef XB_FUNCTION_SUPPORT + +/* +struct XBDLLEXPORT xbFunctionInfo { + const char * FuncName; // function name + char ReturnType; // return type of function + xbInt16 iReturnLenCalc; // used to calculate the function return value is + // 1 = use value specified in iReturnLenVal + // 2 = use length of operand specified in col 4 + // 3 = use valued of numeric operand specified in col 4 + // 4 = length of parm 1 * numeric value parm + // 5 = larger length of parm 2 or length of parm 3 + // 6 = if two or more parms, use numeric value from second parm, + // otherwise use col4 value + xbInt16 iReturnLenVal; // Used in combination with iReturnLenCalc + +}; +*/ +/* +static xbFunctionInfo FunctionList[] = +// Func Return -Rtrn Len- +// Name Type -Calc Val- +{ + { "ABS", 'N', 1, 4 }, + { "ALLTRIM", 'C', 2, 1 }, + { "ASC", 'N', 1, 4 }, + { "AT", 'N', 1, 4 }, + { "CDOW", 'C', 1, 9 }, + { "CHR", 'C', 1, 1 }, + { "CMONTH", 'C', 1, 9 }, + { "CTOD", 'D', 1, 8 }, + { "DATE", 'D', 1, 8 }, + { "DAY", 'N', 1, 4 }, + { "DEL", 'C', 1, 1 }, + { "DELETED", 'L', 1, 1 }, + { "DESCEND", '1', 2, 1 }, + { "DOW", 'N', 1, 4 }, + { "DTOC", 'C', 1, 8 }, + { "DTOS", 'C', 1, 8 }, + { "EXP", 'N', 1, 4 }, + { "IIF", 'C', 5, 0 }, + { "INT", 'N', 1, 4 }, + { "ISALPHA", 'L', 1, 1 }, + { "ISLOWER", 'L', 1, 1 }, + { "ISUPPER", 'L', 1, 1 }, + { "LEFT", 'C', 3, 2 }, + { "LEN", 'N', 1, 4 }, + { "LOG", 'N', 1, 4 }, + { "LOWER", 'C', 2, 1 }, + { "LTRIM", 'C', 2, 1 }, + { "MAX", 'N', 1, 4 }, + { "MIN", 'N', 1, 4 }, + { "MONTH", 'N', 1, 4 }, + { "RECNO", 'N', 1, 4 }, + { "RECCOUNT", 'N', 1, 4 }, + { "REPLICATE", 'C', 4, 0 }, + { "RIGHT", 'C', 3, 2 }, + { "RTRIM", 'C', 2, 1 }, + { "SPACE", 'C', 3, 1 }, + { "SQRT", 'N', 1, 4 }, + { "STOD", 'D', 1, 8 }, + { "STR", 'C', 6, 10 }, + { "STRZERO", 'C', 3, 2 }, + { "SUBSTR", 'C', 3, 3 }, + { "TRIM", 'C', 2, 1 }, + { "UPPER", 'C', 2, 1 }, + { "VAL", 'N', 1, 4 }, + { "YEAR", 'N', 1, 4 }, + { 0, 0, 0, 0 }, +}; +*/ +#endif + + + +//! @brief xbXbase class. +/*! + The xbXBase class is the core class that needs to be in every application program. +*/ + +class XBDLLEXPORT xbXBase : public xbTblMgr{ + public: + xbXBase(); + ~xbXBase(); + + xbInt16 CloseAllTables(); + void DisableMsgLogging(); + void EnableMsgLogging (); + xbInt16 FlushLog(); + + const xbString &GetLogDirectory () const; + const xbString &GetLogFileName () const; + const xbString &GetLogFqFileName() const; + xbBool GetLogStatus () const; + + xbInt16 OpenHighestVersion( const xbString &sTableName, const xbString &sAlias, xbDbf &dbf, int dummy ); + xbInt16 OpenHighestVersion( const xbString &sTableName, const xbString &sAlias, xbDbf **dbf ); + + xbDbf * Open( const xbString &sTableName, xbInt16 &iRc ); + xbDbf * Open( const xbString &sTableName, const xbString &sAlias, xbInt16 iOpenMode, xbInt16 iShareMode, xbInt16 iVersion, xbInt16 &iRc ); + void SetLogDirectory( const xbString &sLogFileDirectory ); + void SetLogFileName ( const xbString &sLogFileName ); + void SetLogSize ( size_t lSize ); + xbInt16 WriteLogMessage( const xbString &sLogMessage, xbInt16 iOutputOpt = 0 ); + xbInt16 WriteLogBytes ( xbUInt32 lCnt, const char *p ); + + void xbSleep ( xbInt32 lMillisecs ); + + + /* xbase functions */ + #ifdef XB_FUNCTION_SUPPORT + xbInt16 ABS( xbDouble dIn, xbDouble &dOut ); + xbInt16 ALLTRIM( const xbString &sIn, xbString &sOut ); + xbInt16 ASC( const xbString &s, xbDouble &dAscOut ); + xbInt16 AT( const xbString &sSrchFor, const xbString &sBase, xbDouble &dPos ); + xbInt16 CDOW( xbDate &dInDate, xbString &sOutDow ); + xbInt16 CHR( xbDouble dAsciCd, xbString &sOut ); + xbInt16 CMONTH( xbDate &dInDate, xbString &sOutMonth ); + xbInt16 CTOD( const xbString &sInDate, xbDate &dOutDate ); + xbInt16 DATE( xbDate &dOutDate ); + xbInt16 DAY( const xbDate &dInDate, xbDouble &dOutDay ); + xbInt16 DEL( xbDbf * d, xbString &sOut, xbInt16 iRecBufSw = 0 ); + xbInt16 DELETED( xbDbf * d, xbBool &bOut, xbInt16 iRecBufSw = 0 ); + xbInt16 DESCEND( const xbString &sIn, xbString &sOut ); + xbInt16 DESCEND( const xbDate &dInDate, xbDate &dOutDate ); + xbInt16 DESCEND( const xbDouble dIn, xbDouble &dsOut ); + xbInt16 DOW( const xbDate &sInDate, xbDouble &dDowOut ); + xbInt16 DTOC( xbDate &dInDate, xbString &sOutFmtDate ); + xbInt16 DTOS( xbDate &dInDate, xbString &sOutFmtDate ); + xbInt16 EXP( xbDouble dIn, xbDouble &dOut ); + xbInt16 IIF( xbBool bResult, const xbString &sTrueResult, const xbString &sFalseResult, xbString &sResult ); + xbInt16 INT( xbDouble dIn, xbDouble &dOut ); + xbInt16 ISALPHA( const xbString &s, xbBool &bResult ); + xbInt16 ISLOWER( const xbString &s, xbBool &bResult ); + xbInt16 ISUPPER( const xbString &s, xbBool &bResult ); + xbInt16 LEFT( const xbString &sIn, xbUInt32 lCharCnt, xbString &sOut ); + xbInt16 LEN( const xbString &sIn, xbDouble &dLen ); + xbInt16 LOG( xbDouble dIn, xbDouble &dOut ); + xbInt16 LOWER( const xbString &sIn, xbString &sOut ); + xbInt16 LTRIM( const xbString &sIn, xbString & sOut ); + xbInt16 MAX( xbDouble dIn1, xbDouble dIn2, xbDouble &dOut ); + xbInt16 MIN( xbDouble dIn1, xbDouble dIn2, xbDouble &dOut ); + xbInt16 MONTH( xbDate &dInDate, xbDouble &dMonthOut ); + xbInt16 RECCOUNT( xbDbf * d, xbDouble &dRecOut ); + xbInt16 RECNO( xbDbf * d, xbDouble &dRecOut ); + xbInt16 REPLICATE( const xbString &sIn, xbUInt32 ulRepCnt, xbString &sOut ); + xbInt16 RIGHT( const xbString &sIn, xbUInt32 iCharCnt, xbString &sOut ); + xbInt16 RTRIM( const xbString &sIn, xbString &sOut ); + xbInt16 SPACE( xbInt32 lCnt, xbString &sOut ); + xbInt16 SQRT( xbDouble dBase, xbDouble &dSqrRt ); + xbInt16 STOD( const xbString &sIn, xbDate &sDateOut ); + xbInt16 STR( xbDouble dIn, xbString &sOut ); + xbInt16 STR( xbDouble dIn, xbUInt32 ulLen, xbString &sOut ); + xbInt16 STR( xbDouble dIn, xbUInt32 ulLen, xbUInt32 ulDec, xbString &sOut ); + xbInt16 STR( xbDouble dIn, xbUInt32 ulLen, xbUInt32 ulDec, xbString &sPadChar, xbString &sOut ); + xbInt16 STRZERO( xbDouble dIn, xbUInt32 ulLen, xbUInt32 ulDec, xbString &sOut ); + xbInt16 SUBSTR( const xbString &sIn, xbUInt32 ulStartPos, xbUInt32 ulLen, xbString &sOut ); + xbInt16 TRIM( const xbString &sIn, xbString &sOut ); + xbInt16 UPPER( const xbString &sIn, xbString &sOut ); + xbInt16 VAL( const xbString &sIn, xbDouble &dOut ); + xbInt16 YEAR( xbDate &dInDate, xbDouble &dOutYear ); + #endif + + protected: + friend class xbBcd; + friend class xbExp; + + xbInt16 GetFunctionInfo( const xbString &sExpLine, char &cReturnType, xbInt16 &iReturnLenCalc, xbInt32 &lReturnLenVal ) const; + static xbInt16 xbMemcmp( const unsigned char *s1, const unsigned char *s2, size_t n ); + + private: + +#ifdef XB_LOGGING_SUPPORT + xbLog *xLog; +#endif + +}; + +} /* namespace xb */ +#endif /* __XB_DBLIST_H__ */ \ No newline at end of file diff --git a/src/sql/xbcrix.cpp b/src/sql/xbcrix.cpp new file mode 100755 index 0000000..b24782e --- /dev/null +++ b/src/sql/xbcrix.cpp @@ -0,0 +1,279 @@ +/* xbcrix.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_SQL_SUPPORT + +namespace xb{ + + +/***********************************************************************/ +xbInt16 xbSql::SqlCreateIndex( const xbString &sCmdLine ){ + + // std::cout << "CREATE INDEX " << sCmdLine << std::endl; + + // expected format to create an Dbase 3, NDX index: + // CREATE INDEX ixname.ndx ON tablename.dbf ( EXPRESSION ) [ASSOCIATE] + + // expected format to create an Dbase 4, tag on an MDX index: + // CREATE [UNIQUE] INDEX tagname ON tablename.dbf ( EXPRESSION ) [DESC] [FILTER .NOT. DELETED()] + + // The ASSOCIATE parameter is specific to Xbase64 library, it is used to associate + // a non production (NDX) index file to a dbf file so it will be automatically + // opened with the dbf file whenever the dbf file is opened by the xbase64 routines. + + // The [ASSOCIATE] parameter is not used with MDX production indices + + // This method first looks for ".NDX" in the file name to determine if an NDX + // index should be created. + // if .NDX is not in the filename, it looks in the uda for "IXTYPE" for either + // NDX or MDX to detmermine the index type to create + // if IXTYPE not found, create an MDX tag + + // The optional DESC parameter defines an entire index key as descending. This is + // different than other SQL implementations where specific fields can be descending. + + // The optional FILTER parameter is specific to the XBASE64 library, is it used to + // assign a filter to a tag in an MDX style index. Everything to the right of + // the keyword FILTER is considered part of the filter. + + // The original DBASE indices used to '+' to create an index on more than one field + // ie: FIELD1+FIELD2+FIELD3 + // SQL uses commas: ie: FIELD1, FIELD2, FIELD3 + // The Xbase library supports either '+' or ',' when creating mutli field indices. + + xbInt16 iRc = 0; + xbInt16 iErrorStop = 0; + xbString sTableName; + xbString sIxName; + xbString sIxType; + xbUInt32 ulPos; + xbString sCmd = sCmdLine; + xbString sNode; + xbBool bUnique = xbFalse; + xbDbf * dbf = NULL; + + try{ + + // std::cout << "xbSql::CreateIx()\n" ; + + // drop off the first node + ulPos = sCmd.Pos( ' ' ); + sCmd.Ltrunc( ulPos ); + sCmd.Ltrim(); + + sNode.ExtractElement( sCmd, ' ', 1, 0 ); + sNode.ToUpperCase(); + + if( sNode == "UNIQUE" ){ + //std::cout << "unique ix\n"; + bUnique = xbTrue; + ulPos = sCmd.Pos( ' ' ); + sCmd.Ltrunc( ulPos ); + sCmd.Ltrim(); + } + + // go past the index keyword + ulPos = sCmd.Pos( ' ' ); + sCmd.Ltrunc( ulPos ); + sCmd.Ltrim(); + + // pull the index name off the cmd line + sIxName.ExtractElement( sCmd, ' ', 1, 0 ); + + #ifdef XB_NDX_SUPPORT + xbString sTemp = sIxName; + sTemp.ToUpperCase(); + ulPos = sTemp.Pos( ".NDX" ); + if(ulPos == (sTemp.Len() - 3) ) + sIxType = "NDX"; + #endif // XB_NDX_SUPPORT + + if( sIxType == "" ){ + if(( iRc = uda.GetTokenForKey( "IXTYPE", sIxType )) != XB_NO_ERROR ){ + iErrorStop = 20; + throw iRc; + } + } + + #ifdef XB_NDX_SUPPORT + if( sIxType == "NDX" ){ + xbFile f( xbase ); + f.SetFileName( sIxName ); + if( f.FileExists()){ + iErrorStop = 30; + iRc = XB_FILE_EXISTS; + throw iRc; + } + } + #endif // XB_NDX_SUPPORT + + // skip past index name + ulPos = sCmd.Pos( ' ' ); + sCmd.Ltrunc( ulPos ); + sCmd.Ltrim(); + + // skip past "ON" + ulPos = sCmd.Pos( ' ' ); + sCmd.Ltrunc( ulPos ); + sCmd.Ltrim(); + + // get the table name + ulPos = sCmd.Pos( '(' ); + sTableName.ExtractElement( sCmd, '(', 1, 0 ); + sTableName.Trim(); + + xbFile fDbf( xbase ); + fDbf.SetFileName( sTableName ); + + // if not open, attempt to open it + dbf = xbase->GetDbfPtr( fDbf.GetFqFileName()); + if( !dbf ){ + if(( iRc = xbase->OpenHighestVersion( sTableName, "", &dbf )) != XB_NO_ERROR ){ + iErrorStop = 40; + throw iRc; + } + } + if( dbf == NULL ){ + iErrorStop = 50; + iRc = XB_FILE_NOT_FOUND; + throw iRc; + } + sCmd.Ltrunc( ulPos ); + + // std::cout << "cp1 ulPos = " << ulPos << " scmd = " << sCmd << "\n"; + + //ulPos = sCmd.GetLastPos( ')' ); + xbString sKeyExpression; + xbBool bDone = xbFalse; + xbUInt32 lPos = 1; + xbInt16 iParenCtr = 0; + while( !bDone && lPos < sCmd.Len()){ + if( sCmd[lPos] == '(' ){ + iParenCtr++; + sKeyExpression.Append( sCmd[lPos] ); + } else if( sCmd[lPos] == ')' ){ + if( iParenCtr > 0 ){ + iParenCtr--; + sKeyExpression.Append( sCmd[lPos] ); + } else { + bDone = xbTrue; + } + } else if( sCmd[lPos] == ',' ){ + sKeyExpression.Append( '+' ); + } else if( sCmd[lPos] != ' ' ){ + sKeyExpression.Append( sCmd[lPos] ); + } + lPos++; + } +// std::cout << "Key Expression =[" << sKeyExpression << "]\n"; + sCmd.Ltrunc( lPos ); + sCmd.Trim(); + + xbBool bDesc = xbFalse; +// std::cout << "sCmd - looking for DESC [" << sCmd << "]\n"; + if( sCmd.Len() > 4 ){ + sNode = sCmd; + sNode.ToUpperCase(); + ulPos = sNode.Pos( "DESC" ); + if( ulPos > 0 ){ + bDesc = xbTrue; + sCmd.Ltrunc( 4 ); + sCmd.Trim(); + // std::cout << "Descending\n"; + } + } + +// std::cout << "sCmd - looking for FILTER stuff [" << sCmd << "]\n"; + xbString sFilter; + if( sCmd.Len() > 6 ){ + sNode = sCmd; + sNode.ToUpperCase(); + ulPos = sNode.Pos( "FILTER" ); + if( ulPos > 0 ){ + sFilter = sCmd; + sFilter.Ltrunc( ulPos + 7 ); + sFilter.Trim(); + } + } +// std::cout << "sCmd - FILTER = [" << sFilter << "]\n"; + + #ifdef XB_LOCKING_SUPPORT + if(( iRc = dbf->LockTable( XB_LOCK )) != XB_NO_ERROR ){ + iErrorStop = 60; + throw iRc; + } + #endif // XB_LOCKING_SUPPORT + +// std::cout << "SqlCreateIndex() - ixtype = " << sIxType << "\n"; + + xbIx *pIx; + void *vpTag; + + if(( iRc = dbf->CreateTag( sIxType, sIxName, sKeyExpression, sFilter, bDesc, bUnique, xbFalse, &pIx, &vpTag )) != XB_NO_ERROR ){ + iErrorStop = 70; + throw iRc; + } +// std::cout << "SqlCreateIndex() - back from tag create\n"; + #ifdef XB_NDX_SUPPORT + if( sIxType == "NDX"){ + sCmd.Ltrunc( ulPos ); + sCmd.Trim(); + if( sCmd.Len() > 0 ){ + sCmd.ToUpperCase(); + if( sCmd.Pos( "ASSOCIATE" )){ + if(( iRc = dbf->AssociateIndex( "NDX", sIxName, 0 )) != XB_NO_ERROR ){ + iErrorStop = 80; + throw iRc; + } + } + } + } + if( sIxType == "NDX"){ + if(( iRc = pIx->Reindex( &vpTag )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + } + #endif // XB_NDX_SUPPORT + + #ifdef XB_LOCKING_SUPPORT + if(( iRc = dbf->LockTable( XB_UNLOCK )) != XB_NO_ERROR ){ + iErrorStop = 140; + throw iRc; + } + #endif // XB_LOCKING_SUPPORT + + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbSql::SqlCreateIndex() Exception Caught. Error Stop = [%d] rc = [%d] table = [%s]", iErrorStop, iRc, sTableName.Str() ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + + } + + #ifdef XB_LOCKING_SUPPORT + dbf->LockTable( XB_UNLOCK ); + #endif // XB_LOCKING_SUPPORT + + return iRc; +} + +/***********************************************************************/ +} /* namespace */ +#endif /* XB_SQL_SUPPORT */ + diff --git a/src/sql/xbcrtbl.cpp b/src/sql/xbcrtbl.cpp new file mode 100755 index 0000000..4193d01 --- /dev/null +++ b/src/sql/xbcrtbl.cpp @@ -0,0 +1,256 @@ +/* xbcrtbl.cpp + +XBase64 Software Library + +Copyright (c) 1997,2003,2014,2019,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_SQL_SUPPORT + +namespace xb{ + + +/***********************************************************************/ +xbInt16 xbSql::SqlCreateTable( const xbString &sCmdLine ){ + + + // std::cout << "CREATE TABLE " << sCmdLine << std::endl; + + // expected format: + // CREATE TABLE tablename.dbf (Field1 CHAR(10), INTFLD1 INTEGER, ... ) + + // supported fields types + // + // SQL TYPE XBASE Field Type + // -------- ---------------- + // SMALLINT NUMERIC(6,0) + // INTEGER NUMERIC(11,0) + // DECIMAL(x,y) NUMERIC(x+1,y) + // NUMERIC(x,y) NUMERIC(x,y) + // FLOAT(x,y) FLOAT(x,y) + // CHAR(n) CHARACTER(n) + // DATE DATE + // VARCHAR MEMO + // LOGICAL LOGICAL + + xbInt16 iRc = 0; + xbInt16 iErrorStop = 0; + xbString sTableName; + xbString sFieldName; + xbString sDataType; + xbString sAttrib1; + xbString sAttrib2; + xbString sLlEntry; + xbInt16 iType = 0; + xbInt16 iAttribCnt; // number of attributes for a given data type + xbString s; + xbUInt32 lPos = 0; + xbUInt32 lSpPos = 0; // space position + xbUInt32 lCmPos = 0; // comma position + xbUInt32 lLpPos = 0; // left paren position + xbLinkList ll; + xbSchema *schema = NULL; + xbString sMsg; + + try{ + // retrieve table name + s.ExtractElement( sCmdLine, '(', 1, 0 ); + sTableName.ExtractElement( s, ' ', 3, 0 ); + sTableName.Trim(); + + // std::cout << "Tablename = [" << sTableName << "]\n"; + + + // determine if it already exists + xbFile f( xbase ); + f.SetFileName( sTableName ); + if( f.FileExists() ){ + iErrorStop = 10; + iRc = XB_FILE_EXISTS; + throw iRc; + } + + // build out table structure with parms from the sql string + lPos = sCmdLine.Pos( '(' ); + s = sCmdLine; + s.Ltrunc( lPos ); + s.Trim(); + + // remove the last byte, should be a ) + s.Remove( s.Len(), 1 ); + + s.Trim(); + //std::cout << "s = [" << s << "]\n"; + + + xbBool bDone = xbFalse; + xbInt16 iLoop = 0; + while( !bDone && iLoop++ < 255 ){ + sFieldName.ExtractElement( s, ' ', 1 , 0 ); + lPos = sFieldName.Len(); + sFieldName.Trim(); + if( sFieldName.Len() > 10 ) + sFieldName.Mid( 1, 10 ); // shrink to 10 buytes if too big + + //std::cout << "field name=[" << sFieldName << "]\n"; + s.Ltrunc( lPos + 1 ); + s.Ltrim(); + //std::cout << "remainder after field name removed = [" << s << "]\n"; + + // Data type is delimited with either a space, comma or left paren + lPos = 9999999; + lSpPos = s.Pos( ' ' ); + lCmPos = s.Pos( ',' ); + lLpPos = s.Pos( '(' ); + if( lSpPos != 0 ) lPos = lSpPos; + if( lCmPos != 0 && lCmPos < lPos ) lPos = lCmPos; + if( lLpPos != 0 && lLpPos < lPos ) lPos = lLpPos; + + //sMsg.Sprintf( "SpPos=[%d] CmPos=[%d] LpPos=[%d] lPos=[%d]", lSpPos, lCmPos, lLpPos, lPos ); + //std::cout << sMsg << "\n"; + + sDataType.Assign( s, 1, lPos-1 ); + + //std::cout << "DataType=[" << sDataType << "]\n"; + if( sDataType == "CHAR" ) + iAttribCnt = 1; + else if( sDataType == "DECIMAL" || sDataType == "NUMERIC" || sDataType == "FLOAT" ) + iAttribCnt = 2; + else if( sDataType == "SMALLINT" || sDataType == "INTEGER" || sDataType == "DATE" || sDataType == "VARCHAR" || sDataType == "LOGICAL" ) + iAttribCnt = 0; + else{ + iErrorStop = 20; + iRc = XB_INVALID_FIELD_TYPE; + throw iRc; + } + + sAttrib1 = ""; + sAttrib2 = "0"; + + if( iAttribCnt == 0 ){ + s.Ltrunc( sDataType.Len()); + + } else if( iAttribCnt > 0 ){ + lPos = s.Pos( '(' ); + if( lPos <= 0 ){ + iErrorStop = 30; + iRc = XB_INVALID_FIELD_LEN; + throw iRc; + } + s.Ltrunc( lPos ); + if( iAttribCnt == 1 ) + lPos = s.Pos( ')' ); + else + lPos = s.Pos( ',' ); + + sAttrib1.Assign( s, 1, lPos-1 ); + sAttrib1.Trim(); + s.Ltrunc( lPos ); + + if( iAttribCnt > 1 ){ + lPos = s.Pos( ')' ); + sAttrib2.Assign( s, 1, lPos-1 ); + s.Ltrunc( lPos ); + } + } + + s.Ltrim(); + s.ZapLeadingChar( ',' ); + s.Ltrim(); + + if( sDataType == "CHAR" ){ + iType = XB_CHAR_FLD; + } else if( sDataType == "DECIMAL" ){ + xbInt32 lVal = atol( sAttrib1.Str()) + 1; + sAttrib1.Sprintf( "%d", lVal ); + iType = XB_NUMERIC_FLD; + } else if( sDataType == "SMALLINT" ){ + sAttrib1 = "6"; + iType = XB_NUMERIC_FLD; + } else if( sDataType == "INTEGER" ){ + sAttrib1 = "11"; + iType = XB_NUMERIC_FLD; + } else if( sDataType == "NUMERIC" ){ + iType = XB_NUMERIC_FLD; + } else if( sDataType == "FLOAT" ) { + iType = XB_FLOAT_FLD; + } else if( sDataType == "DATE" ){ + iType = XB_DATE_FLD; + sAttrib1 = "8"; + } else if( sDataType == "VARCHAR" ){ + iType = XB_MEMO_FLD; + sAttrib1 = "10"; + } else if( sDataType == "LOGICAL" ){ + iType = XB_LOGICAL_FLD; + sAttrib1 = "1"; + } + sLlEntry.Sprintf( "%s,%s,%c,%s,%s", sFieldName.Str(), sDataType.Str(), iType, sAttrib1.Str(), sAttrib2.Str()); + ll.InsertAtEnd( sLlEntry ); + + if( s.Len() == 0 ) + bDone = xbTrue; + } + + schema = (xbSchema *) calloc( ll.GetNodeCnt()+1, sizeof( xbSchema )); + xbLinkListNode * llN = ll.GetHeadNode(); + xbUInt32 ulCnt = ll.GetNodeCnt(); + + char *pTrg; + for( xbUInt32 i = 0; i < ulCnt; i++ ){ + s = llN->GetKey(); + sFieldName.ExtractElement( s, ',', 1 , 0 ); + pTrg = schema[i].cFieldName; + for( xbUInt32 j = 0; j < sFieldName.Len(); j++ ) + *pTrg++ = sFieldName[j+1]; + sDataType.ExtractElement( s, ',', 3, 0 ); + schema[i].cType = sDataType[1]; + sAttrib1.ExtractElement( s, ',', 4, 0 ); + schema[i].iFieldLen = atoi( sAttrib1.Str()); + sAttrib2.ExtractElement( s, ',', 5, 0 ); + schema[i].iNoOfDecs = atoi( sAttrib2.Str()); + llN = llN->GetNextNode(); + } + + // create the table + xbDbf * dbf = NULL; + #ifdef XB_DBF4_SUPPORT + dbf = new xbDbf4( xbase ); + #elif XB_DBF3_SUPPORT + dbf = new xbDbf3( xbase ); + #endif + if(( iRc = dbf->CreateTable( sTableName, "", schema, 0, XB_MULTI_USER )) != XB_NO_ERROR ){ + iErrorStop = 50; + throw iRc; + } + + free( schema ); + ll.Clear(); + } + catch (xbInt16 iRc ){ + xbString sMsg; + if( sFieldName.Len() > 0 ) + sMsg.Sprintf( "xbSql::SqlCreateTbl() Exception Caught. Error Stop = [%d] rc = [%d] table = [%s] field = [%s]", iErrorStop, iRc, sTableName.Str(), sFieldName.Str() ); + else + sMsg.Sprintf( "xbSql::SqlCreateTbl() Exception Caught. Error Stop = [%d] rc = [%d] table = [%s]", iErrorStop, iRc, sTableName.Str() ); + + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + if( schema ) free( schema ); + } + return iRc; +} + +/***********************************************************************/ +} /* namespace */ +#endif /* XB_SQL_SUPPORT */ + diff --git a/src/sql/xbdrpix.cpp b/src/sql/xbdrpix.cpp new file mode 100755 index 0000000..9e3bc3b --- /dev/null +++ b/src/sql/xbdrpix.cpp @@ -0,0 +1,143 @@ +/* xbdrpix.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_SQL_SUPPORT + +namespace xb{ + + +/***********************************************************************/ +xbInt16 xbSql::SqlDropIndex( const xbString &sCmdLine ){ + + // std::cout << "DROP INDEX [" << sCmdLine << "]\n"; + + // expected format: + // DROP INDEX [IF EXISTS] ixname.ndx ON tablename.dbf + + + xbInt16 iRc = 0; + xbInt16 iErrorStop = 0; + xbString sTableName; + xbString sIxName; + xbUInt32 ulPos; + xbString sCmd = sCmdLine; + xbString sNode; + xbBool bIfExists = xbFalse; + xbDbf * dbf = NULL; + + try{ + + // drop off the first node "DROP" + ulPos = sCmd.Pos( ' ' ); + sCmd.Ltrunc( ulPos ); + sCmd.Ltrim(); + + // drop off the second node "INDEX" + ulPos = sCmd.Pos( ' ' ); + sCmd.Ltrunc( ulPos ); + sCmd.Ltrim(); + + sNode.ExtractElement( sCmd, ' ', 1, 0 ); + sNode.Trim(); + sNode.ToUpperCase(); + + if( sNode == "IF" ){ + ulPos = sCmd.Pos( ' ' ); + sCmd.Ltrunc( ulPos ); + sCmd.Ltrim(); + sNode.ExtractElement( sCmd, ' ', 1, 0 ); + sNode.Trim(); + sNode.ToUpperCase(); + if( sNode != "EXISTS" ){ + iErrorStop = 10; + iRc = XB_SYNTAX_ERROR; + throw iRc; + } else { + ulPos = sCmd.Pos( ' ' ); + sCmd.Ltrunc( ulPos ); + sCmd.Ltrim(); + bIfExists = xbTrue; + } + } + + // get the index name + sIxName.ExtractElement( sCmd, ' ', 1, 0 ); + sIxName.Trim(); + + ulPos = sCmd.Pos( ' ' ); + sCmd.Ltrunc( ulPos ); + sCmd.Ltrim(); + + // go past "ON" + ulPos = sCmd.Pos( ' ' ); + sCmd.Ltrunc( ulPos ); + sCmd.Ltrim(); + + sTableName = sCmd; + sTableName.Trim(); + + xbFile fDbf( xbase ); + fDbf.SetFileName( sTableName ); + + // if not open, attempt to open it + dbf = xbase->GetDbfPtr( fDbf.GetFqFileName()); + if( !dbf ){ + if(( iRc = xbase->OpenHighestVersion( sTableName, "", &dbf )) != XB_NO_ERROR ){ + iErrorStop = 20; + throw iRc; + } + } + if( dbf == NULL ){ + iErrorStop = 40; + iRc = XB_FILE_NOT_FOUND; + throw iRc; + } + + #ifdef XB_LOCKING_SUPPORT + if(( iRc = dbf->LockTable( XB_LOCK )) != XB_NO_ERROR ){ + iErrorStop = 50; + throw iRc; + } + #endif // XB_LOCKING_SUPPORT + + iRc = dbf->DeleteTag( "NDX", sIxName ); + if( iRc == XB_FILE_NOT_FOUND && !bIfExists ){ + iErrorStop = 60; + throw( iRc ); + } else if( iRc != XB_NO_ERROR ){ + iErrorStop = 70; + throw iRc; + } + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbSql::SqlDropIndex() Exception Caught. Error Stop = [%d] rc = [%d] table = [%s]", iErrorStop, iRc, sTableName.Str() ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + + #ifdef XB_LOCKING_SUPPORT + dbf->LockTable( XB_UNLOCK ); + #endif // XB_LOCKING_SUPPORT + + return iRc; +} + +/***********************************************************************/ +} /* namespace */ +#endif /* XB_SQL_SUPPORT */ + diff --git a/src/sql/xbdrptbl.cpp b/src/sql/xbdrptbl.cpp new file mode 100755 index 0000000..e2cfa9d --- /dev/null +++ b/src/sql/xbdrptbl.cpp @@ -0,0 +1,125 @@ +/* xbdrptbl.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_SQL_SUPPORT + +namespace xb{ + + +/***********************************************************************/ +xbInt16 xbSql::SqlDropTable( const xbString &sCmdLine ){ + + // std::cout << "DROP TABLE [" << sCmdLine << "]\n"; + // expected format: + // DROP TABLE [IF EXISTS] tablename.dbf + + + xbInt16 iRc = 0; + xbInt16 iErrorStop = 0; + xbString sTableName = ""; + xbUInt32 ulPos = 0; + xbString sCmd = sCmdLine; + xbString sNode = ""; + xbBool bIfExists = xbFalse; + xbDbf * dbf = NULL; + + try{ + + // drop off the first node "DROP" + ulPos = sCmd.Pos( ' ' ); + sCmd.Ltrunc( ulPos ); + sCmd.Ltrim(); + + // drop off the second node "TABLE" + ulPos = sCmd.Pos( ' ' ); + sCmd.Ltrunc( ulPos ); + sCmd.Ltrim(); + + sNode.ExtractElement( sCmd, ' ', 1, 0 ); + sNode.Trim(); + sNode.ToUpperCase(); + + if( sNode == "IF" ){ + ulPos = sCmd.Pos( ' ' ); + sCmd.Ltrunc( ulPos ); + sCmd.Ltrim(); + sNode.ExtractElement( sCmd, ' ', 1, 0 ); + sNode.Trim(); + sNode.ToUpperCase(); + if( sNode != "EXISTS" ){ + iErrorStop = 10; + iRc = XB_SYNTAX_ERROR; + throw iRc; + } else { + ulPos = sCmd.Pos( ' ' ); + sCmd.Ltrunc( ulPos ); + sCmd.Ltrim(); + bIfExists = xbTrue; + } + } + + // get the table name + sTableName.ExtractElement( sCmd, ' ', 1, 0 ); + sTableName.Trim(); + + xbFile fDbf( xbase ); + fDbf.SetFileName( sTableName ); + + if( bIfExists && ! fDbf.FileExists()) + return XB_NO_ERROR; + + // if not open, attempt to open it + dbf = xbase->GetDbfPtr( fDbf.GetFqFileName()); + + if( !dbf ){ + dbf = xbase->Open( sTableName, iRc ); + if( iRc != XB_NO_ERROR ){ + if( iRc == XB_FILE_NOT_FOUND && bIfExists ){ + return XB_NO_ERROR; + } else { + iErrorStop = 20; + throw iRc; + } + } + } + if( dbf == NULL ){ + iErrorStop = 40; + iRc = XB_OPEN_ERROR; + throw iRc; + } + + if(( dbf->DeleteTable()) != XB_NO_ERROR ){ + iErrorStop = 50; + throw iRc; + } + + } + + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbSql::SqlDropTable() Exception Caught. Error Stop = [%d] rc = [%d] table = [%s]", iErrorStop, iRc, sTableName.Str() ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + + return iRc; +} + +/***********************************************************************/ +} /* namespace */ +#endif /* XB_SQL_SUPPORT */ + diff --git a/src/sql/xbinsert.cpp b/src/sql/xbinsert.cpp new file mode 100755 index 0000000..1095202 --- /dev/null +++ b/src/sql/xbinsert.cpp @@ -0,0 +1,190 @@ +/* xbinsert.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_SQL_SUPPORT + +namespace xb{ + + +/***********************************************************************/ +xbInt16 xbSql::SqlInsert( const xbString &sCmdLine ){ + + // expected format: + // INSERT INTO tablename (field1, field2, field3,...) VALUES ( 'charval', numval, 'what is the correct odbc date format to use? CCYYMMDD'); + + xbInt16 iRc = 0; + xbInt16 iErrorStop = 0; + xbString sTableName; + xbDbf * dbf = NULL; + + xbString sWork1; + xbString sFieldList; + xbString sDataList; + xbString sFieldName; + xbString sFieldData; + + // queue the memo data to post after the append occurs + // dbase does not support usage of memo fields with insert commands + xbLinkList llMemoFieldNos; + xbLinkList llMemoFieldData; + + try{ + // retrieve table name + sTableName.ExtractElement( sCmdLine, ' ', 3, 0 ); + sTableName.Trim(); + + // if not open, attempt to open it + dbf = xbase->GetDbfPtr( sTableName ); + + if( !dbf ){ + if(( iRc = xbase->OpenHighestVersion( sTableName, "", &dbf )) != XB_NO_ERROR ){ + iErrorStop = 10; + throw iRc; + } + } + + if( !dbf ){ + iErrorStop = 20; + iRc = XB_FILE_NOT_FOUND; + throw iRc; + } + + // blank the record buffer + dbf->BlankRecord(); + + sWork1.ExtractElement( sCmdLine, ')', 1, 0 ); + sFieldList.ExtractElement( sWork1, '(', 2, 0 ); + + sDataList.ExtractElement( sCmdLine, '(', 3, 0 ); + sDataList.Trim(); + sDataList.ZapTrailingChar( ')' ); + + xbUInt32 iFldCnt = sFieldList.CountChar( ',' ); + xbUInt32 iDataCnt = sDataList.CountChar( ',', 1 ); + + // verify there are the same count in the field list and values list + if( iFldCnt != iDataCnt ){ + iErrorStop = 30; + iRc = XB_SYNTAX_ERROR; + throw iRc; + } + + iFldCnt++; + xbInt16 iFldNo = -1; + char cFldType = 0x00; + for( xbUInt32 i = 1; i <= iFldCnt; i++ ){ + sFieldName.ExtractElement( sFieldList, ',', i, 0 ); + sFieldName.Trim(); + if(( iRc = dbf->GetFieldNo( sFieldName, iFldNo )) != XB_NO_ERROR ){ + iErrorStop = 40; + throw iRc; + } + if(( iRc = dbf->GetFieldType( iFldNo, cFldType )) != XB_NO_ERROR ){ + iErrorStop = 50; + throw iRc; + } + + // get the field data here + sFieldData.ExtractElement( sDataList, ',', i, 1 ); + sFieldData.Trim(); + + // remove beginning and ending quotes + if(( sFieldData[1] == '\'' && sFieldData[sFieldData.Len()] == '\'') || (sFieldData[1] == '"' && sFieldData[sFieldData.Len()] == '"' )){ + sFieldData.Remove( sFieldData.Len(), 1 ); + sFieldData.Remove( 1, 1 ); + } + + switch( cFldType ){ + case 'C': + case 'N': + case 'L': + if(( iRc = dbf->PutField( iFldNo, sFieldData )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + break; + + case 'D': + // assumes input date format of yyyy-mm-dd + if( sFieldData.Len() != 10 || sFieldData[5] != '-' || sFieldData[8] != '-' ){ + iErrorStop = 110; + iRc = XB_INVALID_DATA; + throw iRc; + } + sWork1 = sFieldData; + sWork1.Remove( 8, 1 ); + sWork1.Remove( 5, 1 ); + if(( iRc = dbf->PutField( iFldNo, sWork1 )) != XB_NO_ERROR ){ + iErrorStop = 120; + throw iRc; + } + break; + + case 'M': + llMemoFieldNos.InsertAtFront( iFldNo ); + llMemoFieldData.InsertAtFront( sFieldData ); + break; + + default: + iErrorStop= 150; + iRc = XB_INVALID_FIELD_TYPE; + throw iRc; + } + } + + if(( iRc = dbf->AppendRecord()) != XB_NO_ERROR ){ + iErrorStop = 300; + throw iRc; + } + + // Add any memo fields + xbLinkListNode * llN = llMemoFieldNos.GetHeadNode(); + xbLinkListNode * llD = llMemoFieldData.GetHeadNode(); + xbUInt32 ulCnt = llMemoFieldNos.GetNodeCnt(); + for( xbUInt32 i = 0; i < ulCnt; i++ ){ + iFldNo = llN->GetKey(); + sFieldData = llD->GetKey(); + if(( iRc = dbf->UpdateMemoField( iFldNo, sFieldData )) != XB_NO_ERROR ){ + iErrorStop = 400; + throw iRc; + } + llN = llN->GetNextNode(); + llD = llD->GetNextNode(); + } + + if(( iRc == dbf->Commit()) != XB_NO_ERROR ){ + iErrorStop = 500; + throw iRc; + } + } + catch (xbInt16 iRc ){ + xbString sMsg; + xbase->WriteLogMessage( sCmdLine ); + sMsg.Sprintf( "xbSql::SqlInsert() Exception Caught. Error Stop = [%d] rc = [%d] table = [%s] field = [%s] data = [%s]", iErrorStop, iRc, sTableName.Str(), sFieldName.Str(), sFieldData.Str() ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + if( dbf ) + dbf->Abort(); + } + return iRc; + +} + +/***********************************************************************/ +} /* namespace */ +#endif /* XB_SQL_SUPPORT */ + diff --git a/src/sql/xbset.cpp b/src/sql/xbset.cpp new file mode 100755 index 0000000..98de309 --- /dev/null +++ b/src/sql/xbset.cpp @@ -0,0 +1,91 @@ +/* xbset.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_SQL_SUPPORT + +namespace xb{ + + +/***********************************************************************/ +xbInt16 xbSql::SqlSet( const xbString &sCmdLine ){ + + // std::cout << "SET [" << sCmdLine << "]\n"; + // expected format: + // SET ATTRIBUTE = DATAVALUE + // SET ATTRIBUTE = ^ (to delete an entry) + + xbInt16 iRc = 0; + xbInt16 iErrorStop = 0; + xbUInt32 ulPos; + + try{ + + xbString sCmd = sCmdLine; + sCmd.Trim(); + sCmd.ZapTrailingChar( ';' ); + sCmd.Trim(); + + // drop off the first node "SET" + ulPos = sCmd.Pos( ' ' ); + sCmd.Ltrunc( ulPos ); + sCmd.Ltrim(); + + xbString sNode1 = sCmd; + sNode1.ToUpperCase(); + + if( sNode1 == "SET" ) { + uda.DumpUda(); + + } else { + + xbString sKey; + sKey.ExtractElement( sCmd, '=', 1, 0 ); + sKey.Trim(); + + xbString sToken; + sToken.ExtractElement( sCmd, '=', 2, 0 ); + sToken.Trim(); + + + if( sToken == '^' ){ + if(( iRc = uda.DelTokenForKey( sKey )) != XB_NO_ERROR ){ + iErrorStop = 10; + throw iRc; + } + } else { + if(( iRc = uda.UpdTokenForKey( sKey, sToken )) != XB_NO_ERROR ){ + iErrorStop = 10; + throw iRc; + } + } + } + + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbSql::SqlSet() Exception Caught. Error Stop = [%d] rc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + + return iRc; +} + +/***********************************************************************/ +} /* namespace */ +#endif /* XB_SQL_SUPPORT */ + diff --git a/src/sql/xbsql.cpp b/src/sql/xbsql.cpp new file mode 100755 index 0000000..25cd2c8 --- /dev/null +++ b/src/sql/xbsql.cpp @@ -0,0 +1,143 @@ +/* xbsql.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_SQL_SUPPORT + +namespace xb{ + +/***********************************************************************/ +xbSql::xbSql( xbXBase *x ){ + xbase = x; + xbString sIxType; + + #ifdef XB_MDX_SUPPORT + sIxType = "MDX"; + #endif + + #ifdef XB_NDX_SUPPORT + if( sIxType == "" ) + sIxType = "NDX"; + #endif + + if( sIxType != "" ) + uda.AddTokenForKey( "IXTYPE", sIxType ); + +} +/***********************************************************************/ +xbSql::~xbSql(){ +} + +/***********************************************************************/ +xbInt16 xbSql::ExecuteNonQuery( const xbString &sCmdLine ) { + +// std::cout << "\n\n\nExecute() " << sCmdLine.Str() << "\n"; + + xbInt16 iRc = XB_NO_ERROR; + xbString sCmd = sCmdLine; + + sCmd.Trim(); + xbString sNode1; + sNode1.ExtractElement( sCmd.Str(), ' ', 1, 0 ); + sNode1.ToUpperCase(); + sNode1.Trim(); + +// std::cout << "node 1 = " << sNode1.Str() << std::endl; + + if( sNode1 == "CREATE" ){ + + xbString sNode2; + sNode2.ExtractElement( sCmd.Str(), ' ', 2, 0 ); + sNode2.ToUpperCase(); + sNode2.Trim(); + if( sNode2 == "TABLE" ) + iRc = SqlCreateTable( sCmd ); + else if( sNode2 == "INDEX" || sNode2 == "UNIQUE" ) + iRc = SqlCreateIndex( sCmd ); + + else + iRc = XB_INVALID_FUNCTION; + + } else if( sNode1 == "DROP" ){ + + xbString sNode2; + sNode2.ExtractElement( sCmd.Str(), ' ', 2, 0 ); + sNode2.ToUpperCase(); + sNode2.Trim(); + + if( sNode2 == "TABLE" ) + iRc = SqlDropTable( sCmd ); + + else if( sNode2 == "INDEX" ) + iRc = SqlDropIndex( sCmd ); + + } else if( sNode1 == "HELP" ) + SqlHelp(); + + else if( sNode1 == "INSERT" ){ + + iRc = SqlInsert( sCmd ); + + // else if( sNode1 == "UPDATE" ) + // iRc = SqlUpdate( sCmd ); + } + else if( sNode1 == "USE" ) + iRc = SqlUse( sCmd ); + + else if( sNode1 == "SET" ) + iRc = SqlSet( sCmd ); + + else + return XB_INVALID_FUNCTION; + + return iRc; +} + +/***********************************************************************/ +xbXBase *xbSql::GetXbasePtr() const { + return xbase; +} + + +/***********************************************************************/ +void xbSql::SqlHelp() const { + std::cout << "************************" << std::endl; + std::cout << "Valid XBase SQL commands" << std::endl << std::endl; + std::cout << "HELP" << std::endl; + std::cout << "USE /dir/to/dbf/tables" << std::endl; + std::cout << "CREATE TABLE" << std::endl << std::endl << std::endl; +} + +/***********************************************************************/ +xbInt16 xbSql::SqlUse( const xbString &sCmdLine ){ + + xbString sNode2; + sNode2.ExtractElement( sCmdLine.Str(), ' ', 2, 0 ); + sNode2.Trim(); + xbase->SetDataDirectory( sNode2 ); +// std::cout << "USE " << sNode2 << std::endl; + return XB_NO_ERROR; +} + +/***********************************************************************/ +/***********************************************************************/ +/***********************************************************************/ +/***********************************************************************/ +/***********************************************************************/ + +/***********************************************************************/ +} /* namespace */ +#endif /* XB_SQL_SUPPORT */ diff --git a/src/tests/tstfuncs.cpp b/src/tests/tstfuncs.cpp new file mode 100755 index 0000000..c499d3f --- /dev/null +++ b/src/tests/tstfuncs.cpp @@ -0,0 +1,581 @@ +/* tstfuncs.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 + +*/ + +time_t timer; + +xbDouble ChronTime(); +void InitTime(); + +xbBool dblEquals( xbDouble a, xbDouble b, xbDouble epsilon ); +xbInt16 TestMethod( xbInt16 PrintOption, const char * title, const xbString &result, const char * expectedResult, size_t expectedLen ); +xbInt16 TestMethod( xbInt16 PrintOption, const char * title, char result, char expectedResult ); +xbInt16 TestMethod( xbInt16 PrintOption, const char * title, xbInt32 result, xbInt32 expectedResult ); +//xbInt16 TestMethod( xbInt16 PrintOption, const char * title, xbUInt32 result, xbUInt32 expectedResult ); +xbInt16 TestMethod( xbInt16 PrintOption, const char * title, xbUInt64 result, xbUInt64 expectedResult ); +xbInt16 TestMethod( xbInt16 PrintOption, const char * title, xbFloat result, xbFloat expectedResult ); +xbInt16 TestMethod( xbInt16 PrintOption, const char * title, xbDouble result, xbDouble expectedResult ); +xbInt16 TestMethod( xbInt16 PrintOption, const char * title, xbDouble result, xbDouble expectedResult, xbDouble dEpsilon ); + + +xbInt16 FileCompare( xbXBase &x, xbInt16 iPo, const xbString &sFile1, const xbString &sFile2, const xbString &sSkipBytes ); + + +#ifdef XB_LOCKING_SUPORT +xbInt16 SetCmd( xbXBase &x, const xbString &sFileName, const xbString &sCmd, const char cSrc, xbInt16 iPo ); +xbInt16 GetCmd( xbXBase &x, const xbString &sFileName, xbString &sCmd, const char cSrc, xbInt16 iPo ); +#endif + +#if defined( XB_DBF4_SUPPORT ) && defined( XB_MEMO_SUPPORT ) + +xbInt16 TestDbt4Method( xbInt16 PrintOption, const char * title, xbMemo * m, xbUInt32 ulHdrNext, xbUInt32 ulBlockCnt, xbString sNodeChain ); +xbInt16 TestDbt4Method( xbInt16 PrintOption, const char * title, xbMemo * m, xbUInt32 ulHdrNext, xbUInt32 ulBlockCnt, xbString sNodeChain ){ + + xbUInt32 ulLastDataBlock = 0L; + xbUInt32 ulHdrNextBlock = 0L; + xbInt16 iRc = 0; + + iRc = m->GetHdrNextBlock( ulHdrNextBlock ); + if( iRc != XB_NO_ERROR ){ + std::cout << std::endl << "[FAIL 1] " << title << std::endl; + std::cout << "Error [" << iRc << "] in GetHdrNextBlock" << std::endl; + return -1; + } + + iRc = m->CalcLastDataBlock( ulLastDataBlock ); + if( iRc != XB_NO_ERROR ){ + std::cout << std::endl << "[FAIL 2] " << title << std::endl; + std::cout << "Error [" << iRc << "] in CalcLastDataBlock" << std::endl; + return -1; + } + + if( ulHdrNext > 0 ){ + if( ulHdrNextBlock != ulHdrNext ){ + std::cout << std::endl << "[FAIL 3] " << title << std::endl; + std::cout << "Expected Header Next Block = [" << ulHdrNext << "] Actual = [" << ulHdrNextBlock << "]" << std::endl; + return -1; + } + if( PrintOption == 2 ){ + xbString sMsg; + sMsg.Sprintf( "[PASS][%3.4f] %s Expected matches actual Len=[%ld] Data=[%lds]\n", + ChronTime(), title, ulHdrNextBlock, ulHdrNext ); + std::cout << sMsg.Str(); + // std::cout << "[PASS][" << ChronTime() << "] " << title << " Expected matches actual HdrBlock=[" << ulHdrNextBlock << "] Data=[" << ulHdrNext << "]" << std::endl; + } else if( PrintOption == 1 ) + std::cout << "[PASS] " << title << std::endl; + } + + if( ulBlockCnt > 0 ){ + if( ulLastDataBlock != ulBlockCnt ){ + std::cout << std::endl << "[FAIL 4] " << title << std::endl; + std::cout << "Expected Block Count = [" << ulBlockCnt << "] Actual = [" << ulLastDataBlock << "]" << std::endl; + return -1; + } + if( PrintOption == 2 ){ + xbString sMsg; + sMsg.Sprintf( "[PASS][%3.4f] %s Expected matches actual Len=[%ld] Data=[%lds]\n", + ChronTime(), title, ulLastDataBlock, ulBlockCnt ); + std::cout << sMsg.Str(); + // std::cout << "[PASS][" << ChronTime() << "] " << title << " Expected matches actual LastBlock=[" << ulLastDataBlock << "] Data=[" << ulBlockCnt << "]" << std::endl; + } else if( PrintOption == 1 ) + std::cout << "[PASS] " << title << std::endl; + } + + #ifdef XB_DEBUG_SUPPORT + xbMemoDbt4 *m4 = (xbMemoDbt4 *) m; + if( sNodeChain != "-1" ){ + xbUInt32 ulNode = ulHdrNextBlock; + xbUInt32 ulNextBlock; + xbUInt32 ulFreeBlockCnt; + xbString sActualNodeChain; + + while( ulNode < ulLastDataBlock ){ + iRc = m4->ReadFreeBlockHeader( ulNode, ulNextBlock, ulFreeBlockCnt ); + if( iRc != XB_NO_ERROR ){ + std::cout << std::endl << "[FAIL 5] " << title << std::endl; + std::cout << "Error [" << iRc << "] in CalcLastDataBlock" << std::endl; + return -1; + } + if( sActualNodeChain != "" ) + sActualNodeChain += ","; + sActualNodeChain.Sprintf( "%s%ld,%ld,%ld", sActualNodeChain.Str(), ulNode, ulFreeBlockCnt, ulNextBlock ); + ulNode = ulNextBlock; + } + if( sNodeChain != sActualNodeChain ){ + std::cout << std::endl << "[FAIL 6] " << title << std::endl; + std::cout << "Expected Block Chain = [" << sNodeChain.Str() << "] Actual = [" << sActualNodeChain.Str() << "]" << std::endl; + return -1; + } + if( PrintOption == 2 ){ + xbString sMsg; + sMsg.Sprintf( "[PASS][%3.4f] %s Expected matches actual Len=[%d] Data=[%s]\n", ChronTime(), title, sNodeChain.Str() ); + std::cout << sMsg.Str(); + //std::cout << "[PASS][" << ChronTime() << "] " << title << " Expected matches actual BlockChain=[" << sNodeChain.Str() << "]" << std::endl; + } else if( PrintOption == 1 ) + std::cout << "[PASS] " << title << std::endl; + } + #endif // XB_DEBUG_SUPPORT" + + return 0; +} +#endif + + + +xbInt16 TestMethod( xbInt16 PrintOption, const char * title, const xbString &sResult, const char * expectedResult, size_t expectedLen ){ + xbInt16 rc = 0; + + if( sResult == expectedResult && sResult.Len() == expectedLen ){ + if( PrintOption == 2 ){ + xbString sMsg; + sMsg.Sprintf( "[PASS][%3.4f] %s Expected matches actual Len=[%d] Data=[%s]\n", ChronTime(), title, expectedLen, expectedResult ); + std::cout << sMsg.Str(); + } else if( PrintOption == 1 ) { + std::cout << "[PASS] " << title << std::endl; + } + } else { + std::cout << std::endl << "[FAIL 1] " << title << std::endl; + std::cout << " Expected Len = [" << expectedLen << "] Expected Data = [" << expectedResult << "]" << std::endl; + std::cout << " Actual Len = [" << sResult.Len() << "] Actual Data = [" << sResult.Str() << "]" << std::endl; + + if( sResult.Len() == expectedLen ) + printf( "lengths match\n" ); + else + printf( "lengths dont match\n" ); + + if( sResult == expectedResult ) + printf( "result matches\n" ); + else + printf( "result does not match\n" ); + + rc = -1; + } + return rc; +} + +xbInt16 TestMethod( xbInt16 PrintOption, const char * title, char result, char expectedResult ){ + xbInt16 rc = 0; + if( result == expectedResult ){ + if( PrintOption == 2 ){ + xbString sMsg; + sMsg.Sprintf( "[PASS][%3.4f] %s Expected matches actual Data=[%c]\n", ChronTime(), title, expectedResult ); + std::cout << sMsg.Str(); + // std::cout << "[PASS][" << ChronTime() << "] " << title << " Expected matches actual Data=[" << expectedResult << "]" << std::endl; + } else if( PrintOption == 1 ) + std::cout << "[PASS] " << title << std::endl; + rc = 0; + } else { + std::cout << std::endl << "[FAIL 2] " << title << std::endl; + std::cout << " Expected Data = [" << expectedResult << "]" << std::endl; + std::cout << " Actual Data = [" << result << "]" << std::endl; + rc = -1; + } + return rc; +} + +xbInt16 TestMethod( xbInt16 PrintOption, const char * title, xbInt32 result, xbInt32 expectedResult ){ + xbInt16 rc = 0; + if( result == expectedResult ){ + if( PrintOption == 2 ){ + xbString sMsg; + sMsg.Sprintf( "[PASS][%3.4f] %s Expected matches actual Data=[%d]\n", ChronTime(), title, expectedResult ); + std::cout << sMsg.Str(); + // std::cout << "[PASS][" << ChronTime() << "] " << title << " Expected matches actual Data=[" << expectedResult << "]" << std::endl; + } else if( PrintOption == 1 ) + std::cout << "[PASS] " << title << std::endl; + rc = 0; + } else { + std::cout << std::endl << "[FAIL 3] " << title << std::endl; + std::cout << " Expected Data = [" << expectedResult << "]" << std::endl; + std::cout << " Actual Data = [" << result << "]" << std::endl; + rc = -1; + } + return rc; +} + + +/* +xbInt16 TestMethod( xbInt16 PrintOption, const char * title, xbUInt32 result, xbUInt32 expectedResult ){ + xbInt16 rc = 0; + if( result == expectedResult ){ + if( PrintOption == 2 ) + std::cout << "[PASS][" << ChronTime() << "] " << title << " Expected matches actual Data=[" << expectedResult << "]" << std::endl; + else if( PrintOption == 1 ) + std::cout << "[PASS] " << title << std::endl; + rc = 0; + } else { + std::cout << std::endl << "[FAIL 3] " << title << std::endl; + std::cout << " Expected Data = [" << expectedResult << "]" << std::endl; + std::cout << " Actual Data = [" << result << "]" << std::endl; + rc = -1; + } + return rc; +} +*/ + +xbInt16 TestMethod( xbInt16 PrintOption, const char * title, xbUInt64 result, xbUInt64 expectedResult ){ + xbInt16 rc = 0; + if( result == expectedResult ){ + if( PrintOption == 2 ){ + xbString sMsg; + sMsg.Sprintf( "[PASS][%f] %3.4s Expected matches actual Data=[%ld]\n", ChronTime(), title, expectedResult ); + std::cout << sMsg.Str(); + // std::cout << "[PASS][" << ChronTime() << "] " << title << " Expected matches actual Data=[" << expectedResult << "]" << std::endl; + } else if( PrintOption == 1 ) + std::cout << "[PASS] " << title << std::endl; + rc = 0; + } else { + std::cout << std::endl << "[FAIL 4] " << title << std::endl; + std::cout << " Expected Data = [" << expectedResult << "]" << std::endl; + std::cout << " Actual Data = [" << result << "]" << std::endl; + rc = -1; + } + return rc; +} + +xbInt16 TestMethod( xbInt16 PrintOption, const char * title, xbFloat result, xbFloat expectedResult ){ + xbInt16 rc = 0; + if( result == expectedResult ){ + if( PrintOption == 2 ){ + xbString sMsg; + sMsg.Sprintf( "[PASS][%f] %3.4s Expected matches actual Data=[%f]\n", ChronTime(), title, expectedResult ); + std::cout << sMsg.Str(); + // std::cout << "[PASS][" << ChronTime() << "] " << title << " Expected matches actual Data=[" << expectedResult << "]" << std::endl; + } else if( PrintOption == 1 ) + std::cout << "[PASS] " << title << std::endl; + rc = 0; + } else { + std::cout << std::endl << "[FAIL 5] " << title << std::endl; + std::cout << " Expected Data = [" << expectedResult << "]" << std::endl; + std::cout << " Actual Data = [" << result << "]" << std::endl; + rc = -1; + } + return rc; +} + +xbInt16 TestMethod( xbInt16 PrintOption, const char * title, xbDouble result, xbDouble expectedResult ){ + xbInt16 rc = 0; + if( result == expectedResult ){ + // if( dblEquals( result, expectedResult ) == 0 ){ + if( PrintOption == 2 ){ + xbString sMsg; + sMsg.Sprintf( "[PASS][%3.4f] %s Expected matches actual Data=[%f]\n", ChronTime(), title, expectedResult ); + std::cout << sMsg.Str(); + //std::cout << "[PASS][" << ChronTime() << "] " << title << " Expected matches actual Data=[" << expectedResult << "]" << std::endl; + } else if( PrintOption == 1 ) + std::cout << "[PASS] " << title << std::endl; + rc = 0; + } else { + std::cout << std::endl << "[FAIL 6] " << title << std::endl; + std::cout << " Expected Data = [" << expectedResult << "]" << std::endl; + std::cout << " Actual Data = [" << result << "]" << std::endl; + rc = -1; + } + return rc; +} + + +// next three routines used to do a compare on double values +xbDouble dblMax( xbDouble a, xbDouble b ){ + if( a > b ) + return a; + else + return b; +} +xbDouble dblMin( xbDouble a, xbDouble b ){ + if( a < b ) + return a; + else + return b; +} +xbBool dblEquals( xbDouble a, xbDouble b, xbDouble epsilon = 0.0001 ){ + + // std::cout << "a=[" << a << "] b=[ " << b << "] dblmax [" << dblAbsMax( a, b ) << "] min [" << dblAbsMin( a, b ) << "]\n"; + if( a < 0 && b >= 0 ) + return xbFalse; + else if( a >= 0 && b < 0 ) + return xbFalse; + if( (dblMax( a ,b ) - dblMin( a, b )) < epsilon ) + return xbTrue; + else + { +// std::cout << "no match a=[" << a << "] b=[" << b << "] dblmax [" << dblAbsMax( a, b ) << "] min [" << dblAbsMin( a, b ) << "]\n"; + return xbFalse; + } +} + +xbInt16 TestMethod( xbInt16 PrintOption, const char * title, xbDouble result, xbDouble expectedResult, xbDouble dEpsilon ){ + xbInt16 rc; + if( dblEquals( result, expectedResult, dEpsilon ) == xbTrue ){ + if( PrintOption == 2 ){ + xbString sMsg; + sMsg.Sprintf( "[PASS][%3.4f] %s Expected matches actual Data=[%f]\n", ChronTime(), title, expectedResult ); + std::cout << sMsg.Str(); + //std::cout << "[PASS][" << ChronTime() << "] " << title << " Expected matches actual Data=[" << expectedResult << "]" << std::endl; + } else if( PrintOption == 1 ) + std::cout << "[PASS] " << title << std::endl; + rc = 0; + } else { + std::cout << std::endl << "[FAIL 6] " << title << std::endl; + std::cout << " Expected Data = [" << expectedResult << "]" << std::endl; + std::cout << " Actual Data = [" << result << "]" << std::endl; + rc = -1; + } + return rc; +} + + +#ifdef XB_LOCKING_SUPPORT + +xbInt16 GetCmd( xbXBase &x, const xbString &sFileName, xbString &sCmd, const char cSrc, xbInt16 iPo ){ + xbInt16 iTryCnt = 0; + xbInt16 iMaxTries = 10; + char cInBuf[256]; + FILE *f; + xbBool bDone = xbFalse; + xbString sMsg; + + while( !bDone && iTryCnt < iMaxTries ){ + + #ifdef HAVE__FSOPEN_F + // 0x40 is SH_DENYNO or _SH_DENYNO + if(( f = _fsopen( sFileName.Str(), "r", 0x40 )) == NULL){ + sMsg.Sprintf( "GetCmd _fsopen() error opening [%s]", sFileName.Str() ); + x.WriteLogMessage( sMsg ); + std::cout << sMsg.Str() << "\n"; + x.xbSleep( 250 ); + iTryCnt++; + } + + #else + + if(( f = fopen( sFileName.Str(), "r" )) == NULL ){ + sMsg.Sprintf( "GetCmd fopen() error opening [%s]", sFileName.Str() ); + x.WriteLogMessage( sMsg ); + std::cout << sMsg.Str() << "\n"; + x.xbSleep( 250 ); + iTryCnt++; + } + #endif + + else { + memset( cInBuf, 0x00, 256 ); + fgets( cInBuf, 256, f ); + fclose( f ); + sCmd = cInBuf; + if( sCmd != "" ) + bDone = xbTrue; + else{ + x.xbSleep( 250 ); + } + } + } + + #ifdef XB_LOGGING_SUPPORT + if( iPo > 0 ){ + sMsg.Sprintf( "[%c] GetCmd [%s]", cSrc, sCmd.Str()); + x.WriteLogMessage( sMsg ); + x.FlushLog(); + } + #endif + + if( iTryCnt >= iMaxTries ) + return -1; + else + return 0; +} + +xbInt16 SetCmd( xbXBase &x, const xbString &sFileName, const xbString &sCmd, const char cSrc, xbInt16 iPo ){ + + xbInt16 iTryCnt = 0; + FILE *f; + xbBool bDone = xbFalse; + + #ifdef XB_LOGGING_SUPPORT + if( iPo > 0 ){ + xbString sMsg; + sMsg.Sprintf( "[%c] SetCmd [%s] FileName [%s]", cSrc, sCmd.Str(), sFileName.Str()); + x.WriteLogMessage( sMsg ); + x.FlushLog(); + } + #endif + + while( !bDone && iTryCnt < 10 ){ + + #ifdef HAVE__FSOPEN_F + // 0x40 is SH_DENYNO or _SH_DENYNO + if(( f = _fsopen( sFileName.Str(), "r", 0x40 )) == NULL){ + x.xbSleep( 250 ); + iTryCnt++; + } + + #else + if(( f = fopen( sFileName.Str(), "w" )) == NULL ){ + x.xbSleep( 250 ); + iTryCnt++; + } + #endif + + else { + fputs( sCmd.Str(), f ); + fflush( f ); + fclose( f ); + bDone = xbTrue; + } + } + if( iTryCnt == 10 ) + return -1; + else{ + x.xbSleep( 250 ); + return 0; + } +} + + + +#endif +void InitTime(){ + time( &timer ); +} + + +xbDouble ChronTime(){ + time_t tWork = timer; + time( &timer ); + return difftime( timer, tWork ); + +} + + + +xbInt16 FileCompare( xbXBase &x, xbInt16 iPo, const xbString &sFile1, const xbString &sFile2, const xbString &sSkipBytes ){ + +/* + iPo print option + sFile1 - File 1 + sFile2 - File 2 + sSkipBytes - comma separated list of bytes to skip the compare on +*/ + + xbInt16 iRc = 0; + xbInt16 iErrorStop = 0; + xbInt16 iErrorCnt = 0; + xbFile f1( &x ); + xbFile f2( &x ); + xbString sMsg; + char c1; + char c2; + + if( iPo > 0 ){ + std::cout << "FileCompare\n"; + std::cout << "Skip bytes = " << sSkipBytes.Str() << std::endl; + std::cout << sFile1.Str() << std::endl; + std::cout << sFile2.Str() << std::endl; + } + + xbLinkListOrd llO; + llO.SetDupKeys( 0 ); + if( sSkipBytes.Len() > 0 ){ + xbString sNode; + xbUInt32 iCommaCnt = sSkipBytes.CountChar( ',' ); + for( xbUInt32 i = 0; i < (iCommaCnt+1); i++ ){ + sNode.ExtractElement( sSkipBytes, ',', i+1, 0 ); + //std::cout << "Adding key = " << atol( sNode.Str()) << std::endl; + llO.InsertKey( atol( sNode.Str())); + } + } + + xbInt32 iPos = 0; + try{ + + if(( iRc = f1.xbFopen( "r", sFile1, XB_SINGLE_USER )) != XB_NO_ERROR ){ + iErrorStop = 10; + iErrorCnt++; + throw iRc; + } + + if(( iRc = f2.xbFopen( "r", sFile2, XB_SINGLE_USER )) != XB_NO_ERROR ){ + iErrorStop = 20; + iErrorCnt++; + throw iRc; + } + + xbUInt64 uiFs1; + xbUInt64 uiFs2; + + if(( iRc = f1.GetFileSize( uiFs1 )) != XB_NO_ERROR ){ + iErrorStop = 30; + iErrorCnt++; + throw iRc; + } + + if(( iRc = f2.GetFileSize( uiFs2 )) != XB_NO_ERROR ){ + iErrorStop = 40; + iErrorCnt++; + throw iRc; + } + + // std::cout << "file size " << uiFs1 << " " << uiFs2 << "\n"; + + f1.xbRewind(); + f2.xbRewind(); + + while( !f1.xbFeof() && !f2.xbFeof()){ + f1.xbFgetc( c1 ); + f2.xbFgetc( c2 ); + if( c1 != c2 ){ + if( !llO.KeyExists( iPos )){ + iErrorCnt++; + if( iPo > 0 ){ + sMsg.Sprintf( "Diff %ld %x %x\n", iPos, c1, c2 ); + std::cout << sMsg.Str(); + } + } + } + iPos++; + } + } + + catch( xbInt16 ){ + if( iPo > 0 ){ + std::cout << "File Compare Error Count = " << iErrorCnt << std::endl; + + switch( iErrorStop ){ + case 10: + std::cout << "Error opening file = " << sFile1.Str() << std::endl; + break; + case 20: + std::cout << "Error opening file = " << sFile2.Str() << std::endl; + break; + case 30: + std::cout << "GetFileSize() error " << sFile1.Str() << std::endl; + break; + case 40: + std::cout << "GetFileSize() error " << sFile2.Str() << std::endl; + break; + default: + break; + } + } + } + + if( f1.FileIsOpen() ) + f1.xbFclose(); + if( f1.FileIsOpen() ) + f1.xbFclose(); + + return iRc; +} + + diff --git a/src/tests/xb_test_bcd.cpp b/src/tests/xb_test_bcd.cpp new file mode 100755 index 0000000..1eab20f --- /dev/null +++ b/src/tests/xb_test_bcd.cpp @@ -0,0 +1,128 @@ +/* xb_test_bcd.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 + +*/ + +// This program tests the xb bcd functions +// usage: xb_test_expnode QUITE|NORMAL|VERBOSE + +#include "xbase.h" +using namespace xb; + +#include "tstfuncs.cpp" + +/**************************************************************************/ + +int main( int argCnt, char **av ) +{ + + #ifdef XB_INDEX_SUPPORT + + xbInt16 iRc = 0; + xbInt16 iPo = 1; /* print option */ + /* 0 - QUIET */ + /* 1 - NORMAL */ + /* 2 - VERBOSE */ + + if( argCnt > 1 ) { + if( av[1][0] == 'Q' ) + iPo = 0; + else if( av[1][0] == 'V' ) + iPo = 2; + } + + + xbXBase x; + + #ifdef XB_LOGGING_SUPPORT + x.EnableMsgLogging(); + if( iPo ){ + std::cout << "Logfile is [" << x.GetLogFqFileName().Str() << "]" << std::endl; + } + xbString sMsg; + sMsg.Sprintf( "Program [%s] initializing...", av[0] ); + x.WriteLogMessage( sMsg ); + #endif + + x.SetDataDirectory( PROJECT_DATA_DIR ); + x.EnableMsgLogging(); + + + if( iPo > 0 ){ + std::cout << "XBase bcd testing program.." << std::endl; + std::cout << "This program tests the bcd logic." << std::endl; + } + + iRc += TestMethod( iPo, "sizeof( xbBcdStruct )", (xbInt32) sizeof( xbBcdStruct ), (xbInt32) 12 ); + + xbString s1( -12345.60 ); + xbBcd bcd1( s1 ); + xbDouble d1; + bcd1.ToDouble( d1 ); + xbString s2( d1 ); + bcd1.ToString( s2 ); + + iRc += TestMethod( iPo, "String Constructor1", d1, -12345.6 ); + iRc += TestMethod( iPo, "String Constructor2", s2.Str(), "-12345.6", 8 ); + + d1 = 0034.04; + xbBcd bcd2( d1 ); + bcd2.ToString( s2 ); + iRc += TestMethod( iPo, "xbDouble Constructor1", s2.Str(), "34.04", 5 ); + + // test the sign comparison logic + xbInt16 iComp = bcd1.Compare( bcd2 ); + iRc += TestMethod( iPo, "Sign Compare 1", iComp, -1 ); + iComp = bcd2.Compare( bcd1 ); + iRc += TestMethod( iPo, "Sign Compare 2", iComp, 1 ); + + // bcd length compare scenarios + bcd1.Set( 123 ); + iRc += TestMethod( iPo, "Length Compare 1", bcd1.Compare( 12 ), 1 ); + iRc += TestMethod( iPo, "Length Compare 2", bcd1.Compare( 1234 ), -1 ); + bcd1.Set( -456 ); + iRc += TestMethod( iPo, "Length Compare 3", bcd1.Compare( -12 ), -1 ); + iRc += TestMethod( iPo, "Length Compare 4", bcd1.Compare( -1234 ), 1 ); + + // same length, value compares + bcd1.Set( 11 ); + iRc += TestMethod( iPo, "Value Compare 1", bcd1.Compare( 10 ), 1 ); + + bcd1.Set( 111 ); + iRc += TestMethod( iPo, "Value Compare 2", bcd1.Compare( 110 ), 1 ); + + + bcd1.Set( 111.111 ); + iRc += TestMethod( iPo, "Value Compare 3", bcd1.Compare( 111.112 ), -1 ); + + bcd1.Set( -100 ); + iRc += TestMethod( iPo, "Value Compare 4", bcd1.Compare( -111 ), 1 ); + iRc += TestMethod( iPo, "Value Compare 5", bcd1.Compare( -99 ), -1 ); + + bcd1.Set( (xbDouble) 0 ); + iRc += TestMethod( iPo, "Value Compare 6", bcd1.Compare( (xbDouble) 0 ), 0 ); + + if( iPo > 0 || iRc < 0 ) + fprintf( stdout, "Total Errors = %d\n", iRc * -1 ); + + #ifdef XB_LOGGING_SUPPORT + sMsg.Sprintf( "Program [%s] terminating with [%d] errors...", av[0], iRc * -1 ); + x.WriteLogMessage( sMsg ); + #endif + + return iRc; + #else + return XB_NO_ERROR; + #endif // XB_INDEX_SUPPORT + +} diff --git a/src/tests/xb_test_date.cpp b/src/tests/xb_test_date.cpp new file mode 100755 index 0000000..1d9b523 --- /dev/null +++ b/src/tests/xb_test_date.cpp @@ -0,0 +1,189 @@ +/* xb_test_date.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 program tests the date class xbDate + +// usage: xb_test_date QUITE|NORMAL|VERBOSE + + +#include "xbase.h" + +using namespace xb; + +#include "tstfuncs.cpp" + + +int main( int argCnt, char **av ) +{ + int iRc = 0; + xbInt16 po = 1; /* print option */ + /* 0 - QUIET */ + /* 1 - NORMAL */ + /* 2 - VERBOSE */ + + if( argCnt > 1 ) { + if( av[1][0] == 'Q' ) + po = 0; + else if( av[1][0] == 'V' ) + po = 2; + } + +#ifdef XB_LOGGING_SUPPORT + xbXBase x; + x.EnableMsgLogging(); + if( po ){ + std::cout << "Logfile is [" << x.GetLogFqFileName().Str() << "]" << std::endl; + } + xbString sMsg; + sMsg.Sprintf( "Program [%s] initializing...", av[0] ); + x.WriteLogMessage( sMsg ); +#endif + InitTime(); + + xbDate d1; +#ifdef XB_DEBUG_SUPPORT + if( po > 0 ) + d1.DumpDateTables(); +#endif + + xbDate d2( "19890209" ); + xbString s3 = "20120709"; + xbDate d3( s3 ); + xbString s4; + xbString sFmt = ""; + xbString sOutDate; + xbString s; + + iRc += TestMethod( po, "FormatDate( '', sOutDate ) (sys default format)", d3.FormatDate( sFmt, sOutDate ), 0 ); + iRc += TestMethod( po, "FormatDate( '', sOutDate ) (sys default format)", sOutDate, "07/09/12", 8 ); + iRc += TestMethod( po, "CenturyOf()" , d3.CenturyOf(), 20 ); + iRc += TestMethod( po, "YearOf()", d3.YearOf(), 2012 ); + iRc += TestMethod( po, "MonthOf()", d3.MonthOf(), 7 ); + + iRc += TestMethod( po, "DayOf( XB_FMT_DAY )", d3.DayOf( XB_FMT_WEEK ), 2 ); + iRc += TestMethod( po, "DayOf( XB_FMT_MONTH )", d3.DayOf( XB_FMT_MONTH ), 9 ); + iRc += TestMethod( po, "DayOf( XB_FMT_YEAR )", d3.DayOf( XB_FMT_YEAR ), 191 ); + + iRc += TestMethod( po, "DateIsValid( '12345678' )", d3.DateIsValid( "12345678" ), 0 ); + iRc += TestMethod( po, "d3.DateIsValid( '20120708' )", d3.DateIsValid( "20120708" ), 1 ); + iRc += TestMethod( po, "d3.IsLeapYear()", d3.IsLeapYear(), 1 ); + iRc += TestMethod( po, "d2.IsLeapYear()", d2.IsLeapYear(), 0 ); + + d1.Set( "20010102" ); + iRc += TestMethod( po, "d1.Set('20010102')", d1.Str(), "20010102", 8 ); + iRc += TestMethod( po, "d1.CalcRollingCenturyForYear(10)", d1.CalcRollingCenturyForYear( 10 ), 20 ); + + iRc += TestMethod( po, "d1.JulianDays()", d1.JulianDays(), 2451912 ); + + + iRc += TestMethod( po, "d1.Set( '20140709' )", d1.Set( "20140709" ) , 0 ); + d1.CharDayOf( s ); + iRc += TestMethod( po, "d1.CharDayOf(s)", s, "Wednesday", 9 ); + + d1.CharMonthOf( s ); + iRc += TestMethod( po, "d1.CharMonthOf()", s, "July", 4 ); + + iRc += TestMethod( po, "d1.JulToDate8( 2451913 )", d1.JulToDate8( 2451912 ), 0 ); + iRc += TestMethod( po, "d1.JulToDate8( 2451913 )", d1.Str(), "20010102", 8 ); + iRc += TestMethod( po, "d1.LastDayOfMonth()", d1.LastDayOfMonth(), 0 ); + iRc += TestMethod( po, "d1.LastDayOfMonth()", d1.Str(), "20010131", 8 ); + + iRc += TestMethod( po, "d1.CTOD( '12/01/02' )", d1.CTOD( "12/01/02" ), 0 ); + iRc += TestMethod( po, "d1.CTOD( '12/01/02' )", d1.Str(), "20021201", 8 ); + + xbString s1, s2; + s1 = "MM-DD-YY"; + d1 = "20130402"; + iRc+= TestMethod( po, "d1.FormatDate( s1, sOutDate )", d1.FormatDate( s1, sOutDate ), 0 ); + iRc+= TestMethod( po, "d1.FormatDate( s1, sOutDate )", sOutDate, "04-02-13", 8 ); + + d1 = "20120203"; + iRc += TestMethod( po, "d1=20120203", d1.Str(), "20120203", 8 ); + + s1 = "20130405"; + d1 = s1; + iRc += TestMethod( po, "d1=s1", d1.Str(), "20130405", 8 ); + + d2 = d1; + iRc += TestMethod( po, "d2=d1", d2.Str(), "20130405", 8 ); + + d2+=5; + iRc += TestMethod( po, "d2+=5", d2.Str(), "20130410", 8 ); + + d2-=7; + iRc += TestMethod( po, "d2-=5", d2.Str(), "20130403", 8 ); + + d2++; + iRc += TestMethod( po, "d2++", d2.Str(), "20130404", 8 ); + + d2--; + iRc+= TestMethod( po, "d2--", d2.Str(), "20130403", 8 ); + iRc+= TestMethod( po, "d2-d1", d1-d2, 2 ); + iRc+= TestMethod( po, "d1+2", d1+2, "20130407", 8 ); + iRc+= TestMethod( po, "d1-5", d1-5, "20130402", 8 ); + +// d1.Dump( "d1" ); +// d2.Dump( "d2" ); + + iRc+= TestMethod( po, "d1==d2", d1==d2, 0 ); + d2 = d1; + iRc+= TestMethod( po, "d1==d2", d1==d2, 1 ); + iRc+= TestMethod( po, "d1>=d2", d1>=d2, 1 ); + iRc+= TestMethod( po, "d1<=d2", d1<=d2, 1 ); + iRc+= TestMethod( po, "d1!=d2", d1!=d2, 0 ); + d2++; + iRc+= TestMethod( po, "d1!=d2", d1!=d2, 1 ); + + iRc+= TestMethod( po, "d1>d2", d1>d2, 0 ); + iRc+= TestMethod( po, "d1>=d2", d1>=d2, 0 ); + iRc+= TestMethod( po, "d1d2", d1>d2, 1 ); + iRc+= TestMethod( po, "d1>=d2", d1>=d2, 1 ); + iRc+= TestMethod( po, "d1 0 || iRc < 0 ) + fprintf( stdout, "Total Errors = %d\n", iRc * -1 ); + +#ifdef XB_LOGGING_SUPPORT + sMsg.Sprintf( "Program [%s] terminating with [%d] errors...", av[0], iRc * -1 ); + x.WriteLogMessage( sMsg ); +#endif + + return iRc; +} diff --git a/src/tests/xb_test_dbf_v3_memos.cpp b/src/tests/xb_test_dbf_v3_memos.cpp new file mode 100755 index 0000000..e364741 --- /dev/null +++ b/src/tests/xb_test_dbf_v3_memos.cpp @@ -0,0 +1,233 @@ +/* xb_test_xbdbf_v3_memos.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 program tests the version III memo field logic + +// usage: xb_test_dbf_v3_memos QUITE|NORMAL|VERBOSE + + +#include "xbase.h" + +using namespace xb; + +#include "tstfuncs.cpp" + + +int main( int argCnt, char **av ) +{ + int rc = 0; + int rc2 = 0; + int po = 1; /* print option */ + /* 0 - QUIET */ + /* 1 - NORMAL */ + /* 2 - VERBOSE */ + + + if( argCnt > 1 ) { + if( av[1][0] == 'Q' ) + po = 0; + else if( av[1][0] == 'V' ) + po = 2; + } + + + xbSchema MyV3Record[] = + { + { "RECID", XB_NUMERIC_FLD, 8, 0 }, + { "MEMOFLD1", XB_MEMO_FLD, 10, 0 }, + { "MEMOFLD2", XB_MEMO_FLD, 10, 0 }, + { "",0,0,0 } + }; + + + xbXBase x; + +#ifdef XB_LOGGING_SUPPORT + + x.EnableMsgLogging(); + if( po ){ + std::cout << "Logfile is [" << x.GetLogFqFileName().Str() << "]" << std::endl; + } + xbString sMsg; + sMsg.Sprintf( "Program [%s] initializing...", av[0] ); + x.WriteLogMessage( sMsg ); + +#endif + + x.SetDataDirectory( PROJECT_DATA_DIR ); + x.EnableMsgLogging(); + x.DisableDefaultAutoLock(); + InitTime(); + + if( po > 0 ) + std::cout << "Default Data Directory is [" << x.GetDataDirectory().Str() << "]" << std::endl; + + xbDbf3 V3Dbf( &x ); /* version 3 dbf file */ + + rc2 = V3Dbf.CreateTable( "TestV3_memos.DBF", "TestV3", MyV3Record, XB_OVERLAY, XB_MULTI_USER ); + rc += TestMethod( po, "CreateTable()", (xbInt32) rc2, XB_NO_ERROR ); + + + if( rc2 ) + x.DisplayError( rc2 ); + else{ + + xbInt16 fldRecId = V3Dbf.GetFieldNo( "RECID" ); + xbInt16 fldMemo1 = V3Dbf.GetFieldNo( "MEMOFLD1" ); + xbInt16 fldMemo2 = V3Dbf.GetFieldNo( "MEMOFLD2" ); + xbString sData; + + // Record 1 + rc += TestMethod( po, "BlankRecord()", V3Dbf.BlankRecord(), XB_NO_ERROR ); + rc += TestMethod( po, "PutField()", V3Dbf.PutField( fldRecId, "1" ), XB_NO_ERROR ); + sData = "Memo data"; + rc += TestMethod( po, "UpdateMemoField()", V3Dbf.UpdateMemoField( fldMemo1, sData ), XB_NO_ERROR ); + + sData = "Some other memo data"; + rc += TestMethod( po, "UpdateMemoField()", V3Dbf.UpdateMemoField( fldMemo2, sData ), XB_NO_ERROR ); + rc += TestMethod( po, "AppendRecord()", V3Dbf.AppendRecord(), XB_NO_ERROR ); + + // Record 2 + rc += TestMethod( po, "BlankRecord()", V3Dbf.BlankRecord(), XB_NO_ERROR ); + rc += TestMethod( po, "PutField()", V3Dbf.PutField( fldRecId, "2" ), XB_NO_ERROR ); + sData = "Memo data Rec 2 ..["; + sData.PadRight( 'Z', 1036 ); + sData += ']'; + rc += TestMethod( po, "UpdateMemoField()", V3Dbf.UpdateMemoField( fldMemo1, sData ), XB_NO_ERROR ); + sData = "Some other memo data Rec 2 ... ["; + sData.PadRight( 'W', 1555 ); + sData += ']'; + rc += TestMethod( po, "UpdateMemoField()", V3Dbf.UpdateMemoField( fldMemo2, sData ), XB_NO_ERROR ); + rc += TestMethod( po, "AppendRecord()", V3Dbf.AppendRecord(), XB_NO_ERROR ); + + // Check memo header file + xbUInt32 ulNextBlock; + xbMemo *Memo = V3Dbf.GetMemoPtr(); + rc += TestMethod( po, "GetHdrNextBlock()", Memo->GetHdrNextBlock( ulNextBlock ), XB_NO_ERROR ); + rc += TestMethod( po, "GetHdrNextBlock()", (xbInt32) ulNextBlock, 10 ); + + + // Zap a table with memo fields + rc += TestMethod( po, "Zap(0)", V3Dbf.Zap(), XB_NO_ERROR ); + xbUInt32 ulRecCnt = 0; + rc += TestMethod( po, "GetRecCount()", V3Dbf.GetRecordCnt( ulRecCnt ), XB_NO_ERROR ); + rc += TestMethod( po, "GetRecCount() b", (xbInt32) ulRecCnt, 0 ); + Memo = V3Dbf.GetMemoPtr(); + rc += TestMethod( po, "GetHdrNextBlock()", Memo->GetHdrNextBlock( ulNextBlock ), XB_NO_ERROR ); + rc += TestMethod( po, "GetHdrNextBlock()", (xbInt32) ulNextBlock, 1 ); + + + + // Add records back into the table + // Record 1 + rc += TestMethod( po, "BlankRecord()", V3Dbf.BlankRecord(), XB_NO_ERROR ); + rc += TestMethod( po, "PutField()", V3Dbf.PutField( fldRecId, "1" ), XB_NO_ERROR ); + sData = "Memo data rec 1 memo 1"; + rc += TestMethod( po, "UpdateMemoField()", V3Dbf.UpdateMemoField( fldMemo1, sData ), XB_NO_ERROR ); + + sData = "Memo data rec 1 memo 2"; + rc += TestMethod( po, "UpdateMemoField()", V3Dbf.UpdateMemoField( fldMemo2, sData ), XB_NO_ERROR ); + rc += TestMethod( po, "AppendRecord()", V3Dbf.AppendRecord(), XB_NO_ERROR ); + + // Record 2 + rc += TestMethod( po, "BlankRecord()", V3Dbf.BlankRecord(), XB_NO_ERROR ); + rc += TestMethod( po, "PutField()", V3Dbf.PutField( fldRecId, "2" ), XB_NO_ERROR ); + sData = "Memo data Rec 2 memo 1..["; + sData.PadRight( 'Z', 1036 ); + sData += ']'; + rc += TestMethod( po, "UpdateMemoField()", V3Dbf.UpdateMemoField( fldMemo1, sData ), XB_NO_ERROR ); + sData = "Some other memo data Rec 2 memo 2 ... ["; + sData.PadRight( 'W', 1555 ); + sData += ']'; + rc += TestMethod( po, "UpdateMemoField()", V3Dbf.UpdateMemoField( fldMemo2, sData ), XB_NO_ERROR ); + rc += TestMethod( po, "AppendRecord()", V3Dbf.AppendRecord(), XB_NO_ERROR ); + + // Record 3 + rc += TestMethod( po, "BlankRecord()", V3Dbf.BlankRecord(), XB_NO_ERROR ); + rc += TestMethod( po, "PutField()", V3Dbf.PutField( fldRecId, "3" ), XB_NO_ERROR ); + sData = "Memo data Rec 3 memo 1"; + rc += TestMethod( po, "UpdateMemoField()", V3Dbf.UpdateMemoField( fldMemo1, sData ), XB_NO_ERROR ); + + sData = "Memo data Rec 3 memo 2"; + rc += TestMethod( po, "UpdateMemoField()", V3Dbf.UpdateMemoField( fldMemo2, sData ), XB_NO_ERROR ); + rc += TestMethod( po, "AppendRecord()", V3Dbf.AppendRecord(), XB_NO_ERROR ); + + + // Record 4 + rc += TestMethod( po, "BlankRecord()", V3Dbf.BlankRecord(), XB_NO_ERROR ); + rc += TestMethod( po, "PutField()", V3Dbf.PutField( fldRecId, "4" ), XB_NO_ERROR ); + sData = "Memo data Rec 4 memo 1"; + rc += TestMethod( po, "UpdateMemoField()", V3Dbf.UpdateMemoField( fldMemo1, sData ), XB_NO_ERROR ); + sData = "Memo data Rec 4 memo 2"; + rc += TestMethod( po, "UpdateMemoField()", V3Dbf.UpdateMemoField( fldMemo2, sData ), XB_NO_ERROR ); + rc += TestMethod( po, "AppendRecord()", V3Dbf.AppendRecord(), XB_NO_ERROR ); + + + // Record 5 + rc += TestMethod( po, "BlankRecord()", V3Dbf.BlankRecord(), XB_NO_ERROR ); + rc += TestMethod( po, "PutField()", V3Dbf.PutField( fldRecId, "5" ), XB_NO_ERROR ); + sData = "Memo data Rec 5 memo 1"; + rc += TestMethod( po, "UpdateMemoField()", V3Dbf.UpdateMemoField( fldMemo1, sData ), XB_NO_ERROR ); + sData = "Memo data Rec 5 memo 2"; + rc += TestMethod( po, "UpdateMemoField()", V3Dbf.UpdateMemoField( fldMemo2, sData ), XB_NO_ERROR ); + rc += TestMethod( po, "AppendRecord()", V3Dbf.AppendRecord(), XB_NO_ERROR ); + + + // Record 6 + rc += TestMethod( po, "BlankRecord()", V3Dbf.BlankRecord(), XB_NO_ERROR ); + rc += TestMethod( po, "PutField()", V3Dbf.PutField( fldRecId, "6" ), XB_NO_ERROR ); + sData = "Memo data Rec 6 memo 1"; + rc += TestMethod( po, "UpdateMemoField()", V3Dbf.UpdateMemoField( fldMemo1, sData ), XB_NO_ERROR ); + sData = "Memo data Rec 6 memo 2"; + rc += TestMethod( po, "UpdateMemoField()", V3Dbf.UpdateMemoField( fldMemo2, sData ), XB_NO_ERROR ); + rc += TestMethod( po, "AppendRecord()", V3Dbf.AppendRecord(), XB_NO_ERROR ); + + + // Record 7 + rc += TestMethod( po, "BlankRecord()", V3Dbf.BlankRecord(), XB_NO_ERROR ); + rc += TestMethod( po, "PutField()", V3Dbf.PutField( fldRecId, "7" ), XB_NO_ERROR ); + sData = "Memo data Rec 7 memo 1"; + rc += TestMethod( po, "UpdateMemoField()", V3Dbf.UpdateMemoField( fldMemo1, sData ), XB_NO_ERROR ); + sData = "Memo data Rec 7 memo 2"; + rc += TestMethod( po, "UpdateMemoField()", V3Dbf.UpdateMemoField( fldMemo2, sData ), XB_NO_ERROR ); + rc += TestMethod( po, "AppendRecord()", V3Dbf.AppendRecord(), XB_NO_ERROR ); + + + // Flag 1, 3, 5 and 7 for deletion + rc += TestMethod( po, "GetRecord()", V3Dbf.GetRecord( 1 ), XB_NO_ERROR ); + rc += TestMethod( po, "DeleteRecord()", V3Dbf.DeleteRecord(), XB_NO_ERROR ); + + rc += TestMethod( po, "GetRecord()", V3Dbf.GetRecord( 3 ), XB_NO_ERROR ); + rc += TestMethod( po, "DeleteRecord()", V3Dbf.DeleteRecord(), XB_NO_ERROR ); + + rc += TestMethod( po, "GetRecord()", V3Dbf.GetRecord( 5 ), XB_NO_ERROR ); + rc += TestMethod( po, "DeleteRecord()", V3Dbf.DeleteRecord(), XB_NO_ERROR ); + + rc += TestMethod( po, "GetRecord()", V3Dbf.GetRecord( 7 ), XB_NO_ERROR ); + rc += TestMethod( po, "DeleteRecord()", V3Dbf.DeleteRecord(), XB_NO_ERROR ); + rc += TestMethod( po, "Pack()", V3Dbf.Pack(), XB_NO_ERROR ); + rc += TestMethod( po, "Close()", V3Dbf.Close(), XB_NO_ERROR ); + } + + if( po > 0 || rc < 0 ) + fprintf( stdout, "Total Errors = %d\n", rc * -1 ); + +#ifdef XB_LOGGING_SUPPORT + sMsg.Sprintf( "Program [%s] terminating with [%d] errors...", av[0], rc * -1 ); + x.WriteLogMessage( sMsg ); +#endif + + return rc; +} diff --git a/src/tests/xb_test_dbf_v3_nomemos.cpp b/src/tests/xb_test_dbf_v3_nomemos.cpp new file mode 100755 index 0000000..aab118f --- /dev/null +++ b/src/tests/xb_test_dbf_v3_nomemos.cpp @@ -0,0 +1,323 @@ +/* xb_test_xbdbf_v3_nomemos.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 program tests the class xbXdbf + +// usage: xb_test_dbf_v3_nomemos QUITE|NORMAL|VERBOSE + + +#include "xbase.h" + +using namespace xb; + +#include "tstfuncs.cpp" + + +int main( int argCnt, char **av ) +{ + int iRc = 0; + int iRc2 = 0; + int po = 1; /* print option */ + /* 0 - QUIET */ + /* 1 - NORMAL */ + /* 2 - VERBOSE */ + + if( argCnt > 1 ) { + if( av[1][0] == 'Q' ) + po = 0; + else if( av[1][0] == 'V' ) + po = 2; + } + + + xbSchema MyV3Record[] = + { + { "FIRSTNAME", XB_CHAR_FLD, 15, 0 }, + { "LASTNAME", XB_CHAR_FLD, 20, 0 }, + { "MIDDLEINIT", XB_CHAR_FLD, 1, 0 }, + { "BIRTHDATE", XB_DATE_FLD, 8, 0 }, + { "STARTDATE", XB_DATE_FLD, 8, 0 }, + { "AMOUNT", XB_NUMERIC_FLD, 9, 2 }, + { "TESTNUM", XB_NUMERIC_FLD, 12, 4 }, + { "SWITCH", XB_LOGICAL_FLD, 1, 0 }, + { "ZIPCODE", XB_NUMERIC_FLD, 5, 0 }, + { "",0,0,0 } + }; + + + xbXBase x; + +#ifdef XB_LOGGING_SUPPORT + x.EnableMsgLogging(); + if( po ){ + std::cout << "Logfile is [" << x.GetLogFqFileName().Str() << "]" << std::endl; + } + xbString sMsg; + sMsg.Sprintf( "Program [%s] initializing...", av[0] ); + x.WriteLogMessage( sMsg ); +#endif + + x.SetDataDirectory( PROJECT_DATA_DIR ); + x.EnableMsgLogging(); + x.DisableDefaultAutoLock(); + InitTime(); + + if( po > 0 ) + std::cout << "Default Data Directory is [" << x.GetDataDirectory().Str() << "]" << std::endl; + + + xbDbf3 V3Dbf( &x ); /* version 3 dbf file */ + + iRc2 = V3Dbf.CreateTable( "TestV3.DBF", "TestV3", MyV3Record, XB_OVERLAY, XB_MULTI_USER ); + + iRc += TestMethod( po, "CreateTable()", (xbInt32) iRc2, XB_NO_ERROR ); + if( iRc2 ) + x.DisplayError( iRc2 ); + + if( po == 2 ){ + std::cout << "There should be one entry in the table list" << std::endl; + x.DisplayTableList(); + } + + xbDbf3 V3Dbf2( &x ); + // next occurrence should error as a dup + x.WriteLogMessage( "Second create attempt" ); + iRc2 = V3Dbf2.CreateTable( "TestV3.DBF", "TestV3", MyV3Record, XB_DONTOVERLAY, XB_MULTI_USER ); + iRc += TestMethod( po, "CreateTable()", iRc2, XB_FILE_EXISTS ); + if( iRc2 != XB_FILE_EXISTS ){ + x.DisplayError( iRc2 ); + } + + if( po == 2 ){ + std::cout << "There should be one entry in the table list" << std::endl; + x.DisplayTableList(); + } + + iRc += TestMethod( po, "BlankRecord()", V3Dbf.BlankRecord(), XB_NO_ERROR ); + iRc += TestMethod( po, "GetFieldNo()", V3Dbf.GetFieldNo("LASTNAME"), 1 ); + iRc += TestMethod( po, "GetFieldNo()", V3Dbf.GetFieldNo("MIDDLEINIT"), 2 ); + + xbInt16 fldLastName = V3Dbf.GetFieldNo( "LASTNAME" ); + + iRc += TestMethod( po, "PutField()", V3Dbf.PutField( fldLastName, "NELSON" ), XB_NO_ERROR ); + iRc += TestMethod( po, "PutField()", V3Dbf.PutField( "FIRSTNAME", "WILLIE" ), XB_NO_ERROR ); + + iRc += TestMethod( po, "PutField()", V3Dbf.PutField( "AMOUNT", "12.34" ), XB_NO_ERROR ); + xbDate d( "19991201" ); + iRc += TestMethod( po, "PutDateField()", V3Dbf.PutDateField( "STARTDATE", d ), XB_NO_ERROR ); + iRc += TestMethod( po, "PutField()", V3Dbf.PutField( "STARTDATE", "19991301" ), XB_INVALID_DATA ); + iRc += TestMethod( po, "PutField()", V3Dbf.PutField( "STARTDATE", "19991201" ), XB_NO_ERROR ); + + + iRc += TestMethod( po, "PutLongField()", V3Dbf.PutLongField( "ZIPCODE", 12345 ), XB_NO_ERROR ); + iRc += TestMethod( po, "PutLogicalField()", V3Dbf.PutLogicalField( "SWITCH", "Y" ), XB_NO_ERROR ); + iRc += TestMethod( po, "PutFloatField()", V3Dbf.PutFloatField( "TESTNUM", (xbFloat) 1234.5678 ), XB_NO_ERROR ); + iRc += TestMethod( po, "AppendRecord()", V3Dbf.AppendRecord(), XB_NO_ERROR ); + + xbString sf; + iRc += TestMethod( po, "GetField1()", V3Dbf.GetField( fldLastName, sf, 0 ), XB_NO_ERROR ); + iRc += TestMethod( po, "GetField2()", sf, "NELSON ", 20 ); + iRc += TestMethod( po, "GetField3()", V3Dbf.GetField( fldLastName, sf ), XB_NO_ERROR ); + iRc += TestMethod( po, "GetField4()", sf, "NELSON ", 20 ); + iRc += TestMethod( po, "GetField5()", V3Dbf.GetField( "LASTNAME", sf ), XB_NO_ERROR ); + iRc += TestMethod( po, "GetField6()", sf, "NELSON ", 20 ); + xbInt16 iNoOfDecimals; + iRc += TestMethod( po, "GetFieldDecimal()", V3Dbf.GetFieldDecimal( "AMOUNT", iNoOfDecimals ), XB_NO_ERROR ); + iRc += TestMethod( po, "GetFieldDecimal()", iNoOfDecimals, 2 ); + + char FieldType; + iRc += TestMethod( po, "GetFieldType()", V3Dbf.GetFieldType( "STARTDATE", FieldType ), XB_NO_ERROR ); + iRc += TestMethod( po, "GetFieldType()", FieldType , 'D' ); + + xbInt16 iFieldLen; + iRc += TestMethod( po, "GetFieldLen()", V3Dbf.GetFieldLen( "STARTDATE", iFieldLen ), XB_NO_ERROR ); + iRc += TestMethod( po, "GetFieldLen()", iFieldLen, 8 ); + + xbInt16 fldAMT = V3Dbf.GetFieldNo( "AMOUNT" ); + iRc += TestMethod( po, "GetRawField1()", V3Dbf.GetField( fldAMT, sf ), XB_NO_ERROR ); + iRc += TestMethod( po, "GetRawField2()", sf, " 12.34", 9 ); + + xbInt32 lZip; + iRc += TestMethod( po, "GetLongField()", V3Dbf.GetLongField( "ZIPCODE", lZip ), XB_NO_ERROR ); + iRc += TestMethod( po, "GetLongField()", lZip, 12345 ); + + xbString sSwitch; + iRc += TestMethod( po, "GetLogicalField()", V3Dbf.GetLogicalField( "SWITCH", sSwitch ), XB_NO_ERROR ); + iRc += TestMethod( po, "GetLogicalField()", sSwitch, "Y", 1 ); + + xbFloat fNum; + iRc += TestMethod( po, "GetFloatField()", V3Dbf.GetFloatField( "TESTNUM", fNum ), 0 ); + iRc += TestMethod( po, "GetFloatField()", fNum, (xbFloat) 1234.5678 ); + + iRc += TestMethod( po, "GetDateField()", V3Dbf.GetDateField( "STARTDATE", d ), 0 ); + iRc += TestMethod( po, "GetDateField()", d.Str(), "19991201", 8 ); + + // init a second record for more testing + iRc += TestMethod( po, "BlankRecord()", V3Dbf.BlankRecord(), XB_NO_ERROR ); + iRc += TestMethod( po, "PutLogicalField()", V3Dbf.PutLogicalField( "SWITCH", xbTrue ), XB_NO_ERROR ); + iRc += TestMethod( po, "AppendRecord()", V3Dbf.AppendRecord(), XB_NO_ERROR ); + xbBool bSwitch; + iRc += TestMethod( po, "GetLogicalField()", V3Dbf.GetLogicalField( "SWITCH", bSwitch ), XB_NO_ERROR ); + iRc += TestMethod( po, "GetLogicalField()", bSwitch, xbTrue ); + + // init a third record for more testing + iRc += TestMethod( po, "BlankRecord()", V3Dbf.BlankRecord(), XB_NO_ERROR ); + iRc += TestMethod( po, "PutLogicalField()", V3Dbf.PutLogicalField( "SWITCH", xbFalse ), XB_NO_ERROR ); + iRc += TestMethod( po, "AppendRecord()", V3Dbf.AppendRecord(), XB_NO_ERROR ); + + + iRc += TestMethod( po, "GetLogicalField()", V3Dbf.GetLogicalField( "SWITCH", bSwitch ), XB_NO_ERROR ); + iRc += TestMethod( po, "GetLogicalField()", bSwitch, xbFalse ); + + iRc += TestMethod( po, "PutField()", V3Dbf.PutField( "TESTNUM", "12345678" ), XB_INVALID_DATA ); + iRc += TestMethod( po, "PutField()", V3Dbf.PutField( "TESTNUM", "1234567" ), XB_NO_ERROR ); + iRc += TestMethod( po, "PutField()", V3Dbf.PutField( "TESTNUM", "1234567.12345" ), XB_INVALID_DATA ); + iRc += TestMethod( po, "PutField()", V3Dbf.PutField( "TESTNUM", ".12345" ), XB_INVALID_DATA ); + iRc += TestMethod( po, "PutField()", V3Dbf.PutField( "TESTNUM", ".1234" ), XB_NO_ERROR ); + iRc += TestMethod( po, "PutField()", V3Dbf.PutField( "TESTNUM", "12345678.1234" ), XB_INVALID_DATA ); + iRc += TestMethod( po, "PutField()", V3Dbf.PutField( "TESTNUM", "1234567.1234" ), XB_NO_ERROR ); + iRc += TestMethod( po, "AppendRecord()", V3Dbf.AppendRecord(), XB_NO_ERROR ); + + iRc += TestMethod( po, "GetFieldCnt()", V3Dbf.GetFieldCnt(), 9 ); + iRc += TestMethod( po, "GetTblAlias()", V3Dbf.GetTblAlias(), "TestV3", 6 ); + iRc += TestMethod( po, "GetDbfStatus()", V3Dbf.GetDbfStatus(), XB_OPEN ); + iRc += TestMethod( po, "GetCurRecNo()", (xbInt32) V3Dbf.GetCurRecNo(), 4 ); + iRc += TestMethod( po, "GetFirstRecord()", V3Dbf.GetFirstRecord(), XB_NO_ERROR ); + iRc += TestMethod( po, "GetCurRecNo()", (xbInt32) V3Dbf.GetCurRecNo(), 1 ); + iRc += TestMethod( po, "GetNextRecord()", V3Dbf.GetNextRecord(), XB_NO_ERROR ); + iRc += TestMethod( po, "GetCurRecNo()", (xbInt32) V3Dbf.GetCurRecNo(), 2 ); + iRc += TestMethod( po, "GetLastRecord()", V3Dbf.GetLastRecord(), XB_NO_ERROR ); + iRc += TestMethod( po, "GetCurRecNo()", (xbInt32) V3Dbf.GetCurRecNo(), 4 ); + iRc += TestMethod( po, "GetPrevRecord()", V3Dbf.GetPrevRecord(), XB_NO_ERROR ); + iRc += TestMethod( po, "GetCurRecNo()", (xbInt32) V3Dbf.GetCurRecNo(), 3 ); + iRc += TestMethod( po, "GetRecordLen()", V3Dbf.GetRecordLen(), 80 ); + + iRc += TestMethod( po, "GetFirstRecord( XB_ALL_RECS )", V3Dbf.GetFirstRecord( XB_ALL_RECS ), XB_NO_ERROR ); + iRc += TestMethod( po, "GetCurRecNo()", (xbInt32) V3Dbf.GetCurRecNo(), 1 ); + iRc += TestMethod( po, "GetNextRecord( XB_ALL_RECS )", V3Dbf.GetNextRecord( XB_ALL_RECS ), XB_NO_ERROR ); + iRc += TestMethod( po, "GetCurRecNo()", (xbInt32) V3Dbf.GetCurRecNo(), 2 ); + iRc += TestMethod( po, "GetLastRecord( XB_ALL_RECS )", V3Dbf.GetLastRecord( XB_ALL_RECS ), XB_NO_ERROR ); + iRc += TestMethod( po, "GetCurRecNo()", (xbInt32) V3Dbf.GetCurRecNo(), 4 ); + iRc += TestMethod( po, "GetPrevRecord( XB_ALL_RECS )", V3Dbf.GetPrevRecord( XB_ALL_RECS ), XB_NO_ERROR ); + iRc += TestMethod( po, "GetCurRecNo()", (xbInt32) V3Dbf.GetCurRecNo(), 3 ); + + iRc += TestMethod( po, "GetFirstRecord( XB_DELETED_RECS )", V3Dbf.GetFirstRecord( XB_DELETED_RECS ), XB_EOF ); + iRc += TestMethod( po, "GetLastRecord( XB_DELETED_RECS )", V3Dbf.GetLastRecord( XB_DELETED_RECS ), XB_EOF ); + + iRc += TestMethod( po, "GetFirstRecord( XB_ALL_RECS )", V3Dbf.GetFirstRecord( XB_ALL_RECS ), XB_NO_ERROR ); + iRc += TestMethod( po, "GetNextRecord( XB_DELETED_RECS )", V3Dbf.GetNextRecord( XB_DELETED_RECS ), XB_EOF ); + + iRc += TestMethod( po, "GetLastRecord( XB_ALL_RECS )", V3Dbf.GetLastRecord( XB_ALL_RECS ), XB_NO_ERROR ); + iRc += TestMethod( po, "GetPrevRecord( XB_DELETED_RECS )", V3Dbf.GetPrevRecord( XB_DELETED_RECS ), XB_BOF ); + + iRc += TestMethod( po, "DeleteAllRecords()", V3Dbf.DeleteAllRecords(), XB_NO_ERROR ); + + + iRc += TestMethod( po, "GetFirstRecord()", V3Dbf.GetFirstRecord(), XB_EOF ); + iRc += TestMethod( po, "GetLastRecord()", V3Dbf.GetLastRecord(), XB_EOF ); + + iRc += TestMethod( po, "GetFirstRecord( XB_ALL_RECS )", V3Dbf.GetFirstRecord( XB_ALL_RECS ), XB_NO_ERROR ); + iRc += TestMethod( po, "GetCurRecNo()", (xbInt32) V3Dbf.GetCurRecNo(), 1 ); + + iRc += TestMethod( po, "GetNextRecord( XB_ALL_RECS )", V3Dbf.GetNextRecord( XB_ALL_RECS ), XB_NO_ERROR ); + iRc += TestMethod( po, "GetCurRecNo()", (xbInt32) V3Dbf.GetCurRecNo(), 2 ); + + iRc += TestMethod( po, "GetNextRecord()", V3Dbf.GetNextRecord(), XB_EOF ); + + iRc += TestMethod( po, "GetLastRecord( XB_ALL_RECS )", V3Dbf.GetLastRecord( XB_ALL_RECS ), XB_NO_ERROR ); + iRc += TestMethod( po, "GetCurRecNo()", (xbInt32) V3Dbf.GetCurRecNo(), 4 ); + + iRc += TestMethod( po, "GetPrevRecord( XB_ALL_RECS )", V3Dbf.GetPrevRecord( XB_ALL_RECS ), XB_NO_ERROR ); + iRc += TestMethod( po, "GetCurRecNo()", (xbInt32) V3Dbf.GetCurRecNo(), 3 ); + + iRc += TestMethod( po, "UndeleteAllRecords()", V3Dbf.UndeleteAllRecords(), XB_NO_ERROR ); + + char * p = V3Dbf.GetRecordBuf(); + iRc += TestMethod( po, "GetRecordBuf()", p[0], ' ' ); + + if( po == 2 ){ + V3Dbf.DumpHeader( 3 ); + V3Dbf.DumpRecord( 4 ); + } + + + + iRc += TestMethod( po, "PutField()", V3Dbf.PutField( "LASTNAME", "CLINTON" ), XB_NO_ERROR ); + iRc += TestMethod( po, "PutRecord()", V3Dbf.PutRecord(), XB_NO_ERROR ); + iRc += TestMethod( po, "DeleteRecord()", V3Dbf.DeleteRecord(), XB_NO_ERROR ); + iRc += TestMethod( po, "UndeleteRecord()", V3Dbf.UndeleteRecord(), XB_NO_ERROR ); + xbUInt32 ulRecCnt = 0; + iRc += TestMethod( po, "GetRecordCnt()", V3Dbf.GetRecordCnt( ulRecCnt ), XB_NO_ERROR ); + iRc += TestMethod( po, "GetRecordCnt()b",(xbInt32) ulRecCnt, 4 ); + iRc += TestMethod( po, "DeleteAllRecords()", V3Dbf.DeleteAllRecords(), XB_NO_ERROR ); + iRc += TestMethod( po, "UndeleteAllRecords()", V3Dbf.UndeleteAllRecords(), XB_NO_ERROR ); + iRc += TestMethod( po, "GetRecord()", V3Dbf.GetRecord( 4 ), XB_NO_ERROR ); + iRc += TestMethod( po, "DeleteRecord()", V3Dbf.DeleteRecord(), XB_NO_ERROR ); + iRc += TestMethod( po, "Commit()", V3Dbf.Commit(), XB_NO_ERROR ); + iRc += TestMethod( po, "GetDbfStatus()", V3Dbf.GetDbfStatus(), XB_OPEN ); + + if( po == 2 ){ + V3Dbf.DumpRecord( 4 ); + } + + xbDbf3 CopyDbf( &x ); + iRc += TestMethod( po, "CopyDbfStructure()", V3Dbf.CopyDbfStructure( &CopyDbf, "CopyStructureV3NoMemos", "CopyAliasV3NoMemos", XB_OVERLAY, XB_MULTI_USER ), XB_NO_ERROR ); + iRc += TestMethod( po, "CopyDbf.Close()", CopyDbf.Close(), XB_NO_ERROR ); + + if( po == 2 ){ + std::cout << "There should be one entry in the table list" << std::endl; + x.DisplayTableList(); + } + + iRc += TestMethod( po, "GetDbfStatus()", CopyDbf.GetDbfStatus(), XB_CLOSED ); + iRc += TestMethod( po, "Open()", CopyDbf.xbDbf::Open( "CopyStructureV3NoMemos.DBF", "CopyAliasV3NoMemos" ), XB_NO_ERROR ); + + if( po == 2 ){ + std::cout << "There should be two entries in the table list" << std::endl; + x.DisplayTableList(); + } + + iRc += TestMethod( po, "CopyDbf.Close()", CopyDbf.Close(), XB_NO_ERROR ); + + #ifdef XB_DEBUG_SUPPORT + #ifdef XB_LOCKING_SUPPORT + V3Dbf.DumpTableLockStatus(); + #endif + #endif + + iRc += TestMethod( po, "PackTable( 0, 0 )", V3Dbf.Pack(), XB_NO_ERROR ); + iRc += TestMethod( po, "GetDbfStatus()", V3Dbf.GetDbfStatus(), XB_OPEN ); + + iRc += TestMethod( po, "Zap()", V3Dbf.Zap(), XB_NO_ERROR ); + if( po == 2 ) + x.DisplayTableList(); + + iRc += TestMethod( po, "Close()", V3Dbf.Close(), XB_NO_ERROR ); + if( po == 2 ) + x.DisplayTableList(); + + if( po > 0 || iRc < 0 ) + fprintf( stdout, "Total Errors = %d\n", iRc * -1 ); + + + V3Dbf.Abort(); // don't crash the program if uncommited updates + +#ifdef XB_LOGGING_SUPPORT + sMsg.Sprintf( "Program [%s] terminating with [%d] errors...", av[0], iRc * -1 ); + x.WriteLogMessage( sMsg ); +#endif + + return iRc; +} diff --git a/src/tests/xb_test_dbf_v4_memos.cpp b/src/tests/xb_test_dbf_v4_memos.cpp new file mode 100755 index 0000000..e92b5b2 --- /dev/null +++ b/src/tests/xb_test_dbf_v4_memos.cpp @@ -0,0 +1,335 @@ +/* xb_test_xbdbf_v4_memos.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 program tests the version III memo field logic +// usage: xb_test_dbf_v4_memos QUITE|NORMAL|VERBOSE + +#include "xbase.h" +using namespace xb; +#include "tstfuncs.cpp" + +int main( int argCnt, char **av ) +{ + int rc = 0; + int rc2 = 0; + int po = 1; /* print option */ + /* 0 - QUIET */ + /* 1 - NORMAL */ + /* 2 - VERBOSE */ + + if( argCnt > 1 ) { + if( av[1][0] == 'Q' ) + po = 0; + else if( av[1][0] == 'V' ) + po = 2; + } + + xbSchema MyV4Record[] = + { + { "RECID", XB_NUMERIC_FLD, 8, 0 }, + { "MEMOFLD1", XB_MEMO_FLD, 10, 0 }, + { "MEMOFLD2", XB_MEMO_FLD, 10, 0 }, + { "MEMOFLD3", XB_MEMO_FLD, 10, 0 }, + { "",0,0,0 } + }; + + xbXBase x; + +#ifdef XB_LOGGING_SUPPORT + + x.EnableMsgLogging(); + if( po ){ + std::cout << "Logfile is [" << x.GetLogFqFileName().Str() << "]" << std::endl; + } + xbString sMsg; + sMsg.Sprintf( "Program [%s] initializing...", av[0] ); + x.WriteLogMessage( sMsg ); + +#endif + + x.SetDataDirectory( PROJECT_DATA_DIR ); + x.EnableMsgLogging(); + x.DisableDefaultAutoLock(); + InitTime(); + + + if( po > 0 ) + std::cout << "Default Data Directory is [" << x.GetDataDirectory().Str() << "]" << std::endl; + + xbDbf4 V4Dbf( &x ); + rc += TestMethod( po, "SetCreateMemoBlockSize", V4Dbf.SetCreateMemoBlockSize( 1024 ), XB_NO_ERROR ); + rc2 = V4Dbf.CreateTable( "TestV4_memos.DBF", "TestV4", MyV4Record, XB_OVERLAY, XB_MULTI_USER ); + + xbUInt64 ullDbfOrigFileSize = 0; + xbUInt64 ullDbtOrigFileSize = 0; + rc = V4Dbf.GetFileSize( ullDbfOrigFileSize ); + rc = V4Dbf.GetMemoPtr()->GetFileSize( ullDbtOrigFileSize ); + + rc += TestMethod( po, "CreateTable()", (xbInt32) rc2, XB_NO_ERROR ); + if( rc2 ) + x.DisplayError( rc2 ); + else{ + + xbInt16 fldRecId = V4Dbf.GetFieldNo( "RECID" ); + xbInt16 fldMemo1 = V4Dbf.GetFieldNo( "MEMOFLD1" ); + xbInt16 fldMemo2 = V4Dbf.GetFieldNo( "MEMOFLD2" ); + xbInt16 fldMemo3 = V4Dbf.GetFieldNo( "MEMOFLD3" ); + xbString sData; + + + + + // Record 1 + rc += TestMethod( po, "BlankRecord()", V4Dbf.BlankRecord(), XB_NO_ERROR ); + rc += TestMethod( po, "PutField()", V4Dbf.PutField( fldRecId, "1" ), XB_NO_ERROR ); + sData = "Memo data"; + + rc += TestMethod( po, "UpdateMemoField()", V4Dbf.UpdateMemoField( fldMemo1, sData ), XB_NO_ERROR ); + rc += TestDbt4Method( po, "Add one block", V4Dbf.GetMemoPtr(), 2, 2, "" ); + + sData = "Some other memo data"; + rc += TestMethod( po, "UpdateMemoField()", V4Dbf.UpdateMemoField( fldMemo2, sData ), XB_NO_ERROR ); + rc += TestDbt4Method( po, "Add one block", V4Dbf.GetMemoPtr(), 3, 3, "" ); + rc += TestMethod( po, "AppendRecord()", V4Dbf.AppendRecord(), XB_NO_ERROR ); + + + + + // Record 2 + rc += TestMethod( po, "BlankRecord()", V4Dbf.BlankRecord(), XB_NO_ERROR ); + rc += TestMethod( po, "PutField()", V4Dbf.PutField( fldRecId, "2" ), XB_NO_ERROR ); + sData = "Memo data Rec 2 ..["; + sData.PadRight( 'Z', 1036 ); + sData += ']'; + rc += TestMethod( po, "UpdateMemoField()", V4Dbf.UpdateMemoField( fldMemo1, sData ), XB_NO_ERROR ); + sData = "Some other memo data Rec 2 ... ["; + sData.PadRight( 'W', 1555 ); + sData += ']'; + rc += TestMethod( po, "UpdateMemoField()", V4Dbf.UpdateMemoField( fldMemo2, sData ), XB_NO_ERROR ); + rc += TestDbt4Method( po, "Add 7 blocks", V4Dbf.GetMemoPtr(), 7, 7, "" ); + + rc += TestMethod( po, "UpdateMemoField()", V4Dbf.UpdateMemoField( fldMemo1, "" ), XB_NO_ERROR ); + rc += TestDbt4Method( po, "Delete 2 blocks", V4Dbf.GetMemoPtr(), 3, 7, "3,2,7" ); + rc += TestMethod( po, "AppendRecord()", V4Dbf.AppendRecord(), XB_NO_ERROR ); + + // Record 3 + rc += TestMethod( po, "BlankRecord()", V4Dbf.BlankRecord(), XB_NO_ERROR ); + rc += TestMethod( po, "PutField()", V4Dbf.PutField( fldRecId, "3" ), XB_NO_ERROR ); + sData = "Memo data r3f1"; + rc += TestMethod( po, "UpdateMemoField()", V4Dbf.UpdateMemoField( fldMemo1, sData ), XB_NO_ERROR ); + rc += TestDbt4Method( po, "Add one block", V4Dbf.GetMemoPtr(), 4, 7, "4,1,7" ); + sData = "Memo data r3f2"; + rc += TestMethod( po, "UpdateMemoField()", V4Dbf.UpdateMemoField( fldMemo2, sData ), XB_NO_ERROR ); + rc += TestDbt4Method( po, "Add one block", V4Dbf.GetMemoPtr(), 7, 7, "" ); + rc += TestMethod( po, "UpdateMemoField()", V4Dbf.UpdateMemoField( fldMemo1, "" ), XB_NO_ERROR ); + rc += TestDbt4Method( po, "Delete one block", V4Dbf.GetMemoPtr(), 3, 7, "3,1,7" ); + rc += TestMethod( po, "AppendRecord()", V4Dbf.AppendRecord(), XB_NO_ERROR ); + + // Record 4 + rc += TestMethod( po, "BlankRecord()", V4Dbf.BlankRecord(), XB_NO_ERROR ); + rc += TestMethod( po, "PutField()", V4Dbf.PutField( fldRecId, "4" ), XB_NO_ERROR ); + sData = "Memo data Rec 4 fld1 ..["; + sData.PadRight( 'Q', 1036 ); + sData += ']'; + rc += TestMethod( po, "UpdateMemoField()", V4Dbf.UpdateMemoField( fldMemo1, sData ), XB_NO_ERROR ); + rc += TestDbt4Method( po, "Update Memo Field", V4Dbf.GetMemoPtr(), 3, 9, "3,1,9" ); + rc += TestMethod( po, "AppendRecord()", V4Dbf.AppendRecord(), XB_NO_ERROR ); + + // go back a record and delete a memo field and test if all blocks filled in + rc += TestMethod( po, "GetPrevRecord()", V4Dbf.GetPrevRecord(), XB_NO_ERROR ); + rc += TestMethod( po, "UpdateMemoField()", V4Dbf.UpdateMemoField( fldMemo2, "" ), XB_NO_ERROR ); + rc += TestMethod( po, "Commit()", V4Dbf.Commit(), XB_NO_ERROR ); + rc += TestDbt4Method( po, "Delete one block", V4Dbf.GetMemoPtr(), 3, 9, "3,2,9" ); + + // Record 5 + rc += TestMethod( po, "BlankRecord()", V4Dbf.BlankRecord(), XB_NO_ERROR ); + rc += TestMethod( po, "PutField()", V4Dbf.PutField( fldRecId, "5" ), XB_NO_ERROR ); + rc += TestMethod( po, "AppendRecord()", V4Dbf.AppendRecord(), XB_NO_ERROR ); + + // Zap a table with memo fields + rc += TestMethod( po, "Zap()", V4Dbf.Zap(), XB_NO_ERROR ); + xbUInt32 ulRecCnt = 0; + rc += TestMethod( po, "GetRecCount()", V4Dbf.GetRecordCnt( ulRecCnt ), XB_NO_ERROR ); + rc += TestMethod( po, "GetRecCount()b", (xbInt32) ulRecCnt, 0 ); + + // Add records back into the table, and verify the block chains created as designed + // Record 1 + rc += TestMethod( po, "BlankRecord()", V4Dbf.BlankRecord(), XB_NO_ERROR ); + rc += TestMethod( po, "PutField()", V4Dbf.PutField( fldRecId, "1" ), XB_NO_ERROR ); + sData = "Memo data rec 1 memo 1"; + rc += TestMethod( po, "UpdateMemoField()", V4Dbf.UpdateMemoField( fldMemo1, sData ), XB_NO_ERROR ); + rc += TestMethod( po, "AppendRecord()", V4Dbf.AppendRecord(), XB_NO_ERROR ); + rc += TestDbt4Method( po, "Update Memo Field", V4Dbf.GetMemoPtr(), 2, 2, "" ); + + // Revise the memo field + sData = "Updated memo data rec1 memo 1"; + rc += TestMethod( po, "UpdateMemoField()", V4Dbf.UpdateMemoField( fldMemo1, sData ), XB_NO_ERROR ); + rc += TestMethod( po, "Commit()", V4Dbf.Commit(), XB_NO_ERROR ); + rc += TestDbt4Method( po, "UpdateMemoField", V4Dbf.GetMemoPtr(), 2, 2, "" ); + + sData = "Memo data rec 1 memo 2"; + rc += TestMethod( po, "UpdateMemoField()", V4Dbf.UpdateMemoField( fldMemo2, sData ), XB_NO_ERROR ); + rc += TestMethod( po, "Commit()", V4Dbf.Commit(), XB_NO_ERROR ); + rc += TestDbt4Method( po, "UpdateMemoField", V4Dbf.GetMemoPtr(), 3, 3, "" ); + + // Record 2 + rc += TestMethod( po, "BlankRecord()", V4Dbf.BlankRecord(), XB_NO_ERROR ); + rc += TestMethod( po, "PutField()", V4Dbf.PutField( fldRecId, "2" ), XB_NO_ERROR ); + sData = "Memo data rec 2 memo 1"; + rc += TestMethod( po, "UpdateMemoField()", V4Dbf.UpdateMemoField( fldMemo1, sData ), XB_NO_ERROR ); + sData = "Memo data rec 2 memo 2"; + rc += TestMethod( po, "UpdateMemoField()", V4Dbf.UpdateMemoField( fldMemo2, sData ), XB_NO_ERROR ); + rc += TestMethod( po, "AppendRecord()", V4Dbf.AppendRecord(), XB_NO_ERROR ); + rc += TestDbt4Method( po, "Add one block", V4Dbf.GetMemoPtr(), 5, 5, "" ); + rc += TestMethod( po, "UpdateMemoField()", V4Dbf.UpdateMemoField( fldMemo1, "" ), XB_NO_ERROR ); + rc += TestMethod( po, "Commit()", V4Dbf.Commit(), XB_NO_ERROR ); + rc += TestDbt4Method( po, "UpdateMemoField", V4Dbf.GetMemoPtr(), 3, 5, "3,1,5" ); + + sData = "Test the abort code"; + rc += TestMethod( po, "UpdateMemoField()", V4Dbf.UpdateMemoField( fldMemo1, sData ), XB_NO_ERROR ); + rc += TestMethod( po, "Abort()", V4Dbf.Abort(), XB_NO_ERROR ); + rc += TestDbt4Method( po, "UpdateMemoField", V4Dbf.GetMemoPtr(), 3, 5, "3,1,5" ); + + sData = "Test mulitple updates before commit"; + rc += TestMethod( po, "UpdateMemoField()", V4Dbf.UpdateMemoField( fldMemo1, sData ), XB_NO_ERROR ); + sData = "Test mulitple updates before commit2"; + rc += TestMethod( po, "UpdateMemoField()", V4Dbf.UpdateMemoField( fldMemo1, sData ), XB_NO_ERROR ); + rc += TestDbt4Method( po, "UpdateMemoField", V4Dbf.GetMemoPtr(), 5, 5, "" ); + sData = "Test mulitple updates before commit3"; + + rc += TestMethod( po, "UpdateMemoField()", V4Dbf.UpdateMemoField( fldMemo1, sData ), XB_NO_ERROR ); + rc += TestMethod( po, "Commit()", V4Dbf.Commit(), XB_NO_ERROR ); + rc += TestDbt4Method( po, "UpdateMemoField", V4Dbf.GetMemoPtr(), 5, 5, "" ); + +// V4Dbf.GetMemoPtr()->DumpMemoInternals(); + + rc += TestMethod( po, "Zap(0)", V4Dbf.Zap(), XB_NO_ERROR ); + + xbUInt64 ullDbfPostZapFileSize = 0; + xbUInt64 ullDbtPostZapFileSize = 0; + rc += V4Dbf.GetFileSize( ullDbfPostZapFileSize ); + rc += V4Dbf.GetMemoPtr()->GetFileSize( ullDbtPostZapFileSize ); + + rc += TestMethod( po, "Zap()", ullDbfPostZapFileSize, ullDbfOrigFileSize ); + rc += TestMethod( po, "Memo->Zap()", ullDbtPostZapFileSize, ullDbtOrigFileSize ); + rc += TestDbt4Method( po, "Zap() block check", V4Dbf.GetMemoPtr(), 1, 1, "" ); + + rc += TestMethod( po, "GetRecCount()", V4Dbf.GetRecordCnt( ulRecCnt ), XB_NO_ERROR ); + rc += TestMethod( po, "GetRecCount()", (xbInt32) ulRecCnt, 0 ); + + rc += TestMethod( po, "BlankRecord()", V4Dbf.BlankRecord(), XB_NO_ERROR ); + rc += TestMethod( po, "PutField()", V4Dbf.PutField( fldRecId, "1" ), XB_NO_ERROR ); + sData = "Memo data rec 1 memo 1"; + rc += TestMethod( po, "UpdateMemoField()", V4Dbf.UpdateMemoField( fldMemo1, sData ), XB_NO_ERROR ); + sData = "Memo data rec 1 memo 2"; + rc += TestMethod( po, "UpdateMemoField()", V4Dbf.UpdateMemoField( fldMemo2, sData ), XB_NO_ERROR ); + sData = "Memo data rec 1 memo 3"; + rc += TestMethod( po, "UpdateMemoField()", V4Dbf.UpdateMemoField( fldMemo3, sData ), XB_NO_ERROR ); + rc += TestMethod( po, "AppendRecord()", V4Dbf.AppendRecord(), XB_NO_ERROR ); + + sData = "Memo data rec 1 memo 1 A"; + rc += TestMethod( po, "UpdateMemoField()", V4Dbf.UpdateMemoField( fldMemo1, sData ), XB_NO_ERROR ); + sData = "Memo data rec 1 memo 2 A"; + rc += TestMethod( po, "UpdateMemoField()", V4Dbf.UpdateMemoField( fldMemo2, sData ), XB_NO_ERROR ); + sData = "Memo data rec 1 memo 3A "; + rc += TestMethod( po, "UpdateMemoField()", V4Dbf.UpdateMemoField( fldMemo3, sData ), XB_NO_ERROR ); + rc += TestMethod( po, "Commit()", V4Dbf.Commit(), XB_NO_ERROR ); + rc += TestDbt4Method( po, "Update Memo Field", V4Dbf.GetMemoPtr(), 4, 4, "" ); + + // Record 2 + rc += TestMethod( po, "BlankRecord()", V4Dbf.BlankRecord(), XB_NO_ERROR ); + rc += TestMethod( po, "PutField()", V4Dbf.PutField( fldRecId, "2" ), XB_NO_ERROR ); + sData = "Memo data rec 2 memo 1"; + rc += TestMethod( po, "UpdateMemoField()", V4Dbf.UpdateMemoField( fldMemo1, sData ), XB_NO_ERROR ); + sData = "Memo data rec 2 memo 2"; + rc += TestMethod( po, "UpdateMemoField()", V4Dbf.UpdateMemoField( fldMemo2, sData ), XB_NO_ERROR ); + rc += TestMethod( po, "AppendRecord()", V4Dbf.AppendRecord(), XB_NO_ERROR ); + rc += TestDbt4Method( po, "Add two blocks", V4Dbf.GetMemoPtr(), 6, 6, "" ); + + // Record 3 + rc += TestMethod( po, "BlankRecord()", V4Dbf.BlankRecord(), XB_NO_ERROR ); + rc += TestMethod( po, "PutField()", V4Dbf.PutField( fldRecId, "3" ), XB_NO_ERROR ); + sData = "Memo data rec 3 memo 1"; + rc += TestMethod( po, "UpdateMemoField()", V4Dbf.UpdateMemoField( fldMemo1, sData ), XB_NO_ERROR ); + sData = "Memo data rec 3 memo 2"; + rc += TestMethod( po, "UpdateMemoField()", V4Dbf.UpdateMemoField( fldMemo2, sData ), XB_NO_ERROR ); + rc += TestMethod( po, "AppendRecord()", V4Dbf.AppendRecord(), XB_NO_ERROR ); + rc += TestDbt4Method( po, "Add two blocks", V4Dbf.GetMemoPtr(), 8, 8, "" ); + + // Record 4 + rc += TestMethod( po, "BlankRecord()", V4Dbf.BlankRecord(), XB_NO_ERROR ); + rc += TestMethod( po, "PutField()", V4Dbf.PutField( fldRecId, "4" ), XB_NO_ERROR ); + sData = "Memo data rec 4 memo 1"; + rc += TestMethod( po, "UpdateMemoField()", V4Dbf.UpdateMemoField( fldMemo1, sData ), XB_NO_ERROR ); + sData = "Memo data rec 4 memo 2"; + rc += TestMethod( po, "UpdateMemoField()", V4Dbf.UpdateMemoField( fldMemo2, sData ), XB_NO_ERROR ); + rc += TestMethod( po, "AppendRecord()", V4Dbf.AppendRecord(), XB_NO_ERROR ); + rc += TestDbt4Method( po, "Add two blocks", V4Dbf.GetMemoPtr(), 10, 10, "" ); + + // Record 5 + rc += TestMethod( po, "BlankRecord()", V4Dbf.BlankRecord(), XB_NO_ERROR ); + rc += TestMethod( po, "PutField()", V4Dbf.PutField( fldRecId, "5" ), XB_NO_ERROR ); + sData = "Memo data rec 5 memo 1"; + rc += TestMethod( po, "UpdateMemoField()", V4Dbf.UpdateMemoField( fldMemo1, sData ), XB_NO_ERROR ); + sData = "Memo data rec 5 memo 2"; + rc += TestMethod( po, "UpdateMemoField()", V4Dbf.UpdateMemoField( fldMemo2, sData ), XB_NO_ERROR ); + rc += TestMethod( po, "AppendRecord()", V4Dbf.AppendRecord(), XB_NO_ERROR ); + rc += TestDbt4Method( po, "Add two blocks", V4Dbf.GetMemoPtr(), 12, 12, "" ); + + // Record 6 + rc += TestMethod( po, "BlankRecord()", V4Dbf.BlankRecord(), XB_NO_ERROR ); + rc += TestMethod( po, "PutField()", V4Dbf.PutField( fldRecId, "6" ), XB_NO_ERROR ); + sData = "Memo data rec 6 memo 1"; + rc += TestMethod( po, "UpdateMemoField()", V4Dbf.UpdateMemoField( fldMemo1, sData ), XB_NO_ERROR ); + sData = "Memo data rec 6 memo 2"; + rc += TestMethod( po, "UpdateMemoField()", V4Dbf.UpdateMemoField( fldMemo2, sData ), XB_NO_ERROR ); + rc += TestMethod( po, "AppendRecord()", V4Dbf.AppendRecord(), XB_NO_ERROR ); + rc += TestDbt4Method( po, "Add two blocks", V4Dbf.GetMemoPtr(), 14, 14, "" ); + + // Record 7 + rc += TestMethod( po, "BlankRecord()", V4Dbf.BlankRecord(), XB_NO_ERROR ); + rc += TestMethod( po, "PutField()", V4Dbf.PutField( fldRecId, "7" ), XB_NO_ERROR ); + sData = "Memo data rec 6 memo 1"; + rc += TestMethod( po, "UpdateMemoField()", V4Dbf.UpdateMemoField( fldMemo1, sData ), XB_NO_ERROR ); + sData = "Memo data rec 6 memo 2"; + rc += TestMethod( po, "UpdateMemoField()", V4Dbf.UpdateMemoField( fldMemo2, sData ), XB_NO_ERROR ); + rc += TestMethod( po, "AppendRecord()", V4Dbf.AppendRecord(), XB_NO_ERROR ); + rc += TestDbt4Method( po, "Add two blocks", V4Dbf.GetMemoPtr(), 16, 16, "" ); + + // delete records 1, 3, 5, 7 + rc += TestMethod( po, "GetRecord()", V4Dbf.GetRecord( 1 ), XB_NO_ERROR ); + rc += TestMethod( po, "DeleteRecord()", V4Dbf.DeleteRecord(), XB_NO_ERROR ); + rc += TestMethod( po, "GetRecord()", V4Dbf.GetRecord( 3 ), XB_NO_ERROR ); + rc += TestMethod( po, "DeleteRecord()", V4Dbf.DeleteRecord(), XB_NO_ERROR ); + rc += TestMethod( po, "GetRecord()", V4Dbf.GetRecord( 5 ), XB_NO_ERROR ); + rc += TestMethod( po, "DeleteRecord()", V4Dbf.DeleteRecord(), XB_NO_ERROR ); + rc += TestMethod( po, "GetRecord()", V4Dbf.GetRecord( 7 ), XB_NO_ERROR ); + rc += TestMethod( po, "DeleteRecord()", V4Dbf.DeleteRecord(), XB_NO_ERROR ); + + + rc += TestMethod( po, "Pack()", V4Dbf.Pack(), XB_NO_ERROR ); + rc += TestMethod( po, "Close()", V4Dbf.Close(), XB_NO_ERROR ); + } + + if( po > 0 || rc < 0 ) + fprintf( stdout, "Total Errors = %d\n", rc * -1 ); + +#ifdef XB_LOGGING_SUPPORT + sMsg.Sprintf( "Program [%s] terminating with [%d] errors...", av[0], rc * -1 ); + x.WriteLogMessage( sMsg ); +#endif + + return rc; +} diff --git a/src/tests/xb_test_dbf_v4_nomemos.cpp b/src/tests/xb_test_dbf_v4_nomemos.cpp new file mode 100755 index 0000000..68ecda0 --- /dev/null +++ b/src/tests/xb_test_dbf_v4_nomemos.cpp @@ -0,0 +1,330 @@ +/* xb_test_xbdbf_v4_nomemos.cpp + +XBase 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 program tests the class xbXdbf + +// usage: xb_test_dbf_v4_nomemos QUITE|NORMAL|VERBOSE + + +#include "xbase.h" + +using namespace xb; + +#include "tstfuncs.cpp" + + +int main( int argCnt, char **av ) +{ + int rc = 0; + int rc2 = 0; + int po = 1; /* print option */ + /* 0 - QUIET */ + /* 1 - NORMAL */ + /* 2 - VERBOSE */ + + if( argCnt > 1 ) { + if( av[1][0] == 'Q' ) + po = 0; + else if( av[1][0] == 'V' ) + po = 2; + } + + xbSchema MyV4Record[] = + { + { "FIRSTNAME", XB_CHAR_FLD, 15, 0 }, + { "LASTNAME", XB_CHAR_FLD, 20, 0 }, + { "MIDDLEINIT", XB_CHAR_FLD, 1, 0 }, + { "BIRTHDATE", XB_DATE_FLD, 8, 0 }, + { "STARTDATE", XB_DATE_FLD, 8, 0 }, + { "AMOUNT", XB_NUMERIC_FLD, 9, 2 }, + { "TESTNUM", XB_NUMERIC_FLD, 12, 4 }, + { "SWITCH", XB_LOGICAL_FLD, 1, 0 }, + { "ZIPCODE", XB_NUMERIC_FLD, 5, 0 }, + { "DBLFLDTST", XB_FLOAT_FLD, 14, 4 }, + { "",0,0,0 } + }; + + xbXBase x; + +#ifdef XB_LOGGING_SUPPORT + x.EnableMsgLogging(); + if( po ){ + std::cout << "Logfile is [" << x.GetLogFqFileName().Str() << "]" << std::endl; + } + xbString sMsg; + sMsg.Sprintf( "Program [%s] initializing...", av[0] ); + x.WriteLogMessage( sMsg ); +#endif + + x.SetDataDirectory( PROJECT_DATA_DIR ); + x.EnableMsgLogging(); + x.DisableDefaultAutoLock(); + InitTime(); + + if( po > 0 ) + std::cout << "Default Data Directory is [" << x.GetDataDirectory().Str() << "]" << std::endl; + + xbDbf4 V4Dbf( &x ); // version 4 dbf file + rc2 = V4Dbf.CreateTable( "TestV4.DBF", "TestV4", MyV4Record, XB_OVERLAY, XB_MULTI_USER ); + rc += TestMethod( po, "CreateTable()", (xbInt32) rc2, XB_NO_ERROR ); + if( rc2 ) + x.DisplayError( rc2 ); + + xbUInt64 ullFileSize; + rc2 = V4Dbf.GetFileSize( ullFileSize ); + + rc = TestMethod( po, "CheckTableSize", ullFileSize, (xbUInt64) 353 ); + + if( po == 2 ){ + std::cout << "There should be one entry in the table list" << std::endl; + x.DisplayTableList(); + } + + xbDbf4 V4Dbf2( &x ); + + // next occurrence should error as a dup + x.WriteLogMessage( "Second create attempt" ); + rc2 = V4Dbf2.CreateTable( "TestV4.DBF", "TestV4", MyV4Record, XB_DONTOVERLAY, XB_MULTI_USER ); + rc += TestMethod( po, "CreateTable()", rc2, XB_FILE_EXISTS ); + if( rc2 != XB_FILE_EXISTS ){ + x.DisplayError( rc2 ); + } + + if( po == 2 ){ + std::cout << "There should be one entry in the table list" << std::endl; + x.DisplayTableList(); + } + + + rc += TestMethod( po, "BlankRecord()", V4Dbf.BlankRecord(), XB_NO_ERROR ); + rc += TestMethod( po, "GetFieldNo()", V4Dbf.GetFieldNo("LASTNAME"), 1 ); + rc += TestMethod( po, "GetFieldNo()", V4Dbf.GetFieldNo("MIDDLEINIT"), 2 ); + + xbInt16 fldLastName = V4Dbf.GetFieldNo( "LASTNAME" ); + rc += TestMethod( po, "PutField()", V4Dbf.PutField( fldLastName, "NELSON" ), XB_NO_ERROR ); + rc += TestMethod( po, "PutField()", V4Dbf.PutField( "FIRSTNAME", "WILLIE" ), XB_NO_ERROR ); + rc += TestMethod( po, "PutField()", V4Dbf.PutField( "AMOUNT", "12.34" ), XB_NO_ERROR ); + xbDate dt( "19500209" ); + rc += TestMethod( po, "PutDateField()", V4Dbf.PutDateField( "BIRTHDATE", dt ), XB_NO_ERROR ); + rc += TestMethod( po, "PutLongField()", V4Dbf.PutLongField( "ZIPCODE", 12345 ), XB_NO_ERROR ); + rc += TestMethod( po, "PutLogicalField()", V4Dbf.PutLogicalField( "SWITCH", "Y" ), XB_NO_ERROR ); + rc += TestMethod( po, "PutFloatField()", V4Dbf.PutFloatField( "TESTNUM", (xbFloat) 1234.5678 ), XB_NO_ERROR ); + rc += TestMethod( po, "PutDoubleField()", V4Dbf.PutDoubleField( "DBLFLDTST", (xbDouble) 9876.5432 ), XB_NO_ERROR ); + rc += TestMethod( po, "AppendRecord()", V4Dbf.AppendRecord(), XB_NO_ERROR ); + + + xbString sf; + rc += TestMethod( po, "GetField()", V4Dbf.GetField( fldLastName, sf, 0 ), XB_NO_ERROR ); + rc += TestMethod( po, "GetField()", sf, "NELSON ", 20 ); + rc += TestMethod( po, "GetField()", V4Dbf.GetField( fldLastName, sf ), XB_NO_ERROR ); + rc += TestMethod( po, "GetField()", sf, "NELSON ", 20 ); + rc += TestMethod( po, "GetField()", V4Dbf.GetField( "LASTNAME", sf ), XB_NO_ERROR ); + rc += TestMethod( po, "GetField()", sf, "NELSON ", 20 ); + xbInt16 iNoOfDecimals; + rc += TestMethod( po, "GetFieldDecimal()", V4Dbf.GetFieldDecimal( "AMOUNT", iNoOfDecimals ), XB_NO_ERROR ); + rc += TestMethod( po, "GetFieldDecimal()", iNoOfDecimals, 2 ); + + xbDouble d; + rc += TestMethod( po, "GetDoubleField()", V4Dbf.GetDoubleField( "DBLFLDTST", d ), XB_NO_ERROR ); + rc += TestMethod( po, "GetDoubleField()-b", d, (xbDouble) 9876.5432 ); + + char FieldType; + rc += TestMethod( po, "GetFieldType()", V4Dbf.GetFieldType( "STARTDATE", FieldType ), XB_NO_ERROR ); + rc += TestMethod( po, "GetFieldType()", FieldType , 'D' ); + + xbInt16 iFieldLen; + rc += TestMethod( po, "GetFieldLen()", V4Dbf.GetFieldLen( "STARTDATE", iFieldLen ), XB_NO_ERROR ); + rc += TestMethod( po, "GetFieldLen()", iFieldLen, 8 ); + + xbInt16 fldAMT = V4Dbf.GetFieldNo( "AMOUNT" ); + rc += TestMethod( po, "GetField()", V4Dbf.GetField( fldAMT, sf ), XB_NO_ERROR ); + rc += TestMethod( po, "GetField()", sf, " 12.34", 9 ); + + xbInt32 lZip; + rc += TestMethod( po, "GetLongField()", V4Dbf.GetLongField( "ZIPCODE", lZip ), XB_NO_ERROR ); + rc += TestMethod( po, "GetLongField()", lZip, 12345 ); + + xbString sSwitch; + rc += TestMethod( po, "GetLogicalField()", V4Dbf.GetLogicalField( "SWITCH", sSwitch ), XB_NO_ERROR ); + rc += TestMethod( po, "GetLogicalField()", sSwitch, "Y", 1 ); + + xbFloat fNum; + rc += TestMethod( po, "GetFloatField()", V4Dbf.GetFloatField( "TESTNUM", fNum ), 0 ); + rc += TestMethod( po, "GetFloatField()", fNum, (xbFloat) 1234.5678 ); + + // init a second record for more testing + rc += TestMethod( po, "BlankRecord()", V4Dbf.BlankRecord(), XB_NO_ERROR ); + rc += TestMethod( po, "PutLogicalField()", V4Dbf.PutLogicalField( "SWITCH", xbTrue ), XB_NO_ERROR ); + rc += TestMethod( po, "AppendRecord()", V4Dbf.AppendRecord(), XB_NO_ERROR ); + xbBool bSwitch; + rc += TestMethod( po, "GetLogicalField()", V4Dbf.GetLogicalField( "SWITCH", bSwitch ), 0 ); + rc += TestMethod( po, "GetLogicalField()", bSwitch, xbTrue ); + + // init a third record for more testing + rc += TestMethod( po, "BlankRecord()", V4Dbf.BlankRecord(), XB_NO_ERROR ); + rc += TestMethod( po, "PutLogicalField()", V4Dbf.PutLogicalField( "SWITCH", xbFalse ), XB_NO_ERROR ); + rc += TestMethod( po, "AppendRecord()", V4Dbf.AppendRecord(), XB_NO_ERROR ); + rc += TestMethod( po, "GetLogicalField()", V4Dbf.GetLogicalField( "SWITCH", bSwitch ), 0 ); + rc += TestMethod( po, "GetLogicalField()", bSwitch, xbFalse ); + + rc += TestMethod( po, "PutField()", V4Dbf.PutField( "TESTNUM", "12345678" ), XB_INVALID_DATA ); + rc += TestMethod( po, "PutField()", V4Dbf.PutField( "TESTNUM", "1234567" ), XB_NO_ERROR ); + rc += TestMethod( po, "PutField()", V4Dbf.PutField( "TESTNUM", "1234567.12345" ), XB_INVALID_DATA ); + rc += TestMethod( po, "PutField()", V4Dbf.PutField( "TESTNUM", ".12345" ), XB_INVALID_DATA ); + rc += TestMethod( po, "PutField()", V4Dbf.PutField( "TESTNUM", ".1234" ), XB_NO_ERROR ); + rc += TestMethod( po, "PutField()", V4Dbf.PutField( "TESTNUM", "12345678.1234" ), XB_INVALID_DATA ); + rc += TestMethod( po, "PutField()", V4Dbf.PutField( "TESTNUM", "1234567.1234" ), XB_NO_ERROR ); + + rc += TestMethod( po, "AppendRecord()", V4Dbf.AppendRecord(), XB_NO_ERROR ); + + rc += TestMethod( po, "GetFieldCnt()", V4Dbf.GetFieldCnt(), 10 ); + rc += TestMethod( po, "GetTblAlias()", V4Dbf.GetTblAlias(), "TestV4", 6 ); + rc += TestMethod( po, "GetDbfStatus()", V4Dbf.GetDbfStatus(), XB_OPEN ); + rc += TestMethod( po, "GetCurRecNo()", (xbInt32) V4Dbf.GetCurRecNo(), 4 ); + rc += TestMethod( po, "GetFirstRecord()", V4Dbf.GetFirstRecord(), XB_NO_ERROR ); + rc += TestMethod( po, "GetCurRecNo()", (xbInt32) V4Dbf.GetCurRecNo(), 1 ); + rc += TestMethod( po, "GetNextRecord()", V4Dbf.GetNextRecord(), XB_NO_ERROR ); + rc += TestMethod( po, "GetCurRecNo()", (xbInt32) V4Dbf.GetCurRecNo(), 2 ); + rc += TestMethod( po, "GetLastRecord()", V4Dbf.GetLastRecord(), XB_NO_ERROR ); + rc += TestMethod( po, "GetCurRecNo()", (xbInt32) V4Dbf.GetCurRecNo(), 4 ); + rc += TestMethod( po, "GetPrevRecord()", V4Dbf.GetPrevRecord(), XB_NO_ERROR ); + rc += TestMethod( po, "GetCurRecNo()", (xbInt32) V4Dbf.GetCurRecNo(), 3 ); + rc += TestMethod( po, "GetRecordLen()", (xbInt32) V4Dbf.GetRecordLen(), 94 ); + + char * p = V4Dbf.GetRecordBuf(); + rc += TestMethod( po, "GetRecordBuf()", p[0], ' ' ); + + if( po == 2 ){ + V4Dbf.DumpHeader( 3 ); + V4Dbf.DumpRecord( 4 ); + } + + rc += TestMethod( po, "PutField()", V4Dbf.PutField( "LASTNAME", "CLINTON" ), XB_NO_ERROR ); + rc += TestMethod( po, "PutRecord()", V4Dbf.PutRecord(), XB_NO_ERROR ); + rc += TestMethod( po, "DeleteRecord()", V4Dbf.DeleteRecord(), XB_NO_ERROR ); + rc += TestMethod( po, "UndeleteRecord()", V4Dbf.UndeleteRecord(), XB_NO_ERROR ); + xbUInt32 ulRecCnt = 0; + rc += TestMethod( po, "GetRecordCnt()", V4Dbf.GetRecordCnt( ulRecCnt ), XB_NO_ERROR ); + rc += TestMethod( po, "GetRecordCnt()", (xbInt32) ulRecCnt, 4 ); + rc += TestMethod( po, "DeleteAllRecords()", V4Dbf.DeleteAllRecords(), XB_NO_ERROR ); + rc += TestMethod( po, "UndeleteAllRecords()", V4Dbf.UndeleteAllRecords(), XB_NO_ERROR ); + rc += TestMethod( po, "GetRecord()", V4Dbf.GetRecord( 2L ), XB_NO_ERROR ); + rc += TestMethod( po, "DeleteRecord()", V4Dbf.DeleteRecord(), XB_NO_ERROR ); + + if( po == 2 ){ + V4Dbf.DumpRecord( 4 ); + } + + xbDbf4 CopyDbf( &x ); + rc += TestMethod( po, "CopyDbfStructure()", V4Dbf.CopyDbfStructure( &CopyDbf, "CopyStructureV4NoMemos", "CopyAliasV4NoMemos", XB_OVERLAY, XB_MULTI_USER ), XB_NO_ERROR ); + rc += TestMethod( po, "CopyDbf.Close()", CopyDbf.Close(), XB_NO_ERROR ); + + if( po == 2 ){ + std::cout << "There should be one entry in the table list" << std::endl; + x.DisplayTableList(); + } + + rc += TestMethod( po, "GetDbfStatus()", CopyDbf.GetDbfStatus(), XB_CLOSED ); + rc += TestMethod( po, "Open()", CopyDbf.xbDbf::Open( "CopyStructureV4NoMemos.DBF", "CopyAliasV4NoMemos" ), XB_NO_ERROR ); + + if( po == 2 ){ + std::cout << "There should be two entries in the table list" << std::endl; + x.DisplayTableList(); + } + + rc += TestMethod( po, "Pack()", V4Dbf.Pack(), XB_NO_ERROR ); + rc += TestMethod( po, "GetDbfStatus()", V4Dbf.GetDbfStatus(), XB_OPEN ); + + rc += TestMethod( po, "Zap()", V4Dbf.Zap(), XB_NO_ERROR ); + if( po == 2 ) + x.DisplayTableList(); + + xbUInt64 ullFileSize2 = 0; + rc2 = V4Dbf.GetFileSize( ullFileSize2 ); + rc = TestMethod( po, "CheckTableSize", ullFileSize2, ullFileSize ); + +// Test of auto commit code here + rc += TestMethod( po, "BlankRecord()", V4Dbf.BlankRecord(), XB_NO_ERROR ); + rc += TestMethod( po, "PutField()", V4Dbf.PutField( fldLastName, "NELSON" ), XB_NO_ERROR ); + rc += TestMethod( po, "PutField()", V4Dbf.PutField( "FIRSTNAME", "WILLIE" ), XB_NO_ERROR ); + rc += TestMethod( po, "PutField()", V4Dbf.PutField( "AMOUNT", "12.34" ), XB_NO_ERROR ); + rc += TestMethod( po, "PutLongField()", V4Dbf.PutLongField( "ZIPCODE", 12345 ), XB_NO_ERROR ); + rc += TestMethod( po, "PutLogicalField()", V4Dbf.PutLogicalField( "SWITCH", "Y" ), XB_NO_ERROR ); + rc += TestMethod( po, "PutFloatField()", V4Dbf.PutFloatField( "TESTNUM", (xbFloat) 1234.5678 ), XB_NO_ERROR ); + rc += TestMethod( po, "PutDoubleField()", V4Dbf.PutDoubleField( "DBLFLDTST", (xbDouble) 9876.5432 ), XB_NO_ERROR ); + rc += TestMethod( po, "AppendRecord()", V4Dbf.AppendRecord(), XB_NO_ERROR ); + + rc += TestMethod( po, "BlankRecord()", V4Dbf.BlankRecord(), XB_NO_ERROR ); + rc += TestMethod( po, "PutField()", V4Dbf.PutField( fldLastName, "JOHNSON" ), XB_NO_ERROR ); + rc += TestMethod( po, "PutField()", V4Dbf.PutField( "FIRSTNAME", "JIMMY" ), XB_NO_ERROR ); + rc += TestMethod( po, "PutField()", V4Dbf.PutField( "AMOUNT", "555.33" ), XB_NO_ERROR ); + rc += TestMethod( po, "PutLongField()", V4Dbf.PutLongField( "ZIPCODE", 76523 ), XB_NO_ERROR ); + rc += TestMethod( po, "PutLogicalField()", V4Dbf.PutLogicalField( "SWITCH", "N" ), XB_NO_ERROR ); + rc += TestMethod( po, "PutFloatField()", V4Dbf.PutFloatField( "TESTNUM", (xbFloat) 8765.4321 ), XB_NO_ERROR ); + rc += TestMethod( po, "PutDoubleField()", V4Dbf.PutDoubleField( "DBLFLDTST", (xbDouble) 9876.5432 ), XB_NO_ERROR ); + rc += TestMethod( po, "AppendRecord()", V4Dbf.AppendRecord(), XB_NO_ERROR ); + + rc += TestMethod( po, "PutField()", V4Dbf.PutField( "AMOUNT", "666.22" ), XB_NO_ERROR ); + rc += TestMethod( po, "Abort()", V4Dbf.Abort(), XB_NO_ERROR ); + rc += TestMethod( po, "GetField()", V4Dbf.GetField( "AMOUNT", sf ), XB_NO_ERROR ); + rc += TestMethod( po, "GetField()", sf, " 555.33", 9 ); + + rc += TestMethod( po, "PutField()", V4Dbf.PutField( "AMOUNT", "666.22" ), XB_NO_ERROR ); + rc += TestMethod( po, "Commit()", V4Dbf.Commit(), XB_NO_ERROR ); + rc += TestMethod( po, "GetField()", V4Dbf.GetField( fldAMT, sf ), XB_NO_ERROR ); + rc += TestMethod( po, "GetField()", sf, " 666.22", 9 ); + + rc += TestMethod( po, "GetPrevRecord()", V4Dbf.GetPrevRecord(), XB_NO_ERROR ); + rc += TestMethod( po, "GetNextRecord()", V4Dbf.GetNextRecord(), XB_NO_ERROR ); + rc += TestMethod( po, "GetField()", V4Dbf.GetField( fldAMT, sf ), XB_NO_ERROR ); + rc += TestMethod( po, "GetField()", sf, " 666.22", 9 ); + + rc += TestMethod( po, "AutoCommit()", V4Dbf.SetAutoCommit( 0 ), XB_NO_ERROR ); + rc += TestMethod( po, "PutField()", V4Dbf.PutField( "AMOUNT", "999.33" ), XB_NO_ERROR ); + rc += TestMethod( po, "GetPrevRecord()", V4Dbf.GetPrevRecord(), XB_NO_ERROR ); + rc += TestMethod( po, "GetNextRecord()", V4Dbf.GetNextRecord(), XB_NO_ERROR ); + rc += TestMethod( po, "GetField()", V4Dbf.GetField( fldAMT, sf ), XB_NO_ERROR ); + rc += TestMethod( po, "GetField()", sf, " 666.22", 9 ); + + rc += TestMethod( po, "AutoCommit()", V4Dbf.SetAutoCommit( -1 ), XB_NO_ERROR ); + rc += TestMethod( po, "PutField()", V4Dbf.PutField( "AMOUNT", "999.33" ), XB_NO_ERROR ); + rc += TestMethod( po, "GetCurRecNo()", (xbInt32) V4Dbf.GetCurRecNo(), 2 ); + rc += TestMethod( po, "GetPrevRecord()", V4Dbf.GetPrevRecord(), XB_NO_ERROR ); + rc += TestMethod( po, "GetNextRecord()", V4Dbf.GetNextRecord(), XB_NO_ERROR ); + rc += TestMethod( po, "GetField()", V4Dbf.GetField( fldAMT, sf ), XB_NO_ERROR ); + rc += TestMethod( po, "GetField()", sf, " 999.33", 9 ); + + rc += TestMethod( po, "AutoCommit()", V4Dbf.SetAutoCommit( 1 ), XB_NO_ERROR ); + rc += TestMethod( po, "PutField()", V4Dbf.PutField( "AMOUNT", "432.55" ), XB_NO_ERROR ); + rc += TestMethod( po, "GetCurRecNo()", (xbInt32) V4Dbf.GetCurRecNo(), 2 ); + rc += TestMethod( po, "GetPrevRecord()", V4Dbf.GetPrevRecord(), XB_NO_ERROR ); + rc += TestMethod( po, "GetNextRecord()", V4Dbf.GetNextRecord(), XB_NO_ERROR ); + rc += TestMethod( po, "GetField()", V4Dbf.GetField( fldAMT, sf ), XB_NO_ERROR ); + rc += TestMethod( po, "GetField()", sf, " 432.55", 9 ); + + rc += TestMethod( po, "Close()", V4Dbf.Close(), XB_NO_ERROR ); + if( po == 2 ) + x.DisplayTableList(); + + if( po > 0 || rc < 0 ) + fprintf( stdout, "Total Errors = %d\n", rc * -1 ); + +#ifdef XB_LOGGING_SUPPORT + sMsg.Sprintf( "Program [%s] terminating with [%d] errors...", av[0], rc * -1 ); + x.WriteLogMessage( sMsg ); +#endif + + return rc; +} diff --git a/src/tests/xb_test_expnode.cpp b/src/tests/xb_test_expnode.cpp new file mode 100755 index 0000000..3fb716d --- /dev/null +++ b/src/tests/xb_test_expnode.cpp @@ -0,0 +1,123 @@ +/* xb_test_expnode.cpp + +XBase 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 + +*/ + +// This program tests the xb expression node functions +// usage: xb_test_expnode QUITE|NORMAL|VERBOSE + +#include "xbase.h" +using namespace xb; + +#include "tstfuncs.cpp" + + +/**************************************************************************/ + +int main( int argCnt, char **av ) +{ + xbInt16 iRc = 0; + xbInt16 po = 1; /* print option */ + /* 0 - QUIET */ + /* 1 - NORMAL */ + /* 2 - VERBOSE */ + + if( argCnt > 1 ) { + if( av[1][0] == 'Q' ) + po = 0; + else if( av[1][0] == 'V' ) + po = 2; + } + + xbXBase x; + + #ifdef XB_LOGGING_SUPPORT + x.EnableMsgLogging(); + if( po ){ + std::cout << "Logfile is [" << x.GetLogFqFileName().Str() << "]" << std::endl; + } + xbString sMsg; + sMsg.Sprintf( "Program [%s] initializing...", av[0] ); + x.WriteLogMessage( sMsg ); + #endif + + x.SetDataDirectory( PROJECT_DATA_DIR ); + x.EnableMsgLogging(); + InitTime(); + + if( po > 0 ){ + std::cout << "XBase Expression Node testing program.." << std::endl; + std::cout << "This program tests the XBase expression node logic." << std::endl; + } + + xbString s1 = "TestNode1"; + xbExpNode *n1 = new xbExpNode(); + n1->SetNodeText( s1 ); + n1->SetNodeType( XB_EXP_OPERATOR ); + n1->SetReturnType( XB_EXP_LOGICAL ); + + iRc += TestMethod( po, "GetNodeType()", n1->GetNodeType(), 'O' ); + iRc += TestMethod( po, "GetReturnType()", n1->GetReturnType(), 'L' ); + + xbString s2 = "TestNode2"; + xbExpNode *n2 = new xbExpNode( s2, XB_EXP_FUNCTION, XB_EXP_CHAR ); + + xbString s3 = "TestNode3"; + xbExpNode *n3 = new xbExpNode( s3, XB_EXP_CONSTANT, XB_EXP_NUMERIC ); + + xbString s4 = "TestNode4"; + xbExpNode *n4 = new xbExpNode( s4, XB_EXP_OPERATOR, XB_EXP_DATE ); + + xbString s5 = "TestNode5"; + xbExpNode *n5 = new xbExpNode( s5, XB_EXP_FIELD, XB_EXP_LOGICAL ); + + n1->AddChild( n2 ); + n1->AddChild( n3 ); + n1->AddChild( n4 ); + n1->AddChild( n5 ); + + iRc += TestMethod( po, "GetChildCnt()", (xbInt32) n1->GetChildCnt(), 4 ); + + n1->SetResult( s1 ); + iRc += TestMethod( po, "SetResult() / GetStringResult()", s1, n1->GetStringResult(), 9 ); + + xbBool bVal = xbTrue; + n1->SetResult( bVal ); + iRc += TestMethod( po, "SetResult() / GetBoolResult()", xbTrue, n1->GetBoolResult() ); + + xbDouble d = 123456.789; + n1->SetResult( d ); + iRc += TestMethod( po, "SetResult() / GetNumericResult()", d, n1->GetNumericResult() ); + + #ifdef XB_DEBUG_SUPPORT + if( po > 0 ){ + n1->DumpNode( xbTrue ); + n1->GetChild( 0 )->DumpNode( xbTrue ); + n1->GetChild( 1 )->DumpNode( xbTrue ); + n1->GetChild( 2 )->DumpNode( xbTrue ); + n1->GetChild( 3 )->DumpNode( xbTrue ); + } + #endif + + delete n1; + + if( po > 0 || iRc < 0 ) + fprintf( stdout, "Total Errors = %d\n", iRc * -1 ); + + #ifdef XB_LOGGING_SUPPORT + sMsg.Sprintf( "Program [%s] terminating with [%d] errors...", av[0], iRc * -1 ); + x.WriteLogMessage( sMsg ); + #endif + + return iRc; +} diff --git a/src/tests/xb_test_expression.cpp b/src/tests/xb_test_expression.cpp new file mode 100755 index 0000000..e3b47cc --- /dev/null +++ b/src/tests/xb_test_expression.cpp @@ -0,0 +1,791 @@ +/* xb_test_expression.cpp + +XBase 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 program tests the class xbExp +// usage: xb_test_expression QUITE|NORMAL|VERBOSE + +#include "xbase.h" +using namespace xb; + +#include "tstfuncs.cpp" + + +/**************************************************************************/ +///@cond DOXYOFF + +class xbExpTest : public xbExp { + + public: + xbExpTest( xbXBase * x ); + xbInt16 GetNextTokenTest( xbExpToken &t ); + xbInt16 OperatorWeightTest( const xbString &sOperator ); + xbExpNode *GetNextNodeTest( xbExpNode * ); +}; + +/**************************************************************************/ +xbExpTest::xbExpTest( xbXBase * x ) : xbExp( x ){ +} +/**************************************************************************/ +xbExpNode *xbExpTest::GetNextNodeTest( xbExpNode *n ){ + return GetNextNode( n ); +} +/**************************************************************************/ +xbInt16 xbExpTest::GetNextTokenTest( xbExpToken &t ){ + return GetNextToken( t ); +} +/**************************************************************************/ +xbInt16 xbExpTest::OperatorWeightTest( const xbString &sOperator ){ + return OperatorWeight( sOperator ); +} +///@endcond DOXYOFF + + +/**************************************************************************/ +xbInt16 DevTester( xbXBase * xb, xbDbf * dbf, const char * sExpIn ){ + + xbExpTest exp( xb ); + xbExpNode *n; + + std::cout << "going to parse expression [" << sExpIn << "]\n"; + xbInt16 iRc = exp.ParseExpression( dbf, sExpIn ); + + std::cout << "back from parse expression\n"; + std::cout << "dump nodes\n"; + n = exp.GetNextNodeTest( NULL ); + if( iRc == XB_NO_ERROR ){ + exp.DumpTree( xbTrue ); + std::cout << "dump nodes\n"; + n = exp.GetNextNodeTest( NULL ); + if( !n ){ + std::cout << "n is null\n"; + } else { + xbInt16 i = 0; + while( n && i < 30 ){ + std::cout << "Node Text = [" << n->GetStringResult().Str() << "]\n"; + n = exp.GetNextNodeTest( n ); + i++; + } + } + std::cout << "end dump nodes\n"; + return 0; + }else{ + std::cout << "Parse Error [" << iRc << "]\n"; + return -1; + } + return 0; +} + +/**************************************************************************/ +xbInt16 TestWeight( xbXBase * xb, xbInt16 iPrintOption, const char * title, const char *sOperator, xbInt16 iExpectedWeight ); +xbInt16 TestWeight( xbXBase * xb, xbInt16 iPrintOption, const char * title, const char *sOperator, xbInt16 iExpectedWeight ){ + + xbInt16 iRc = 0; + xbExpTest expT1( xb ); + + if(( iRc = expT1.OperatorWeightTest( sOperator )) != iExpectedWeight ){ + std::cout << std::endl << "[FAIL O1] " << title << std::endl; + std::cout << " Operator [" << sOperator << "] Weight [" << iRc << "] Expected [" << iExpectedWeight << "]" << std::endl; + return -1; + } + + if( iPrintOption == 2 ) + std::cout << "[PASS] " << title << " Expected matches actual Data=[" << sOperator << "][" << iExpectedWeight << "]" << std::endl; + else if( iPrintOption == 1 ) + std::cout << "[PASS] " << title << std::endl; + + return XB_NO_ERROR; +} +/**************************************************************************/ +xbInt16 TestTokenMethod( xbXBase *xb, xbInt16 iPrintOption, const char * title, const char *sExpression, + const char *sExpectedToken, const char *sExpectedRemainder, char cExpectedNodeType, + char cExpectedReturnType, xbInt16 iErc, xbInt16 iTrc ); + +xbInt16 TestTokenMethod( xbXBase *xb, xbInt16 iPrintOption, const char * title, const char *sExpression, + const char *sExpectedToken, const char *sExpectedRemainder, char cExpectedNodeType, + char cExpectedReturnType, xbInt16 iErc, xbInt16 iTrc ){ + + xbExpTest expT1( xb ); + xbExpToken t; + xbInt16 iRc = XB_NO_ERROR; + t.sExpression = sExpression; + + if(( iRc = expT1.GetNextTokenTest( t )) != iErc ){ + std::cout << std::endl << "[FAIL T1] " << title << std::endl; + std::cout << " Expression [" << sExpression << "]" << std::endl; + std::cout << " GetNextToken iRc = [" << iRc << "]" << std::endl; + return -1; + } + + if( iTrc != t.iSts ){ + std::cout << std::endl << "[FAIL T2] " << title << std::endl; + std::cout << " Expression [" << sExpression << "]" << std::endl; + std::cout << " GetNextToken Token Return Code = [" << iTrc << "]" << std::endl; + return -1; + } + if( iErc == XB_NO_ERROR && iTrc == XB_NO_ERROR && t.sToken != sExpectedToken ){ + std::cout << std::endl << "[FAIL T3] " << title << std::endl; + std::cout << " Expression " << sExpression << std::endl; + std::cout << " Expected Result = [" << sExpectedToken << "] Actual = [" << t.sToken.Str() << "]\n"; + return -1; + } + if( iErc == XB_NO_ERROR && iTrc == XB_NO_ERROR && t.sExpression != sExpectedRemainder ){ + std::cout << std::endl << "[FAIL T4] " << title << std::endl; + std::cout << " Expression " << sExpression << std::endl; + std::cout << " Expected Remainder = [" << sExpectedRemainder << "] Actual = [" << t.sExpression.Str() << "]\n"; + return -1; + } + if( iErc == XB_NO_ERROR && iTrc == XB_NO_ERROR&& t.cNodeType != cExpectedNodeType ){ + std::cout << std::endl << "[FAIL T5] " << title << std::endl; + std::cout << " Expression " << sExpression << std::endl; + std::cout << " Expected Node type = [" << cExpectedNodeType << "] Actual = [" << t.cNodeType << "]\n"; + return -1; + } + if( iErc == XB_NO_ERROR && iTrc == XB_NO_ERROR&& t.cReturnType != cExpectedReturnType ){ + std::cout << std::endl << "[FAIL T6] " << title << std::endl; + std::cout << " Expression " << sExpression << std::endl; + std::cout << " Expected Return type = [" << cExpectedReturnType << "] Actual = [" << t.cReturnType << "]\n"; + return -1; + } + if( iPrintOption == 2 ){ + if( iErc == XB_NO_ERROR ) + std::cout << "[PASS] " << title << " Expected matches actual Data=[" << sExpectedToken << "]" << std::endl; + else + std::cout << "[PASS] " << title << " Expected return code matches actual =[" << iErc << "]" << std::endl; + } else if( iPrintOption == 1 ) + std::cout << "[PASS] " << title << std::endl; + + return 0; +} + +/**************************************************************************/ +xbInt16 TestMethod( xbXBase *xb, xbDbf *d, xbInt16 PrintOption, const char * title, + const xbString &sExpression, const char * sExpectedResult, xbInt16 iExpectedResultLen ); + +xbInt16 TestMethod( xbXBase *xb, xbDbf *d, xbInt16 PrintOption, const char * title, + const xbString &sExpression, const char * sExpectedResult, xbInt16 iExpectedResultLen ){ + + xbInt16 iRc = XB_NO_ERROR; + xbString sResult; + xbExp exp( xb ); + + if(( iRc = exp.ParseExpression( d, sExpression )) != XB_NO_ERROR ){ + std::cout << std::endl << "[FAIL 1] " << title << std::endl; + std::cout << " Return Code = [" << iRc << "]" << std::endl; + return -1; + } + if(( iRc = exp.GetReturnType()) != XB_EXP_CHAR ){ + std::cout << std::endl << "[FAIL 2] " << title << std::endl; + std::cout << " Result type not XB_EXP_CHAR = [" << exp.GetReturnType() << "]" << std::endl; + return -1; + } + if(( iRc = exp.ProcessExpression()) != XB_NO_ERROR ){ + std::cout << std::endl << "[FAIL 3] " << title << std::endl; + std::cout << " Return Code = [" << iRc << "]" << std::endl; + return -1; + } + if(( iRc = exp.GetStringResult( sResult )) != XB_NO_ERROR ){ + std::cout << std::endl << "[FAIL 4] " << title << std::endl; + std::cout << " Return Code = [" << iRc << "]" << std::endl; + return -1; + } + if( sResult != sExpectedResult ){ + std::cout << std::endl << "[FAIL 5] " << title << std::endl; + std::cout << " Expected Result = [" << sExpectedResult << "] Actual Result =[" << sResult.Str() << "]" << std::endl; + exp.DumpTree( 1 ); + return -1; + } + if( exp.GetResultLen() != iExpectedResultLen ){ + std::cout << std::endl << "[FAIL 6] " << title << std::endl; + std::cout << " Expected Result Len = [" << iExpectedResultLen << "] Actual Result =[" << exp.GetResultLen() << "]" << std::endl; + exp.DumpTree( 1 ); + return -1; + } + if( PrintOption == 2 ) + std::cout << "[PASS] " << title << " Expected [" << sExpectedResult << "] matches actual [" << exp.GetResultLen() << "]" << std::endl; + else if( PrintOption == 1 ) + std::cout << "[PASS] " << title << std::endl; + + return 0; +} + +/**************************************************************************/ +xbInt16 TestMethod( xbXBase * xb, xbDbf * d, xbInt16 PrintOption, const char * title, + const xbString &sExpression, xbDouble dExpectedResult ); + +xbInt16 TestMethod( xbXBase * xb, xbDbf * d, xbInt16 PrintOption, const char * title, + const xbString &sExpression, xbDouble dExpectedResult ){ + + xbDouble dResult; + xbInt16 iRc = XB_NO_ERROR; + xbExp exp( xb ); + + if(( iRc = exp.ParseExpression( d, sExpression )) != XB_NO_ERROR ){ + std::cout << std::endl << "[FAIL 1] " << title << std::endl; + std::cout << " Return Code = [" << iRc << "]" << std::endl; + return -1; + } + if(( iRc = exp.GetReturnType()) != XB_EXP_NUMERIC ){ + std::cout << std::endl << "[FAIL 2] " << title << std::endl; + std::cout << " Result type not XB_EXP_NUMERIC = [" << exp.GetReturnType() << "]" << std::endl; + return -1; + } + if(( iRc = exp.ProcessExpression()) != XB_NO_ERROR ){ + std::cout << std::endl << "[FAIL 3] " << title << std::endl; + std::cout << " Return Code = [" << iRc << "]" << std::endl; + return -1; + } + if(( iRc = exp.GetNumericResult( dResult )) != XB_NO_ERROR ){ + std::cout << std::endl << "[FAIL 4] " << title << std::endl; + std::cout << " Return Code = [" << iRc << "]" << std::endl; + return -1; + } + if( !dblEquals( dResult, dExpectedResult, .01 )){ + std::cout << std::endl << "[FAIL 5] " << title << std::endl; + std::cout << " Expected Result = [" << dExpectedResult << "] Actual Result =[" << dResult << "]" << std::endl; + // dump out the tree + exp.DumpTree( 1 ); + return -1; + } + if( PrintOption == 2 ) + std::cout << "[PASS] " << title << " Expected [" << dExpectedResult << "] matches actual [" << exp.GetResultLen() << "]" << std::endl; + else if( PrintOption == 1 ) + std::cout << "[PASS] " << title << std::endl; + return 0; +} +/**************************************************************************/ +xbInt16 TestMethod( xbXBase * xb, xbDbf * d, xbInt16 PrintOption, const char * title, + const xbString &sExpression, xbDate dtExpectedResult ); +xbInt16 TestMethod( xbXBase * xb, xbDbf * d, xbInt16 PrintOption, const char * title, + const xbString &sExpression, xbDate dtExpectedResult ){ + + xbDate dtResult; + xbInt16 iRc = XB_NO_ERROR; + xbExp exp( xb ); + if(( iRc = exp.ParseExpression( d, sExpression )) != XB_NO_ERROR ){ + std::cout << std::endl << "[FAIL 1] " << title << std::endl; + std::cout << " Return Code = [" << iRc << "]" << std::endl; + return -1; + } + if(( iRc = exp.GetReturnType()) != XB_EXP_DATE ){ + std::cout << std::endl << "[FAIL 2] " << title << std::endl; + std::cout << " Result type not XB_EXP_DATE = [" << exp.GetReturnType() << "]" << std::endl; + return -1; + } + if(( iRc = exp.ProcessExpression()) != XB_NO_ERROR ){ + std::cout << std::endl << "[FAIL 3] " << title << std::endl; + std::cout << " Return Code = [" << iRc << "]" << std::endl; + return -1; + } + if(( iRc = exp.GetDateResult( dtResult )) != XB_NO_ERROR ){ + std::cout << std::endl << "[FAIL 4] " << title << std::endl; + std::cout << " Return Code = [" << iRc << "]" << std::endl; + return -1; + } + if( dtResult != dtExpectedResult ){ + std::cout << std::endl << "[FAIL 5] " << title << std::endl; + std::cout << " Expected Result = [" << dtExpectedResult.Str() << "] Actual Result =[" << dtResult.Str() << "]" << std::endl; + // dump out the tree + exp.DumpTree( 1 ); + return -1; + } + if( PrintOption == 2 ) + std::cout << "[PASS] " << title << " Expected [" << dtExpectedResult.Str() << "] matches actual [" << exp.GetResultLen() << "]" << std::endl; + else if( PrintOption == 1 ) + std::cout << "[PASS] " << title << std::endl; + + return 0; +} +/**************************************************************************/ +xbInt16 TestMethod( xbXBase * xb, xbDbf * d, xbInt16 PrintOption, const char * title, + const xbString &sExpression, xbBool bExpectedResult ); +xbInt16 TestMethod( xbXBase * xb, xbDbf * d, xbInt16 PrintOption, const char * title, + const xbString &sExpression, xbBool bExpectedResult ){ + + xbInt16 iRc = XB_NO_ERROR; + + xbBool bResult; + xbExp exp( xb ); + if(( iRc = exp.ParseExpression( d, sExpression )) != XB_NO_ERROR ){ + std::cout << std::endl << "[FAIL 1] " << title << std::endl; + std::cout << " Return Code = [" << iRc << "]" << std::endl; + return -1; + } + if(( iRc = exp.GetReturnType()) != XB_EXP_LOGICAL ){ + std::cout << std::endl << "[FAIL 2] " << title << std::endl; + std::cout << " Result type not XB_EXP_LOGICAL = [" << exp.GetReturnType() << "]" << std::endl; + return -1; + } + if(( iRc = exp.ProcessExpression()) != XB_NO_ERROR ){ + std::cout << std::endl << "[FAIL 3] " << title << std::endl; + std::cout << " Return Code = [" << iRc << "]" << std::endl; + return -1; + } + if(( iRc = exp.GetBoolResult( bResult )) != XB_NO_ERROR ){ + std::cout << std::endl << "[FAIL 4] " << title << std::endl; + std::cout << " Return Code = [" << iRc << "]" << std::endl; + return -1; + } + if( bResult != bExpectedResult ){ + std::cout << std::endl << "[FAIL 5] " << title << std::endl; + std::cout << " Expected Result = [" << bExpectedResult << "] Actual Result =[" << bResult << "]" << std::endl; + // dump out the tree + exp.DumpTree( 1 ); + return -1; + } + if( PrintOption == 2 ) + std::cout << "[PASS] " << title << " Expected [" << bExpectedResult << "] matches actual [" << bResult << "]" << std::endl; + else if( PrintOption == 1 ) + std::cout << "[PASS] " << title << std::endl; + return 0; +} +/**************************************************************************/ +int main( int argCnt, char **av ) +{ + xbInt16 iRc = 0; + xbInt16 rc2 = 0; + xbInt16 po = 1; /* print option */ + /* 0 - QUIET */ + /* 1 - NORMAL */ + /* 2 - VERBOSE */ + + if( argCnt > 1 ) { + if( av[1][0] == 'Q' ) + po = 0; + else if( av[1][0] == 'V' ) + po = 2; + } + + xbSchema MyRecord[] = + { + { "CHAR1", XB_CHAR_FLD, 5, 0 }, + { "CHAR2", XB_CHAR_FLD, 7, 0 }, + { "DATE1", XB_DATE_FLD, 8, 0 }, + { "DATE2", XB_DATE_FLD, 8, 0 }, + { "NUM1", XB_NUMERIC_FLD, 9, 2 }, + { "",0,0,0 } + }; + + xbXBase x; + xbDbf * MyFile; + xbDate d; + + #ifdef XB_LOGGING_SUPPORT + x.EnableMsgLogging(); + if( po ){ + std::cout << "Logfile is [" << x.GetLogFqFileName().Str() << "]" << std::endl; + } + xbString sMsg; + sMsg.Sprintf( "Program [%s] initializing...", av[0] ); + x.WriteLogMessage( sMsg ); + #endif + + x.SetDataDirectory( PROJECT_DATA_DIR ); + x.EnableMsgLogging(); + InitTime(); + + if( po > 0 ){ + std::cout << "XBase Expression testing program.." << std::endl; + std::cout << "This program tests the XBase expression logic." << std::endl; + std::cout << "Default Data Directory is [" << x.GetDataDirectory().Str() << "]" << std::endl; + } + +// return 0; + + #ifdef XB_DBF4_SUPPORT + MyFile = new xbDbf4( &x ); /* version 4 dbf file */ + #else + MyFile = new xbDbf3( &x ); /* version 3 dbf file */ + #endif + + + +// return 0; + + rc2 = MyFile->CreateTable( "ExpTest.DBF", "ExpTest", MyRecord, XB_OVERLAY, XB_MULTI_USER ); + +// return 0; + + iRc += TestMethod( po, "CreateTable()", rc2, XB_NO_ERROR ); + iRc += TestMethod( po, "PutField()", MyFile->PutField( "CHAR1", "TEST" ), XB_NO_ERROR ); + iRc += TestMethod( po, "PutField()", MyFile->PutField( "CHAR2", "TEST7B" ), XB_NO_ERROR ); + iRc += TestMethod( po, "PutField()", MyFile->PutFloatField( "NUM1", 5 ), XB_NO_ERROR ); + iRc += TestMethod( po, "PutField()", MyFile->PutField( "DATE1", "19890303" ), XB_NO_ERROR ); + iRc += TestMethod( po, "PutField()", MyFile->PutField ( "DATE2", "20120708" ), XB_NO_ERROR ); + iRc += TestMethod( po, "AppendRecord()", MyFile->AppendRecord(), XB_NO_ERROR ); + + +// return 0; + + + iRc += TestTokenMethod( &x, po, "EOX Test1", "", "", "", '?', XB_EXP_UNKNOWN, XB_NO_ERROR, XB_END_OF_EXPRESSION ); + iRc += TestTokenMethod( &x, po, "EOX Test2 ", " ", "", "", '?', XB_EXP_UNKNOWN, XB_NO_ERROR, XB_END_OF_EXPRESSION ); + iRc += TestTokenMethod( &x, po, "Paren Test1 ", "(SOMETOKEN)+5-100", "SOMETOKEN", "+5-100", XB_EXP_NOTROOT, XB_EXP_UNKNOWN, XB_NO_ERROR , XB_NO_ERROR ); + iRc += TestTokenMethod( &x, po, "Paren Test2 ", "{ANOTHERTOKEN} + 55-100", "ANOTHERTOKEN", " + 55-100", XB_EXP_NOTROOT, XB_EXP_UNKNOWN, XB_NO_ERROR, XB_NO_ERROR ); + iRc += TestTokenMethod( &x, po, "Paren Test3 ", "{{NESTED}TOKEN} + 55-100", "{NESTED}TOKEN", " + 55-100", XB_EXP_NOTROOT, XB_EXP_UNKNOWN, XB_NO_ERROR, XB_NO_ERROR ); + + // next line generates log message + iRc += TestTokenMethod( &x, po, "Paren Test4 ", "{{NESTED}TOKEN + 55-100", "{NESTED}TOKEN", " + 55-100", XB_EXP_NOTROOT, XB_EXP_UNKNOWN, XB_PARSE_ERROR, XB_UNBALANCED_PARENS ); + iRc += TestTokenMethod( &x, po, "Paren Test5 ", " (SOMETOKEN )+5-100", "SOMETOKEN ", "+5-100", XB_EXP_NOTROOT, XB_EXP_UNKNOWN, XB_NO_ERROR, XB_NO_ERROR ); + iRc += TestTokenMethod( &x, po, "Quote Test1 ", "\"SOMETOKEN\"+5-100", "SOMETOKEN", "+5-100", XB_EXP_CONSTANT, XB_EXP_CHAR, XB_NO_ERROR, XB_NO_ERROR ); + iRc += TestTokenMethod( &x, po, "Quote Test2 ", "'SOMETOKEN2'", "SOMETOKEN2", "", XB_EXP_CONSTANT, XB_EXP_CHAR, XB_NO_ERROR, XB_NO_ERROR ); + iRc += TestTokenMethod( &x, po, "Quote Test3 ", " 'SOMETOKEN3 '", "SOMETOKEN3 ", "", XB_EXP_CONSTANT, XB_EXP_CHAR, XB_NO_ERROR, XB_NO_ERROR ); + + + + // next line generates log message + + iRc += TestTokenMethod( &x, po, "Quote Test3 ", " 'SOMETOKEN4 ", "SOMETOKEN3 ", "", XB_EXP_CONSTANT, XB_EXP_CHAR, XB_PARSE_ERROR, XB_UNBALANCED_QUOTES ); + + + iRc += TestTokenMethod( &x, po, "Logical Constant1 ", ".T.", "T", "", XB_EXP_CONSTANT, XB_EXP_LOGICAL, XB_NO_ERROR, XB_NO_ERROR ); + iRc += TestTokenMethod( &x, po, "Logical Constant2 ", ".F.", "F", "", XB_EXP_CONSTANT, XB_EXP_LOGICAL, XB_NO_ERROR, XB_NO_ERROR ); + iRc += TestTokenMethod( &x, po, "Logical Constant3 ", ".TRUE.", "T", "", XB_EXP_CONSTANT, XB_EXP_LOGICAL, XB_NO_ERROR, XB_NO_ERROR ); + iRc += TestTokenMethod( &x, po, "Logical Constant4 ", ".FALSE.", "F", "", XB_EXP_CONSTANT, XB_EXP_LOGICAL, XB_NO_ERROR, XB_NO_ERROR ); + iRc += TestTokenMethod( &x, po, "Logical Constant5 ", ".T. and x", "T", " and x", XB_EXP_CONSTANT, XB_EXP_LOGICAL, XB_NO_ERROR, XB_NO_ERROR ); + iRc += TestTokenMethod( &x, po, "Numeric Constant1 ", "123", "123", "", XB_EXP_CONSTANT, XB_EXP_NUMERIC, XB_NO_ERROR, XB_NO_ERROR ); + + + + iRc += TestTokenMethod( &x, po, "Numeric Constant2 ", "-123", "-123", "", XB_EXP_CONSTANT, XB_EXP_NUMERIC, XB_NO_ERROR, XB_NO_ERROR ); + iRc += TestTokenMethod( &x, po, "Numeric Constant3 ", " - 123", "-123", "", XB_EXP_CONSTANT, XB_EXP_NUMERIC, XB_NO_ERROR, XB_NO_ERROR ); + iRc += TestTokenMethod( &x, po, "Numeric Constant4 ", " - .456", "-.456", "", XB_EXP_CONSTANT, XB_EXP_NUMERIC, XB_NO_ERROR, XB_NO_ERROR ); + iRc += TestTokenMethod( &x, po, "Numeric Constant5 ", " -.002", "-.002", "", XB_EXP_CONSTANT, XB_EXP_NUMERIC, XB_NO_ERROR, XB_NO_ERROR ); + iRc += TestTokenMethod( &x, po, "Numeric Constant6 ", " - .002 + 1", "-.002", " + 1", XB_EXP_CONSTANT, XB_EXP_NUMERIC, XB_NO_ERROR, XB_NO_ERROR ); + iRc += TestTokenMethod( &x, po, "Operator 1", "-", "-", "", XB_EXP_OPERATOR, XB_EXP_UNKNOWN, XB_NO_ERROR, XB_NO_ERROR ); + iRc += TestTokenMethod( &x, po, "Operator 2", "+", "+", "", XB_EXP_OPERATOR, XB_EXP_UNKNOWN, XB_NO_ERROR, XB_NO_ERROR ); + iRc += TestTokenMethod( &x, po, "Operator 3", "*", "*", "", XB_EXP_OPERATOR, XB_EXP_NUMERIC, XB_NO_ERROR, XB_NO_ERROR ); + iRc += TestTokenMethod( &x, po, "Operator 4", "/", "/", "", XB_EXP_OPERATOR, XB_EXP_NUMERIC, XB_NO_ERROR, XB_NO_ERROR ); + iRc += TestTokenMethod( &x, po, "Operator 5", "^", "^", "", XB_EXP_OPERATOR, XB_EXP_NUMERIC, XB_NO_ERROR, XB_NO_ERROR ); + iRc += TestTokenMethod( &x, po, "Operator 6", "%", "%", "", XB_EXP_OPERATOR, XB_EXP_NUMERIC, XB_NO_ERROR, XB_NO_ERROR ); + iRc += TestTokenMethod( &x, po, "Operator 7", "=", "=", "", XB_EXP_OPERATOR, XB_EXP_LOGICAL, XB_NO_ERROR, XB_NO_ERROR ); + iRc += TestTokenMethod( &x, po, "Operator 8", "<", "<", "", XB_EXP_OPERATOR, XB_EXP_LOGICAL, XB_NO_ERROR, XB_NO_ERROR ); + iRc += TestTokenMethod( &x, po, "Operator 9", ">", ">", "", XB_EXP_OPERATOR, XB_EXP_LOGICAL, XB_NO_ERROR, XB_NO_ERROR ); + iRc += TestTokenMethod( &x, po, "Operator 10", "$", "$", "", XB_EXP_OPERATOR, XB_EXP_LOGICAL, XB_NO_ERROR, XB_NO_ERROR ); + iRc += TestTokenMethod( &x, po, "Operator 11", "**", "**", "", XB_EXP_OPERATOR, XB_EXP_NUMERIC, XB_NO_ERROR, XB_NO_ERROR ); + iRc += TestTokenMethod( &x, po, "Operator 12", "++", "++", "", XB_EXP_POST_OPERATOR, XB_EXP_NUMERIC, XB_NO_ERROR, XB_NO_ERROR ); + iRc += TestTokenMethod( &x, po, "Operator 13", "--", "--", "", XB_EXP_POST_OPERATOR, XB_EXP_NUMERIC, XB_NO_ERROR, XB_NO_ERROR ); + iRc += TestTokenMethod( &x, po, "Operator 14", "-- ", "--", " ", XB_EXP_POST_OPERATOR, XB_EXP_NUMERIC, XB_NO_ERROR, XB_NO_ERROR ); + iRc += TestTokenMethod( &x, po, "Operator 15", "--X", "--", "X", XB_EXP_PRE_OPERATOR, XB_EXP_NUMERIC, XB_NO_ERROR, XB_NO_ERROR ); + iRc += TestTokenMethod( &x, po, "Operator 16", "++X", "++", "X", XB_EXP_PRE_OPERATOR, XB_EXP_NUMERIC, XB_NO_ERROR, XB_NO_ERROR ); + iRc += TestTokenMethod( &x, po, "Operator 17", "+=", "+=", "", XB_EXP_OPERATOR, XB_EXP_NUMERIC, XB_NO_ERROR, XB_NO_ERROR ); + iRc += TestTokenMethod( &x, po, "Operator 18", "-=", "-=", "", XB_EXP_OPERATOR, XB_EXP_NUMERIC, XB_NO_ERROR, XB_NO_ERROR ); + iRc += TestTokenMethod( &x, po, "Operator 19", "*=", "*=", "", XB_EXP_OPERATOR, XB_EXP_NUMERIC, XB_NO_ERROR, XB_NO_ERROR ); + iRc += TestTokenMethod( &x, po, "Operator 20", "/=", "/=", "", XB_EXP_OPERATOR, XB_EXP_NUMERIC, XB_NO_ERROR, XB_NO_ERROR ); + iRc += TestTokenMethod( &x, po, "Operator 21", "<>", "<>", "", XB_EXP_OPERATOR, XB_EXP_LOGICAL, XB_NO_ERROR, XB_NO_ERROR ); + iRc += TestTokenMethod( &x, po, "Operator 22", "<=", "<=", "", XB_EXP_OPERATOR, XB_EXP_LOGICAL, XB_NO_ERROR, XB_NO_ERROR ); + iRc += TestTokenMethod( &x, po, "Operator 23", ">=", ">=", "", XB_EXP_OPERATOR, XB_EXP_LOGICAL, XB_NO_ERROR, XB_NO_ERROR ); + iRc += TestTokenMethod( &x, po, "Operator 24", ">= grapes", ">=", " grapes", XB_EXP_OPERATOR, XB_EXP_LOGICAL, XB_NO_ERROR, XB_NO_ERROR ); + iRc += TestTokenMethod( &x, po, "Operator 25", "< apples ", "<", " apples ", XB_EXP_OPERATOR, XB_EXP_LOGICAL, XB_NO_ERROR, XB_NO_ERROR ); + iRc += TestTokenMethod( &x, po, "Operator 26", ".NOT.", ".NOT.", "", XB_EXP_OPERATOR, XB_EXP_LOGICAL, XB_NO_ERROR, XB_NO_ERROR ); + iRc += TestTokenMethod( &x, po, "Operator 27", ".AND.", ".AND.", "", XB_EXP_OPERATOR, XB_EXP_LOGICAL, XB_NO_ERROR, XB_NO_ERROR ); + iRc += TestTokenMethod( &x, po, "Operator 28", ".OR.", ".OR.", "", XB_EXP_OPERATOR, XB_EXP_LOGICAL, XB_NO_ERROR, XB_NO_ERROR ); + + iRc += TestTokenMethod( &x, po, "Function 1", "STOD ( \"08252017\" )", "STOD ( \"08252017\" )", "", XB_EXP_FUNCTION, XB_EXP_DATE, XB_NO_ERROR, XB_NO_ERROR ); + iRc += TestTokenMethod( &x, po, "Function 2", "STR( 8 )", "STR( 8 )", "", XB_EXP_FUNCTION, XB_EXP_CHAR, XB_NO_ERROR, XB_NO_ERROR ); + iRc += TestTokenMethod( &x, po, "Function 3", "STR( (8-3)+3 )", "STR( (8-3)+3 )", "", XB_EXP_FUNCTION, XB_EXP_CHAR, XB_NO_ERROR, XB_NO_ERROR ); + iRc += TestTokenMethod( &x, po, "Function 4", "STR( (8-3)+3 ) + \"A\"", "STR( (8-3)+3 )", " + \"A\"", XB_EXP_FUNCTION, XB_EXP_CHAR, XB_NO_ERROR, XB_NO_ERROR ); + iRc += TestTokenMethod( &x, po, "Function 5", "ISALPHA( \"A\" )", "ISALPHA( \"A\" )", "", XB_EXP_FUNCTION, XB_EXP_LOGICAL, XB_NO_ERROR, XB_NO_ERROR ); + iRc += TestTokenMethod( &x, po, "Function 6", "EXP( 6 )", "EXP( 6 )", "", XB_EXP_FUNCTION, XB_EXP_NUMERIC, XB_NO_ERROR, XB_NO_ERROR ); + iRc += TestTokenMethod( &x, po, "Field 1", "NUM1", "NUM1", "", XB_EXP_FIELD, XB_EXP_UNKNOWN, XB_NO_ERROR, XB_NO_ERROR ); + iRc += TestTokenMethod( &x, po, "Field 2", "NUM1 + X", "NUM1", " + X", XB_EXP_FIELD, XB_EXP_UNKNOWN, XB_NO_ERROR, XB_NO_ERROR ); + iRc += TestTokenMethod( &x, po, "Field 3", "ExpTest->NUM1", "ExpTest->NUM1", "", XB_EXP_FIELD, XB_EXP_UNKNOWN, XB_NO_ERROR, XB_NO_ERROR ); + iRc += TestTokenMethod( &x, po, "Field 4", "ExpTest ->NUM1", "ExpTest ->NUM1", "", XB_EXP_FIELD, XB_EXP_UNKNOWN, XB_NO_ERROR, XB_NO_ERROR ); + iRc += TestTokenMethod( &x, po, "Field 5", "ExpTest -> NUM1", "ExpTest -> NUM1", "", XB_EXP_FIELD, XB_EXP_UNKNOWN, XB_NO_ERROR, XB_NO_ERROR ); + iRc += TestTokenMethod( &x, po, "Field 6", "ExpTest -> NUM1+1", "ExpTest -> NUM1", "+1", XB_EXP_FIELD, XB_EXP_UNKNOWN, XB_NO_ERROR, XB_NO_ERROR ); + + iRc += TestWeight( &x, po, "Weight Test", "", 0 ); + iRc += TestWeight( &x, po, "Weight Test", "toobig", 0 ); + iRc += TestWeight( &x, po, ".OR. Weight Test", ".OR.", 1 ); + iRc += TestWeight( &x, po, ".AND. Weight Test", ".AND.", 2 ); + iRc += TestWeight( &x, po, ".NOT. Weight Test", ".NOT.", 3 ); + iRc += TestWeight( &x, po, "> Weight Test", ">", 4 ); + iRc += TestWeight( &x, po, ">= Weight Test", ">=", 4 ); + iRc += TestWeight( &x, po, "< Weight Test", "<", 4 ); + iRc += TestWeight( &x, po, "<= Weight Test", "<=", 4 ); + iRc += TestWeight( &x, po, "<> Weight Test", "<>", 4 ); + iRc += TestWeight( &x, po, "# Weight Test", "#", 4 ); + iRc += TestWeight( &x, po, "$ Weight Test", "$", 4 ); + iRc += TestWeight( &x, po, "= Weight Test", "=", 4 ); + iRc += TestWeight( &x, po, "-- Prefix decrement Weight Test", "--0", 9 ); + iRc += TestWeight( &x, po, "++ Prefix increment Weight Test", "++0", 9 ); + iRc += TestWeight( &x, po, "** Weight Test", "**", 8 ); + iRc += TestWeight( &x, po, "^ Weight Test", "^", 8 ); + iRc += TestWeight( &x, po, "* Weight Test", "*", 7 ); + iRc += TestWeight( &x, po, "* Weight Test", "/", 7 ); + iRc += TestWeight( &x, po, "* Weight Test", "%", 7 ); + iRc += TestWeight( &x, po, "* Weight Test", "*=", 7 ); + iRc += TestWeight( &x, po, "* Weight Test", "/=", 7 ); + iRc += TestWeight( &x, po, "+ Weight Test", "+", 6 ); + iRc += TestWeight( &x, po, "- Weight Test", "-", 6 ); + iRc += TestWeight( &x, po, "+= Weight Test", "+=", 6 ); + iRc += TestWeight( &x, po, "-= Weight Test", "-=", 6 ); + iRc += TestWeight( &x, po, "-- Postfix decrement Weight Test", "--1", 5 ); + iRc += TestWeight( &x, po, "++ Postfix increment Weight Test", "++1", 5 ); + + iRc += TestMethod( &x, MyFile, po, "CharTest1", "CHAR1", "TEST ", 5 ); + iRc += TestMethod( &x, MyFile, po, "CharTest2", "ExpTest->CHAR1", "TEST ", 5 ); + iRc += TestMethod( &x, MyFile, po, "CharTest3", "ExpTest->CHAR1+CHAR1", "TEST TEST ", 10 ); + iRc += TestMethod( &x, MyFile, po, "CharTest4", "\"PRE_ \"+\" _POST\"", "PRE_ _POST", 11 ); + iRc += TestMethod( &x, MyFile, po, "CharTest5", "\"PRE_ \"-\" _POST\"", "PRE_ _POST ", 11 ); + iRc += TestMethod( &x, MyFile, po, "CharTest6", "\"PRE_\"+ExpTest->CHAR1+\"_POST\"", "PRE_TEST _POST", 14 ); + iRc += TestMethod( &x, MyFile, po, "CharTest7", "STR(123)", " 123", 10 ); + iRc += TestMethod( &x, MyFile, po, "CharTest8", "STR(123)+LTRIM(STR(456))", " 123456", 20 ); + iRc += TestMethod( &x, MyFile, po, "CharTest9", "STR(8)+STR(7)", " 8 7", 20 ); + iRc += TestMethod( &x, MyFile, po, "FuncTest1", "ALLTRIM( \" ABCD \" )", "ABCD", 11 ); + iRc += TestMethod( &x, MyFile, po, "FuncTest2", "CDOW( STOD( \"20171014\" ))", "Saturday", 9 ); + iRc += TestMethod( &x, MyFile, po, "FuncTest3", "CHR( 66 )", "B", 1 ); + iRc += TestMethod( &x, MyFile, po, "FuncTest4", "CMONTH( STOD( \"20171114\" ) )", "November", 9 ); + + // next line causes MAC compiler to cough up a furball + //iRc += TestMethod( &x, MyFile, po, "FuncTest5", "DESCEND( \"ABCDE\" )", "¾½¼»º", 5 ); + + char sDescendResult[6]; // = "¾½¼»º"; + sDescendResult[0] = (char) 0xBE; + sDescendResult[1] = (char) 0xBD; + sDescendResult[2] = (char) 0xBC; + sDescendResult[3] = (char) 0xBB; + sDescendResult[4] = (char) 0xBA; + sDescendResult[5] = (char) 0x00; + iRc += TestMethod( &x, MyFile, po, "FuncTest5", "DESCEND( \"ABCDE\" )", sDescendResult, 5 ); + + iRc += TestMethod( &x, MyFile, po, "FuncTest6", "DTOC( STOD( \"20171114\" ))", "11/14/17", 8 ); + iRc += TestMethod( &x, MyFile, po, "FuncTest7", "DTOS( STOD( \"20171114\" ))", "20171114", 8 ); + iRc += TestMethod( &x, MyFile, po, "FuncTest8", "LEFT( \"ABCDEFGH\", 5 )", "ABCDE", 5 ); + iRc += TestMethod( &x, MyFile, po, "FuncTest9", "LOWER( \"ABCDEFGH\" )", "abcdefgh", 8 ); + iRc += TestMethod( &x, MyFile, po, "FuncTest10", "LTRIM( \" ABC\" )", "ABC", 6 ); + iRc += TestMethod( &x, MyFile, po, "FuncTest11", "REPLICATE( \"ABC\", 3 )", "ABCABCABC", 9 ); + iRc += TestMethod( &x, MyFile, po, "FuncTest12", "RIGHT( \"ABCDEFGH\", 5 )", "DEFGH", 5 ); + iRc += TestMethod( &x, MyFile, po, "FuncTest13", "RTRIM( \"ABCD \" )", "ABCD", 7 ); + iRc += TestMethod( &x, MyFile, po, "FuncTest14", "SPACE( 5 )", " ", 5 ); + iRc += TestMethod( &x, MyFile, po, "FuncTest15", "STR( 8 )", " 8", 10 ); + iRc += TestMethod( &x, MyFile, po, "FuncTest16", "STR( 3, 4 )", " 3", 4 ); + iRc += TestMethod( &x, MyFile, po, "FuncTest17", "STR( 12.7, 5, 2 )", "12.70", 5 ); + iRc += TestMethod( &x, MyFile, po, "FuncTest18", "STR( 43.17, 9, 4, \"0\" )", "0043.1700", 9 ); + iRc += TestMethod( &x, MyFile, po, "FuncTest19", "STRZERO( 56.21, 9, 4 )", "0056.2100", 9 ); + iRc += TestMethod( &x, MyFile, po, "FuncTest20", "SUBSTR( \"SOMESTRING\", 3, 5 )", "MESTR", 5 ); + iRc += TestMethod( &x, MyFile, po, "FuncTest21", "TRIM( \" abc123 \" )", " abc123", 12 ); + iRc += TestMethod( &x, MyFile, po, "FuncTest22", "UPPER( \"abc123RRR\" )", "ABC123RRR", 9 ); + + // numeric logic tests + iRc += TestMethod( &x, MyFile, po, "NumericTest1", "1 + 1", (xbDouble) 2 ); + iRc += TestMethod( &x, MyFile, po, "NumericTest2", "6 - 1", (xbDouble) 5 ); + iRc += TestMethod( &x, MyFile, po, "NumericTest3", "6 * 4", (xbDouble) 24 ); + iRc += TestMethod( &x, MyFile, po, "NumericTest4", "8 / 2", (xbDouble) 4 ); + iRc += TestMethod( &x, MyFile, po, "NumericTest5", "3 ^ 3", (xbDouble) 27 ); + iRc += TestMethod( &x, MyFile, po, "NumericTest6", "4 ** 4", (xbDouble) 256 ); + iRc += TestMethod( &x, MyFile, po, "NumericTest7", "NUM1", (xbDouble) 5 ); + iRc += TestMethod( &x, MyFile, po, "NumericTest8", "NUM1 + 2.3", (xbDouble) 7.3 ); + iRc += TestMethod( &x, MyFile, po, "NumericTest9", "++NUM1", (xbDouble) 6 ); + iRc += TestMethod( &x, MyFile, po, "NumericTest10", "NUM1++", (xbDouble) 5 ); + iRc += TestMethod( &x, MyFile, po, "NumericTest11", "--NUM1", (xbDouble) 4 ); + iRc += TestMethod( &x, MyFile, po, "NumericTest12", "NUM1--", (xbDouble) 5 ); + iRc += TestMethod( &x, MyFile, po, "NumericTest13", "3++", (xbDouble) 3 ); + iRc += TestMethod( &x, MyFile, po, "NumericTest14", "++3", (xbDouble) 4 ); + iRc += TestMethod( &x, MyFile, po, "NumericTest15", "4--", (xbDouble) 4 ); + iRc += TestMethod( &x, MyFile, po, "NumericTest16", "--4", (xbDouble) 3 ); + iRc += TestMethod( &x, MyFile, po, "NumericTest17", "3 += 12", (xbDouble) 15 ); + iRc += TestMethod( &x, MyFile, po, "NumericTest18", "3 -= 12", (xbDouble) -9 ); + iRc += TestMethod( &x, MyFile, po, "NumericTest19", "3 *= 12", (xbDouble) 36 ); + iRc += TestMethod( &x, MyFile, po, "NumericTest20", "12 /= 3", (xbDouble) 4 ); + iRc += TestMethod( &x, MyFile, po, "NumericTest21", "(12+3)*(15-13)", (xbDouble) 30 ); + + // numeric functions + iRc += TestMethod( &x, MyFile, po, "FuncTest23", "ABS( -22 )", (xbDouble) 22 ); + iRc += TestMethod( &x, MyFile, po, "FuncTest24", "ABS( 23 )", (xbDouble) 23 ); + iRc += TestMethod( &x, MyFile, po, "FuncTest25", "ASC( \"A\" )", (xbDouble) 65 ); + iRc += TestMethod( &x, MyFile, po, "FuncTest26", "AT( \",\", \"Booth, Joseph\" )", (xbDouble) 6 ); + iRc += TestMethod( &x, MyFile, po, "FuncTest27", "DAY( STOD( \"20171017\" ))", (xbDouble) 17 ); + iRc += TestMethod( &x, MyFile, po, "FuncTest28", "DESCEND( 1991 )", (xbDouble) -1991 ); + iRc += TestMethod( &x, MyFile, po, "FuncTest29", "DOW( STOD( \"20171017\" ) )", (xbDouble) 3 ); + iRc += TestMethod( &x, MyFile, po, "FuncTest30", "DOW( STOD( \"20171021\" ) )", (xbDouble) 0 ); + iRc += TestMethod( &x, MyFile, po, "FuncTest31", "EXP( 0 )", (xbDouble) 1 ); + iRc += TestMethod( &x, MyFile, po, "FuncTest32", "INT( 123.45 )", (xbDouble) 123 ); + iRc += TestMethod( &x, MyFile, po, "FuncTest33", "LEN( \"ABC123\" )", (xbDouble) 6 ); + iRc += TestMethod( &x, MyFile, po, "FuncTest34", "LOG( 10 )", (xbDouble) 2.3 ); + iRc += TestMethod( &x, MyFile, po, "FuncTest35", "MAX( 10, 20 )", (xbDouble) 20 ); + iRc += TestMethod( &x, MyFile, po, "FuncTest36", "MIN( 10, 20 )", (xbDouble) 10 ); + iRc += TestMethod( &x, MyFile, po, "FuncTest37", "MONTH( STOD( \"20171017\" ))", (xbDouble) 10 ); + iRc += TestMethod( &x, MyFile, po, "FuncTest38", "RECCOUNT()", (xbDouble) 1 ); + iRc += TestMethod( &x, MyFile, po, "FuncTest39", "RECNO()", (xbDouble) 1 ); + iRc += TestMethod( &x, MyFile, po, "FuncTest40", "SQRT( 9 )", (xbDouble) 3 ); + iRc += TestMethod( &x, MyFile, po, "FuncTest41", "VAL( \"89\" )", (xbDouble) 89 ); + iRc += TestMethod( &x, MyFile, po, "FuncTest42", "VAL( \"22.13 and some text\" )", (xbDouble) 22.13 ); + iRc += TestMethod( &x, MyFile, po, "FuncTest43", "YEAR( STOD( \"20171017\" ))", (xbDouble) 2017 ); + + // date logic tests + xbDate dtTest1( "19890303" ); + xbDate dtTest2( "20120708" ); + iRc += TestMethod( &x, MyFile, po, "DateTest1", "ExpTest->DATE1", dtTest1 ); + iRc += TestMethod( &x, MyFile, po, "DateTest2", "DATE1", dtTest1 ); + iRc += TestMethod( &x, MyFile, po, "DateTest3", "ExpTest->DATE2", dtTest2 ); + iRc += TestMethod( &x, MyFile, po, "DateTest4", "DATE2", dtTest2 ); + iRc += TestMethod( &x, MyFile, po, "DateTest5", "DATE2 - DATE1", (xbDouble) 8528 ); + + dtTest1.Set( "20120705" ); + iRc += TestMethod( &x, MyFile, po, "DateTest6", "DATE2 - 3", dtTest1 ); + iRc += TestMethod( &x, MyFile, po, "DateTest7", "DATE2 -= 3", dtTest1 ); + + dtTest1.Set( "20120718" ); + iRc += TestMethod( &x, MyFile, po, "DateTest8", "DATE2 + 10", dtTest1 ); + iRc += TestMethod( &x, MyFile, po, "DateTest9", "DATE2 += 10", dtTest1 ); + + dtTest1.Set( "20120709" ); + iRc += TestMethod( &x, MyFile, po, "DateTest10", "++DATE2", dtTest1 ); + dtTest1.Set( "20120707" ); + iRc += TestMethod( &x, MyFile, po, "DateTest11", "--DATE2", dtTest1 ); + + dtTest1.Set( "20120708" ); + iRc += TestMethod( &x, MyFile, po, "DateTest12", "DATE2++", dtTest1 ); + iRc += TestMethod( &x, MyFile, po, "DateTest13", "DATE2--", dtTest1 ); + + iRc += TestMethod( &x, MyFile, po, "DateTest14", "{07/08/12}", dtTest1 ); + iRc += TestMethod( &x, MyFile, po, "DateTest15", "{07/08/2012}", dtTest1 ); + + iRc += TestMethod( &x, MyFile, po, "DateTest16", "{07/11/12} -3", dtTest1 ); + iRc += TestMethod( &x, MyFile, po, "DateTest17", "{07/06/2012} + 2", dtTest1 ); + + iRc += TestMethod( &x, MyFile, po, "FuncTest44", "CTOD( \"07\\08\\12\" )", dtTest1 ); + + xbDate dtToday; + iRc += TestMethod( &x, MyFile, po, "FuncTest45", "DATE()", dtToday ); + + dtTest2 = "28870625"; + iRc += TestMethod( &x, MyFile, po, "FuncTest46", "DESCEND( DATE2 )", dtTest2 ); + iRc += TestMethod( &x, MyFile, po, "FuncTest47", "STOD( \"20120708\" )", dtTest1 ); + + // boolean logic tests + iRc += TestMethod( &x, MyFile, po, "LogicTest1", "3=5", (xbBool) xbFalse ); + iRc += TestMethod( &x, MyFile, po, "LogicTest2", "5=5", (xbBool) xbTrue ); + iRc += TestMethod( &x, MyFile, po, "LogicTest3", "\"abc\"=\"def\"", (xbBool) xbFalse ); + iRc += TestMethod( &x, MyFile, po, "LogicTest4", "\"abc\"=\"abc\"", (xbBool) xbTrue ); + iRc += TestMethod( &x, MyFile, po, "LogicTest5", "CTOD( \"07\\08\\12\" ) = CTOD ( \"07\\08\\12\" )", (xbBool) xbTrue ); + iRc += TestMethod( &x, MyFile, po, "LogicTest6", "CTOD( \"07\\09\\12\" ) = CTOD ( \"07\\08\\12\" )", (xbBool) xbFalse ); + iRc += TestMethod( &x, MyFile, po, "LogicTest7", "3<>5", (xbBool) xbTrue ); + iRc += TestMethod( &x, MyFile, po, "LogicTest8", "5<>5", (xbBool) xbFalse ); + iRc += TestMethod( &x, MyFile, po, "LogicTest9", "3!=5", (xbBool) xbTrue ); + iRc += TestMethod( &x, MyFile, po, "LogicTest10", "5!=5", (xbBool) xbFalse ); + iRc += TestMethod( &x, MyFile, po, "LogicTest11", "3 # 5", (xbBool) xbTrue ); + iRc += TestMethod( &x, MyFile, po, "LogicTest12", "5#5", (xbBool) xbFalse ); + iRc += TestMethod( &x, MyFile, po, "LogicTest13", "\"3\"<>\"5\"", (xbBool) xbTrue ); + iRc += TestMethod( &x, MyFile, po, "LogicTest14", "\"5\"<>\"5\"", (xbBool) xbFalse ); + iRc += TestMethod( &x, MyFile, po, "LogicTest15", "\"3\"!=\"5\"", (xbBool) xbTrue ); + iRc += TestMethod( &x, MyFile, po, "LogicTest16", "\"5\"!=\"5\"", (xbBool) xbFalse ); + iRc += TestMethod( &x, MyFile, po, "LogicTest17", "\"3\" # \"5\"", (xbBool) xbTrue ); + iRc += TestMethod( &x, MyFile, po, "LogicTest18", "\"5\"#\"5\"", (xbBool) xbFalse ); + iRc += TestMethod( &x, MyFile, po, "LogicTest19", "CTOD( \"07\\08\\12\" ) <> CTOD ( \"07\\08\\12\" )", (xbBool) xbFalse ); + iRc += TestMethod( &x, MyFile, po, "LogicTest20", "CTOD( \"07\\09\\12\" ) <> CTOD ( \"07\\08\\12\" )", (xbBool) xbTrue ); + iRc += TestMethod( &x, MyFile, po, "LogicTest21", "CTOD( \"07\\08\\12\" ) != CTOD ( \"07\\08\\12\" )", (xbBool) xbFalse ); + iRc += TestMethod( &x, MyFile, po, "LogicTest22", "CTOD( \"07\\09\\12\" ) != CTOD ( \"07\\08\\12\" )", (xbBool) xbTrue ); + iRc += TestMethod( &x, MyFile, po, "LogicTest23", "CTOD( \"07\\08\\12\" ) # CTOD ( \"07\\08\\12\" )", (xbBool) xbFalse ); + iRc += TestMethod( &x, MyFile, po, "LogicTest24", "CTOD( \"07\\09\\12\" ) # CTOD ( \"07\\08\\12\" )", (xbBool) xbTrue ); + iRc += TestMethod( &x, MyFile, po, "LogicTest25", "3<5", (xbBool) xbTrue ); + iRc += TestMethod( &x, MyFile, po, "LogicTest26", "5<5", (xbBool) xbFalse ); + iRc += TestMethod( &x, MyFile, po, "LogicTest27", "5<4", (xbBool) xbFalse ); + iRc += TestMethod( &x, MyFile, po, "LogicTest28", "\"a\"<\"b\"", (xbBool) xbTrue ); + iRc += TestMethod( &x, MyFile, po, "LogicTest29", "\"a\"<\"a\"", (xbBool) xbFalse ); + iRc += TestMethod( &x, MyFile, po, "LogicTest30", "\"c\"<\"b\"", (xbBool) xbFalse ); + iRc += TestMethod( &x, MyFile, po, "LogicTest31", "CTOD( \"07\\07\\12\" ) < CTOD ( \"07\\08\\12\" )", (xbBool) xbTrue ); + iRc += TestMethod( &x, MyFile, po, "LogicTest32", "CTOD( \"07\\08\\12\" ) < CTOD ( \"07\\08\\12\" )", (xbBool) xbFalse ); + iRc += TestMethod( &x, MyFile, po, "LogicTest33", "CTOD( \"07\\09\\12\" ) < CTOD ( \"07\\08\\12\" )", (xbBool) xbFalse ); + iRc += TestMethod( &x, MyFile, po, "LogicTest34", "3>5", (xbBool) xbFalse ); + iRc += TestMethod( &x, MyFile, po, "LogicTest35", "5>5", (xbBool) xbFalse ); + iRc += TestMethod( &x, MyFile, po, "LogicTest36", "5>4", (xbBool) xbTrue ); + iRc += TestMethod( &x, MyFile, po, "LogicTest37", "\"a\">\"b\"", (xbBool) xbFalse ); + iRc += TestMethod( &x, MyFile, po, "LogicTest38", "\"a\">\"a\"", (xbBool) xbFalse ); + iRc += TestMethod( &x, MyFile, po, "LogicTest39", "\"c\">\"b\"", (xbBool) xbTrue ); + iRc += TestMethod( &x, MyFile, po, "LogicTest40", "CTOD( \"07\\07\\12\" ) > CTOD ( \"07\\08\\12\" )", (xbBool) xbFalse ); + iRc += TestMethod( &x, MyFile, po, "LogicTest41", "CTOD( \"07\\08\\12\" ) > CTOD ( \"07\\08\\12\" )", (xbBool) xbFalse ); + iRc += TestMethod( &x, MyFile, po, "LogicTest42", "CTOD( \"07\\09\\12\" ) > CTOD ( \"07\\08\\12\" )", (xbBool) xbTrue ); + iRc += TestMethod( &x, MyFile, po, "LogicTest43", "3<=5", (xbBool) xbTrue ); + iRc += TestMethod( &x, MyFile, po, "LogicTest44", "5<=5", (xbBool) xbTrue ); + iRc += TestMethod( &x, MyFile, po, "LogicTest45", "5<=4", (xbBool) xbFalse ); + iRc += TestMethod( &x, MyFile, po, "LogicTest46", "\"a\"<=\"b\"", (xbBool) xbTrue ); + iRc += TestMethod( &x, MyFile, po, "LogicTest47", "\"a\"<=\"a\"", (xbBool) xbTrue ); + iRc += TestMethod( &x, MyFile, po, "LogicTest48", "\"c\"<=\"b\"", (xbBool) xbFalse ); + iRc += TestMethod( &x, MyFile, po, "LogicTest49", "CTOD( \"07\\07\\12\" ) <= CTOD ( \"07\\08\\12\" )", (xbBool) xbTrue ); + iRc += TestMethod( &x, MyFile, po, "LogicTest50", "CTOD( \"07\\08\\12\" ) <= CTOD ( \"07\\08\\12\" )", (xbBool) xbTrue ); + iRc += TestMethod( &x, MyFile, po, "LogicTest51", "CTOD( \"07\\09\\12\" ) <= CTOD ( \"07\\08\\12\" )", (xbBool) xbFalse ); + iRc += TestMethod( &x, MyFile, po, "LogicTest52", "3>=5", (xbBool) xbFalse ); + iRc += TestMethod( &x, MyFile, po, "LogicTest53", "5>=5", (xbBool) xbTrue ); + iRc += TestMethod( &x, MyFile, po, "LogicTest54", "5>=4", (xbBool) xbTrue ); + iRc += TestMethod( &x, MyFile, po, "LogicTest55", "\"a\">=\"b\"", (xbBool) xbFalse ); + iRc += TestMethod( &x, MyFile, po, "LogicTest56", "\"a\">=\"a\"", (xbBool) xbTrue ); + iRc += TestMethod( &x, MyFile, po, "LogicTest57", "\"c\">=\"b\"", (xbBool) xbTrue ); + iRc += TestMethod( &x, MyFile, po, "LogicTest58", "CTOD( \"07\\07\\12\" ) >= CTOD ( \"07\\08\\12\" )", (xbBool) xbFalse ); + iRc += TestMethod( &x, MyFile, po, "LogicTest59", "CTOD( \"07\\08\\12\" ) >= CTOD ( \"07\\08\\12\" )", (xbBool) xbTrue ); + iRc += TestMethod( &x, MyFile, po, "LogicTest60", "CTOD( \"07\\09\\12\" ) >= CTOD ( \"07\\08\\12\" )", (xbBool) xbTrue ); + iRc += TestMethod( &x, MyFile, po, "LogicTest61", "\"abc123\" $ \"abc\"", (xbBool) xbFalse ); + iRc += TestMethod( &x, MyFile, po, "LogicTest62", "\"abc\" $ \"abc123\"", (xbBool) xbTrue ); + iRc += TestMethod( &x, MyFile, po, "LogicTest63", "\"abc\" $ \"zzabc123\"", (xbBool) xbTrue ); + iRc += TestMethod( &x, MyFile, po, "LogicTest64", ".T.", (xbBool) xbTrue ); + iRc += TestMethod( &x, MyFile, po, "LogicTest65", ".F.", (xbBool) xbFalse ); + iRc += TestMethod( &x, MyFile, po, "LogicTest66", ".TRUE.", (xbBool) xbTrue ); + iRc += TestMethod( &x, MyFile, po, "LogicTest67", ".FALSE.", (xbBool) xbFalse ); + iRc += TestMethod( &x, MyFile, po, "LogicTest68", ".NOT. .F.", (xbBool) xbTrue ); + iRc += TestMethod( &x, MyFile, po, "LogicTest69", " NOT .F.", (xbBool) xbTrue ); + iRc += TestMethod( &x, MyFile, po, "LogicTest70", ".NOT. .T.", (xbBool) xbFalse ); + iRc += TestMethod( &x, MyFile, po, "LogicTest71", " NOT .T.", (xbBool) xbFalse ); + iRc += TestMethod( &x, MyFile, po, "LogicTest72", ".T. .AND. .T.", (xbBool) xbTrue ); + iRc += TestMethod( &x, MyFile, po, "LogicTest73", ".T. AND .T.", (xbBool) xbTrue ); + iRc += TestMethod( &x, MyFile, po, "LogicTest74", ".T. .AND. .NOT. .T.", (xbBool) xbFalse ); + iRc += TestMethod( &x, MyFile, po, "LogicTest75", ".T. AND NOT .T.", (xbBool) xbFalse ); + iRc += TestMethod( &x, MyFile, po, "LogicTest76", ".NOT. .T. .AND. .T.", (xbBool) xbFalse ); + iRc += TestMethod( &x, MyFile, po, "LogicTest77", " NOT .T. AND .T.", (xbBool) xbFalse ); + iRc += TestMethod( &x, MyFile, po, "LogicTest78", ".NOT. .T. .AND. .NOT. .T.", (xbBool) xbFalse ); + iRc += TestMethod( &x, MyFile, po, "LogicTest79", " NOT .T. AND NOT .T.", (xbBool) xbFalse ); + iRc += TestMethod( &x, MyFile, po, "LogicTest80", ".T. .OR. .T.", (xbBool) xbTrue ); + iRc += TestMethod( &x, MyFile, po, "LogicTest81", ".T. OR .T.", (xbBool) xbTrue ); + iRc += TestMethod( &x, MyFile, po, "LogicTest82", ".T. .OR. .NOT. .T.", (xbBool) xbTrue ); + iRc += TestMethod( &x, MyFile, po, "LogicTest83", ".T. OR NOT .T.", (xbBool) xbTrue ); + iRc += TestMethod( &x, MyFile, po, "LogicTest84", ".NOT. .T. .OR. .T.", (xbBool) xbTrue ); + iRc += TestMethod( &x, MyFile, po, "LogicTest85", " NOT .T. OR .T.", (xbBool) xbTrue ); + iRc += TestMethod( &x, MyFile, po, "LogicTest86", ".NOT. .T. .OR. .NOT. .T.", (xbBool) xbFalse ); + iRc += TestMethod( &x, MyFile, po, "LogicTest87", " NOT .T. OR NOT .T.", (xbBool) xbFalse ); + + iRc += TestMethod( &x, MyFile, po, "FuncTest48", "ISALPHA( \"12345\" )", (xbBool) xbFalse ); + iRc += TestMethod( &x, MyFile, po, "FuncTest49", "ISALPHA( \"ABCDEF\" )", (xbBool) xbTrue ); + iRc += TestMethod( &x, MyFile, po, "FuncTest50", "ISALPHA( \"A1234\" )", (xbBool) xbTrue ); + iRc += TestMethod( &x, MyFile, po, "FuncTest51", "ISLOWER( \"12345\" )", (xbBool) xbFalse ); + iRc += TestMethod( &x, MyFile, po, "FuncTest52", "ISLOWER( \"ABCDEF\" )", (xbBool) xbFalse ); + iRc += TestMethod( &x, MyFile, po, "FuncTest53", "ISLOWER( \"abc123\" )", (xbBool) xbTrue ); + iRc += TestMethod( &x, MyFile, po, "FuncTest54", "ISLOWER( \"xyz\" )", (xbBool) xbTrue ); + iRc += TestMethod( &x, MyFile, po, "FuncTest55", "ISUPPER( \"12345\" )", (xbBool) xbFalse ); + iRc += TestMethod( &x, MyFile, po, "FuncTest56", "ISUPPER( \"ABCDEF\" )", (xbBool) xbTrue ); + iRc += TestMethod( &x, MyFile, po, "FuncTest57", "ISUPPER( \"abc123\" )", (xbBool) xbFalse ); + iRc += TestMethod( &x, MyFile, po, "FuncTest58", "ISUPPER( \"xyz\" )", (xbBool) xbFalse ); + iRc += TestMethod( &x, MyFile, po, "FuncTest59", "ISUPPER( \"Xyz\" )", (xbBool) xbTrue ); + iRc += TestMethod( &x, MyFile, po, "FuncTest60", "IIF( .T., \"TRUE \", \"FALSE\" )", "TRUE ", 5 ); + iRc += TestMethod( &x, MyFile, po, "FuncTest61", "IIF( .F., \"TRUE \", \"FALSE\" )", "FALSE", 5 ); + iRc += TestMethod( &x, MyFile, po, "FuncTest62", "DEL()", " ", 1 ); + iRc += TestMethod( &x, MyFile, po, "FuncTest63", "DELETED()", (xbBool) xbFalse ); + iRc += TestMethod( po, "DeleteRecord()", MyFile->DeleteRecord(), XB_NO_ERROR ); + iRc += TestMethod( &x, MyFile, po, "FuncTest64", "DEL()", "*", 1 ); + iRc += TestMethod( &x, MyFile, po, "FuncTest65", "DELETED()", (xbBool) xbTrue ); + iRc += TestMethod( po, "UndeleteRecord()", MyFile->UndeleteRecord(), XB_NO_ERROR ); + + + iRc += TestMethod( po, "Close()", MyFile->Close(), XB_NO_ERROR ); + delete MyFile; + + if( po > 0 || iRc < 0 ) + fprintf( stdout, "Total Errors = %d\n", iRc * -1 ); + + #ifdef XB_LOGGING_SUPPORT + sMsg.Sprintf( "Program [%s] terminating with [%d] errors...", av[0], iRc * -1 ); + x.WriteLogMessage( sMsg ); + #endif + + return iRc; +} diff --git a/src/tests/xb_test_file.cpp b/src/tests/xb_test_file.cpp new file mode 100755 index 0000000..ca32693 --- /dev/null +++ b/src/tests/xb_test_file.cpp @@ -0,0 +1,212 @@ +/* xb_test_file.cpp + +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 program tests the file functions + +// usage: xb_test_file QUITE|NORMAL|VERBOSE + + +#include "xbase.h" + + +using namespace xb; + + +#include "tstfuncs.cpp" + + +int main( int argCnt, char **av ) +{ + int rc = 0; + int po = 1; /* print option */ + /* 0 - QUIET */ + /* 1 - NORMAL */ + /* 2 - VERBOSE */ + + if( argCnt > 1 ) { + if( av[1][0] == 'Q' ) + po = 0; + else if( av[1][0] == 'V' ) + po = 2; + } + +#ifdef XB_LOGGING_SUPPORT + xbXBase x; + x.EnableMsgLogging(); + if( po ){ + std::cout << "Logfile is [" << x.GetLogFqFileName().Str() << "]" << std::endl; + } + xbString sMsg; + sMsg.Sprintf( "Program [%s] initializing...", av[0] ); + x.WriteLogMessage( sMsg ); +#endif + + InitTime(); + xbFile f( &x ); + xbString sWrkStr; + xbString sWrkStr2; + sWrkStr = PROJECT_DATA_DIR; + f.SetDataDirectory( PROJECT_DATA_DIR ); + + #ifdef WIN32 + sWrkStr.SwapChars( '/', '\\' ); + #else + sWrkStr.SwapChars( '\\', '/' ); + #endif + + rc += TestMethod( po, "Set/GetDataDirectory()", f.GetDataDirectory(), sWrkStr, sWrkStr.Len()); + + f.SetFileName( "TestFile.txt" ); + sWrkStr = "TestFile.txt"; + rc += TestMethod( po, "Set/GetFileName()", f.GetFileName(), sWrkStr, sWrkStr.Len()); + + f.GetFileType( sWrkStr ); + rc += TestMethod( po, "GetFileType()", sWrkStr, "TXT", 3 ); + + #ifdef WIN32 + sWrkStr = "\\my\\directory\\"; + #else + sWrkStr = "/my/directory/"; + #endif + + f.SetDirectory( sWrkStr ); + rc += TestMethod( po, "Set/GetDirectory()", f.GetDirectory(), sWrkStr, sWrkStr.Len()); + + sWrkStr += "TestFile.txt"; + rc += TestMethod( po, "GetFqFileName()", f.GetFqFileName(), sWrkStr, 26 ); + +#ifdef WIN32 + sWrkStr = "\\some\\directory\\myfile.dat"; + sWrkStr2 = "\\some\\directory\\"; +#else + sWrkStr = "/some/directory/myfile.dat"; + sWrkStr2 = "/some/directory/"; +#endif + + + f.SetFqFileName( sWrkStr ); + rc += TestMethod( po, "GetDirectory()", f.GetDirectory(), sWrkStr2, 16 ); + rc += TestMethod( po, "GetFileName()", f.GetFileName(), "myfile.dat", 10 ); + + rc += TestMethod( po, "NameSuffixMissing()", f.NameSuffixMissing( "myfile.dbf", 1 ), 0 ); + rc += TestMethod( po, "NameSuffixMissing()", f.NameSuffixMissing( "myfile", 1 ), 1 ); + rc += TestMethod( po, "NameSuffixMissing()", f.NameSuffixMissing( "MYFILE", 1 ), 2 ); + + f.SetDirectory( PROJECT_DATA_DIR ); + f.SetFileName( "xbfile.txt" ); + + rc += TestMethod( po, "xbFopen()", f.xbFopen( "w+b", XB_MULTI_USER ), XB_NO_ERROR ); + + + xbString sTest; + sTest = "Test Data"; + rc += TestMethod( po, "xbWrite()", f.xbFwrite( sTest.Str(), 9, 1 ), XB_NO_ERROR ); + rc += TestMethod( po, "xbFclose()", f.xbFclose(), XB_NO_ERROR ); + + rc += TestMethod( po, "xbFopen()", f.xbFopen( "r+b", XB_MULTI_USER ), XB_NO_ERROR ); + rc += TestMethod( po, "xbFseek()", f.xbFseek( 0, SEEK_SET ), XB_NO_ERROR ); + char buf[10]; + for( int i = 0; i < 10; i++ ) + buf[i] = 0x00; + rc += TestMethod( po, "xbFread()", f.xbFread( buf, 5, 1 ), XB_NO_ERROR ); + rc += TestMethod( po, "xbFread()", buf, "Test ", 5 ); + + rc += TestMethod( po, "xbFclose()", f.xbFclose(), XB_NO_ERROR ); + rc += TestMethod( po, "xbRemove()", f.xbRemove(), XB_NO_ERROR ); + xbInt16 iWork = 100; + char cBuf[9]; + char *p = cBuf; + f.ePutInt16( cBuf, iWork ); + rc += TestMethod( po, "Put/GetShort()", f.eGetInt16( p ), 100 ); + + xbInt32 lWork = 10101; + f.ePutInt32( p, lWork ); + rc += TestMethod( po, "Put/GetLong()", f.eGetInt32( p ), 10101 ); + + lWork = 2147483647; + f.ePutInt32( p, lWork ); + rc += TestMethod( po, "Put/GetLong()", f.eGetInt32( p ), 2147483647 ); + rc += TestMethod( po, "Put/GetLong()", (xbInt32) f.eGetUInt32( p ), 2147483647 ); + + xbDouble d = 123456.789; + f.ePutDouble( p, d ); + rc += TestMethod( po, "Put/GetDouble()", f.eGetDouble( p ), 123456.789 ); + + xbString sFqnS; + xbString sFqnT; + xbFile f2( &x ); + rc += TestMethod( po, "CreateUniqueFileName()", f2.CreateUniqueFileName( PROJECT_DATA_DIR, "dbf", sFqnS ), XB_NO_ERROR ); + + rc += TestMethod( po, "FileExists()", f2.FileExists( sFqnS ), xbFalse ); + rc += TestMethod( po, "xbFopen()", f2.xbFopen( "w+b", sFqnS, XB_SINGLE_USER ), XB_NO_ERROR ); + rc += TestMethod( po, "xbFclose()", f2.xbFclose(), XB_NO_ERROR ); + rc += TestMethod( po, "FileExists()", f2.FileExists( sFqnS ), xbTrue ); + + rc += TestMethod( po, "CreateUniqueFileName()", f2.CreateUniqueFileName( PROJECT_DATA_DIR, "dbf", sFqnT ), XB_NO_ERROR ); + rc += TestMethod( po, "xbRename()", f2.xbRename( sFqnS, sFqnT ), XB_NO_ERROR ); + rc += TestMethod( po, "xbRemove()", f.xbRemove( sFqnT ), XB_NO_ERROR ); + + xbString sFn; + rc += TestMethod( po, "GetFileNamePart()", f2.GetFileNamePart( sFqnS , sFn ), XB_NO_ERROR ); + rc += TestMethod( po, "GetFileExtPart()", f2.GetFileExtPart( sFqnS , sFn ), XB_NO_ERROR ); + rc += TestMethod( po, "GetFileExtPart()", f2.GetFileDirPart( sFqnS , sFn ), XB_NO_ERROR ); + + + rc += TestMethod( po, "SetBlockSize()", f.SetBlockSize( 100 ), XB_INVALID_BLOCK_SIZE ); + rc += TestMethod( po, "SetBlockSize()", f.SetBlockSize( 512 ), XB_NO_ERROR ); + rc += TestMethod( po, "GetBlockSize()", (xbInt32) f.GetBlockSize(), 512 ); + + + char BlockBuf[513]; + memset( BlockBuf, 0x00, 513 ); + rc += TestMethod( po, "xbFopen()", f.xbFopen( "w+b", XB_SINGLE_USER ), XB_NO_ERROR ); + + for( int i = 0; i < 512; i++ ) + BlockBuf[i] = 'A'; + rc += TestMethod( po, "WriteBlock()", f.WriteBlock( 0L, 512, BlockBuf ), XB_NO_ERROR ); + + for( int i = 0; i < 512; i++ ) + BlockBuf[i] = 'B'; + rc += TestMethod( po, "WriteBlock()", f.WriteBlock( 1L, 512, BlockBuf ), XB_NO_ERROR ); + + for( int i = 0; i < 512; i++ ) + BlockBuf[i] = 'C'; + rc += TestMethod( po, "WriteBlock()", f.WriteBlock( 2L, 512, BlockBuf ), XB_NO_ERROR ); + + char BlockBuf2[513]; + memset( BlockBuf2, 0x00, 513 ); + rc += TestMethod( po, "ReadBlock()", f.ReadBlock( 2L, 512, BlockBuf2 ), XB_NO_ERROR ); + + xbString s1 = BlockBuf; + xbString s2 = BlockBuf2; + + rc += TestMethod( po, "ReadBlock()", s1, s2, 512 ); + + rc += TestMethod( po, "xbTruncate()", f.xbTruncate( 1000 ), XB_NO_ERROR ); + + xbUInt64 ullFsize; + rc += TestMethod( po, "GetFileSize()", f.GetFileSize( ullFsize ), XB_NO_ERROR ); + rc += TestMethod( po, "xbGetFileSize()", (xbInt32) ullFsize, 1000 ); + rc += TestMethod( po, "xbFclose()", f.xbFclose(), XB_NO_ERROR ); + + if( po > 0 || rc < 0 ) + fprintf( stdout, "Total Errors = %d\n", rc * -1 ); + +#ifdef XB_LOGGING_SUPPORT + sMsg.Sprintf( "Program [%s] terminating with [%d] errors...", av[0], rc * -1 ); + x.WriteLogMessage( sMsg ); +#endif + + return rc; +} + diff --git a/src/tests/xb_test_filter.cpp b/src/tests/xb_test_filter.cpp new file mode 100755 index 0000000..2aeade3 --- /dev/null +++ b/src/tests/xb_test_filter.cpp @@ -0,0 +1,185 @@ +/* xb_test_filter.cpp + +XBase64 Software Library + +Copyright (c) 1997,2003,2014,2020,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 program tests the class xbIxFilter + +// usage: xb_test_filter QUITE|NORMAL|VERBOSE + + +#include "xbase.h" + +using namespace xb; + +#include "tstfuncs.cpp" + + +int main( int argCnt, char **av ) +{ + int iRc = 0; + int iRc2 = 0; + int iPo = 1; /* print option */ + /* 0 - QUIET */ + /* 1 - NORMAL */ + /* 2 - VERBOSE */ + + if( argCnt > 1 ) { + if( av[1][0] == 'Q' ) + iPo = 0; + else if( av[1][0] == 'V' ) + iPo = 2; + } + + + xbSchema MyV4Record[] = + { + { "CFLD", XB_CHAR_FLD, 6, 0 }, + { "NFLD", XB_NUMERIC_FLD, 6, 0 }, + { "ZFLD", XB_CHAR_FLD, 1, 0 }, + { "",0,0,0 } + }; + + + xbXBase x; +#ifdef XB_LOGGING_SUPPORT + x.EnableMsgLogging(); + if( iPo ){ + std::cout << "Logfile is [" << x.GetLogFqFileName().Str() << "]" << std::endl; + } + xbString sMsg; + sMsg.Sprintf( "Program [%s] initializing...", av[0] ); + x.WriteLogMessage( sMsg ); + x.SetLogSize( 1000000 ); +#endif + + x.SetDataDirectory( PROJECT_DATA_DIR ); + x.EnableMsgLogging(); + + char c; + xbString s; + xbInt32 lRecCnt = 0; + iRc = 0; + + if( iPo > 0 ) + std::cout << "Default Data Directory is [" << x.GetDataDirectory().Str() << "]" << std::endl; + + xbDbf *V4Dbf = new xbDbf4( &x ); + + iRc2 = V4Dbf->CreateTable( "TestFilt.DBF", "TestFilter", MyV4Record, XB_OVERLAY, XB_MULTI_USER ); + iRc += TestMethod( iPo, "CreateTable()", (xbInt32) iRc2, XB_NO_ERROR ); + + + #ifdef XB_NDX_SUPPORT + xbIx *ixPtr; + void *ndx; + iRc2 = V4Dbf->CreateTag( "NDX", "TestFilt.NDX", "CFLD", "", 0, 0, XB_OVERLAY, &ixPtr, &ndx ); + iRc += TestMethod( iPo, "CreateTag()", (xbInt32) iRc2, XB_NO_ERROR ); + if( iRc2 ) + x.DisplayError( iRc2 ); + #endif // XB_NDX_SUPPORT + + + if( iRc2 ) + x.DisplayError( iRc2 ); + + for( xbUInt16 i = 0; i < 5 && iRc == XB_NO_ERROR; i++ ){ + // for( xbUInt16 j = 0; j < 26 && iRc == XB_NO_ERROR; j++ ){ + for( xbInt16 j = 25; j >= 0 && iRc == XB_NO_ERROR; j-- ){ + c = j + 65; + s = c; + s.PadRight( c, (xbUInt32) i + 1 ); + V4Dbf->BlankRecord(); + V4Dbf->PutField( "CFLD", s ); + V4Dbf->PutLongField( "NFLD", ++lRecCnt ); + iRc = V4Dbf->AppendRecord(); + } + } + + iRc += TestMethod( iPo, "SetCurTag()", V4Dbf->SetCurTag( "" ), XB_NO_ERROR ); + xbFilter f1( &x, V4Dbf ); + xbString sMyFilterExpression = "LEFT( CFLD, 2 ) = 'YY'"; + iRc += TestMethod( iPo, "Set()", f1.Set( sMyFilterExpression ), XB_NO_ERROR ); + + iRc += TestMethod( iPo, "SetCurTag()", V4Dbf->SetCurTag( "" ), XB_NO_ERROR ); + iRc += TestMethod( iPo, "Set()", f1.Set( sMyFilterExpression ), XB_NO_ERROR ); + iRc += TestMethod( iPo, "GetFirstRecord()", f1.GetFirstRecord(), XB_NO_ERROR ); + iRc += TestMethod( iPo, "GetCurRecNo()", (xbInt32) V4Dbf->GetCurRecNo(), 28 ); + iRc += TestMethod( iPo, "GetNextRecord()", f1.GetNextRecord(), XB_NO_ERROR ); + iRc += TestMethod( iPo, "GetCurRecNo()", (xbInt32) V4Dbf->GetCurRecNo(), 54 ); + iRc += TestMethod( iPo, "GetNextRecord()", f1.GetNextRecord(), XB_NO_ERROR ); + iRc += TestMethod( iPo, "GetCurRecNo()", (xbInt32) V4Dbf->GetCurRecNo(), 80 ); + iRc += TestMethod( iPo, "GetLast()", f1.GetLastRecord(), XB_NO_ERROR ); + iRc += TestMethod( iPo, "GetCurRecNo()", (xbInt32) V4Dbf->GetCurRecNo(), 106 ); + iRc += TestMethod( iPo, "GetPrev()", f1.GetPrevRecord(), XB_NO_ERROR ); + iRc += TestMethod( iPo, "GetCurRecNo()", (xbInt32) V4Dbf->GetCurRecNo(), 80 ); + + sMyFilterExpression = "LEFT( CFLD, 2 ) = 'CC'"; + iRc += TestMethod( iPo, "Set()", f1.Set( sMyFilterExpression ), XB_NO_ERROR ); + iRc += TestMethod( iPo, "GetFirstRecord()", f1.GetFirstRecord(), XB_NO_ERROR ); + iRc += TestMethod( iPo, "GetCurRecNo()", (xbInt32) V4Dbf->GetCurRecNo(), 50 ); + iRc += TestMethod( iPo, "GetNextRecord()", f1.GetNextRecord(), XB_NO_ERROR ); + iRc += TestMethod( iPo, "GetCurRecNo()", (xbInt32) V4Dbf->GetCurRecNo(), 76 ); + iRc += TestMethod( iPo, "GetNextRecord()", f1.GetNextRecord(), XB_NO_ERROR ); + iRc += TestMethod( iPo, "GetCurRecNo()", (xbInt32) V4Dbf->GetCurRecNo(), 102 ); + iRc += TestMethod( iPo, "GetLast()", f1.GetLastRecord(), XB_NO_ERROR ); + iRc += TestMethod( iPo, "GetCurRecNo()", (xbInt32) V4Dbf->GetCurRecNo(), 128 ); + iRc += TestMethod( iPo, "GetPrev()", f1.GetPrevRecord(), XB_NO_ERROR ); + iRc += TestMethod( iPo, "GetCurRecNo()", (xbInt32) V4Dbf->GetCurRecNo(), 102 ); + + + #ifdef XB_NDX_SUPPORT + f1.SetLimit( 0 ); + + // change things up a bit + iRc += TestMethod( iPo, "GetRecord()", V4Dbf->GetRecord( 32 ), XB_NO_ERROR ); + iRc += TestMethod( iPo, "PutField()", V4Dbf->PutField( "ZFLD", "Z" ), XB_NO_ERROR ); + iRc += TestMethod( iPo, "GetRecord()", V4Dbf->GetRecord( 52 ), XB_NO_ERROR ); + iRc += TestMethod( iPo, "PutField()", V4Dbf->PutField( "ZFLD", "Z" ), XB_NO_ERROR ); + iRc += TestMethod( iPo, "GetRecord()", V4Dbf->GetRecord( 76 ), XB_NO_ERROR ); + iRc += TestMethod( iPo, "PutField()", V4Dbf->PutField( "ZFLD", "Z" ), XB_NO_ERROR ); + iRc += TestMethod( iPo, "GetRecord()", V4Dbf->GetRecord( 103 ), XB_NO_ERROR ); + iRc += TestMethod( iPo, "PutField()", V4Dbf->PutField( "ZFLD", "Z" ), XB_NO_ERROR ); + iRc += TestMethod( iPo, "Commit()", V4Dbf->Commit(), XB_NO_ERROR ); + + iRc += TestMethod( iPo, "SetCurTag()", V4Dbf->SetCurTag( "TestFilt" ), XB_NO_ERROR ); + sMyFilterExpression = "ZFLD = 'Z'"; + iRc += TestMethod( iPo, "Set()", f1.Set( sMyFilterExpression ), XB_NO_ERROR ); + + iRc += TestMethod( iPo, "GetFirstRecord()", f1.GetFirstRecord(), XB_NO_ERROR ); + iRc += TestMethod( iPo, "GetCurRecNo()", (xbInt32) V4Dbf->GetCurRecNo(), 52 ); + iRc += TestMethod( iPo, "GetNextRecord()", f1.GetNextRecord(), XB_NO_ERROR ); + iRc += TestMethod( iPo, "GetCurRecNo()", (xbInt32) V4Dbf->GetCurRecNo(), 103 ); + iRc += TestMethod( iPo, "GetNextRecord()", f1.GetNextRecord(), XB_NO_ERROR ); + iRc += TestMethod( iPo, "GetCurRecNo()", (xbInt32) V4Dbf->GetCurRecNo(), 76 ); + iRc += TestMethod( iPo, "GetLast()", f1.GetLastRecord(), XB_NO_ERROR ); + iRc += TestMethod( iPo, "GetCurRecNo()", (xbInt32) V4Dbf->GetCurRecNo(), 32 ); + iRc += TestMethod( iPo, "GetPrev()", f1.GetPrevRecord(), XB_NO_ERROR ); + iRc += TestMethod( iPo, "GetCurRecNo()", (xbInt32) V4Dbf->GetCurRecNo(), 76 ); + + #endif // XB_NDX_SUPPORT + + + iRc += TestMethod( iPo, "Close()", V4Dbf->Close(), XB_NO_ERROR ); + delete V4Dbf; + + if( iPo > 0 || iRc < 0 ) + fprintf( stdout, "Total Errors = %d\n", iRc * -1 ); + +#ifdef XB_LOGGING_SUPPORT + sMsg.Sprintf( "Program [%s] terminating with [%d] errors...", av[0], iRc * -1 ); + x.WriteLogMessage( sMsg, 2 ); +#endif + + return iRc; +} diff --git a/src/tests/xb_test_funcs.cpp b/src/tests/xb_test_funcs.cpp new file mode 100755 index 0000000..07e2c17 --- /dev/null +++ b/src/tests/xb_test_funcs.cpp @@ -0,0 +1,288 @@ +/* xb_test_funcs.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 program tests the xb functions +// usage: xb_test_funcs QUITE|NORMAL|VERBOSE + +#include "xbase.h" +using namespace xb; + +#include "tstfuncs.cpp" + + +/**************************************************************************/ + +int main( int argCnt, char **av ) +{ + xbInt16 iRc = 0; + xbInt16 rc2 = 0; + xbInt16 po = 1; /* print option */ + /* 0 - QUIET */ + /* 1 - NORMAL */ + /* 2 - VERBOSE */ + + if( argCnt > 1 ) { + if( av[1][0] == 'Q' ) + po = 0; + else if( av[1][0] == 'V' ) + po = 2; + } + + xbSchema MyRecord[] = + { + { "NUM1", XB_NUMERIC_FLD, 9, 2 }, + { "DATE1", XB_DATE_FLD, 8, 0 }, + { "DATE2", XB_DATE_FLD, 8, 0 }, + { "",0,0,0 } + }; + + + xbXBase x; + xbDbf * MyFile; + xbDate d; + + xbString sResult; + xbDate dtResult; + xbDouble dResult; + xbBool bResult; + xbDate dtIn( "19890209" ); + + + #ifdef XB_LOGGING_SUPPORT + x.EnableMsgLogging(); + if( po ){ + std::cout << "Logfile is [" << x.GetLogFqFileName().Str() << "]" << std::endl; + } + xbString sMsg; + sMsg.Sprintf( "Program [%s] initializing...", av[0] ); + x.WriteLogMessage( sMsg ); + #endif + + x.SetDataDirectory( PROJECT_DATA_DIR ); + x.EnableMsgLogging(); + InitTime(); + + if( po > 0 ){ + std::cout << "XBase Expression testing program.." << std::endl; + std::cout << "This program tests the XBase expression logic." << std::endl; + std::cout << "Default Data Directory is [" << x.GetDataDirectory().Str() << "]" << std::endl; + } + + #ifdef XB_DBF4_SUPPORT + MyFile = new xbDbf4( &x ); /* version 4 dbf file */ + #else + MyFile = new xbDbf3( &x ); /* version 3 dbf file */ + #endif + + rc2 = MyFile->CreateTable( "Functest.DBF", "ExpTest", MyRecord, XB_OVERLAY, XB_MULTI_USER ); + iRc += TestMethod( po, "CreateTable()", rc2, XB_NO_ERROR ); + iRc += TestMethod( po, "PutField()", MyFile->PutFloatField( "NUM1", 5 ), XB_NO_ERROR ); + iRc += TestMethod( po, "PutField()", MyFile->PutField( "DATE1", "19890303" ), XB_NO_ERROR ); + iRc += TestMethod( po, "PutField()", MyFile->PutField ( "DATE2", "20120708" ), XB_NO_ERROR ); + + MyFile->SetAutoLock( xbFalse ); + iRc += TestMethod( po, "AppendRecord()", MyFile->AppendRecord(), XB_NO_ERROR ); + + iRc += TestMethod( po, "ABS( -222, dResult )", x.ABS( -222, dResult ), XB_NO_ERROR ); + iRc += TestMethod( po, "ABS( -222, dResult )", -222, -222 ); + iRc += TestMethod( po, "ABS( 333, dResult )", x.ABS( 333, dResult ), XB_NO_ERROR ); + iRc += TestMethod( po, "ABS( 333, dResult )", 333, 333 ); + + iRc += TestMethod( po, "x.ALLTRIM( \" zzz \", sResult )", x.ALLTRIM( " zzz ", sResult ), XB_NO_ERROR ); + iRc += TestMethod( po, "x.ALLTRIM( \" zzz \", sResult )", sResult, "zzz", 3 ); + + iRc += TestMethod( po, "ASC( \"A\", dResult )", x.ASC( "A", dResult ), XB_NO_ERROR ); + iRc += TestMethod( po, "ASC( \"A\" )", dResult, (xbDouble) 65 ); + iRc += TestMethod( po, "ASC( \"B\", dResult )", x.ASC( "B", dResult ), XB_NO_ERROR ); + iRc += TestMethod( po, "ASC( \"B\" )", dResult, (xbDouble) 66 ); + iRc += TestMethod( po, "AT( \"ABC\", \"XYZABC\", 4 )", x.AT( "ABC", "XYZABC", dResult ), XB_NO_ERROR ); + iRc += TestMethod( po, "AT( \"ABC\", \"XYZABC\", 4 )", dResult, (xbDouble) 4 ); + iRc += TestMethod( po, "CDOW( dtIn, sResult )", x.CDOW( dtIn, sResult ), XB_NO_ERROR ); + iRc += TestMethod( po, "CDOW( dtIn, sResult )", sResult, "Thursday", 8 ); + iRc += TestMethod( po, "CHR( 101, sResult )", x.CHR( 101, sResult ), XB_NO_ERROR ); + iRc += TestMethod( po, "CHR( 101, sResult )", sResult, "e", 1 ); + + dtIn = "19870103"; + iRc += TestMethod( po, "CMONTH( dtIn, sResult )", x.CMONTH( dtIn, sResult ), XB_NO_ERROR ); + iRc += TestMethod( po, "CMONTH( dtIn, sResult )", sResult, "January", 7 ); + iRc += TestMethod( po, "CTOD( \"01\\03\\87\", dtResult )", x.CTOD( "01\\03\\87", dtResult ), XB_NO_ERROR ); + iRc += TestMethod( po, "CTOD( \"01\\03\\87\", dtResult )", dtResult.Str(), "19870103", 8 ); + iRc += TestMethod( po, "DATE( dtResult )", x.DATE( dtResult ), XB_NO_ERROR ); + iRc += TestMethod( po, "DATE( dtResult )", dtResult.Str(), d.Str(), 8 ); + iRc += TestMethod( po, "DAY(\"19870103\", dResult )", x.DAY( "19870103", dResult ), XB_NO_ERROR ); + iRc += TestMethod( po, "DAY(\"19870103\", dResult )", dResult, (xbDouble) 3 ); + + iRc += TestMethod( po, "DEL( MyFile, sResult )", x.DEL( MyFile, sResult ), XB_NO_ERROR ); + iRc += TestMethod( po, "DEL( MyFile, sResult )", sResult, " ", 1 ); + + iRc += TestMethod( po, "DELETED( MyFile, bResult )", x.DELETED( MyFile, bResult ), XB_NO_ERROR ); + iRc += TestMethod( po, "DELETED( MyFile, bResult )", (xbInt32) bResult, xbFalse ); + + iRc += TestMethod( po, "DeleteRecord()", MyFile->DeleteRecord(), XB_NO_ERROR ); + + iRc += TestMethod( po, "DEL( MyFile, sResult )", x.DEL( MyFile, sResult ), XB_NO_ERROR ); + iRc += TestMethod( po, "DEL( MyFile, sResult )", sResult, "*", 1 ); + iRc += TestMethod( po, "DELETED( MyFile, bResult )", x.DELETED( MyFile, bResult ), XB_NO_ERROR ); + iRc += TestMethod( po, "DELETED( MyFile, bResult )", (xbInt32) bResult, xbTrue ); + + iRc += TestMethod( po, "UndeleteRecord()", MyFile->UndeleteRecord(), XB_NO_ERROR ); + + iRc += TestMethod( po, "DESCEND(\"ABCDE\", sResult )", x.DESCEND( "ABCDE", sResult ), XB_NO_ERROR ); + + char sDescendResult[6]; // = "¾½¼»º"; + sDescendResult[0] = (char) 0xBE; + sDescendResult[1] = (char) 0xBD; + sDescendResult[2] = (char) 0xBC; + sDescendResult[3] = (char) 0xBB; + sDescendResult[4] = (char) 0xBA; + sDescendResult[5] = (char) 0x00; + iRc += TestMethod( po, "DESCEND(\"ABCDE\", sResult )", sResult, sDescendResult, 5 ); + + iRc += TestMethod( po, "DESCEND( 12345, dResult )", x.DESCEND( 12345, dResult ), XB_NO_ERROR ); + iRc += TestMethod( po, "DESCEND( 12345, dResult )", dResult, (xbDouble) -12345 ); + dtIn = "19890303"; + iRc += TestMethod( po, "DESCEND( dtIn, dtResult )", x.DESCEND( dtIn, dtResult ), XB_NO_ERROR ); + iRc += TestMethod( po, "DESCEND( dtIn, dtResult )", dtResult.Str(), "29101031", 8 ); + dtIn = "20120708"; + iRc += TestMethod( po, "DESCEND( dtIn, dtResult )", x.DESCEND( dtIn, dtResult ), XB_NO_ERROR ); + iRc += TestMethod( po, "DESCEND( dtIn, dtResult )", dtResult.Str(), "28870625" , 8 ); + + dtIn = "20171014"; + iRc += TestMethod( po, "DOW( dtIn, dResult )", x.DOW( dtIn, dResult ), XB_NO_ERROR ); + iRc += TestMethod( po, "DOW(\"20171015\", dResult )", dResult, (xbDouble) 0 ); + dtIn++; + iRc += TestMethod( po, "DOW( dtIn, dResult )", x.DOW( dtIn, dResult ), XB_NO_ERROR ); + iRc += TestMethod( po, "DOW(\"20171016\", dResult )", dResult, (xbDouble) 1 ); + dtIn++; + iRc += TestMethod( po, "DOW( dtIn, dResult )", x.DOW( dtIn, dResult ), XB_NO_ERROR ); + iRc += TestMethod( po, "DOW(\"20171021\", dResult )", dResult, (xbDouble) 2 ); + + dtIn = "20000101"; + iRc += TestMethod( po, "DTOC( dtIn, sResult )", x.DTOC( dtIn, sResult ), XB_NO_ERROR ); + iRc += TestMethod( po, "DTOC( dtIn, sResult )", sResult, "01/01/00", 8 ); + iRc += TestMethod( po, "DTOS( dtIn, sResult )", x.DTOS( dtIn, sResult ), XB_NO_ERROR ); + iRc += TestMethod( po, "DTOS( dtIn, sResult )", sResult, "20000101", 8 ); + iRc += TestMethod( po, "EXP( 1, dResult )", x.EXP( 1, dResult ), XB_NO_ERROR ); + iRc += TestMethod( po, "EXP( 1, dResult )", dResult, 2.71828, .001 ); + iRc += TestMethod( po, "x.INT( 621.5, dResult )", x.INT( 621.5, dResult ), XB_NO_ERROR ); + iRc += TestMethod( po, "x.INT( 621.5, dResult )", dResult, (xbDouble) 621 ); + iRc += TestMethod( po, "x.ISALPHA( \"1\", bResult )", x.ISALPHA( "1", bResult ), 0 ); + iRc += TestMethod( po, "x.ISALPHA( \"1\", bResult )", (xbInt32) bResult, xbFalse ); + iRc += TestMethod( po, "x.ISALPHA( \"A\", bResult )", x.ISALPHA( "A", bResult ), XB_NO_ERROR ); + iRc += TestMethod( po, "x.ISALPHA( \"A\", bResult )", (xbInt32) bResult, xbTrue ); + iRc += TestMethod( po, "x.ISLOWER( \"A\", bResult )", x.ISLOWER( "A", bResult ), XB_NO_ERROR ); + iRc += TestMethod( po, "x.ISLOWER( \"A\", bResult )", bResult, xbFalse ); + iRc += TestMethod( po, "x.ISLOWER( \"a\", bResult )", x.ISLOWER( "a", bResult ), XB_NO_ERROR ); + iRc += TestMethod( po, "x.ISLOWER( \"a\", bResult )", bResult, xbTrue ); + iRc += TestMethod( po, "x.ISUPPER( \"A\", bResult )", x.ISUPPER( "A", bResult ), XB_NO_ERROR ); + iRc += TestMethod( po, "x.ISUPPER( \"A\", bResult )", bResult, xbTrue ); + iRc += TestMethod( po, "x.ISUPPER( \"a\", bResult )", x.ISUPPER( "a", bResult ), XB_NO_ERROR ); + iRc += TestMethod( po, "x.ISUPPER( \"a\", bResult )", bResult, xbFalse ); + iRc += TestMethod( po, "x.LEFT( \"STRING\", 3, sResult )", x.LEFT( "STRING", 3, sResult ), XB_NO_ERROR ); + iRc += TestMethod( po, "x.LEFT( \"STRING\", 3, sResult )", sResult, "STR", 3 ); + iRc += TestMethod( po, "x.LEN( \"AAAAA\", dResult )", x.LEN( "AAAAA", dResult ), XB_NO_ERROR ); + iRc += TestMethod( po, "x.LEN( \"AAAAA\", dResult )", dResult, (xbDouble) 5 ); + iRc += TestMethod( po, "x.LOG( 2, dResult )", x.LOG( 2, dResult ), XB_NO_ERROR ); + iRc += TestMethod( po, "x.LOG( 2, dResult )", dResult, (xbDouble) 0.69314700, .0001 ); + iRc += TestMethod( po, "x.LOWER( \"AAAA\", sResult )", x.LOWER( "AAAA", sResult ), XB_NO_ERROR ); + iRc += TestMethod( po, "x.LOWER( \"AAAA\", sResult )", sResult, "aaaa", 4 ); + iRc += TestMethod( po, "x.LTRIM( \" xxxxxx\" )", x.LTRIM( " xxxxxx", sResult ), XB_NO_ERROR ); + iRc += TestMethod( po, "x.LTRIM( \" xxxxxx\" )", sResult, "xxxxxx", 6 ); + iRc += TestMethod( po, "x.MAX( 10, 27, dResult )", x.MAX( 10, 27, dResult ), XB_NO_ERROR ); + iRc += TestMethod( po, "x.MAX( 10, 27, dResult )", dResult, (xbDouble) 27 ); + iRc += TestMethod( po, "x.MIN( 10, 5, dResult )", x.MIN( 10, 5, dResult ), XB_NO_ERROR ); + iRc += TestMethod( po, "x.MIN( 10, 5, dResult )", dResult, (xbDouble) 5 ); + iRc += TestMethod( po, "x.MONTH( dtIn, dResult )", x.MONTH( dtIn, dResult ), XB_NO_ERROR ); + iRc += TestMethod( po, "x.MONTH( dtIn, dResult )", dResult, (xbDouble) 1 ); + iRc += TestMethod( po, "x.RECCOUNT( MyFile, dResult)", x.RECNO( MyFile, dResult ), XB_NO_ERROR ); + iRc += TestMethod( po, "x.RECCOUNT( MyFile, dResult)", dResult, (xbDouble) 1 ); + iRc += TestMethod( po, "x.RECNO( MyFile, dResult)", x.RECNO( MyFile, dResult ), XB_NO_ERROR ); + iRc += TestMethod( po, "x.RECNO( MyFile, dResult)", dResult, (xbDouble) 1 ); + iRc += TestMethod( po, "x.REPLICATE( \"abc\", 3, sResult )", x.REPLICATE( "abc", 3, sResult ), XB_NO_ERROR ); + iRc += TestMethod( po, "x.REPLICATE( \"abc\", 3, sResult )", sResult, "abcabcabc", 9 ); + iRc += TestMethod( po, "x.RIGHT( \"STRING\", 3, sResult )", x.RIGHT( "STRING", 3, sResult ), XB_NO_ERROR ); + iRc += TestMethod( po, "x.RIGHT( \"STRING\", 3, sResult )", sResult, "ING", 3 ); + iRc += TestMethod( po, "x.RIGHT( \"STRING\", 3, sResult )", x.RIGHT( "STRING", 5, sResult ), XB_NO_ERROR ); + iRc += TestMethod( po, "x.RIGHT( \"STRING\", 3, sResult )", sResult, "TRING", 5 ); + iRc += TestMethod( po, "x.RIGHT( \"STRING\", 3, sResult )", x.RIGHT( "STRING", 6, sResult ), XB_NO_ERROR ); + iRc += TestMethod( po, "x.RIGHT( \"STRING\", 3, sResult )", sResult, "STRING", 6 ); + iRc += TestMethod( po, "x.RIGHT( \"STRING\", 3, sResult )", x.RIGHT( "STRING", 7, sResult ), XB_NO_ERROR ); + iRc += TestMethod( po, "x.RIGHT( \"STRING\", 3, sResult )", sResult, "STRING", 6 ); + iRc += TestMethod( po, "x.RTRIM( \"zzz \", sResult )", x.RTRIM( "zzz ", sResult ), XB_NO_ERROR ); + iRc += TestMethod( po, "x.RTRIM( \"zzz \", sResult )", sResult, "zzz", 3 ); + iRc += TestMethod( po, "x.TRIM( \"aaa \", sResult )", x.TRIM( "aaa ", sResult ), XB_NO_ERROR ); + iRc += TestMethod( po, "x.TRIM( \"aaa \", sResult )", sResult, "aaa", 3 ); + iRc += TestMethod( po, "x.SPACE( 3, sResult )", x.SPACE( 3, sResult ), XB_NO_ERROR ); + iRc += TestMethod( po, "x.SPACE( 3, sResult )", sResult, " ", 3 ); + iRc += TestMethod( po, "x.SQRT( 9, dResult )", x.SQRT( 9, dResult ), XB_NO_ERROR ); + iRc += TestMethod( po, "x.SQRT( 9, dResult )", dResult, (xbDouble) 3 ); + iRc += TestMethod( po, "x.STOD( \"20000101\", dtResult )", x.STOD( "20000101", dtResult ), XB_NO_ERROR ); + iRc += TestMethod( po, "x.STOD( \"20000101\", dtResult )",dtResult.Str(), "20000101", 8 ); + + xbString sPadChar = " "; + iRc += TestMethod( po, "x.STR( 2001, 4, 0, sPadChar, sResult )", x.STR( 2001, 4, 0, sPadChar, sResult ), XB_NO_ERROR ); + iRc += TestMethod( po, "x.STR( 2001, 4, 0, sPadChar, sResult )", sResult, "2001", 4 ); + iRc += TestMethod( po, "x.STR( 2002, 3, 0, sPadChar, sResult )", x.STR( 2002, 3, 0, sPadChar, sResult ), XB_NO_ERROR ); + iRc += TestMethod( po, "x.STR( 2002, 3, 0, sPadChar, sResult )", sResult, "***", 3 ); + iRc += TestMethod( po, "x.STR( 203.2, 6, 2, sPadChar, sResult )", x.STR( 203.2, 6, 2, sPadChar, sResult ), XB_NO_ERROR ); + iRc += TestMethod( po, "x.STR( 203.2, 6, 2, sPadChar, sResult )", sResult, "203.20", 6 ); + iRc += TestMethod( po, "x.STR( 204.11, 8, 2, sPadChar, sResult )", x.STR( 204.11, 8, 2, sPadChar, sResult ), XB_NO_ERROR ); + iRc += TestMethod( po, "x.STR( 204.11, 8, 2, sPadChar, sResult )", sResult, " 204.11", 8 ); + iRc += TestMethod( po, "x.STR( -205.45, 8, 2, sPadChar, sResult )", x.STR( -205.45, 8, 2, sPadChar, sResult ), XB_NO_ERROR ); + iRc += TestMethod( po, "x.STR( -205.45, 8, 2, sPadChar, sResult )", sResult, " -205.45", 8 ); + iRc += TestMethod( po, "x.STR( -306.45, 8, 2, sPadChar, sResult )", x.STR( -306.45, 8, 2, sPadChar, sResult ), XB_NO_ERROR ); + iRc += TestMethod( po, "x.STR( -306.45, 8, 2, sPadChar, sResult )", sResult, " -306.45", 8 ); + iRc += TestMethod( po, "x.STR( 6.56, 5, 0, sResult )", x.STR( 6.56, 5, 0, sResult ), XB_NO_ERROR ); + iRc += TestMethod( po, "x.STR( 6.56, 5, 0, sResult )", sResult, " 7", 5 ); + iRc += TestMethod( po, "x.STR( 7.77, 5, sResult )", x.STR( 7.77, 5, sResult ), XB_NO_ERROR ); + iRc += TestMethod( po, "x.STR( 7.77, 5, sResult )", sResult, " 8", 5 ); + iRc += TestMethod( po, "x.STR( 8, sResult )", x.STR( 8, sResult ), XB_NO_ERROR ); + iRc += TestMethod( po, "x.STR( 8, sResult )", sResult, " 8", 10 ); + + sPadChar = "0"; + iRc += TestMethod( po, "x.STR( -5.2, 10, 2, sPadChar, sResult )", x.STR( -5.2, 10, 2, sPadChar, sResult ), XB_NO_ERROR ); + iRc += TestMethod( po, "x.STR( -5.2, 10, 2, sPadChar, sResult )", sResult, "00000-5.20", 10 ); + iRc += TestMethod( po, "x.STRZERO( 8, 8, 0, sResult )", x.STRZERO( 8, 8, 0, sResult ), XB_NO_ERROR ); + iRc += TestMethod( po, "x.STRZERO( 8, 8, 0, sResult )", sResult, "00000008", 8 ); + iRc += TestMethod( po, "x.STRZERO( -8, 8, 0, sResult )", x.STRZERO( -8, 8, 0, sResult ), XB_NO_ERROR ); + iRc += TestMethod( po, "x.STRZERO( -8, 8, 0, sResult )", sResult, "-0000008", 8 ); + iRc += TestMethod( po, "x.STRZERO( -205.45, 10, 3 )", x.STRZERO( -205.45, 10, 3, sResult ), XB_NO_ERROR ); + iRc += TestMethod( po, "x.STRZERO( -205.45, 10, 3 )", sResult, "-00205.450", 10 ); + iRc += TestMethod( po, "x.STRZERO( -205.45, 3, 1 )", x.STRZERO( -205.45, 3, 1, sResult ), XB_NO_ERROR ); + iRc += TestMethod( po, "x.STRZERO( -205.45, 3, 1 )", sResult, "***", 3 ); + iRc += TestMethod( po, "x.SUBSTR( \"TESTSTRING\", 5, 2, sResult )", x.SUBSTR( "TESTSTRING", 5, 2, sResult ), XB_NO_ERROR ); + iRc += TestMethod( po, "x.SUBSTR( \"TESTSTRING\", 5, 2, sResult )", sResult, "ST", 2 ); + iRc += TestMethod( po, "x.UPPER( \"abababa\", sResult )", x.UPPER( "abababa", sResult ), XB_NO_ERROR ); + iRc += TestMethod( po, "x.UPPER( \"abababa\", sResult )", sResult, "ABABABA", 7 ); + iRc += TestMethod( po, "x.VAL( \"65\", dResult )", x.VAL( "65", dResult ), XB_NO_ERROR ); + iRc += TestMethod( po, "x.VAL( \"65\", dResult )", dResult, (xbDouble) 65 ); + iRc += TestMethod( po, "x.YEAR( dtIn, dResult )", x.YEAR( dtIn, dResult ), XB_NO_ERROR ); + iRc += TestMethod( po, "x.YEAR( dtIn, dResult )", dResult, (xbDouble) 2000 ); + + + iRc += TestMethod( po, "Close()", MyFile->Close(), XB_NO_ERROR ); + + + delete MyFile; + if( po > 0 || iRc < 0 ) + fprintf( stdout, "Total Errors = %d\n", iRc * -1 ); + + #ifdef XB_LOGGING_SUPPORT + sMsg.Sprintf( "Program [%s] terminating with [%d] errors...", av[0], iRc * -1 ); + x.WriteLogMessage( sMsg ); + #endif + + return iRc; +} diff --git a/src/tests/xb_test_linklist.cpp b/src/tests/xb_test_linklist.cpp new file mode 100755 index 0000000..dc253af --- /dev/null +++ b/src/tests/xb_test_linklist.cpp @@ -0,0 +1,340 @@ +/* xb_test_linklist.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 program tests the class xblnklst and xblnknod +// usage: xb_test_linklist QUITE|NORMAL|VERBOSE + + +#include "xbase.h" + +using namespace xb; + +#include "tstfuncs.cpp" + + + +int main( int argCnt, char **av ) +{ + int rc = 0; + + int po = 1; /* print option */ + /* 0 - QUIET */ + /* 1 - NORMAL */ + /* 2 - VERBOSE */ + + if( argCnt > 1 ) { + if( av[1][0] == 'Q' ) + po = 0; + else if( av[1][0] == 'V' ) + po = 2; + } + + xbXBase x; +#ifdef XB_LOGGING_SUPPORT + x.EnableMsgLogging(); + if( po ){ + std::cout << "Logfile is [" << x.GetLogFqFileName().Str() << "]" << std::endl; + } + xbString sMsg; + sMsg.Sprintf( "Program [%s] initializing...", av[0] ); + x.WriteLogMessage( sMsg ); +#endif + InitTime(); + + xbLinkList ll; + ll.InsertAtFront( "A" ); + ll.InsertAtFront( "B" ); + ll.InsertAtFront( "C" ); + ll.InsertAtFront( "D" ); + ll.InsertAtFront( "E" ); + ll.InsertAtFront( "F" ); + ll.InsertAtFront( "G" ); + ll.InsertAtFront( "H" ); + ll.InsertAtFront( "I" ); + ll.InsertAtFront( "J" ); + ll.InsertAtFront( "K" ); + ll.InsertAtFront( "L" ); + ll.InsertAtFront( "M" ); + ll.InsertAtFront( "N" ); + ll.InsertAtFront( "O" ); + + xbLinkListNode * llN = ll.GetHeadNode(); + xbString s; + xbString s2; + xbUInt32 ulCnt = ll.GetNodeCnt(); + for( xbUInt32 i = 0; i < ulCnt; i++ ){ + s2 = llN->GetKey(); + s.Sprintf( "%s%s", s.Str(), s2.Str() ); + llN = llN->GetNextNode(); + } + rc += TestMethod( po, "InsertAtFront()", s, "ONMLKJIHGFEDCBA", 15 ); + + s = ""; + llN = ll.GetHeadNode(); + while( llN ){ + s2 = llN->GetKey(); + s.Sprintf( "%s%s", s.Str(), s2.Str() ); + llN = llN->GetNextNode(); + } + rc += TestMethod( po, "GetNextNode()", s, "ONMLKJIHGFEDCBA", 15 ); + + s = ""; + llN = ll.GetEndNode(); + while( llN ){ + s2 = llN->GetKey(); + s.Sprintf( "%s%s", s.Str(), s2.Str() ); + llN = llN->GetPrevNode(); + } + rc += TestMethod( po, "GetNextNode()", s, "ABCDEFGHIJKLMNO", 15 ); + + + ll.RemoveFromFront( s ); + rc += TestMethod( po, "RemoveFromFront()", s, "O", 1 ); + + llN = ll.GetHeadNode(); + ulCnt = ll.GetNodeCnt(); + rc += TestMethod( po, "RemoveFromFront()", (xbInt32) ulCnt, 14 ); + + s = ""; + for( xbUInt32 i = 0; i < ulCnt; i++ ){ + s2 = llN->GetKey(); + s.Sprintf( "%s%s", s.Str(), s2.Str() ); + llN = llN->GetNextNode(); + } + rc += TestMethod( po, "RemoveFromFront()", s, "NMLKJIHGFEDCBA", 14 ); + + + s = ""; + llN = ll.GetHeadNode(); + while( llN ){ + s2 = llN->GetKey(); + s.Sprintf( "%s%s", s.Str(), s2.Str() ); + llN = llN->GetNextNode(); + } + rc += TestMethod( po, "RemoveFromFront()", s, "NMLKJIHGFEDCBA", 14 ); + + + s = ""; + llN = ll.GetEndNode(); + while( llN ){ + s2 = llN->GetKey(); + s.Sprintf( "%s%s", s.Str(), s2.Str() ); + llN = llN->GetPrevNode(); + } + rc += TestMethod( po, "RemoveFromFront()", s, "ABCDEFGHIJKLMN", 14 ); + + + ll.RemoveFromEnd( s ); + rc = TestMethod( po, "RemoveFromEnd()", s, "A", 1 ); + ulCnt = ll.GetNodeCnt(); + rc += TestMethod( po, "RemoveFromFront()", (xbInt32) ulCnt, 13 ); + + s = ""; + llN = ll.GetHeadNode(); + for( xbUInt32 i = 0; i < ulCnt; i++ ){ + s2 = llN->GetKey(); + s.Sprintf( "%s%s", s.Str(), s2.Str() ); + llN = llN->GetNextNode(); + } + rc += TestMethod( po, "RemoveFromEnd()", s, "NMLKJIHGFEDCB", 13 ); + + s = ""; + llN = ll.GetHeadNode(); + while( llN ){ + s2 = llN->GetKey(); + s.Sprintf( "%s%s", s.Str(), s2.Str() ); + llN = llN->GetNextNode(); + } + rc += TestMethod( po, "RemoveFromEnd()", s, "NMLKJIHGFEDCB", 13 ); + + s = ""; + llN = ll.GetEndNode(); + while( llN ){ + s2 = llN->GetKey(); + s.Sprintf( "%s%s", s.Str(), s2.Str() ); + llN = llN->GetPrevNode(); + } + rc += TestMethod( po, "RemoveFromEnd()", s, "BCDEFGHIJKLMN", 13 ); + + s = "J"; + ll.RemoveByVal( s ); + rc += TestMethod( po, "RemoveByVal()", s, "J", 1 ); + ulCnt = ll.GetNodeCnt(); + rc += TestMethod( po, "RemoveByVal()", (xbInt32) ulCnt, 12 ); + + llN = ll.GetHeadNode(); + s = ""; + for( xbUInt32 i = 0; i < ulCnt; i++ ){ + s2 = llN->GetKey(); + s.Sprintf( "%s%s", s.Str(), s2.Str() ); + llN = llN->GetNextNode(); + } + rc += TestMethod( po, "RemoveByVal()", s, "NMLKIHGFEDCB", 12 ); + + s = ""; + llN = ll.GetHeadNode(); + while( llN ){ + s2 = llN->GetKey(); + s.Sprintf( "%s%s", s.Str(), s2.Str() ); + llN = llN->GetNextNode(); + } + rc += TestMethod( po, "RemoveByVal()", s, "NMLKIHGFEDCB", 12 ); + + s = ""; + llN = ll.GetEndNode(); + while( llN ){ + s2 = llN->GetKey(); + s.Sprintf( "%s%s", s.Str(), s2.Str() ); + llN = llN->GetPrevNode(); + } + rc += TestMethod( po, "RemoveByVal()", s, "BCDEFGHIKLMN", 12 ); + + +// ordered link list + xbLinkListOrd llO; + llO.SetDupKeys( 0 ); + rc += TestMethod( po, "InsertKey( J )", llO.InsertKey( "J" ), 0); + rc += TestMethod( po, "InsertKey( J )", llO.InsertKey( "J" ), XB_KEY_NOT_UNIQUE ); + rc += TestMethod( po, "InsertKey( M )", llO.InsertKey( "M" ), 0 ); + rc += TestMethod( po, "InsertKey( C )", llO.InsertKey( "C" ), 0 ); + rc += TestMethod( po, "InsertKey( F )", llO.InsertKey( "F" ), 0 ); + rc += TestMethod( po, "InsertKey( Q )", llO.InsertKey( "Q" ), 0 ); + rc += TestMethod( po, "InsertKey( A )", llO.InsertKey( "A" ), 0 ); + rc += TestMethod( po, "InsertKey( T )", llO.InsertKey( "T" ), 0 ); + + if( po > 1 ){ + std::cout << "dumping node chain from beginning to end\n"; + llN = llO.GetHeadNode(); + while( llN ){ + s2 = llN->GetKey(); + std::cout << "node = " << s2.Str() << "\n"; + llN = llN->GetNextNode(); + } + + std::cout << "dumping node chain from beginning to beginning\n"; + llN = llO.GetHeadNode(); + while( llN ){ + s2 = llN->GetKey(); + std::cout << "node = " << s2.Str() << "\n"; + llN = llN->GetPrevNode(); + } + + std::cout << "dumping node chain from end to end\n"; + llN = llO.GetEndNode(); + while( llN ){ + s2 = llN->GetKey(); + std::cout << "node = " << s2.Str() << "\n"; + llN = llN->GetNextNode(); + } + + std::cout << "dumping node chain from end to beginning\n"; + llN = llO.GetEndNode(); + while( llN ){ + s2 = llN->GetKey(); + std::cout << "node = " << s2.Str() << "\n"; + llN = llN->GetPrevNode(); + } + + + } + + // xbLinkListNode * psN; + s2 = "A"; + rc += TestMethod( po, "KeyExists('A')", llO.KeyExists( s2 ), xbTrue ); + rc += TestMethod( po, "RemoveKey('A')", llO.RemoveKey( s2 ), 0 ); + s2 = "J"; + rc += TestMethod( po, "RemoveKey('J')", llO.RemoveKey( s2 ), 0 ); + s2 = "T"; + rc += TestMethod( po, "RemoveKey('T')", llO.RemoveKey( s2 ), 0 ); + rc += TestMethod( po, "RemoveKey('T')", llO.RemoveKey( s2 ), XB_NOT_FOUND ); + rc += TestMethod( po, "KeyExists('T')", llO.KeyExists( s2 ), xbFalse ); + + + + xbLinkListOrd lloi; + lloi.SetDupKeys( 0 ); + xbUInt32 i = 3; + + rc += TestMethod( po, "InsertKey( 1 )", lloi.InsertKey( i ), 0 ); + rc += TestMethod( po, "RemoveKey( 1 )", lloi.RemoveKey( i ), 0 ); + rc += TestMethod( po, "RemoveKey( 1 )", lloi.RemoveKey( i ), XB_NOT_FOUND ); + + + if( po > 1 ){ + std::cout << "dumping node chains after delete\n"; + std::cout << "dumping node chain from beginning to end\n"; + llN = llO.GetHeadNode(); + while( llN ){ + s2 = llN->GetKey(); + std::cout << "node = " << s2.Str() << "\n"; + llN = llN->GetNextNode(); + } + + std::cout << "dumping node chain from beginning to beginning\n"; + llN = llO.GetHeadNode(); + while( llN ){ + s2 = llN->GetKey(); + std::cout << "node = " << s2.Str() << "\n"; + llN = llN->GetPrevNode(); + } + + std::cout << "dumping node chain from end to end\n"; + llN = llO.GetEndNode(); + while( llN ){ + s2 = llN->GetKey(); + std::cout << "node = " << s2.Str() << "\n"; + llN = llN->GetNextNode(); + } + + std::cout << "dumping node chain from end to beginning\n"; + llN = llO.GetEndNode(); + while( llN ){ + s2 = llN->GetKey(); + std::cout << "node = " << s2.Str() << "\n"; + llN = llN->GetPrevNode(); + } + } + + + xbLinkListOrd ll32; + // xbLinkListNode * ll32n; + +// for( xbUInt32 ul = 0; ul < 1; ul++ ) +// ll32.InsertKey( ul, ul ); + + + xbUInt32 ul3 = 0; +// ll32.InsertKey( 1, "1" ); + + ll32.InsertKey( ul3, ul3 ); + + ll.Clear(); + + + +#ifdef XB_LOGGING_SUPPORT + sMsg.Sprintf( "Program [%s] terminating with [%d] errors...", av[0], rc * -1 ); + x.WriteLogMessage( sMsg ); +#endif + + if( po > 0 || rc < 0 ) + fprintf( stdout, "Total Errors = %d\n", rc * -1 ); + + return rc; +} + + + + diff --git a/src/tests/xb_test_lock.cpp b/src/tests/xb_test_lock.cpp new file mode 100755 index 0000000..7e9865b --- /dev/null +++ b/src/tests/xb_test_lock.cpp @@ -0,0 +1,874 @@ +/* xb_test_lock.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 program tests the locking functions of xbase +// usage: xb_test_lock QUITE|NORMAL|VERBOSE + + +#include "xbase.h" + +using namespace xb; + +#include "tstfuncs.cpp" + +/****************************************************************/ +int main( int argCnt, char **av ) +{ + xbInt16 iRc = 0; + xbInt16 iRc2; + xbInt16 po = 1; /* print option */ + /* 0 - QUIET */ + /* 1 - NORMAL */ + /* 2 - VERBOSE */ + + xbInt16 iErrorStop = 0; + xbString sLockFile = "locktest.txt"; + xbString sLockCmd; + xbString sResult; + + xbXBase x; + xbDbf * MyFile; + + if( argCnt > 1 ) { + if( av[1][0] == 'Q' ) + po = 0; + else if( av[1][0] == 'V' ) + po = 2; + } + + xbSchema MyRecord[] = + { + { "LOCKTEST", XB_CHAR_FLD, 5, 0 }, + #ifdef XB_MEMO_SUPPORT + { "MEMOTEST", XB_MEMO_FLD, 10, 0 }, + #endif + { "",0,0,0 } + }; + + + #ifdef XB_LOGGING_SUPPORT + x.EnableMsgLogging(); + if( po > 0 ){ + std::cout << "Logfile is [" << x.GetLogFqFileName().Str() << "]" << std::endl; + } + xbString sMsg; + sMsg.Sprintf( "Program [%s] initializing...", av[0] ); + x.WriteLogMessage( sMsg ); + #endif + + x.SetDataDirectory( PROJECT_DATA_DIR ); + x.EnableMsgLogging(); + InitTime(); + #ifdef XB_DBF4_SUPPORT + MyFile = new xbDbf4( &x ); /* version 4 dbf file */ + #else + MyFile = new xbDbf3( &x ); /* version 3 dbf file */ + #endif + + iRc2 = MyFile->CreateTable( "LockTest.DBF", "LockTest", MyRecord, XB_OVERLAY, XB_MULTI_USER ); + iRc += TestMethod( po, "CreateTable()", iRc2, XB_NO_ERROR ); + iRc += TestMethod( po, "PutField()", MyFile->PutField( "LOCKTEST", "TEST" ), XB_NO_ERROR ); + iRc += TestMethod( po, "AppendRecord()", MyFile->AppendRecord(), XB_NO_ERROR ); + iRc += TestMethod( po, "Close()", MyFile->Close(), XB_NO_ERROR ); + MyFile->Close(); + + //unlink( sLockFile ); + remove( sLockFile ); + + #if defined (HAVE_FORK_F) + pid_t pid; + if(( pid = fork()) < 0 ){ + std::cout << "fork error\n"; + exit(1); + } + + if( pid == 0 ){ + // child task + xbDbf * MyFileChld; + xbInt16 iRcChld = 0; + xbBool bTblOpenChld = xbFalse; + xbInt32 iChildLoop = 0; + xbBool bDone = xbFalse; + xbString sLastLockCmd; + + #ifdef XB_DBF4_SUPPORT + MyFileChld = new xbDbf4( &x ); /* version 4 dbf file */ + #else + MyFileChld = new xbDbf3( &x ); /* version 3 dbf file */ + #endif + + x.xbSleep( 250 ); + + while( !bDone ){ + iRc2 = GetCmd( x, sLockFile, sLockCmd, 'C', po ); + + if( sLockCmd == sLastLockCmd ) + iChildLoop++; + else + sLastLockCmd = sLockCmd; + + #ifdef XB_LOGGING_SUPPORT + if( sLockCmd != "OK" && sLockCmd != "FAIL" ){ + sMsg.Sprintf( "Program [%s] Child task retrieved command=[%s] RC=[%d]", av[0], sLockCmd.Str(), iRc2 ); + x.WriteLogMessage( sMsg ); + } + #endif + + if( iRc2 == 0 ){ + + if( sLockCmd == "OK" || sLockCmd == "FAIL" ) + x.xbSleep( 250 ); + + else if( sLockCmd == "EXIT" ){ + bDone = xbTrue; + + } else if( sLockCmd == "START" && bTblOpenChld ){ + // came back before the parent task could process the result + x.xbSleep( 250 ); + + } else { + + if( sLockCmd == "START" ){ + // begin the process + iRcChld = MyFileChld->Open( "LockTest.DBF" ); + if( iRcChld != XB_NO_ERROR ){ + sResult = "FAIL"; + } else { + sResult = "OK"; + bTblOpenChld = xbTrue; + } + + } else if( sLockCmd == "TL" ){ + // table lock + if(( iRcChld = MyFileChld->LockTable( XB_LOCK )) != XB_NO_ERROR ) + sResult = "FAIL"; + else + sResult = "OK"; + + } else if( sLockCmd == "TU" ){ + // table unlock + if(( iRcChld = MyFileChld->LockTable( XB_UNLOCK )) != XB_NO_ERROR ) + sResult = "FAIL"; + else + sResult = "OK"; + + } else if( sLockCmd == "RL" ){ + // record lock + if(( iRcChld = MyFileChld->LockRecord( XB_LOCK, 1 )) != XB_NO_ERROR ) + sResult = "FAIL"; + else + sResult = "OK"; + + } else if( sLockCmd == "RU" ){ + // record unlock + if(( iRcChld = MyFileChld->LockRecord( XB_UNLOCK, 1 )) != XB_NO_ERROR ) + sResult = "FAIL"; + else + sResult = "OK"; + + } else if( sLockCmd == "ML" ){ + // memo lock + #ifdef XB_MEMO_SUPPORT + if(( iRcChld = MyFileChld->LockMemo( XB_LOCK )) != XB_NO_ERROR ) + sResult = "FAIL"; + else + sResult = "OK"; + #else + sLockCmd = "OK"; + #endif + + } else if( sLockCmd == "MU" ){ + // memo unlock + #ifdef XB_MEMO_SUPPORT + if(( iRcChld = MyFileChld->LockMemo( XB_UNLOCK )) != XB_NO_ERROR ) + sResult = "FAIL"; + else + sResult = "OK"; + #else + sLockCmd = "OK"; + #endif + } + + #ifdef XB_LOGGING_SUPPORT + sMsg.Sprintf( "Program [%s] Child task [%s] Result [%s] RC = [%d]", av[0], sLockCmd.Str(), sResult.Str(), iRcChld ); + x.WriteLogMessage( sMsg ); + #endif + SetCmd( x, sLockFile, sResult, 'C', po ); + if( sResult == "FAIL" ){ + bDone = xbTrue; + MyFileChld->Close(); + delete MyFileChld; + } + } + + } else { + iRc = iRc2; + bDone = xbTrue; + } + //std::cout << "clc [" << iChildLoop++ << "][" << bDone << "][" << sLockCmd.Str() << "]\n"; + x.xbSleep( 250 ); + if( iChildLoop > 15 ) + bDone = xbTrue; + } + MyFileChld->Close(); + delete MyFile; + delete MyFileChld; + + if( po > 0 ) + std::cout << "Exiting child\n"; + + #ifdef XB_LOGGING_SUPPORT + sMsg.Sprintf( "Program [%s] Child task terminating", av[0] ); + x.WriteLogMessage( sMsg ); + #endif + + + } else { + + // parent logic + xbInt16 iLoopCtr = 0; + + try{ + + // start + + #ifdef XB_LOGGING_SUPPORT + sMsg.Sprintf( "Program [%s] Parent task issuing START command", av[0] ); + x.WriteLogMessage( sMsg ); + #endif + + sLockCmd = "START"; + SetCmd( x, sLockFile, sLockCmd, 'P', po ); + sResult = ""; + + GetCmd( x, sLockFile, sResult, 'P', po ); + while( sResult != "OK" && sResult != "FAIL" && iLoopCtr < 30 ){ + GetCmd( x, sLockFile, sResult, 'P', po ); + x.xbSleep( 250 ); + iLoopCtr++; + } + + #ifdef XB_LOGGING_SUPPORT + sMsg.Sprintf( "Program [%s] Parent task retrieved result [%s]", av[0], sResult.Str() ); + x.WriteLogMessage( sMsg ); + #endif + + if( sResult != "OK" ){ + iErrorStop = 10; + iRc2 = -1; + throw iRc2; + } + + // table lock + #ifdef XB_LOGGING_SUPPORT + sMsg.Sprintf( "Program [%s] Parent task issuing TL command", av[0] ); + x.WriteLogMessage( sMsg ); + #endif + + sLockCmd = "TL"; + SetCmd( x, sLockFile, sLockCmd, 'P', po ); + sResult = ""; + + GetCmd( x, sLockFile, sResult, 'P', po ); + while( sResult != "OK" && sResult != "FAIL" && iLoopCtr < 30 ){ + GetCmd( x, sLockFile, sResult, 'P', po ); + x.xbSleep( 250 ); + iLoopCtr++; + } + + #ifdef XB_LOGGING_SUPPORT + sMsg.Sprintf( "Program [%s] Parent task retrieved result [%s]", av[0], sResult.Str() ); + x.WriteLogMessage( sMsg ); + #endif + + if( sResult != "OK" ){ + iErrorStop = 20; + iRc2 = -1; + throw iRc2; + } + + // attempt to lock table, should fail + if(( iRc2 = MyFile->Open( "LockTest.DBF" )) != XB_NO_ERROR ){ + iErrorStop = 30; + throw iRc2; + } + + if(( iRc2 = MyFile->LockTable( XB_LOCK )) == XB_NO_ERROR ){ + iErrorStop = 40; + throw iRc2; + } + + if( po > 0 ) + std::cout << "[PASS] LockTable Test 1\n"; + + // table unlock + #ifdef XB_LOGGING_SUPPORT + sMsg.Sprintf( "Program [%s] Parent task issuing TU command", av[0] ); + x.WriteLogMessage( sMsg ); + #endif + + sLockCmd = "TU"; + SetCmd( x, sLockFile, sLockCmd, 'P', po ); + sResult = ""; + + GetCmd( x, sLockFile, sResult, 'P', po ); + while( sResult != "OK" && sResult != "FAIL" && iLoopCtr < 30 ){ + GetCmd( x, sLockFile, sResult, 'P', po ); + x.xbSleep( 250 ); + iLoopCtr++; + } + + #ifdef XB_LOGGING_SUPPORT + sMsg.Sprintf( "Program [%s] Parent task retrieved result [%s]", av[0], sResult.Str() ); + x.WriteLogMessage( sMsg ); + #endif + + if( sResult != "OK" ){ + iErrorStop = 50; + iRc2 = -1; + throw iRc2; + } + + if(( iRc2 = MyFile->LockTable( XB_LOCK )) != XB_NO_ERROR ){ + iErrorStop = 60; + throw iRc2; + } + if( po > 0 ) + std::cout << "[PASS] LockTable Test 2\n"; + + if(( iRc2 = MyFile->LockTable( XB_UNLOCK )) != XB_NO_ERROR ){ + iErrorStop = 70; + throw iRc2; + } + + /* record lock */ + #ifdef XB_LOGGING_SUPPORT + sMsg.Sprintf( "Program [%s] Parent task issuing RL command", av[0] ); + x.WriteLogMessage( sMsg ); + #endif + + sLockCmd = "RL"; + SetCmd( x, sLockFile, sLockCmd, 'P', po ); + sResult = ""; + + GetCmd( x, sLockFile, sResult, 'P', po ); + while( sResult != "OK" && sResult != "FAIL" && iLoopCtr < 30 ){ + GetCmd( x, sLockFile, sResult, 'P', po ); + x.xbSleep( 250 ); + iLoopCtr++; + } + + #ifdef XB_LOGGING_SUPPORT + sMsg.Sprintf( "Program [%s] Parent task retrieved result [%s]", av[0], sResult.Str() ); + x.WriteLogMessage( sMsg ); + #endif + + if( sResult != "OK" ){ + iErrorStop = 80; + iRc2 = -1; + throw iRc2; + } + + if(( iRc2 = MyFile->LockRecord( XB_LOCK, 1 )) == XB_NO_ERROR ){ + iErrorStop = 90; + throw iRc2; + } + if( po > 0 ) + std::cout << "[PASS] LockRecord Test 1\n"; + + /* record unlock */ + #ifdef XB_LOGGING_SUPPORT + sMsg.Sprintf( "Program [%s] Parent task issuing RU command", av[0] ); + x.WriteLogMessage( sMsg ); + #endif + + sLockCmd = "RU"; + SetCmd( x, sLockFile, sLockCmd, 'P', po ); + sResult = ""; + + GetCmd( x, sLockFile, sResult, 'P', po ); + while( sResult != "OK" && sResult != "FAIL" && iLoopCtr < 30 ){ + GetCmd( x, sLockFile, sResult, 'P', po ); + x.xbSleep( 250 ); + iLoopCtr++; + } + + #ifdef XB_LOGGING_SUPPORT + sMsg.Sprintf( "Program [%s] Parent task retrieved result [%s]", av[0], sResult.Str() ); + x.WriteLogMessage( sMsg ); + #endif + + if( sResult != "OK" ){ + iErrorStop = 100; + iRc2 = -1; + throw iRc2; + } + + if(( iRc2 = MyFile->LockRecord( XB_LOCK, 1 )) != XB_NO_ERROR ){ + iErrorStop = 110; + throw iRc2; + } + + std::cout << "[PASS] LockRecord Test 2\n"; + if(( iRc2 = MyFile->LockRecord( XB_UNLOCK, 1 )) != XB_NO_ERROR ){ + iErrorStop = 120; + throw iRc2; + } + + + /* memo lock */ + #ifdef XB_MEMO_SUPPORT + #ifdef XB_LOGGING_SUPPORT + sMsg.Sprintf( "Program [%s] Parent task issuing ML command", av[0] ); + x.WriteLogMessage( sMsg ); + #endif + + sLockCmd = "ML"; + SetCmd( x, sLockFile, sLockCmd, 'P', po ); + sResult = ""; + + GetCmd( x, sLockFile, sResult, 'P', po ); + while( sResult != "OK" && sResult != "FAIL" && iLoopCtr < 30 ){ + GetCmd( x, sLockFile, sResult, 'P', po ); + x.xbSleep( 250 ); + iLoopCtr++; + } + + #ifdef XB_LOGGING_SUPPORT + sMsg.Sprintf( "Program [%s] Parent task retrieved result [%s]", av[0], sResult.Str() ); + x.WriteLogMessage( sMsg ); + #endif + + if( sResult != "OK" ){ + iErrorStop = 130; + iRc2 = -1; + throw iRc2; + } + + if(( iRc2 = MyFile->LockMemo( XB_LOCK )) == XB_NO_ERROR ){ + iErrorStop = 140; + throw iRc2; + } + if( po > 0 ) + std::cout << "[PASS] LockMemo Test 1\n"; + + /* memo unlock */ + #ifdef XB_LOGGING_SUPPORT + sMsg.Sprintf( "Program [%s] Parent task issuing MU command", av[0] ); + x.WriteLogMessage( sMsg ); + #endif + + sLockCmd = "MU"; + SetCmd( x, sLockFile, sLockCmd, 'P', po ); + sResult = ""; + + GetCmd( x, sLockFile, sResult, 'P', po ); + while( sResult != "OK" && sResult != "FAIL" && iLoopCtr < 30 ){ + GetCmd( x, sLockFile, sResult, 'P', po ); + x.xbSleep( 250 ); + iLoopCtr++; + } + + #ifdef XB_LOGGING_SUPPORT + sMsg.Sprintf( "Program [%s] Parent task retrieved result [%s]", av[0], sResult.Str() ); + x.WriteLogMessage( sMsg ); + #endif + + if( sResult != "OK" ){ + iErrorStop = 150; + iRc2 = -1; + throw iRc2; + } + + if(( iRc2 = MyFile->LockMemo( XB_LOCK )) != XB_NO_ERROR ){ + iErrorStop = 160; + throw iRc2; + } + + std::cout << "[PASS] LockMemo Test 2\n"; + if(( iRc2 = MyFile->LockMemo( XB_UNLOCK )) != XB_NO_ERROR ){ + iErrorStop = 170; + throw iRc2; + } + #endif + + // exit + sLockCmd = "EXIT"; + SetCmd( x, sLockFile, sLockCmd, 'P', po ); + + } catch (xbInt16 iRc3 ){ + iRc = iRc3; + if( po > 0 ) + std::cout << "Parent lock task exiting on failure [" << sLockCmd.Str() << "][" << iErrorStop << "]\n"; + + + #ifdef XB_LOGGING_SUPPORT + sMsg.Sprintf( "Program [%s] Parent task terminating with errors [%s][%d][%d][%d]...", av[0], sLockCmd.Str(), iErrorStop, iLoopCtr, iRc3 ); + x.WriteLogMessage( sMsg ); + #endif + + } + + #ifdef XB_LOGGING_SUPPORT + sMsg.Sprintf( "Program [%s] Parent task terminating", av[0] ); + x.WriteLogMessage( sMsg ); + #endif + + if( po > 0 ) + std::cout << "Exiting parent\n"; + + sLockCmd = "EXIT"; + SetCmd( x, sLockFile, sLockCmd, 'P', po ); + MyFile->Close(); + delete MyFile; + } + #elif defined (HAVE_CREATEPROCESSW_F) + + STARTUPINFO si; + PROCESS_INFORMATION pi; + ZeroMemory( &si, sizeof( si )); + si.cb = sizeof( si ); + ZeroMemory( &pi, sizeof( pi )); + + xbString strCmdLine = "xb_test_lock2"; + if( argCnt > 1 ){ + strCmdLine += " "; + strCmdLine += av[1]; + } + + char sCmdLineBuf[25]; + memset( sCmdLineBuf, 0x00, 25 ); + for( xbUInt32 i = 0; i < strCmdLine.Len(); i++ ) + sCmdLineBuf[i] = strCmdLine[i+1]; + + if( !CreateProcess( NULL, sCmdLineBuf, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi )){ + sMsg.Sprintf( "Program [%s] error in CreateProcess call. Processing aborted" ); + #ifdef XB_LOGGING_SUPPORT + x.WriteLogMessage( sMsg ); + #endif + std::cout << sMsg.Str() << "\n"; + iRc -=1; + } else { + + xbInt16 iLoopCtr = 0; + + try{ + #ifdef XB_LOGGING_SUPPORT + sMsg.Sprintf( "Program [%s] Parent task issuing START command", av[0] ); + x.WriteLogMessage( sMsg ); + #endif + + + sLockCmd = "START"; + SetCmd( x, sLockFile, sLockCmd, 'P', po ); + sResult = ""; + + GetCmd( x, sLockFile, sResult, 'P', po ); + while( sResult != "OK" && sResult != "FAIL" && iLoopCtr < 10 ){ + GetCmd( x, sLockFile, sResult, 'P', po ); + x.xbSleep( 250 ); + iLoopCtr++; + } + + #ifdef XB_LOGGING_SUPPORT + sMsg.Sprintf( "Program [%s] Parent task retrieved result [%s]", av[0], sResult.Str() ); + x.WriteLogMessage( sMsg ); + #endif + + // table lock + #ifdef XB_LOGGING_SUPPORT + sMsg.Sprintf( "Program [%s] Parent task issuing TL command", av[0] ); + x.WriteLogMessage( sMsg ); + #endif + + sLockCmd = "TL"; + SetCmd( x, sLockFile, sLockCmd, 'P', po ); + sResult = ""; + + GetCmd( x, sLockFile, sResult, 'P', po ); + while( sResult != "OK" && sResult != "FAIL" && iLoopCtr < 10 ){ + GetCmd( x, sLockFile, sResult, 'P', po ); + x.xbSleep( 250 ); + iLoopCtr++; + } + + #ifdef XB_LOGGING_SUPPORT + sMsg.Sprintf( "Program [%s] Parent task retrieved result [%s]", av[0], sResult.Str() ); + x.WriteLogMessage( sMsg ); + #endif + + if( sResult != "OK" ){ + iErrorStop = 200; + iRc2 = -1; + throw iRc2; + } + + // attempt to lock table, should fail + if(( iRc2 = MyFile->Open( "LockTest.DBF" )) != XB_NO_ERROR ){ + iErrorStop = 210; + throw iRc2; + } + + if(( iRc2 = MyFile->LockTable( XB_LOCK )) == XB_NO_ERROR ){ + iErrorStop = 220; + throw iRc2; + } + + if( po > 0 ){ + std::cout << "[PASS] LockTable Test 1\n"; + + #ifdef XB_LOGGING_SUPPORT + sMsg.Sprintf( "Program [%s] Parent task LockTable Test 1 Success.", av[0] ); + x.WriteLogMessage( sMsg ); + #endif + } + + // table unlock + #ifdef XB_LOGGING_SUPPORT + sMsg.Sprintf( "Program [%s] Parent task issuing TU command", av[0] ); + x.WriteLogMessage( sMsg ); + #endif + + sLockCmd = "TU"; + SetCmd( x, sLockFile, sLockCmd, 'P', po ); + sResult = ""; + + GetCmd( x, sLockFile, sResult, 'P', po ); + while( sResult != "OK" && sResult != "FAIL" && iLoopCtr < 10 ){ + GetCmd( x, sLockFile, sResult, 'P', po ); + x.xbSleep( 250 ); + iLoopCtr++; + } + + #ifdef XB_LOGGING_SUPPORT + sMsg.Sprintf( "Program [%s] Parent task retrieved result [%s]", av[0], sResult.Str() ); + x.WriteLogMessage( sMsg ); + #endif + + if( sResult != "OK" ){ + iErrorStop = 230; + iRc2 = -1; + throw iRc2; + } + + if(( iRc2 = MyFile->LockTable( XB_LOCK )) != XB_NO_ERROR ){ + iErrorStop = 240; + throw iRc2; + } + if( po > 0 ){ + std::cout << "[PASS] LockTable Test 2\n"; + #ifdef XB_LOGGING_SUPPORT + sMsg.Sprintf( "Program [%s] Parent task LockTable Test 1 Success.", av[0] ); + x.WriteLogMessage( sMsg ); + #endif + } + + if(( iRc2 = MyFile->LockTable( XB_UNLOCK )) != XB_NO_ERROR ){ + iErrorStop = 250; + throw iRc2; + } + + /* record lock */ + #ifdef XB_LOGGING_SUPPORT + sMsg.Sprintf( "Program [%s] Parent task issuing RL command", av[0] ); + x.WriteLogMessage( sMsg ); + #endif + + sLockCmd = "RL"; + SetCmd( x, sLockFile, sLockCmd, 'P', po ); + sResult = ""; + + GetCmd( x, sLockFile, sResult, 'P', po ); + while( sResult != "OK" && sResult != "FAIL" && iLoopCtr < 10 ){ + GetCmd( x, sLockFile, sResult, 'P', po ); + x.xbSleep( 250 ); + iLoopCtr++; + } + + #ifdef XB_LOGGING_SUPPORT + sMsg.Sprintf( "Program [%s] Parent task retrieved result [%s]", av[0], sResult.Str() ); + x.WriteLogMessage( sMsg ); + #endif + + if( sResult != "OK" ){ + iErrorStop = 260; + iRc2 = -1; + throw iRc2; + } + + if(( iRc2 = MyFile->LockRecord( XB_LOCK, 1 )) == XB_NO_ERROR ){ + iErrorStop = 270; + throw iRc2; + } + if( po > 0 ) + std::cout << "[PASS] LockRecord Test 1\n"; + + /* record unlock */ + #ifdef XB_LOGGING_SUPPORT + sMsg.Sprintf( "Program [%s] Parent task issuing RU command", av[0] ); + x.WriteLogMessage( sMsg ); + #endif + + sLockCmd = "RU"; + SetCmd( x, sLockFile, sLockCmd, 'P', po ); + sResult = ""; + + GetCmd( x, sLockFile, sResult, 'P', po ); + while( sResult != "OK" && sResult != "FAIL" && iLoopCtr < 10 ){ + GetCmd( x, sLockFile, sResult, 'P', po ); + x.xbSleep( 250 ); + iLoopCtr++; + } + + #ifdef XB_LOGGING_SUPPORT + sMsg.Sprintf( "Program [%s] Parent task retrieved result [%s]", av[0], sResult.Str() ); + x.WriteLogMessage( sMsg ); + #endif + + if( sResult != "OK" ){ + iErrorStop = 280; + iRc2 = -1; + throw iRc2; + } + + if(( iRc2 = MyFile->LockRecord( XB_LOCK, 1 )) != XB_NO_ERROR ){ + iErrorStop = 290; + throw iRc2; + } + + std::cout << "[PASS] LockRecord Test 2\n"; + if(( iRc2 = MyFile->LockRecord( XB_UNLOCK, 1 )) != XB_NO_ERROR ){ + iErrorStop = 300; + throw iRc2; + } + + + /* memo lock */ + #ifdef XB_MEMO_SUPPORT + #ifdef XB_LOGGING_SUPPORT + sMsg.Sprintf( "Program [%s] Parent task issuing ML command", av[0] ); + x.WriteLogMessage( sMsg ); + #endif + + sLockCmd = "ML"; + SetCmd( x, sLockFile, sLockCmd, 'P', po ); + sResult = ""; + + GetCmd( x, sLockFile, sResult, 'P', po ); + while( sResult != "OK" && sResult != "FAIL" && iLoopCtr < 10 ){ + GetCmd( x, sLockFile, sResult, 'P', po ); + x.xbSleep( 250 ); + iLoopCtr++; + } + + #ifdef XB_LOGGING_SUPPORT + sMsg.Sprintf( "Program [%s] Parent task retrieved result [%s]", av[0], sResult.Str() ); + x.WriteLogMessage( sMsg ); + #endif + + if( sResult != "OK" ){ + iErrorStop = 310; + iRc2 = -1; + throw iRc2; + } + + if(( iRc2 = MyFile->LockMemo( XB_LOCK )) == XB_NO_ERROR ){ + iErrorStop = 320; + throw iRc2; + } + if( po > 0 ) + std::cout << "[PASS] LockMemo Test 1\n"; + + /* memo unlock */ + #ifdef XB_LOGGING_SUPPORT + sMsg.Sprintf( "Program [%s] Parent task issuing MU command", av[0] ); + x.WriteLogMessage( sMsg ); + #endif + + sLockCmd = "MU"; + SetCmd( x, sLockFile, sLockCmd, 'P', po ); + sResult = ""; + + GetCmd( x, sLockFile, sResult, 'P', po ); + while( sResult != "OK" && sResult != "FAIL" && iLoopCtr < 10 ){ + GetCmd( x, sLockFile, sResult, 'P', po ); + x.xbSleep( 250 ); + iLoopCtr++; + } + + #ifdef XB_LOGGING_SUPPORT + sMsg.Sprintf( "Program [%s] Parent task retrieved result [%s]", av[0], sResult.Str() ); + x.WriteLogMessage( sMsg ); + #endif + + if( sResult != "OK" ){ + iErrorStop = 330; + iRc2 = -1; + throw iRc2; + } + + if(( iRc2 = MyFile->LockMemo( XB_LOCK )) != XB_NO_ERROR ){ + iErrorStop = 340; + throw iRc2; + } + + std::cout << "[PASS] LockMemo Test 2\n"; + if(( iRc2 = MyFile->LockMemo( XB_UNLOCK )) != XB_NO_ERROR ){ + iErrorStop = 350; + throw iRc2; + } + #endif + + // exit + sLockCmd = "EXIT"; + SetCmd( x, sLockFile, sLockCmd, 'P', po ); + + } catch (xbInt16 iRc3 ){ + if( po > 0 ) + std::cout << "Parent lock task exiting on failure [" << sLockCmd.Str() << "][" << iErrorStop << "]\n"; + + #ifdef XB_LOGGING_SUPPORT + sMsg.Sprintf( "Program [%s] Parent task terminating with errors [%s][%d][%d][%d]...", av[0], sLockCmd.Str(), iErrorStop, iLoopCtr, iRc3 ); + x.WriteLogMessage( sMsg ); + #endif + + sLockCmd = "EXIT"; + SetCmd( x, sLockFile, sLockCmd, 'P', po ); + MyFile->Close(); + delete MyFile; + } + } + + #else + iRc--; + sMsg.Sprintf( "Program [%s] not executed. Library does not support 'fork' or 'CreateProcess' function call", av[0] ); + #ifdef XB_LOGGING_SUPPORT + x.WriteLogMessage( sMsg ); + #endif + if( po > 0 ) + std::cout << sMsg.Str() << "\n"; + #endif + + if( po > 0 || iRc < 0 ) + fprintf( stdout, "[%s] Total Errors = %d\n", av[0], iRc * -1 ); + + #ifdef XB_LOGGING_SUPPORT + sMsg.Sprintf( "Program [%s] terminating with [%d] errors...", av[0], iRc * -1 ); + x.WriteLogMessage( sMsg ); + #endif + + return iRc; +} + diff --git a/src/tests/xb_test_lock2.cpp b/src/tests/xb_test_lock2.cpp new file mode 100755 index 0000000..3dd4b57 --- /dev/null +++ b/src/tests/xb_test_lock2.cpp @@ -0,0 +1,208 @@ +/* xb_test_lock2.cpp + +XBase 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 program tests the locking functions +// This program is the child process that is called if used +// in an environment that supports the CreateProcess library function (ie Windows) + +// usage: xb_test_lock QUITE|NORMAL|VERBOSE + + + +#include "xbase.h" + +using namespace xb; + +#include "tstfuncs.cpp" + +/****************************************************************/ +int main( int argCnt, char **av ) +{ + xbInt16 iRc = 0; + xbInt16 iRc2; + xbInt16 po = 1; /* print option */ + /* 0 - QUIET */ + /* 1 - NORMAL */ + /* 2 - VERBOSE */ + + xbBool bDone = xbFalse; +// xbInt16 iErrorStop = 0; + xbString sLockFile = "locktest.txt"; + xbString sLockCmd; + xbString sResult; + xbInt32 iChildLoop = 0; + + xbXBase x; + xbDbf * MyFile; + + xbBool bTblOpen = xbFalse; + + if( argCnt > 1 ) { + if( av[1][0] == 'Q' ) + po = 0; + else if( av[1][0] == 'V' ) + po = 2; + } + + #ifdef XB_LOGGING_SUPPORT + xbString sLogFileName = x.GetLogFqFileName().Str(); + + sLogFileName.Resize( sLogFileName.Len() - 3 ); + sLogFileName += "_l2.txt"; + x.SetLogFileName( sLogFileName ); + x.EnableMsgLogging(); + if( po > 0 ){ + std::cout << "Logfile is [" << x.GetLogFqFileName().Str() << "]" << std::endl; + } + xbString sMsg; + sMsg.Sprintf( "Program [%s] initializing...", av[0] ); + x.WriteLogMessage( sMsg ); + #endif + + x.SetDataDirectory( PROJECT_DATA_DIR ); + x.EnableMsgLogging(); + InitTime(); + + #ifdef XB_DBF4_SUPPORT + MyFile = new xbDbf4( &x ); /* version 4 dbf file */ + #else + MyFile = new xbDbf3( &x ); /* version 3 dbf file */ + #endif + + x.xbSleep( 250 ); + while( !bDone ){ + iRc2 = GetCmd( x, sLockFile, sLockCmd, 'C', po ); + + #ifdef XB_LOGGING_SUPPORT + if( sLockCmd != "OK" && sLockCmd != "FAIL" ){ + sMsg.Sprintf( "Program [%s] Child task retrieved command=[%s] RC=[%d]", av[0], sLockCmd.Str(), iRc2 ); + x.WriteLogMessage( sMsg ); + } + #endif + + if( iRc2 == 0 ){ + + if( sLockCmd == "OK" || sLockCmd == "FAIL" ) + x.xbSleep( 250 ); + + else if( sLockCmd == "EXIT" ){ + bDone = xbTrue; + + } else if( sLockCmd == "START" && bTblOpen ){ + // came back before the parent task could process the result + x.xbSleep( 250 ); + + } else { + + if( sLockCmd == "START" ){ + + // begin the process + iRc2 = MyFile->Open( "LockTest.DBF" ); + if( iRc2 != XB_NO_ERROR ){ + sResult = "FAIL"; + } else { + sResult = "OK"; + bTblOpen = xbTrue; + } + + } else if( sLockCmd == "TL" ){ + // table lock + if(( iRc2 = MyFile->LockTable( XB_LOCK )) != XB_NO_ERROR ) + sResult = "FAIL"; + else + sResult = "OK"; + + } else if( sLockCmd == "TU" ){ + // table unlock + if(( iRc2 = MyFile->LockTable( XB_UNLOCK )) != XB_NO_ERROR ) + sResult = "FAIL"; + else + sResult = "OK"; + + } else if( sLockCmd == "RL" ){ + // record lock + if(( iRc2 = MyFile->LockRecord( XB_LOCK, 1 )) != XB_NO_ERROR ) + sResult = "FAIL"; + else + sResult = "OK"; + + + } else if( sLockCmd == "RU" ){ + // record unlock + if(( iRc2 = MyFile->LockRecord( XB_UNLOCK, 1 )) != XB_NO_ERROR ) + sResult = "FAIL"; + else + sResult = "OK"; + + } else if( sLockCmd == "ML" ){ + // memo lock + #ifdef XB_MEMO_SUPPORT + if(( iRc2 = MyFile->LockMemo( XB_LOCK )) != XB_NO_ERROR ) + sResult = "FAIL"; + else + sResult = "OK"; + #else + sLockCmd = "OK"; + #endif + + } else if( sLockCmd == "MU" ){ + // memo unlock + #ifdef XB_MEMO_SUPPORT + if(( iRc2 = MyFile->LockMemo( XB_UNLOCK )) != XB_NO_ERROR ) + sResult = "FAIL"; + else + sResult = "OK"; + #else + sLockCmd = "OK"; + #endif + } + #ifdef XB_LOGGING_SUPPORT + sMsg.Sprintf( "Program [%s] Child task [%s] Result [%s] RC = [%d]", av[0], sLockCmd.Str(), sResult.Str(), iRc2 ); + x.WriteLogMessage( sMsg ); + #endif + SetCmd( x, sLockFile, sResult, 'C', po ); + } + } else { + iRc = iRc2; + bDone = xbTrue; + } + + // std::cout << "clc [" << iChildLoop++ << "][" << bDone << "][" << sLockCmd << "]\n"; + x.xbSleep( 250 ); + if( iChildLoop > 10 ) + bDone = xbTrue; + } + MyFile->Close(); + delete MyFile; + if( po > 0 ) + std::cout << "Exiting child\n"; + + remove( sLockFile ); + #ifdef XB_LOGGING_SUPPORT + sMsg.Sprintf( "Program [%s] Child task terminating", av[0] ); + x.WriteLogMessage( sMsg ); + #endif + + if( po > 0 || iRc < 0 ) + fprintf( stdout, "Total Errors = %d\n", iRc * -1 ); + + #ifdef XB_LOGGING_SUPPORT + sMsg.Sprintf( "Program [%s] terminating with [%d] errors...", av[0], iRc * -1 ); + x.WriteLogMessage( sMsg ); + #endif + + ExitProcess( iRc ); +} + diff --git a/src/tests/xb_test_log.cpp b/src/tests/xb_test_log.cpp new file mode 100755 index 0000000..613b7dd --- /dev/null +++ b/src/tests/xb_test_log.cpp @@ -0,0 +1,88 @@ +/* xb_test_log.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 program tests the class xbLog + +// usage: xb_test_log QUITE|NORMAL|VERBOSE + + +#include "xbase.h" + +using namespace xb; + +#include "tstfuncs.cpp" + + + +int main( int argCnt, char **av ) +{ + int rc = 0; + +#ifdef XB_LOGGING_SUPPORT + + + int po = 1; /* print option */ + /* 0 - QUIET */ + /* 1 - NORMAL */ + /* 2 - VERBOSE */ + + if( argCnt > 1 ) { + if( av[1][0] == 'Q' ) + po = 0; + else if( av[1][0] == 'V' ) + po = 2; + } + + xbXBase x; + x.EnableMsgLogging(); + InitTime(); + if( po ){ + std::cout << "Logfile is [" << x.GetLogFqFileName().Str() << "]" << std::endl; + } + xbString sMsg; + sMsg.Sprintf( "Program [%s] initializing...", av[0] ); + x.WriteLogMessage( sMsg ); + + + xbString sNewLogFileName = "Logfile2.txt"; + sMsg.Sprintf( "Switching to logfile [%s]", sNewLogFileName.Str() ); + x.WriteLogMessage( sMsg ); + + + x.DisableMsgLogging(); + rc += TestMethod( po, "Set/Get Log Status()", x.GetLogStatus(), xbFalse ); + x.SetLogFileName( sNewLogFileName ); + x.EnableMsgLogging(); + + rc += TestMethod( po, "Set/Get Log Status()", x.GetLogStatus(), 1 ); + rc += TestMethod( po,"WriteLogMessage()", x.WriteLogMessage( "Test log message........" ), XB_NO_ERROR ); + + + sMsg.Sprintf( "Program [%s] terminating with [%d] errors...", av[0], rc * -1 ); + x.WriteLogMessage( sMsg ); + + if( po > 0 || rc < 0 ) + fprintf( stdout, "Total Errors = %d\n", rc * -1 ); + + + #endif /* XB_LOGGING_SUPPORT */ + + + return rc; +} + + + + diff --git a/src/tests/xb_test_mdx.cpp b/src/tests/xb_test_mdx.cpp new file mode 100755 index 0000000..9351e4d --- /dev/null +++ b/src/tests/xb_test_mdx.cpp @@ -0,0 +1,243 @@ +/* xb_test_ndx.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 program tests the class xbIxNdx + +// usage: xb_test_ndx QUITE|NORMAL|VERBOSE + + +#include "xbase.h" + +using namespace xb; + +#include "tstfuncs.cpp" + +int main( int argCnt, char **av ) +{ + int iRc = 0; + int iRc2; + int iPo = 1; /* print option */ + /* 0 - QUIET */ + /* 1 - NORMAL */ + /* 2 - VERBOSE */ + + xbString sMsg; + char c; + xbString s; + + if( argCnt > 1 ) { + if( av[1][0] == 'Q' ) + iPo = 0; + else if( av[1][0] == 'V' ) + iPo = 2; + } + + + xbSchema MyV4Record[] = + { + { "CITY", XB_CHAR_FLD, 100, 0 }, + { "STATE", XB_CHAR_FLD, 2, 0 }, + { "ZIP", XB_NUMERIC_FLD, 9, 0 }, + { "DATE1", XB_DATE_FLD, 8, 0 }, + { "",0,0,0 } + }; + + + xbXBase x; + +#ifdef XB_LOGGING_SUPPORT + x.EnableMsgLogging(); + if( iPo ){ + std::cout << "Logfile is [" << x.GetLogFqFileName().Str() << "]" << std::endl; + } + sMsg.Sprintf( "Program [%s] initializing...", av[0] ); + x.WriteLogMessage( sMsg ); +#endif + + x.SetDataDirectory( PROJECT_DATA_DIR ); + x.EnableMsgLogging(); + x.SetMultiUser( false ); + InitTime(); + + if( iPo > 0 ) + std::cout << "Default Data Directory is [" << x.GetDataDirectory().Str() << "]" << std::endl; + + + xbFile f( &x ); + xbIx *pIx; + void *pTag; + xbDate dt = "19890209"; + xbString sKey; + + xbDbf *V4DbfX1 = new xbDbf4( &x ); + + + iRc2 = V4DbfX1->CreateTable( "TMDXDB01.DBF", "TestMdxX2", MyV4Record, XB_OVERLAY, XB_MULTI_USER ); + iRc += TestMethod( iPo, "CreateTable(1)", iRc2, 0 ); + + /* + CreateTag( const xbString &sIxType, const xbString &sName, const xbString &sKey, const xbString &sFilter, + xbInt16 iDescending, xbInt16 iUnique, xbInt16 iOverLay, xbIx **xbIxOut, void **vpTagOut ); + */ + + iRc2 = V4DbfX1->CreateTag( "MDX", "CITY_TAGA", "CITY", "", 0, 0, XB_OVERLAY, &pIx, &pTag ); + iRc += TestMethod( iPo, "CreateTag(1)", iRc2, 0 ); + + iRc2 = V4DbfX1->CreateTag( "MDX", "ZIP_TAG", "ZIP", "", xbTrue, 0, XB_OVERLAY, &pIx, &pTag ); + iRc += TestMethod( iPo, "CreateTag(2)", iRc2, 0 ); + + iRc2 = V4DbfX1->CreateTag( "MDX", "DATE_TAG", "DATE1", "", 0, xbTrue, XB_OVERLAY, &pIx, &pTag ); + iRc += TestMethod( iPo, "CreateTag(3)", iRc2, 0 ); + + + // std::cout << "Cur Tag Name = " << V4DbfX1->GetCurTagName().Str() << "\n"; + + // xbDouble d = 4000; +// iRc2 = V4DbfX1->Find( d ); +// std::cout << iRc2 << "\n"; + + +// something in th following block of code causing issues + + xbInt32 uZip = 10000; + for( xbUInt16 i = 0; i < 35; i++ ){ + for( xbUInt16 j = 0; j < 14; j++ ){ + + c = j + 65; + s = c; + s.PadRight( c, (xbUInt32) i + 1 ); + //std::cout << "*********** adding s=[" << s.Str() << "] length = " << s.Len() << "\n"; + iRc2 = V4DbfX1->BlankRecord(); + if( iRc2 != XB_NO_ERROR ) + iRc += TestMethod( iPo, "BlankRecord()", iRc2, XB_NO_ERROR ); + + iRc2 = V4DbfX1->PutField( "CITY", s ); + if( iRc2 != XB_NO_ERROR ) + iRc += TestMethod( iPo, "PutField()", iRc2, XB_NO_ERROR ); + + iRc2 = V4DbfX1->PutLongField( "ZIP", uZip++ ); + if( iRc2 != XB_NO_ERROR ) + iRc += TestMethod( iPo, "PutField()", iRc2, XB_NO_ERROR ); + + iRc2 = V4DbfX1->PutDateField( "DATE1", dt ); + dt++; + if( iRc2 != XB_NO_ERROR ) + iRc += TestMethod( iPo, "PutField()", iRc2, XB_NO_ERROR ); + + iRc2 = V4DbfX1->AppendRecord(); + if( iRc2 != XB_NO_ERROR ) + iRc += TestMethod( iPo, "AppendRecord()", iRc2, XB_NO_ERROR ); + + iRc2 = V4DbfX1->Commit(); + if( iRc2 != XB_NO_ERROR ) + iRc += TestMethod( iPo, "Commit()", iRc2, XB_NO_ERROR ); + } + } + + + iRc += TestMethod( iPo, "CheckTagIntegrity()", V4DbfX1->CheckTagIntegrity( 1, 2 ), XB_NO_ERROR ); + + // attempt to add a dup key, should fail with XB_KEY_NOT_UNIQUE + iRc2 = V4DbfX1->BlankRecord(); + if( iRc2 != XB_NO_ERROR ) + iRc += TestMethod( iPo, "BlankRecord()", iRc2, XB_NO_ERROR ); + + iRc2 = V4DbfX1->PutField( "CITY", "Tampa" ); + if( iRc2 != XB_NO_ERROR ) + iRc += TestMethod( iPo, "PutField()", iRc2, XB_NO_ERROR ); + + iRc2 = V4DbfX1->PutLongField( "ZIP", uZip++ ); + if( iRc2 != XB_NO_ERROR ) + iRc += TestMethod( iPo, "PutField()", iRc2, XB_NO_ERROR ); + + dt.Set( "19890209" ); + iRc2 = V4DbfX1->PutDateField( "DATE1", dt ); + dt++; + if( iRc2 != XB_NO_ERROR ) + iRc += TestMethod( iPo, "PutField()", iRc2, XB_NO_ERROR ); + + iRc2 = V4DbfX1->AppendRecord(); + if( iRc2 != XB_KEY_NOT_UNIQUE ) + iRc += TestMethod( iPo, "AppendRecord()", iRc2, XB_NO_ERROR ); + + iRc2 = V4DbfX1->Abort(); + if( iRc2 != XB_NO_ERROR ) + iRc += TestMethod( iPo, "Abort()", iRc2, XB_NO_ERROR ); + + iRc += TestMethod( iPo, "DeleteTag()", V4DbfX1->DeleteTag( "MDX", "CITY_TAGA" ), XB_NO_ERROR ); + iRc += TestMethod( iPo, "CheckTagIntegrity()", V4DbfX1->CheckTagIntegrity( 1, 2 ), XB_NO_ERROR ); + + iRc2 = V4DbfX1->CreateTag( "MDX", "CITY_TAGF", "CITY", ".NOT. DELETED()", 0, 0, XB_OVERLAY, &pIx, &pTag ); + iRc += TestMethod( iPo, "CreateTag(4)", iRc2, 0 ); + + iRc2 = V4DbfX1->SetCurTag( "CITY_TAGF" ); + iRc += TestMethod( iPo, "SetCurTag()", iRc2, XB_NO_ERROR ); + iRc += TestMethod( iPo, "GetCurTagName()", V4DbfX1->GetCurTagName().Str(), "CITY_TAGF", 9 ); + + iRc += TestMethod( iPo, "CheckTagIntegrity()", V4DbfX1->CheckTagIntegrity( 1, 2 ), XB_INVALID_INDEX ); + + iRc2 = V4DbfX1->Reindex( 0 ); + iRc += TestMethod( iPo, "Reindex( 0 )", iRc2, XB_NO_ERROR ); + iRc += TestMethod( iPo, "CheckTagIntegrity()", V4DbfX1->CheckTagIntegrity( 1, 2 ), XB_NO_ERROR ); + + iRc += TestMethod( iPo, "GetCurTagName()", V4DbfX1->GetCurTagName().Str(), "CITY_TAGF", 9 ); + + iRc2 = V4DbfX1->Reindex( 1 ); + iRc += TestMethod( iPo, "Reindex( 1 )", iRc2, XB_NO_ERROR ); + iRc += TestMethod( iPo, "CheckTagIntegrity()", V4DbfX1->CheckTagIntegrity( 1, 2 ), XB_NO_ERROR ); + + // delete everything, all keys should be removed from the filtered index + iRc += TestMethod( iPo, "DeleteAll(0)", V4DbfX1->DeleteAll( 0 ), XB_NO_ERROR ); + iRc += TestMethod( iPo, "Commit()", V4DbfX1->Commit(), XB_NO_ERROR ); + iRc += TestMethod( iPo, "CheckTagIntegrity()", V4DbfX1->CheckTagIntegrity( 1, 2 ), XB_NO_ERROR ); + + // undelete everything, all keys should be added back into the filtered index + iRc += TestMethod( iPo, "DeleteAll(1)", V4DbfX1->DeleteAll( 1 ), XB_NO_ERROR ); + iRc += TestMethod( iPo, "Commit()", V4DbfX1->Commit(), XB_NO_ERROR ); + iRc += TestMethod( iPo, "CheckTagIntegrity()", V4DbfX1->CheckTagIntegrity( 1, 2 ), XB_NO_ERROR ); + + // std::cout << "Cur Tag Name = " << V4DbfX1->GetCurTagName().Str() << "\n"; + sKey = "abc"; + iRc += TestMethod( iPo, "Find()", V4DbfX1->Find( sKey ), XB_NOT_FOUND ); + sKey = "EEEEE"; + iRc += TestMethod( iPo, "Find()", V4DbfX1->Find( sKey ), XB_NO_ERROR ); + iRc += TestMethod( iPo, "GetCurRecNo()", (xbInt32) V4DbfX1->GetCurRecNo(), (xbInt32) 61 ); + + iRc += TestMethod( iPo, "GetNextKey()", V4DbfX1->GetNextKey(), XB_NO_ERROR ); + iRc += TestMethod( iPo, "GetCurRecNo()", (xbInt32) V4DbfX1->GetCurRecNo(), (xbInt32) 75 ); + + iRc += TestMethod( iPo, "GetPrevKey()", V4DbfX1->GetPrevKey(), XB_NO_ERROR ); + iRc += TestMethod( iPo, "GetCurRecNo()", (xbInt32) V4DbfX1->GetCurRecNo(), (xbInt32) 61 ); + + iRc += TestMethod( iPo, "GetLastKey()", V4DbfX1->GetLastKey(), XB_NO_ERROR ); + iRc += TestMethod( iPo, "GetCurRecNo()", (xbInt32) V4DbfX1->GetCurRecNo(), (xbInt32) 490 ); + + iRc += TestMethod( iPo, "GetFirstKey()", V4DbfX1->GetFirstKey(), XB_NO_ERROR ); + iRc += TestMethod( iPo, "GetCurRecNo()", (xbInt32) V4DbfX1->GetCurRecNo(), (xbInt32) 1 ); + + + x.CloseAllTables(); + delete V4DbfX1; + + if( iPo > 0 || iRc < 0 ) + fprintf( stdout, "Total Errors = %d\n", iRc * -1 ); + +#ifdef XB_LOGGING_SUPPORT + sMsg.Sprintf( "Program [%s] terminating with [%d] errors...", av[0], iRc * -1 ); + x.WriteLogMessage( sMsg ); +#endif + + return iRc; +} diff --git a/src/tests/xb_test_ndx.cpp b/src/tests/xb_test_ndx.cpp new file mode 100755 index 0000000..31306c0 --- /dev/null +++ b/src/tests/xb_test_ndx.cpp @@ -0,0 +1,330 @@ +/* xb_test_ndx.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 program tests the class xbIxNdx + +// usage: xb_test_ndx QUITE|NORMAL|VERBOSE + + + +// fix me - this program needs to test GetUnique + +#include "xbase.h" + +using namespace xb; + +#include "tstfuncs.cpp" + + +int main( int argCnt, char **av ) +{ + int iRc = 0; + int iRc2 = 0; + int iPo = 1; /* print option */ + /* 0 - QUIET */ + /* 1 - NORMAL */ + /* 2 - VERBOSE */ + + if( argCnt > 1 ) { + if( av[1][0] == 'Q' ) + iPo = 0; + else if( av[1][0] == 'V' ) + iPo = 2; + } + + + xbSchema MyV3Record[] = + { + { "CFLD", XB_CHAR_FLD, 30, 0 }, + { "DFLD", XB_DATE_FLD, 8, 0 }, + { "NFLD", XB_NUMERIC_FLD, 12, 0 }, + { "",0,0,0 } + }; + + + xbXBase x; +#ifdef XB_LOGGING_SUPPORT + x.EnableMsgLogging(); + if( iPo ){ + std::cout << "Logfile is [" << x.GetLogFqFileName().Str() << "]" << std::endl; + } + xbString sMsg; + sMsg.Sprintf( "Program [%s] initializing...", av[0] ); + x.WriteLogMessage( sMsg ); + x.SetLogSize( 1000000 ); +#endif + + x.SetDataDirectory( PROJECT_DATA_DIR ); + x.EnableMsgLogging(); +// x.DisableDefaultAutoLock(); + InitTime(); + + char c; + xbString s; + xbInt32 lRecCnt = 0; + iRc = 0; + + + if( iPo > 0 ) + std::cout << "Default Data Directory is [" << x.GetDataDirectory().Str() << "]" << std::endl; + + xbDbf *V3Dbf = new xbDbf3( &x ); + + xbIx *ixPtr; + void *ndx; + + iRc2 = V3Dbf->CreateTable( "TestNdx.DBF", "TestNdx", MyV3Record, XB_OVERLAY, XB_MULTI_USER ); + iRc += TestMethod( iPo, "CreateTable()", (xbInt32) iRc2, XB_NO_ERROR ); + + if( iRc2 ) + x.DisplayError( iRc2 ); + + + iRc2 = V3Dbf->CreateTag( "NDX", "TestNdxC.NDX", "CFLD", "", 0, 0, XB_OVERLAY, &ixPtr, &ndx ); + iRc += TestMethod( iPo, "CreateTag()", (xbInt32) iRc2, XB_NO_ERROR ); + if( iRc2 ) + x.DisplayError( iRc2 ); + + iRc2 = V3Dbf->AssociateIndex( "NDX", "TestNdxC.NDX", 0 ); + iRc += TestMethod( iPo, "Associate()", (xbInt32) iRc2, XB_NO_ERROR ); + + iRc2 = V3Dbf->CreateTag( "NDX", "TestNdxD.NDX", "DFLD", "", 0, 0, XB_OVERLAY, &ixPtr, &ndx ); + iRc += TestMethod( iPo, "CreateTag()", (xbInt32) iRc2, XB_NO_ERROR ); + if( iRc2 ) + x.DisplayError( iRc2 ); + + iRc2 = V3Dbf->AssociateIndex( "NDX", "TestNdxD.NDX", 0 ); + iRc += TestMethod( iPo, "Associate()", (xbInt32) iRc2, XB_NO_ERROR ); + + iRc2 = V3Dbf->CreateTag( "NDX", "TestNdxN.NDX", "NFLD", "", 0, 0, XB_OVERLAY, &ixPtr, &ndx ); + iRc += TestMethod( iPo, "CreateTag()", (xbInt32) iRc2, XB_NO_ERROR ); + if( iRc2 ) + x.DisplayError( iRc2 ); + + iRc2 = V3Dbf->AssociateIndex( "NDX", "TestNdxN.NDX", 0 ); + iRc += TestMethod( iPo, "Associate()", (xbInt32) iRc2, XB_NO_ERROR ); + + + iRc += TestMethod( iPo, "GetCurTagName()", V3Dbf->GetCurTagName().Str(), "TestNdxC", 8 ); + iRc += TestMethod( iPo, "SetCurTag()", V3Dbf->SetCurTag( "TestNdxN" ), XB_NO_ERROR ); + iRc += TestMethod( iPo, "GetCurTagName()", V3Dbf->GetCurTagName().Str(), "TestNdxN", 8 ); + xbDouble dbl = 100; + iRc += TestMethod( iPo, "Find( 100 )", V3Dbf->Find( dbl ), XB_NOT_FOUND ); + + iRc += TestMethod( iPo, "SetCurTag()", V3Dbf->SetCurTag( "TestNdxD" ), XB_NO_ERROR ); + iRc += TestMethod( iPo, "GetCurTagName()", V3Dbf->GetCurTagName().Str(), "TestNdxD", 8 ); + xbDate dt; + iRc += TestMethod( iPo, "Find( dt )", V3Dbf->Find( dt ), XB_NOT_FOUND ); + + iRc += TestMethod( iPo, "GetFirstKey()", ixPtr->GetFirstKey(), XB_EMPTY ); + iRc += TestMethod( iPo, "GetNextKey()", ixPtr->GetFirstKey(), XB_EMPTY ); + iRc += TestMethod( iPo, "GetLasttKey()", ixPtr->GetLastKey(), XB_EMPTY ); + iRc += TestMethod( iPo, "GetPrevKey()", ixPtr->GetPrevKey(), XB_EMPTY ); + iRc += TestMethod( iPo, "FindKey()", ixPtr->FindKey( NULL, "19611101", 8, 0 ), XB_NOT_FOUND ); + + iRc += TestMethod( iPo, "BlankRecord()", V3Dbf->BlankRecord(), XB_NO_ERROR ); + iRc += TestMethod( iPo, "Putfield()", V3Dbf->PutField( "CFLD", "AAA" ), XB_NO_ERROR ); + iRc += TestMethod( iPo, "Putfield()", V3Dbf->PutField( "DFLD", "19611109" ), XB_NO_ERROR ); + iRc += TestMethod( iPo, "PutfieldDouble()", V3Dbf->PutDoubleField( "NFLD", 50 ), XB_NO_ERROR ); + + iRc += TestMethod( iPo, "AppendRecord()", V3Dbf->AppendRecord(), XB_NO_ERROR ); + + xbDate d( "19890209" ); + + //for( xbUInt16 i = 0; i < 35 && iRc == XB_NO_ERROR; i++ ){ + for( xbUInt16 i = 0; i < 2 && iRc == XB_NO_ERROR; i++ ){ + for( xbUInt16 j = 0; j < 35 && iRc == XB_NO_ERROR; j++ ){ + c = j + 65; + s = c; + s.PadRight( c, (xbUInt32) i + 1 ); + V3Dbf->BlankRecord(); + V3Dbf->PutField( "CFLD", s ); + V3Dbf->PutLongField( "NFLD", ++lRecCnt ); + V3Dbf->PutField( "DFLD", d.Str() ); + d++; + + iRc = V3Dbf->AppendRecord(); + } + } + + iRc += TestMethod( iPo, "SetCurTag()", V3Dbf->SetCurTag( "TestNdxN" ), XB_NO_ERROR ); + iRc += TestMethod( iPo, "GetCurTagName()", V3Dbf->GetCurTagName().Str(), "TestNdxN", 8 ); + + dbl = 55.0; + iRc += TestMethod( iPo, "Find( 55.0 )", V3Dbf->Find( dbl ), XB_NO_ERROR ); + + iRc += TestMethod( iPo, "SetCurTag()", V3Dbf->SetCurTag( "TestNdxC" ), XB_NO_ERROR ); + iRc += TestMethod( iPo, "GetCurTagName()", V3Dbf->GetCurTagName().Str(), "TestNdxC", 8 ); + + s = "AAA"; + iRc += TestMethod( iPo, "Find( 'AAA' )", V3Dbf->Find( s ), XB_NO_ERROR ); + + s = "AzAA"; + iRc += TestMethod( iPo, "Find( 'AzAA' )", V3Dbf->Find( s ), XB_NOT_FOUND ); + + iRc += TestMethod( iPo, "SetCurTag()", V3Dbf->SetCurTag( "TestNdxD" ), XB_NO_ERROR ); + iRc += TestMethod( iPo, "GetCurTagName()", V3Dbf->GetCurTagName().Str(), "TestNdxD", 8 ); + + std::cout << d.Str() << "\n"; + iRc += TestMethod( iPo, "Find( '19890420' )", V3Dbf->Find( d ), XB_NOT_FOUND ); + + V3Dbf->GetRecord( 26 ); + V3Dbf->PutField( "CFLD", "AAA" ); + V3Dbf->PutRecord( 26 ); + + V3Dbf->GetRecord( 14 ); + V3Dbf->PutField( "CFLD", "AAAA" ); + V3Dbf->PutRecord( 14 ); + + V3Dbf->GetRecord( 11 ); + V3Dbf->PutField( "CFLD", "III" ); + V3Dbf->PutRecord( 11 ); + + V3Dbf->GetRecord( 25 ); + V3Dbf->PutField( "CFLD", "DDD" ); + V3Dbf->PutRecord( 25 ); + + V3Dbf->GetRecord( 12 ); + V3Dbf->PutField( "CFLD", "EEE" ); + V3Dbf->PutRecord( 12 ); + + V3Dbf->GetRecord( 26 ); + V3Dbf->PutField( "CFLD", "CCC" ); + V3Dbf->PutRecord( 26 ); + + V3Dbf->GetRecord( 13 ); + V3Dbf->PutField( "CFLD", "CCCC" ); + V3Dbf->PutRecord( 13 ); + + V3Dbf->GetRecord( 27 ); + V3Dbf->PutField( "CFLD", "AAA" ); + V3Dbf->PutRecord( 27 ); + + V3Dbf->GetRecord( 28 ); + V3Dbf->PutField( "CFLD", "CCC" ); + V3Dbf->PutRecord( 28 ); + + V3Dbf->GetRecord( 24 ); + V3Dbf->PutField( "CFLD", "FFF" ); + V3Dbf->PutRecord( 24 ); + + V3Dbf->GetRecord( 10 ); + V3Dbf->PutField( "CFLD", "HHH" ); + V3Dbf->PutRecord( 10 ); + + + for( xbUInt16 i = 0; i < 35 && iRc == XB_NO_ERROR; i++ ){ + for( xbUInt16 j = 0; j < 14 && iRc == XB_NO_ERROR; j++ ){ + c = j + 65; + s = c; + s.PadRight( c, (xbUInt32) i + 1 ); + V3Dbf->BlankRecord(); + V3Dbf->PutField( "CFLD", s ); + V3Dbf->PutLongField( "NFLD", ++lRecCnt ); + V3Dbf->PutField( "DFLD", d.Str() ); + d++; + iRc = V3Dbf->AppendRecord(); + } + } + + for( xbUInt16 i = 0; i < 2 && iRc == XB_NO_ERROR; i++ ){ + for( xbUInt16 j = 0; j < 14 && iRc == XB_NO_ERROR; j++ ){ + c = j + 65; + s = c; + s.PadRight( c, (xbUInt32) i + 1 ); + + V3Dbf->BlankRecord(); + V3Dbf->PutField( "CFLD", s ); + V3Dbf->PutLongField( "NFLD", ++lRecCnt ); + V3Dbf->PutField( "DFLD", d.Str() ); + d++; + + iRc = V3Dbf->AppendRecord(); + } + } + + V3Dbf->GetRecord( 26 ); + V3Dbf->PutField( "CFLD", "AAA" ); + V3Dbf->PutRecord( 26 ); + + V3Dbf->GetRecord( 14 ); + V3Dbf->PutField( "CFLD", "AAAA" ); + V3Dbf->PutRecord( 14 ); + + V3Dbf->GetRecord( 11 ); + V3Dbf->PutField( "CFLD", "III" ); + V3Dbf->PutRecord( 11 ); + + V3Dbf->GetRecord( 25 ); + V3Dbf->PutField( "CFLD", "DDD" ); + V3Dbf->PutRecord( 25 ); + + V3Dbf->GetRecord( 12 ); + V3Dbf->PutField( "CFLD", "EEE" ); + V3Dbf->PutRecord( 12 ); + + V3Dbf->GetRecord( 26 ); + V3Dbf->PutField( "CFLD", "CCC" ); + V3Dbf->PutRecord( 26 ); + + V3Dbf->GetRecord( 13 ); + V3Dbf->PutField( "CFLD", "CCCC" ); + V3Dbf->PutRecord( 13 ); + + V3Dbf->GetRecord( 27 ); + V3Dbf->PutField( "CFLD", "AAA" ); + V3Dbf->PutRecord( 27 ); + + V3Dbf->GetRecord( 28 ); + V3Dbf->PutField( "CFLD", "CCC" ); + V3Dbf->PutRecord( 28 ); + + V3Dbf->GetRecord( 24 ); + V3Dbf->PutField( "CFLD", "FFF" ); + V3Dbf->PutRecord( 24 ); + + V3Dbf->GetRecord( 10 ); + V3Dbf->PutField( "CFLD", "HHH" ); + V3Dbf->PutRecord( 10 ); + + // just in case there are any issues outstanding + V3Dbf->Abort(); + + xbIxList *ixl = V3Dbf->GetIxList(); + xbIxNdx *ix; + xbString sTagName; + while( ixl ){ + if( *ixl->sFmt == "NDX" ){ + ix = (xbIxNdx *) ixl->ix; + //ix->GetTagName( 0, sTagName ); + sMsg.Sprintf( "CheckTagIntegrity() - [%s]", ix->GetTagName(ix->GetCurTag()).Str()); + iRc += TestMethod( iPo, sMsg, ix->CheckTagIntegrity( ix->GetCurTag(), 2 ), XB_NO_ERROR ); + ixl = ixl->next; + } + } + + iRc += TestMethod( iPo, "Close()", V3Dbf->Close(), XB_NO_ERROR ); + delete V3Dbf; + + if( iPo > 0 || iRc < 0 ) + fprintf( stdout, "Total Errors = %d\n", iRc * -1 ); + +#ifdef XB_LOGGING_SUPPORT + sMsg.Sprintf( "Program [%s] terminating with [%d] errors...", av[0], iRc * -1 ); + x.WriteLogMessage( sMsg, 2 ); +#endif + + return iRc; +} diff --git a/src/tests/xb_test_ndx2.cpp b/src/tests/xb_test_ndx2.cpp new file mode 100755 index 0000000..f406b05 --- /dev/null +++ b/src/tests/xb_test_ndx2.cpp @@ -0,0 +1,145 @@ +/* xb_test_ndx2.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: + + xb64-devel@lists.sourceforge.net + xb64-users@lists.sourceforge.net + +*/ + +// This program tests the class xbIxNdx + +// usage: xb_test_ndx QUITE|NORMAL|VERBOSE + + + +// fix me - this program needs to test GetUnique + +#include "xbase.h" + +using namespace xb; + +#include "tstfuncs.cpp" + + +int main( int argCnt, char **av ) +{ + int iRc = 0; + int iRc2 = 0; + int po = 1; /* print option */ + /* 0 - QUIET */ + /* 1 - NORMAL */ + /* 2 - VERBOSE */ + + if( argCnt > 1 ) { + if( av[1][0] == 'Q' ) + po = 0; + else if( av[1][0] == 'V' ) + po = 2; + } + + + xbSchema MyV3Record[] = + { + { "CFLD", XB_CHAR_FLD, 30, 0 }, + { "DFLD", XB_DATE_FLD, 8, 0 }, + { "NFLD", XB_NUMERIC_FLD, 12, 0 }, + { "",0,0,0 } + }; + + + xbXBase x; +#ifdef XB_LOGGING_SUPPORT + x.EnableMsgLogging(); + if( po ){ + std::cout << "Logfile is [" << x.GetLogFqFileName().Str() << "]" << std::endl; + } + xbString sMsg; + sMsg.Sprintf( "Program [%s] initializing...", av[0] ); + x.WriteLogMessage( sMsg ); + x.SetLogSize( 1000000 ); +#endif + + x.SetDataDirectory( PROJECT_DATA_DIR ); + x.EnableMsgLogging(); +// x.DisableDefaultAutoLock(); + + x.SetUniqueKeyOpt( XB_EMULATE_DBASE ); + InitTime(); + xbString s; + iRc = 0; + + if( po > 0 ) + std::cout << "Default Data Directory is [" << x.GetDataDirectory().Str() << "]" << std::endl; + + xbDbf *V3Dbf = new xbDbf3( &x ); + xbIx *ixPtr; + void *ndx; + + iRc2 = V3Dbf->CreateTable( "TestNdx.DBF", "TestNdx", MyV3Record, XB_OVERLAY, XB_MULTI_USER ); + iRc += TestMethod( po, "CreateTable()", (xbInt32) iRc2, XB_NO_ERROR ); + + if( iRc2 ) + x.DisplayError( iRc2 ); + + iRc2 = V3Dbf->CreateTag( "NDX", "TestNdxC.NDX", "CFLD", "", 0, xbTrue, XB_OVERLAY, &ixPtr, &ndx ); + iRc += TestMethod( po, "CreateTag()", (xbInt32) iRc2, XB_NO_ERROR ); + if( iRc2 ) + x.DisplayError( iRc2 ); + + iRc2 = V3Dbf->AssociateIndex( "NDX", "TestNdxC.NDX", 0 ); + iRc += TestMethod( po, "Associate()", (xbInt32) iRc2, XB_NO_ERROR ); + + + iRc += TestMethod( po, "BlankRecord()", V3Dbf->BlankRecord(), XB_NO_ERROR ); + iRc += TestMethod( po, "Putfield()", V3Dbf->PutField( "CFLD", "AAA" ), XB_NO_ERROR ); + iRc += TestMethod( po, "Putfield()", V3Dbf->PutField( "DFLD", "19611109" ), XB_NO_ERROR ); + iRc += TestMethod( po, "PutfieldDouble()", V3Dbf->PutDoubleField( "NFLD", 50 ), XB_NO_ERROR ); + iRc += TestMethod( po, "AppendRecord()", V3Dbf->AppendRecord(), XB_NO_ERROR ); + + + iRc += TestMethod( po, "BlankRecord()", V3Dbf->BlankRecord(), XB_NO_ERROR ); + iRc += TestMethod( po, "Putfield()", V3Dbf->PutField( "CFLD", "BBB" ), XB_NO_ERROR ); + iRc += TestMethod( po, "Putfield()", V3Dbf->PutField( "DFLD", "19611109" ), XB_NO_ERROR ); + iRc += TestMethod( po, "PutfieldDouble()", V3Dbf->PutDoubleField( "NFLD", 50 ), XB_NO_ERROR ); + iRc += TestMethod( po, "AppendRecord()", V3Dbf->AppendRecord(), XB_NO_ERROR ); + + iRc += TestMethod( po, "BlankRecord()", V3Dbf->BlankRecord(), XB_NO_ERROR ); + iRc += TestMethod( po, "Putfield()", V3Dbf->PutField( "CFLD", "BBB" ), XB_NO_ERROR ); + iRc += TestMethod( po, "Putfield()", V3Dbf->PutField( "DFLD", "19611109" ), XB_NO_ERROR ); + iRc += TestMethod( po, "PutfieldDouble()", V3Dbf->PutDoubleField( "NFLD", 50 ), XB_NO_ERROR ); + iRc += TestMethod( po, "AppendRecord()", V3Dbf->AppendRecord(), XB_NO_ERROR ); + + + xbIxList *ixl = V3Dbf->GetIxList(); + xbIxNdx *ix; + xbString sTagName; + while( ixl ){ + if( *ixl->sFmt == "NDX" ){ + ix = (xbIxNdx *) ixl->ix; + //ix->GetTagName( 0, sTagName ); + sMsg.Sprintf( "CheckTagIntegrity() - [%s]", ix->GetTagName(ix->GetCurTag()).Str()); + iRc += TestMethod( po, sMsg, ix->CheckTagIntegrity( ix->GetCurTag(), 2 ), XB_NO_ERROR ); + ixl = ixl->next; + } + } + + iRc += TestMethod( po, "Close()", V3Dbf->Close(), XB_NO_ERROR ); + delete V3Dbf; + + if( po > 0 || iRc < 0 ) + fprintf( stdout, "Total Errors = %d\n", iRc * -1 ); + +#ifdef XB_LOGGING_SUPPORT + sMsg.Sprintf( "Program [%s] terminating with [%d] errors...", av[0], iRc * -1 ); + x.WriteLogMessage( sMsg, 2 ); +#endif + + return iRc; +} diff --git a/src/tests/xb_test_sql.cpp b/src/tests/xb_test_sql.cpp new file mode 100755 index 0000000..630cc89 --- /dev/null +++ b/src/tests/xb_test_sql.cpp @@ -0,0 +1,107 @@ +/* xb_test_sql.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: + + xb64-devel@lists.sourceforge.net + xb64-users@lists.sourceforge.net + +*/ + +// This program tests the SQL functions + +// usage: xb_test_sql QUITE|NORMAL|VERBOSE + + + +#include "xbase.h" + +using namespace xb; + +#include "tstfuncs.cpp" + + +int main( int argCnt, char **av ) +{ + int iRc = 0; + int iRc2 = 0; + int po = 1; /* print option */ + /* 0 - QUIET */ + /* 1 - NORMAL */ + /* 2 - VERBOSE */ + + if( argCnt > 1 ) { + if( av[1][0] == 'Q' ) + po = 0; + else if( av[1][0] == 'V' ) + po = 2; + } + + + xbSchema MySqlRecord[] = + { + { "CITY", XB_CHAR_FLD, 30, 0 }, + { "STATE", XB_CHAR_FLD, 2, 0 }, + { "ZIPCODE", XB_NUMERIC_FLD, 9, 0 }, + { "NOTES", XB_MEMO_FLD, 10, 0 }, + { "LASTUPDATE", XB_DATE_FLD, 8, 1 }, + { "ACTIVE", XB_LOGICAL_FLD, 1, 0 }, + { "",0,0,0 } + }; + + + xbXBase x; +#ifdef XB_LOGGING_SUPPORT + x.EnableMsgLogging(); + if( po ){ + std::cout << "Logfile is [" << x.GetLogFqFileName().Str() << "]" << std::endl; + } + xbString sMsg; + sMsg.Sprintf( "Program [%s] initializing...", av[0] ); + x.WriteLogMessage( sMsg ); +#endif + + + + x.SetDataDirectory( PROJECT_DATA_DIR ); + x.EnableMsgLogging(); + InitTime(); + + xbSql sql( &x ); + + if( po > 0 ) + std::cout << "Default Data Directory is [" << x.GetDataDirectory().Str() << "]" << std::endl; + + xbDbf4 SqlDbf( &x ); // version 4 dbf file + iRc2 = SqlDbf.CreateTable( "TestSQL.DBF", "TestSQL", MySqlRecord, XB_OVERLAY, XB_MULTI_USER ); + iRc += TestMethod( po, "CreateTable()", (xbInt32) iRc2, XB_NO_ERROR ); + if( iRc2 ) + x.DisplayError( iRc2 ); + + + xbString sSql = "INSERT INTO TestSQL (CITY, STATE, ZIPCODE, NOTES, LASTUPDATE, ACTIVE ) VALUES ( 'San Diego', 'CA', 92007, 'San Diego is a cool place', '1989-02-09', 'Y')"; + + iRc2 = sql.ExecuteNonQuery( sSql ); + iRc += TestMethod( po, "Insert()", (xbInt32) iRc2, XB_NO_ERROR ); + if( iRc2 ) + x.DisplayError( iRc2 ); + + iRc += TestMethod( po, "Close()", SqlDbf.Close(), XB_NO_ERROR ); + + if( po > 0 || iRc < 0 ) + fprintf( stdout, "Total Errors = %d\n", iRc * -1 ); + + +#ifdef XB_LOGGING_SUPPORT + sMsg.Sprintf( "Program [%s] terminating with [%d] errors...", av[0], iRc * -1 ); + x.WriteLogMessage( sMsg ); +#endif + + return iRc; +} + diff --git a/src/tests/xb_test_string.cpp b/src/tests/xb_test_string.cpp new file mode 100755 index 0000000..f39c101 --- /dev/null +++ b/src/tests/xb_test_string.cpp @@ -0,0 +1,397 @@ +/* xb_test_string.cpp + +XBase63 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 program tests the string class xbString + +// usage: xb_test_string QUIET|NORMAL|VERBOSE + +#define VERBOSE + +#include "xbase.h" + +using namespace xb; + +#include "tstfuncs.cpp" + +int main( int argCnt, char **av = NULL ) + +//int main( int argCnt, char *argv[] ) +{ + int rc = 0; + int po = 1; /* print option */ + /* 0 - QUIET */ + /* 1 - NORMAL */ + /* 2 - VERBOSE */ + + if( argCnt > 1 ) { + if( av[1][0] == 'Q' ) + po = 0; + else if( av[1][0] == 'V' ) + po = 2; + } + + xbXBase x; + + #ifdef XB_LOGGING_SUPPORT + x.EnableMsgLogging(); + if( po ){ + std::cout << "Logfile is [" << x.GetLogFqFileName().Str() << "]" << std::endl; + } + xbString sMsg; + sMsg.Sprintf( "Program [%s] initializing...", av[0] ); + x.WriteLogMessage( sMsg ); + #endif + + x.SetDataDirectory( PROJECT_DATA_DIR ); + x.EnableMsgLogging(); + + InitTime(); + + // create a string, assign a value + xbString s1; + s1 = "Test String 1"; + rc += TestMethod( po, "Constructor s1" , s1, "Test String 1", 13 ); + + // create another string, copy the value from s1 into it + xbString s2; + s2 = s1; + rc += TestMethod( po, "Operator '=' " , s2, "Test String 1", 13 ); + + // create another string with a single character + xbString s3( 'X' ); + + //std::cout << "s3 = [" << s3 << "]" << std::endl; + rc += TestMethod( po, "Constructor s3" , s3, "X", 1 ); + + // create another string and assign data to it yet another way + xbString s4( "Class constructor test 4" ); + rc += TestMethod( po, "Constructor s4" , s4, "Class constructor test 4", 24 ); + + // create another string with a size limit + xbString s5( "Class constructor test 4", 7 ); + rc += TestMethod( po, "Constructor s5" , s5, "Class c", 7 ); + + // create another string from a string + xbString s6( s5 ); + rc += TestMethod( po, "Constructor s6" , s6, "Class c", 7 ); + + // create 15 byte string with nothing in it + xbString s7( (xbUInt32) 15 ); + rc += TestMethod( po, "Constructor s7" , s7, "", 0 ); + + xbString s8( "some test data", 6 ); + rc += TestMethod( po, "Constructor s8" , s8, "some t", 6 ); + + xbString s9( "some test data", 30 ); + rc += TestMethod( po, "Constructor s9" , s9, "some test data", 14 ); + + // Extract character from a particular position in the string + rc += TestMethod( po, "Operator [] ", s1[7], 't' ); + rc += TestMethod( po, "Operator [] ", s1.GetCharacter(7), 't' ); + + s1[6] = 'X'; + rc += TestMethod( po, "Operator assignment [] ", s1.GetCharacter(6), 'X' ); + + // set string 7 to a character + s7 = 'Z'; + rc += TestMethod( po, "Operator =", s7.Str(), "Z", 1 ); + + // Concatenation tests - I + s1 = " part 1 "; + s1 += " part 2 "; + s2 = " part 3 "; + s1 += s2; + s1 += 'Z'; + rc += TestMethod( po, "Concatenation test 1", s1, " part 1 part 2 part 3 Z", 25 ); + + // Concatenation tests - II + s1 = "part 1 "; + s1 -= "part 2 "; + s1 -= 'X'; + s1 -= s2; + rc += TestMethod( po, "Concatenation test 2", s1, "part 1part 2X part 3", 20 ); + + // Concatenation tests - III + s1 = "s1data "; + s2 = "s2data "; + s3 = s1 - s2; + rc += TestMethod( po, "Concatenation test 3", s3, "s1datas2data", 12 ); + + // Concatenation tests - IV + s3 = s1 + s2; + rc += TestMethod( po, "Concatenation test 4", s3, "s1data s2data ", 15 ); + + // Concatenation tests - V + s3 = s1 + " (char * data) " + "xyz " + s2 + 'z'; + rc += TestMethod( po, "Concatenation test 1", s3, "s1data (char * data) xyz s2data z", 36 ); + + //Operator tests + s1 = "aaa"; + s2 = "bbb"; + rc += TestMethod( po, "Operator == ", s1 == s2, 0 ); + rc += TestMethod( po, "Operator != ", s1 != s2, 1 ); + rc += TestMethod( po, "Operator < ", s1 < s2, 1 ); + rc += TestMethod( po, "Operator > ", s1 > s2, 0 ); + rc += TestMethod( po, "Operator < ", s1 <= s2, 1 ); + rc += TestMethod( po, "Operator > ", s1 >= s2, 0 ); + + s1 = s2; + rc += TestMethod( po, "Operator == ", s1 == s2, 1 ); + rc += TestMethod( po, "Operator != ", s1 == s2, 1 ); + rc += TestMethod( po, "Operator < ", s1 < s2, 0 ); + rc += TestMethod( po, "Operator > ", s1 > s2, 0 ); + rc += TestMethod( po, "Operator < ", s1 <= s2, 1 ); + rc += TestMethod( po, "Operator > ", s1 >= s2, 1 ); + + s1 = "XYZ"; + rc += TestMethod( po, "Operator * ", (const char *) s1, "XYZ", 3 ); + + s1 = 'Z'; + rc += TestMethod( po, "Operator = ", s1, "Z", 1 ); + + s1 = "ABC,xyz"; + rc += TestMethod( po, "CountChar(c,1) ", (xbInt32) s1.CountChar( ',',1 ), 1 ); + + s1 = "ABC,xy,z"; + rc += TestMethod( po, "CountChar(c,1) ", (xbInt32) s1.CountChar( ',',1 ), 2 ); + + s1 = "ABC,xy,z'asad,as'adss"; + rc += TestMethod( po, "CountChar(c,1) ", (xbInt32) s1.CountChar( ',',1 ), 2 ); + + s1 = "ABADFDSGA"; + rc += TestMethod( po, "CountChar() ", (xbInt32) s1.CountChar( 'A' ), 3 ); + + s1.Ltrunc( 4 ); + rc += TestMethod( po, "Ltrunc(4) ", s1, "FDSGA", 5 ); + + s1.PutAt( 3, 'Z' ); + rc += TestMethod( po, "PutAt(3,'Z') ", s1, "FDZGA", 5 ); + + s1.AddBackSlash( 'Z' ); + rc += TestMethod( po, "AddBackSlash( 'Z' ) ", s1, "FD\\ZGA", 6 ); + + s1 = "ABCDEFG"; + rc += TestMethod( po, "s1.Append( 'Z' )", s1.Append( 'Z' ).Str(), "ABCDEFGZ", 8 ); + rc += TestMethod( po, "s1.Append( '999' )", s1.Append( "999" ), "ABCDEFGZ999", 11 ); + s2 = "!@#"; + rc += TestMethod( po, "s1.Append( '!@#' )", s1.Append( s2 ), "ABCDEFGZ999!@#", 14 ); + + rc += TestMethod( po, "s1.Assign( 'ABCDE', 3, 2 )", s1.Assign( "ABCDE", 3, 2 ), "CD", 2 ); + rc += TestMethod( po, "s1.Assign( 'ABCDE', 2, 7 )", s1.Assign( "ABCDE", 2, 7 ), "BCDE", 4 ); + rc += TestMethod( po, "s1.Assign( 'ABCDE', 1, 4 )", s1.Assign( "ABCDE", 1, 4 ), "ABCD", 4 ); + rc += TestMethod( po, "s1.Assign( 'ABCDE', 5, 5 )", s1.Assign( "ABCDE", 5, 5 ), "E", 1 ); + rc += TestMethod( po, "s1.Assign( 'ABCDE', 15, 5 )", s1.Assign( "ABCDE", 15, 5 ), "", 0 ); + + rc += TestMethod( po, "s1.Assign( 'ABCDE', 1 )", s1.Assign( "ABCDE", 1 ), "ABCDE", 5 ); + rc += TestMethod( po, "s1.Assign( 'ABCDE', 3 )", s1.Assign( "ABCDE", 3 ), "CDE", 3 ); + rc += TestMethod( po, "s1.Assign( 'ABCDE', 10 )", s1.Assign( "ABCDE", 10 ), "", 0 ); + + s2 = "ABCDE"; + rc += TestMethod( po, "s1.Assign( s2, 3, 2 )", s1.Assign( s2, 3, 2 ), "CD", 2 ); + rc += TestMethod( po, "s1.Assign( s2, 2, 7 )", s1.Assign( s2, 2, 7 ), "BCDE", 4 ); + rc += TestMethod( po, "s1.Assign( s2, 1, 4 )", s1.Assign( s2, 1, 4 ), "ABCD", 4 ); + rc += TestMethod( po, "s1.Assign( s2, 5, 5 )", s1.Assign( s2, 5, 5 ), "E", 1 ); + rc += TestMethod( po, "s1.Assign( s2, 15, 5 )", s1.Assign( s2, 15, 5 ), "", 0 ); + + rc += TestMethod( po, "s1.Assign( s2, 1 )", s1.Assign( s2, 1 ), "ABCDE", 5 ); + rc += TestMethod( po, "s1.Assign( s2, 3 )", s1.Assign( s2, 3 ), "CDE", 3 ); + rc += TestMethod( po, "s1.Assign( s2, 10 )", s1.Assign( s2, 10 ), "", 0 ); + + s2 = "1234567890"; + s1 = s2.Copy(); + rc += TestMethod( po, "Copy() ", s1, "1234567890", 10 ); + + s1 = "0x35"; + char hexChar; + s1.CvtHexChar( hexChar ); + rc += TestMethod( po, "CvtHexChar(hexChar) ", hexChar, '5' ); + + s1 = "0x610x620x630x640x65"; + s1.CvtHexString( s2 ); + rc += TestMethod( po, "CvtHexString() ", s2, "abcde", 5 ); + + s1 = "123"; + s2 = "ABC"; + rc += TestMethod( po, "HasAlphaChars()", s1.HasAlphaChars(), 0 ); + rc += TestMethod( po, "HasAlphaChars()", s2.HasAlphaChars(), 1 ); + + s1 = "\\ABC\\XYZ"; + rc += TestMethod( po, "GetPathSeparator()", s1.GetPathSeparator(), '\\' ); + + s1 = "/ABC/XYZ"; + rc += TestMethod( po, "GetPathSeparator()", s1.GetPathSeparator(), '/' ); + + s2 = ""; + rc += TestMethod( po, "IsEmpty()", s2.IsEmpty(), 1 ); + + // trim tests + s1 = " ABC "; + rc += TestMethod( po, "Ltrim()", s1.Ltrim(), "ABC ", 6 ); + s1 = " ABC "; + rc += TestMethod( po, "Rtrim()", s1.Rtrim(), " ABC", 6 ); + s1 = " ABC "; + rc += TestMethod( po, "Trim() ", s1.Trim(), "ABC", 3 ); + + s1 = "ABC "; + rc += TestMethod( po, "Ltrim()", s1.Ltrim(), "ABC ", 6 ); + + s1 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + rc += TestMethod( po, "Mid(3,0) ", s1.Mid(3,0), "", 0 ); + + s1 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + rc += TestMethod( po, "Mid(4,5) ", s1.Mid(4,5), "DEFGH", 5 ); + + rc += TestMethod( po, "Pos('G') ", (xbInt32) s1.Pos( 'G' ), 4 ); + rc += TestMethod( po, "Pos(\"EFG\") ", (xbInt32) s1.Pos( "EFG" ), 2 ); + + rc += TestMethod( po, "Pos('0') ", (xbInt32) s1.Pos( '0' ), 0 ); + rc += TestMethod( po, "Pos(\"000\") ", (xbInt32) s1.Pos( "000" ), 0 ); + rc += TestMethod( po, "Pos(\"DEF\") ", (xbInt32) s1.Pos( "DEF" ), 1 ); + + s1 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + rc += TestMethod( po, "Remove(3,5) ", s1.Remove( 3, 5 ), "ABHIJKLMNOPQRSTUVWXYZ", 21 ); + + s1 = "ABCABCABZ"; + s1.SwapChars( 'A', '9' ); + rc += TestMethod( po, "SwapChars() ", s1, "9BC9BC9BZ", 9 ); + + s1.ToLowerCase(); + rc += TestMethod( po, "ToLowerCase() ", s1, "9bc9bc9bz", 9 ); + + s1.ToUpperCase(); + rc += TestMethod( po, "ToUpperCase() ", s1, "9BC9BC9BZ", 9 ); + + s1.ZapChar( '9' ); + rc += TestMethod( po, "ZapChar('9') ", s1, "BCBCBZ", 6 ); + + s1.ZapLeadingChar( 'B' ); + rc += TestMethod( po, "ZapLeadingChar('B') ", s1, "CBCBZ", 5 ); + + s1.ZapTrailingChar( 'Z' ); + rc += TestMethod( po, "ZapLeadingChar('B') ", s1, "CBCB", 4 ); + + s1.ExtractElement( "aaaa|bbbb|cccc|dddd", '|', 2, 0 ); + rc += TestMethod( po, "ExtractElement() ", s1, "bbbb", 4 ); + + s1.ExtractElement( "aaaa|b'bb|c'ccc|dddd", '|', 3, 1 ); + rc += TestMethod( po, "ExtractElement() ", s1, "dddd", 4 ); + + s1 = "123"; + s1.PadLeft( '0', 9 ); + rc += TestMethod( po, "PadLeft() ", s1, "000000123", 9 ); + + s1 = "abc"; + s1.PadRight( 'Z', 9 ); + rc += TestMethod( po, "PadRight() ", s1, "abcZZZZZZ", 9 ); + + s1.Left( 4 ); + rc += TestMethod( po, "Left(4) ", s1, "abcZ", 4 ); + + s1.Left( 1 ); + rc += TestMethod( po, "Left(1) ", s1, "a", 1 ); + + s1.Left( 0 ); + rc += TestMethod( po, "Left(0) ", s1, "", 0 ); + + + char buf[5]; + buf[0] = 'W'; + buf[1] = 'X'; + buf[2] = 'Y'; + buf[3] = 'Z'; + buf[4] = 0x00; + xbInt32 l = 1234567; + xbFloat f = (xbFloat) 12.35; + s2 = "test string"; + + rc += TestMethod( po, "s1.Sprintf()", s1.Sprintf( "%s %d %s %ld", buf, 12, s2.Str(), l ), "WXYZ 12 test string 1234567", 27 ); + + // %f format varies depending on compiler + s1.Sprintf( "%6.2f", f ); + s1.Ltrim(); + rc += TestMethod( po, "s1.Sprintf()/s.Trim()", s1, "12.35", 5 ); + + s1.SetNum( (long) 123456 ); + rc += TestMethod( po, "SetNum() ", s1, "123456", 6 ); + + s1 = "T"; + rc += TestMethod( po, "ValidLogicalValue", s1.ValidLogicalValue(), 1 ); + + s1 = "xyz"; + rc += TestMethod( po, "ValidLogicalValue", s1.ValidLogicalValue(), 0 ); + + s1 = "-123456.89"; + rc += TestMethod( po, "ValidNumericValue", s1.ValidNumericValue(), 1 ); + + s1 = "ABC-123456.89"; + rc += TestMethod( po, "ValidNumericValue", s1.ValidNumericValue(), 0 ); + + s1 = "abcabcabx"; + rc += TestMethod( po, "GetLastPos", (xbInt16) s1.GetLastPos( 'b' ), 8 ); + s1 = "abcabcabx"; + rc += TestMethod( po, "GetLastPos", (xbInt16) s1.GetLastPos( 'x' ), 9 ); + s1 = "abcabcabx"; + rc += TestMethod( po, "GetLastPos", (xbInt16) s1.GetLastPos( '$' ), 0 ); + rc += TestMethod( po, "GetLastPos", (xbInt16) s1.GetLastPos( "ab" ), 7 ); + + s1 = ")"; + rc += TestMethod( po, "GetLastPos", (xbInt16) s1.GetLastPos( ')' ), 1 ); + s1 = "))))"; + rc += TestMethod( po, "GetLastPos", (xbInt16) s1.GetLastPos( ')' ), 4 ); + + char * p; + p = (char *) malloc( 5 ); + p[0] = '1'; + p[1] = '2'; + p[2] = '3'; + p[3] = '4'; + p[4] = 0x00; + s1.Set( p, 5 ); + free( p ); + rc += TestMethod( po, "Set", s1, "1234", 4 ); + + + xbDouble d = 12345678.876543; + xbString sD( d ); + rc += TestMethod( po, "xbDouble Constructor", sD, "12345678.876543", 15 ); + + xbString sSet; + sSet.Set( sD ); + rc += TestMethod( po, "Set", sD, sD, 15 ); + sSet.Set( s2 ); + rc += TestMethod( po, "Set", sSet, s2, 11 ); + sSet.Set( "" ); + rc += TestMethod( po, "Set", sSet, "", 0 ); + + s1.Sprintf( "string %d", 1 ); + s2.Sprintf( "string %1.1f", 2.0 ); + s3.Sprintf( "%s and %s", s1.Str(), s2.Str()); + rc += TestMethod( po, "Sprintf", s3, "string 1 and string 2.0", 23 ); + + xbInt16 iErrorStop = 10; + xbInt16 iRc = -100; + sMsg.Sprintf( "class::method() Exception Caught. Error Stop = [%d] iRc = [%d] Expression = [%s]", iErrorStop, iRc, s3.Str() ); + + if( po > 0 || rc < 0 ) + fprintf( stdout, "Total Errors = %d\n", rc * -1 ); + +#ifdef XB_LOGGING_SUPPORT + sMsg.Sprintf( "Program [%s] terminating with [%d] errors...", av[0], rc * -1 ); + x.WriteLogMessage( sMsg ); +#endif + + return rc; +} + diff --git a/src/tests/xb_test_tblmgr.cpp b/src/tests/xb_test_tblmgr.cpp new file mode 100755 index 0000000..26ec96a --- /dev/null +++ b/src/tests/xb_test_tblmgr.cpp @@ -0,0 +1,167 @@ +/* xb_test_tblmgr.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 program tests the class xbXBase +// usage: xb_test_tblmgr QUITE|NORMAL|VERBOSE + + +#include "xbase.h" + +using namespace xb; + +#include "tstfuncs.cpp" + +int main( int argCnt, char **av ) +{ + int rc = 0; + int po = 1; /* print option */ + /* 0 - QUIET */ + /* 1 - NORMAL */ + /* 2 - VERBOSE */ + + if( argCnt > 1 ) { + if( av[1][0] == 'Q' ) + po = 0; + else if( av[1][0] == 'V' ) + po = 2; + } + + xbXBase x; + +#ifdef XB_LOGGING_SUPPORT + x.EnableMsgLogging(); + if( po ){ + std::cout << "Logfile is [" << x.GetLogFqFileName().Str() << "]" << std::endl; + } + xbString sMsg; + sMsg.Sprintf( "Program [%s] initializing...", av[0] ); + x.WriteLogMessage( sMsg ); +#endif + InitTime(); + + if( po == 2 ){ + std::cout << "DisplayError Test ==> "; + x.DisplayError( 0 ); + #ifdef WIN32 + std::cout << "WIN32 environment" << std::endl; + #else + std::cout << "Not WIN32 environment" << std::endl; + #endif + } + + xbDbf * d1; + xbDbf * d2; + + #if defined (XB_DBF3_SUPPORT ) + d1 = new xbDbf3( &x ); + #elif defined( XB_DBF4_SUPPORT ) + d1 = new xbDbf4( &x ); + #else + std::cout << "No dbf file support built into library" << std::endl; + return -1; + #endif + + rc += TestMethod( po, "AddTblToTblLst()", x.AddTblToTblList( d1, "TestTableC" ), 0 ); + rc += TestMethod( po, "AddTblToTblLst()", x.AddTblToTblList( d1, "TestTableA" ), 0 ); + rc += TestMethod( po, "AddTblToTblLst()", x.AddTblToTblList( d1, "TestTableB" ), 0 ); + rc += TestMethod( po, "AddTblToTblLst()", x.AddTblToTblList( d1, "TestTableD" ), 0 ); + + + // Next line should generate an exception + rc += TestMethod( po, "AddTblToTblLst()", x.AddTblToTblList( d1, "TestTableC" ), XB_DUP_TABLE_OR_ALIAS ); + + std::cout << "**** Next list should have one each of TestTableA, B, C, D sorted in alpha order ****" << std::endl; + x.DisplayTableList(); + + d2 = (xbDbf *) x.GetDbfPtr( "TestTableA" ); + if( d2 ) + std::cout << "[PASS] GetDbfPtr()" << std::endl; + else{ + std::cout << "[FAIL] GetDbfPtr()" << std::endl; + rc--; + } + + +std::cout << "cp0\n"; + x.SetDataDirectory( "/ABCDEFG/" ); + + #ifdef WIN32 + rc += TestMethod( po, "Set/GetDataDirectory()", x.GetDataDirectory(), "\\ABCDEFG\\", 9 ); + #else + rc += TestMethod( po, "Set/GetDataDirectory()", x.GetDataDirectory(), "/ABCDEFG/", 9 ); + #endif +std::cout << "cp1\n"; + x.SetDataDirectory( "/ABCDEFG" ); + #ifdef WIN32 + rc += TestMethod( po, "Set/GetDataDirectory()", x.GetDataDirectory(), "\\ABCDEFG", 8 ); + #else + rc += TestMethod( po, "Set/GetDataDirectory()", x.GetDataDirectory(), "/ABCDEFG", 8 ); + #endif + + + std::cout << "cp2\n"; + + + x.SetDataDirectory( "\\ABCDEFG\\"); + #ifdef WIN32 + rc += TestMethod( po, "Set/GetDataDirectory()", x.GetDataDirectory(), "\\ABCDEFG\\", 9 ); + #else + rc += TestMethod( po, "Set/GetDataDirectory()", x.GetDataDirectory(), "/ABCDEFG/", 9 ); + #endif + + std::cout << "cp3\n"; + + + x.SetDataDirectory( "\\ABCDEFG" ); + #ifdef WIN32 + rc += TestMethod( po, "Set/GetDataDirectory()", x.GetDataDirectory(), "\\ABCDEFG", 8 ); + #else + rc += TestMethod( po, "Set/GetDataDirectory()", x.GetDataDirectory(), "/ABCDEFG", 8 ); + #endif + + + std::cout << "cp4\n"; + + + x.SetDataDirectory( "ABCDEFG" ); + rc += TestMethod( po, "Set/GetDataDirectory()", x.GetDataDirectory(), "ABCDEFG", 7 ); + + + rc += TestMethod( po, "RemoveTblFromDbList()", x.RemoveTblFromTblList( "TestTableB" ), 0 ); + rc += TestMethod( po, "RemoveTblFromDbList()", x.RemoveTblFromTblList( "TestTableB" ), XB_NOT_FOUND ); + std::cout << "**** Next list should not have TestTableB in it ****" << std::endl; + x.DisplayTableList(); + + + rc += TestMethod( po, "RemoveTblFromDbList()", x.RemoveTblFromTblList( "TestTableA" ), 0 ); + rc += TestMethod( po, "RemoveTblFromDbList()", x.RemoveTblFromTblList( "TestTableC" ), 0 ); + rc += TestMethod( po, "RemoveTblFromDbList()", x.RemoveTblFromTblList( "TestTableD" ), 0 ); + + + delete d1; + + if( po > 0 || rc < 0 ) + fprintf( stdout, "Total Errors = %d\n", rc * -1 ); + + +#ifdef XB_LOGGING_SUPPORT + sMsg.Sprintf( "Program [%s] terminating with [%d] errors...", av[0], rc * -1 ); + x.WriteLogMessage( sMsg ); +#endif + + + return rc; +} + diff --git a/src/tests/xb_test_uda.cpp b/src/tests/xb_test_uda.cpp new file mode 100755 index 0000000..dec41fa --- /dev/null +++ b/src/tests/xb_test_uda.cpp @@ -0,0 +1,98 @@ +/* xb_test_uda.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 program tests the uda (user data area) functions + +// usage: xb_test_uda QUITE|NORMAL|VERBOSE + + + +#include "xbase.h" + +using namespace xb; + +#include "tstfuncs.cpp" + + +int main( int argCnt, char **av ) +{ + int iRc = 0; + //int iRc2 = 0; + int po = 1; /* print option */ + /* 0 - QUIET */ + /* 1 - NORMAL */ + /* 2 - VERBOSE */ + + if( argCnt > 1 ) { + if( av[1][0] == 'Q' ) + po = 0; + else if( av[1][0] == 'V' ) + po = 2; + } + + + + xbXBase x; +#ifdef XB_LOGGING_SUPPORT + x.EnableMsgLogging(); + if( po ){ + std::cout << "Logfile is [" << x.GetLogFqFileName().Str() << "]" << std::endl; + } + xbString sMsg; + sMsg.Sprintf( "Program [%s] initializing...", av[0] ); + x.WriteLogMessage( sMsg ); +#endif + + + x.SetDataDirectory( PROJECT_DATA_DIR ); + x.EnableMsgLogging(); + + xbUda uda; + iRc += TestMethod( po, "GetTokencCnt()", uda.GetTokenCnt(), 0 ); + xbString s1; + xbString s2; + iRc += TestMethod( po, "AddTokenForKey()", uda.AddTokenForKey( "B", "BBBB" ), XB_NO_ERROR ); + s1 = "C"; + iRc += TestMethod( po, "AddTokenForKey()", uda.AddTokenForKey( s1, "CCCC" ), XB_NO_ERROR ); + s2 = "DDDD"; + iRc += TestMethod( po, "AddTokenForKey()", uda.AddTokenForKey( "D", s2 ), XB_NO_ERROR ); + + s1 = "A"; + s2 = "AAAA"; + iRc += TestMethod( po, "AddTokenForKey()", uda.AddTokenForKey( s1, s2 ), XB_NO_ERROR ); + iRc += TestMethod( po, "AddTokenForKey()", uda.AddTokenForKey( "AA", "AAAAA" ), XB_NO_ERROR ); + iRc += TestMethod( po, "AddTokenForKey()", uda.AddTokenForKey( "BB", "BBBBB" ), XB_NO_ERROR ); + iRc += TestMethod( po, "AddTokenForKey()", uda.AddTokenForKey( "CC", "CCCCC" ), XB_NO_ERROR ); + iRc += TestMethod( po, "AddTokenForKey()", uda.AddTokenForKey( "DD", "DDDDD" ), XB_NO_ERROR ); + iRc += TestMethod( po, "GetTokencCnt()", uda.GetTokenCnt(), 8); + + iRc += TestMethod( po, "UpdTokenForKey()", uda.UpdTokenForKey( "BB", "9999" ), XB_NO_ERROR ); + iRc += TestMethod( po, "UpdTokenForKey()", uda.DelTokenForKey( "CC" ), XB_NO_ERROR ); + iRc += TestMethod( po, "GetTokencCnt()", uda.GetTokenCnt(), 7 ); + +#ifdef XB_DEBUG_SUPPORT + uda.DumpUda(); +#endif // XB_DEBUG_SUPPORT + + uda.Clear(); + iRc += TestMethod( po, "GetTokencCnt()", uda.GetTokenCnt(), 0 ); + +#ifdef XB_LOGGING_SUPPORT + sMsg.Sprintf( "Program [%s] terminating with [%d] errors...", av[0], iRc * -1 ); + x.WriteLogMessage( sMsg ); +#endif + + return iRc; +} diff --git a/src/tests/xb_test_xbase.cpp b/src/tests/xb_test_xbase.cpp new file mode 100755 index 0000000..6dad3f7 --- /dev/null +++ b/src/tests/xb_test_xbase.cpp @@ -0,0 +1,106 @@ +/* xb_test_xbase.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 program tests the class xbXBase + +// usage: xb_test_xbase QUITE|NORMAL|VERBOSE + + +#include "xbase.h" + +using namespace xb; + +#include "tstfuncs.cpp" + +int main( int argCnt, char **av ) +{ + int iRc = 0; + int po = 1; /* print option */ + /* 0 - QUIET */ + /* 1 - NORMAL */ + /* 2 - VERBOSE */ + + if( argCnt > 1 ) { + if( av[1][0] == 'Q' ) + po = 0; + else if( av[1][0] == 'V' ) + po = 2; + } + + + xbXBase x; + + +#ifdef XB_LOGGING_SUPPORT + x.EnableMsgLogging(); + if( po ){ + std::cout << "Logfile is [" << x.GetLogFqFileName().Str() << "]" << std::endl; + } + xbString sMsg; + sMsg.Sprintf( "Program [%s] initializing...", av[0] ); + x.WriteLogMessage( sMsg ); +#endif + InitTime(); + + + x.SetDefaultDateFormat( "YY-MM-DD" ); + iRc += TestMethod( po, "SetDefaultDateFormat", x.GetDefaultDateFormat(), "YY-MM-DD", 8 ); + + if( po == 2 ){ + if( x.GetEndianType() == 'L' ) + std::cout << "Little Endian Architecture" << std::endl; + else if( x.GetEndianType() == 'B' ) + std::cout << "Big Endian Architecture" << std::endl; + else + std::cout << "Undefine Endian Architecture" << std::endl; + } + + iRc += TestMethod( po, "GetErrorMessage", x.GetErrorMessage( XB_DBF_FILE_NOT_OPEN ), "DBF File Not Open", 17 ); + + +#ifdef XB_LOGGING_SUPPORT + xbString sLogDir = PROJECT_LOG_DIR; + iRc += TestMethod( po, "GetDefaultLogDirectory()", x.GetDefaultLogDirectory(), sLogDir, sLogDir.Len()); + + xbString sLogName = CMAKE_SYSTEM_NAME; + sLogName += "_"; + sLogName += XB_PLATFORM; + sLogName += ".xbLog.txt"; + iRc += TestMethod( po, "GetDefaultLogFileName()", x.GetDefaultLogFileName(), sLogName, sLogName.Len()); + + x.WriteLogMessage( "test" ); +#endif + + x.xbSleep( 250 ); + + + + if( po == 2 ){ + std::cout << "DisplayError Test ==> "; + x.DisplayError( 0 ); + } + + if( po > 0 || iRc < 0 ) + fprintf( stdout, "Total Errors = %d\n", iRc * -1 ); + + +#ifdef XB_LOGGING_SUPPORT + sMsg.Sprintf( "Program [%s] terminating with [%d] errors...", av[0], iRc * -1 ); + x.WriteLogMessage( sMsg ); +#endif + + return iRc; +} + diff --git a/src/utils/checkndx.cpp b/src/utils/checkndx.cpp new file mode 100755 index 0000000..adb5e1c --- /dev/null +++ b/src/utils/checkndx.cpp @@ -0,0 +1,71 @@ +/* + Xbase64 project source code + + Copyright (C) 1997,2003 Gary A. Kunkel + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Contact: + + Email: + + xbase64-devel@lists.sourceforge.net + xbase64-users@lists.sourceforge.net + +*/ + + +// FIXME - program needs to be updated + +#include + +int main(int ac,char** av) +{ +#ifdef XBASE_DEBUG + xbXBase x; + xbDbf d( &x ); + xbNdx i( &d ); + xbInt16 rc; + + if( 3 != ac ){ + std::cout << + "\nUsage: checkndx dbf_file index_file\n"; + return 1; + } + + if(( rc = d.Open( av[1] )) != XB_NO_ERROR ) + { + std::cout << "\nCould not open file " << av[1] << " rc = " << rc << "\n"; + return 2; + } + + if(( rc = i.OpenIndex( av[2] )) != XB_NO_ERROR ) + { + std::cout << "\nCould not open file " << av[2] << " rc = " << rc << "\n"; + return 3; + } + + std::cout << "\nRunning...\n"; + rc = i.CheckIndexIntegrity( 1 ); + std::cout << "\nNdx integrity check = " << rc << "\n"; + + i.DumpHdrNode(0); + + d.Close(); +#else + std::cout << "\nXBASE_DEBUG is not compiled in\n"; +#endif + return 0; +} diff --git a/src/utils/dbfxtrct.cpp b/src/utils/dbfxtrct.cpp new file mode 100755 index 0000000..17752c1 --- /dev/null +++ b/src/utils/dbfxtrct.cpp @@ -0,0 +1,182 @@ +/* + Xbase64 project source code + + This program extracts data from a dbf data file and puts it in + a comma delimited output file, suitable for input into an awk or + perl script + + This program excludes all memo fields + + Copyright (C) 1997,2003 Gary A. Kunkel + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Contact: + + Email: + + xbase64-devel@lists.sourceforge.net + xbase64-users@lists.sourceforge.net + +*/ + +#include + + +// FIXME - program need to be updated + +/*************************************************************************/ +void Usage(); +void Usage() +{ + std::cout << "\nUsage: dbfxtrct -iDATABASE.DBF -sINDEX.N[TD]X -f -F -dMM/DD/YY\n"; + std::cout << "\nWhere DATABASE.DBF is the name of the database file to dump\n"; + std::cout << "INDEX.NTX or .NDX is an optional index sort paramater\n"; + std::cout << "-f optional field name list in first record\n"; + std::cout << "-F optional field name and attributes in first record\n"; + std::cout << "MM/DD/YY is an optional output date format for any date fields\n"; + std::cout << "\nThis program creates output suitable for awk and perl scripts\n"; + std::cout << "\nThis program does not support memo fields (yet).\n"; +} +/*************************************************************************/ +int main(int ac,char** av) +{ + xbXBase x; + xbDbf d( &x ); + xbInt16 rc, FieldOption = 0; + xbIndex *ix = 0; + xbNdx z(&d); + + char *dbfname = NULL; + char *ixname = NULL; + char *p; + char buf[200]; + xbExpn exp( &x ); + +/* Get the input paramaters + + -i input datafile name + -s optional sort index name + -f optional field names in record one + -F optional field names and attributes in record one + -d date format +*/ + for( int i = 1; i < ac; i++ ) + { + p = av[i]; + if( *p != '-' ){ + std::cout << "Invalid paramater " << *p << std::endl; + Usage(); + return 1; + } + p++; + if( *p == 'i' ) + dbfname = ++p; + else if( *p == 's' ) + ixname = ++p; + else if( *p == 'f' ) + FieldOption = 1; + else if( *p == 'F' ) + FieldOption = 2; + else if( *p == 'd' ) + x.SetDefaultDateFormat( ++p ); + else{ + std::cout << "Invalid paramater " << *p << std::endl; + Usage(); + return 1; + } + } + +/* if missing params, display a usage message and exit */ + + if( !dbfname ){ + Usage(); + return 1; + } + +/* open the database file */ + + if(( rc = d.Open( dbfname )) != XB_NO_ERROR ) + { + std::cout << "\nCould not open file " << dbfname << " rc = " << rc + << "\n"; + return 2; + } + +/* if an index was specified, open the index file */ + + if( ixname ){ + +#ifdef XB_INDEX_NTX + if( strstr( ixname, "NTX" )) + ix = new xbNtx( &d ); +#endif + +#ifdef XB_INDEX_NDX + if( strstr( ixname, "NDX" )) + ix = new xbNdx( &d ); +#endif + if( !ix ){ + std::cout << "Unknown index type. .NTX and .NDX index file support only\n"; + return 3; + } + if(( rc = ix->OpenIndex( ixname )) != XB_NO_ERROR ) + { + std::cout << "\nCould not open index " << ixname << " rc = " << rc + << "\n"; + return 4; + } + } + + +/* if -f or -F paramater, dump the header information */ + if( FieldOption ){ + for( xbLong l = 0; l < d.FieldCount(); l++ ){ + if( l ) std::cout << ","; + std::cout << d.GetFieldName(l); + if( FieldOption == 2 ){ + std::cout << "|" << d.GetFieldType(l) << "|" << d.GetFieldLen(l); + std::cout << "|" << d.GetFieldDecimal(l); + } + } + std::cout << std::endl; + } + +/* if an index used, then loop thru each record, based on index, else + dump in dbf sort order */ + if( ixname ) + rc = ix->GetFirstKey(); + else + rc = d.GetFirstRecord(); + + while( rc == XB_NO_ERROR ){ + for( xbLong l = 0; l < d.FieldCount(); l++ ){ + if( l ) std::cout << ","; + strcpy( buf, exp.LTRIM( d.GetStringField( l ))); + if( d.GetFieldType( l ) == 'D' ) + std::cout << exp.DTOC( buf ); + else + std::cout << exp.TRIM( buf ); + } + if( ixname ) + rc = ix->GetNextKey(); + else + rc = d.GetNextRecord(); + std::cout << std::endl; + } + + d.Close(); + return 0; +} diff --git a/src/utils/preamble.txt b/src/utils/preamble.txt new file mode 100755 index 0000000..e2aeda8 --- /dev/null +++ b/src/utils/preamble.txt @@ -0,0 +1,19 @@ +/* xbXXXXX.XXX + +XBase64 Software Library + +Copyright (c) 1997,2003,2014,2017 Gary A Kunkel + +The xb64 software library is covered under +the terms of the GPL Version 3, 2007 license. + +Email Contact: + + xb64-devel@lists.sourceforge.net + xb64-users@lists.sourceforge.net + +*/ + + +namespace xb{ + diff --git a/src/utils/reindex.cpp b/src/utils/reindex.cpp new file mode 100755 index 0000000..3013167 --- /dev/null +++ b/src/utils/reindex.cpp @@ -0,0 +1,83 @@ +/* + Xbase64 project source code + + This sample program packs an Xbase DBF file + + Copyright (C) 1997,2003 Gary A. Kunkel + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Contact: + + Email: + + xbase64-devel@lists.sourceforge.net + xbase64-users@lists.sourceforge.net +*/ + + +// FIXME - program needs to be updated + +#include + +static void +showStatus(xbLong itemNum, xbLong numItems) +{ + printf("indexing record %ld of %ld\r", itemNum, numItems); + fflush(stdout); +} + +int main(int ac,char** av) +{ + if (3 != ac) { + std::cout << + "\nUsage: reindex dbf_file ndx_file\n" + ; + return 1; + } + + char* filename = av[1]; + char* filename2 = av[2]; + + xbXBase x; + xbDbf MyFile( &x ); + xbNdx MyIndex( &MyFile ); + + if( MyFile.Open( filename )) { + std::cout << "Could not open file " << filename << "\n"; + return 0; + } + if( MyIndex.OpenIndex( filename2 )) { + std::cout << "Could not open index file " << filename2 << "\n"; + return 0; + } + + xbInt16 rc = MyIndex.ReIndex(showStatus); + printf("\n"); + if( rc != XB_NO_ERROR ) { + std::cout << "\nError reindexing index ==> " << filename2; + std::cout << " Return Code = " << rc; + } + + /* or + if(( rc = MyFile.RebuildAllIndicis()) != XB_NO_ERROR ) + { + std::cout << "\nError reindexing..."; + std::cout << "\nReturn Code = " << rc; + } + */ + MyFile.Close(); + return 0; +} diff --git a/src/utils/xb_cfg_check.cpp b/src/utils/xb_cfg_check.cpp new file mode 100755 index 0000000..25ca46c --- /dev/null +++ b/src/utils/xb_cfg_check.cpp @@ -0,0 +1,370 @@ + +#include +#include +#include "xbase.h" + +using namespace xb; + + +int main() +{ + + fprintf( stdout, "\n%s version %d.%d.%d build options\n", CMAKE_PROJECT_NAME, xbase_VERSION_MAJOR, + xbase_VERSION_MINOR, xbase_VERSION_PATCH ); + // fprintf( stdout, "Usage: xb_cfg_check\n\n" ); + fprintf( stdout, "These options were in effect at library build time:\n" ); + + fprintf( stdout, "\nGeneral system variables:\n" ); + fprintf( stdout, "SYSTEM_NAME = [%s]\n", CMAKE_SYSTEM_NAME ); + fprintf( stdout, "SYSTEM_PROCESSOR = [%s]\n", CMAKE_SYSTEM_PROCESSOR ); + fprintf( stdout, "SYSTEM_VERSION = [%s]\n", CMAKE_SYSTEM_VERSION ); + fprintf( stdout, "PLATFORM = [%s]\n", XB_PLATFORM ); + fprintf( stdout, "BUILD TYPE = [%s]\n", CMAKE_BUILD_TYPE ); + fprintf( stdout, "CMAKE C FLAGS = [%s]\n", CMAKE_C_FLAGS ); + fprintf( stdout, "CMAKE C FLAGS DEBUG = [%s]\n", CMAKE_C_FLAGS_DEBUG ); + fprintf( stdout, "CMAKE C FLAGS RELEASE = [%s]\n", CMAKE_C_FLAGS_RELEASE ); + + + #ifdef XB_PLATFORM_32 + fprintf( stdout, "XB_PLATFORM_32 = [TRUE]\n" ); + #endif + #ifdef XB_PLATFORM_64 + fprintf( stdout, "XB_PLATFORM_64 = [TRUE]\n" ); + #endif + fprintf( stdout, "COMPILER = [%s]\n", CMAKE_COMPILER ); + #ifdef WIN32 + fprintf( stdout, "WIN32 = [ON]\n" ); + #else + fprintf( stdout, "WIN32 = [OFF]\n" ); + #endif + + /* + #ifdef XBDLLEXPORT + fprintf( stdout, "XBDLLEXPORT = [%s]\n", XBDLLEXPORT ); + #endif + */ + printf( "\n" ); + + + #ifdef CMAKE_COMPILER_IS_GNUCC + fprintf( stdout, "CMAKE_COMPILER_IS_GNUCC = [ON]\n" ); + #else + fprintf( stdout, "CMAKE_COMPILER_IS_GNUCC = [OFF]\n" ); + #endif + + #ifdef _FILE_OFFSET_BITS + fprintf( stdout, "_FILE_OFFSET_BITS = [DEFINED]\n" ); + #endif + + fprintf( stdout, "User controlled build options:\n" ); + + + #ifdef XB_DEBUG_SUPPORT + fprintf( stdout, "XB_DEBUG_SUPPORT = [ON]\n" ); + #else + fprintf( stdout, "XB_DEBUG_SUPPORT = [OFF]\n" ); + #endif + + #ifdef XB_LOGGING_SUPPORT + fprintf( stdout, "XB_LOGGING_SUPPORT = [ON]\n" ); + #else + fprintf( stdout, "XB_LOGGING_SUPPORT = [OFF]\n" ); + #endif + + #ifdef XB_MEMO_SUPPORT + fprintf( stdout, "XB_MEMO_SUPPORT = [ON]\n" ); + #else + fprintf( stdout, "XB_MEMO_SUPPORT = [OFF]\n" ); + #endif + + #ifdef XB_DBF3_SUPPORT + fprintf( stdout, "XB_DBF3_SUPPORT = [ON]\n" ); + #else + fprintf( stdout, "XB_DBF3_SUPPORT = [OFF]\n" ); + #endif + + #ifdef XB_DBF4_SUPPORT + fprintf( stdout, "XB_DBF4_SUPPORT = [ON]\n" ); + #else + fprintf( stdout, "XB_DBF4_SUPPORT = [OFF]\n" ); + #endif + + #ifdef XB_LINKLIST_SUPPORT + fprintf( stdout, "XB_LINKLIST_SUPPORT = [ON]\n" ); + #else + fprintf( stdout, "XB_LINKLIST_SUPPORT = [OFF]\n" ); + #endif + + #ifdef XB_LOCKING_SUPPORT + fprintf( stdout, "XB_LOCKING_SUPPORT = [ON]\n" ); + #else + fprintf( stdout, "XB_LOCKING_SUPPORT = [OFF]\n" ); + #endif + + #ifdef XB_EXPRESSION_SUPPORT + fprintf( stdout, "XB_EXPRESSION_SUPPORT = [ON]\n" ); + #else + fprintf( stdout, "XB_EXPRESSION_SUPPORT = [OFF]\n" ); + #endif + + #ifdef XB_LOCKING_SUPPORT + fprintf( stdout, "XB_EXAMPLES_SUPPORT = [ON]\n" ); + #else + fprintf( stdout, "XB_EXAMPLES_SUPPORT = [OFF]\n" ); + #endif + #ifdef XB_LOCKING_SUPPORT + fprintf( stdout, "XB_UTILS_SUPPORT = [ON]\n" ); + #else + fprintf( stdout, "XB_UTILS_SUPPORT = [OFF]\n" ); + #endif + + #ifdef XB_INDEX_SUPPORT + fprintf( stdout, "XB_INDEX_SUPPORT = [ON]\n" ); + #else + fprintf( stdout, "XB_INDEX_SUPPORT = [OFF]\n" ); + #endif + + #ifdef XB_INDEX_SUPPORT + fprintf( stdout, "XB_NDX_SUPPORT = [ON]\n" ); + #else + fprintf( stdout, "XB_NDX_SUPPORT = [OFF]\n" ); + #endif + + #ifdef XB_INDEX_SUPPORT + fprintf( stdout, "XB_MDX_SUPPORT = [ON]\n" ); + #else + fprintf( stdout, "XB_MDX_SUPPORT = [OFF]\n" ); + #endif + + #ifdef XB_SQL_SUPPORT + fprintf( stdout, "XB_SQL_SUPPORT = [ON]\n" ); + #else + fprintf( stdout, "XB_SQL_SUPPORT = [OFF]\n" ); + #endif + + + fprintf( stdout, "\nDirectories:\n" ); + fprintf( stdout, "HOME_DIRECTORY = [%s]\n", CMAKE_HOME_DIRECTORY ); + fprintf( stdout, "PROJECT_PARENT_DIR = [%s]\n", PROJECT_PARENT_DIR ); + fprintf( stdout, "PROJECT_BINARY_DIR = [%s]\n", PROJECT_BINARY_DIR ); + fprintf( stdout, "PROJECT_DATA_DIR = [%s]\n", PROJECT_DATA_DIR ); + fprintf( stdout, "PROJECT_LOG_DIR = [%s]\n", PROJECT_LOG_DIR ); + fprintf( stdout, "PROJECT_DFLT_LOGFILE = [%s]\n", PROJECT_DFLT_LOGFILE ); + fprintf( stdout, "PROJECT_SOURCE_DIR = [%s]\n", PROJECT_SOURCE_DIR ); + fprintf( stdout, "CMAKE_RUNTIME_OUTPUT_DIRECTORY = [%s]\n\n", CMAKE_RUNTIME_OUTPUT_DIRECTORY ); + + fprintf( stdout, "Libraries:\n" ); + fprintf( stdout, "BUILD_SHARED_LIBS = [%s]\n", BUILD_SHARED_LIBS ); + fprintf( stdout, "EXTRA_LIBS = [%s]\n\n", EXTRA_LIBS ); + + fprintf( stdout, "Field sizes:\n" ); + fprintf( stdout, "SIZEOF_VOID_P = [%s]\n", CMAKE_SIZEOF_VOID_P ); + fprintf( stdout, "sizeof(char *) = [%ld]\n", sizeof( char *)); + fprintf( stdout, "sizeof(int) = [%ld]\n", sizeof( int )); + fprintf( stdout, "sizeof(long) = [%ld]\n", sizeof( long )); + fprintf( stdout, "sizeof(char) = [%ld]\n", sizeof( char )); + #ifdef HAVE_WCHAR_H + fprintf( stdout, "sizeof(wchar_t) = [%ld]\n", sizeof( wchar_t )); + #endif + + #ifdef HAVE_WINDOWS_H + fprintf( stdout, "sizeof(DWORD) = [%ld]\n", sizeof( DWORD )); + #endif + + fprintf( stdout, "sizeof(double) = [%ld]\n", sizeof( double )); + fprintf( stdout, "sizeof(float) = [%ld]\n", sizeof( float )); + fprintf( stdout, "sizeof(size_t) = [%ld]\n", sizeof( size_t )); + fprintf( stdout, "sizeof(off_t) = [%ld]\n\n", sizeof( off_t )); + + fprintf( stdout, "sizeof(xbBool) = [%ld]\n", sizeof( xbBool )); + fprintf( stdout, "sizeof(xbInt16) = [%ld]\n", sizeof( xbInt16 )); + fprintf( stdout, "sizeof(xbUInt16) = [%ld]\n", sizeof( xbUInt16 )); + fprintf( stdout, "sizeof(xbInt32) = [%ld]\n", sizeof( xbInt32 )); + fprintf( stdout, "sizeof(xbUInt32) = [%ld]\n", sizeof( xbUInt32 )); + fprintf( stdout, "sizeof(xbInt64) = [%ld]\n", sizeof( xbInt64 )); + fprintf( stdout, "sizeof(xbUInt64) = [%ld]\n", sizeof( xbUInt64 )); + fprintf( stdout, "sizeof(xbFloat) = [%lu]\n", sizeof( xbFloat )); + fprintf( stdout, "sizeof(xbDouble) = [%lu]\n", sizeof( xbDouble )); + + + fprintf( stdout, "\nHeader files:\n" ); + #ifdef HAVE_CTYPE_H + fprintf( stdout, "HAVE_CTYPE_H = [YES]\n" ); + #else + fprintf( stdout, "HAVE_CTYPE_H = [NO]\n" ); + #endif + + #ifdef HAVE_DIRENT_H + fprintf( stdout, "HAVE_DIRENT_H = [YES]\n" ); + #else + fprintf( stdout, "HAVE_DIRENT_H = [NO]\n" ); + #endif + + #ifdef HAVE_FCNTL_H + fprintf( stdout, "HAVE_FCNTL_H = [YES]\n" ); + #else + fprintf( stdout, "HAVE_FCNTL_H = [NO]\n" ); + #endif + + #ifdef HAVE_INTTYPES_H + fprintf( stdout, "HAVE_INTTYPES_H = [YES]\n" ); + #else + fprintf( stdout, "HAVE_INTTYPES_H = [NO]\n" ); + #endif + + #ifdef HAVE_STDARG_H + fprintf( stdout, "HAVE_STDARG_H = [YES]\n" ); + #else + fprintf( stdout, "HAVE_STDARG_H = [NO]\n" ); + #endif + + #ifdef HAVE_STRING_H + fprintf( stdout, "HAVE_STRING_H = [YES]\n" ); + #else + fprintf( stdout, "HAVE_STRING_H = [NO]\n" ); + #endif + + #ifdef HAVE_STRINGS_H + fprintf( stdout, "HAVE_STRINGS_H = [YES]\n" ); + #else + fprintf( stdout, "HAVE_STRINGS_H = [NO]\n" ); + #endif + + #ifdef HAVE_STAT_H + fprintf( stdout, "HAVE_STAT_H = [YES]\n" ); + #else + fprintf( stdout, "HAVE_STAT_H = [NO]\n" ); + #endif + + #ifdef HAVE_UNISTD_H + fprintf( stdout, "HAVE_UNISTD_H = [YES]\n" ); + #else + fprintf( stdout, "HAVE_UNISTD_H = [NO]\n" ); + #endif + + #ifdef HAVE_WINDOWS_H + fprintf( stdout, "HAVE_WINDOWS_H = [YES]\n" ); + #else + fprintf( stdout, "HAVE_WINDOWS_H = [NO]\n" ); + #endif + + + fprintf( stdout, "\nC Library functions:\n" ); + + #ifdef HAVE__CLOSE_F + fprintf( stdout, "HAVE__CLOSE_F = [YES]\n" ); + #else + fprintf( stdout, "HAVE__CLOSE_F = [NO]\n" ); + #endif + + #ifdef HAVE_CREATEPROCESSW_F + fprintf( stdout, "HAVE_CREATEPROCESSW_F = [YES]\n" ); + #else + fprintf( stdout, "HAVE_CREATEPROCESSW_F = [NO]\n" ); + #endif + + #ifdef HAVE_FCNTL_F + fprintf( stdout, "HAVE_FCNTL_F = [YES]\n" ); + #else + fprintf( stdout, "HAVE_FCNTL_F = [NO]\n" ); + #endif + + #ifdef HAVE__FDOPEN_F + fprintf( stdout, "HAVE__FDOPEN_F = [YES]\n" ); + #else + fprintf( stdout, "HAVE__FDOPEN_F = [NO]\n" ); + #endif + + #ifdef HAVE_FOPEN_S_F + fprintf( stdout, "HAVE_FOPEN_S_F = [YES]\n" ); + #else + fprintf( stdout, "HAVE_FOPEN_S_F = [NO]\n" ); + #endif + + #ifdef HAVE__FSOPEN_F + fprintf( stdout, "HAVE__FSOPEN_F = [YES]\n" ); + #else + fprintf( stdout, "HAVE__FSOPEN_F = [NO]\n" ); + #endif + + #ifdef HAVE__FILENO_F + fprintf( stdout, "HAVE__FILENO_F = [YES]\n" ); + #else + fprintf( stdout, "HAVE__FILENO_F = [NO]\n" ); + #endif + + #ifdef HAVE_FORK_F + fprintf( stdout, "HAVE_FORK_F = [YES]\n" ); + #else + fprintf( stdout, "HAVE_FORK_F = [NO]\n" ); + #endif + + #ifdef HAVE__FSEEKI64_F + fprintf( stdout, "HAVE__FSEEKI64_F = [YES]\n" ); + #else + fprintf( stdout, "HAVE__FSEEKI64_F = [NO]\n" ); + #endif + + #ifdef HAVE_FSEEKO_F + fprintf( stdout, "HAVE_FSEEKO_F = [YES]\n" ); + #else + fprintf( stdout, "HAVE_FSEEKO_F = [NO]\n" ); + #endif + + #ifdef HAVE_FTRUNCATE_F + fprintf( stdout, "HAVE_FTRUNCATE_F = [YES]\n" ); + #else + fprintf( stdout, "HAVE_FTRUNCATE_F = [NO]\n" ); + #endif + + #ifdef HAVE__LOCALTIME_S_F + fprintf( stdout, "HAVE__LOCALTIME_S_F = [YES]\n" ); + #else + fprintf( stdout, "HAVE__LOCALTIME_S_F = [NO]\n" ); + #endif + + #ifdef HAVE_LOCKFILE_F + fprintf( stdout, "HAVE_LOCKFILE_F = [YES]\n" ); + #else + fprintf( stdout, "HAVE_LOCKFILE_F = [NO]\n" ); + #endif + + #ifdef HAVE_LOCKING_F + fprintf( stdout, "HAVE_LOCKING_F = [YES]\n" ); + #else + fprintf( stdout, "HAVE_LOCKING_F = [NO]\n" ); + #endif + + #ifdef HAVE__LOCKING_F + fprintf( stdout, "HAVE__LOCKING_F = [YES]\n" ); + #else + fprintf( stdout, "HAVE__LOCKING_F = [NO]\n" ); + #endif + + #ifdef HAVE__OPEN_F + fprintf( stdout, "HAVE__OPEN_F = [YES]\n" ); + #else + fprintf( stdout, "HAVE__OPEN_F = [NO]\n" ); + #endif + + #ifdef HAVE_SETENDOFFILE_F + fprintf( stdout, "HAVE_SETENDOFFILE_F = [YES]\n" ); + #else + fprintf( stdout, "HAVE_SETENDOFFILE_F = [NO]\n" ); + #endif + + #ifdef HAVE_VSNPRINTF_F + fprintf( stdout, "HAVE_VSNPRINTF_F = [YES]\n" ); + #else + fprintf( stdout, "HAVE_VSNPRINTF_F = [NO]\n" ); + #endif + + #ifdef HAVE_VSNPRINTF_S_F + fprintf( stdout, "HAVE_VSNPRINTF_S_F = [YES]\n" ); + #else + fprintf( stdout, "HAVE_VSNPRINTF_S_F = [NO]\n" ); + #endif + + return 0; +} + diff --git a/src/utils/xb_copydbf.cpp b/src/utils/xb_copydbf.cpp new file mode 100755 index 0000000..1f88eb9 --- /dev/null +++ b/src/utils/xb_copydbf.cpp @@ -0,0 +1,62 @@ +/* xb_copydbf.cpp + +XBase64 Software Library + +Copyright (c) 1997,2003,2014,2019 Gary A Kunkel + +The xb64 software library is covered under +the terms of the GPL Version 3, 2007 license. + +Email Contact: + + xb64-devel@lists.sourceforge.net + xb64-users@lists.sourceforge.net + +*/ + +#include +using namespace xb; + +int main(int ac,char** av) +{ + if (3 != ac) { + std::cout << + "\nUsage: xb_copydbf filename1 filename2\n" + "This program copies the structure of one dbf file to another\n"; + return 1; + } + + char* sFileName = av[1]; + char* tFileName = av[2]; + + xbXBase x; + xbInt16 iRc; + xbDbf *MyFile = NULL; + xbDbf *MyNewFile = NULL; + + #ifdef XB_LOGGING_SUPPORT + x.EnableMsgLogging(); + xbString sMsg; + sMsg.Sprintf( "Program [%s] initializing...", av[0] ); + x.WriteLogMessage( sMsg ); + #endif + + if(( iRc = x.OpenHighestVersion( sFileName, "", &MyFile )) != XB_NO_ERROR ){ + std::cout << "Could not open file iRc = " << iRc << " file = " << sFileName << std::endl; + x.DisplayError( iRc ); + return 0; + } + + if(( iRc = MyFile->CopyDbfStructure( MyNewFile, tFileName, tFileName, XB_DONTOVERLAY, XB_SINGLE_USER )) != XB_NO_ERROR ){ + std::cout << "Could not copy file " << tFileName << " Error = " << iRc << "\n"; + return 3; + } + + MyFile->Close(); + return 0; +} + + + + + diff --git a/src/utils/xb_dbfutil.cpp b/src/utils/xb_dbfutil.cpp new file mode 100755 index 0000000..e008155 --- /dev/null +++ b/src/utils/xb_dbfutil.cpp @@ -0,0 +1,3719 @@ +/* xb_dbfutil.cpp + +XBase64 Software Library + +Copyright (c) 1997,2003,2014,2021 Gary A Kunkel + +The xb64 software library is covered under +the terms of the GPL Version 3, 2007 license. + +Email Contact: + + xb64-devel@lists.sourceforge.net + xb64-users@lists.sourceforge.net + +*/ + +#include "xbase.h" + +#ifdef HAVE_DIRENT_H +#include +#endif + +#ifdef WIN32 +#include +#endif + +using namespace xb; + +///@cond DOXYOFF +class xbUtil{ + public: + xbUtil(); + ~xbUtil(); + xbDbf *GetTablePtr( const char * cTitle ); + void DisplayActiveTable() const; + + // menus + xbInt16 GetOption(); + xbInt32 GetLong(); + void ProcessOption( const xbString & sOption ); + void MainMenu(); + void SystemMenu(); + void FileMenu(); + void RecordMenu(); + void FieldMenu(); + void IndexMenu(); + void LockingMenu(); + void ExpressionMenu(); + void DebugMenu(); + void FilterMenu(); + + #ifdef XB_SQL_SUPPORT + void SqlMenu(); + #endif // XB_SQL_MENU + + #ifdef XB_NDXINF_SUPPORT + void InfFileMenu(); + #endif // XB_NDXINF_SUPPORT + + // 2 - SystemMenu options + void ListSystemSettings(); + void UpdateDataDirectory(); + void ToggleDefaultAutoCommit(); + #ifdef XB_MEMO_SUPPORT + void UpdateDefaultMemoBlockSize(); + #endif + void UpdateLogDirectory(); + void UpdateLogFileName(); + void ToggleLoggingStatus(); + void WriteLogMessage(); + #ifdef XB_LOCKING_SUPPORT + void UpdateDefaultLockRetries(); + void ToggleDefaultAutoLock(); + void UpdateDefaultLockFlavor(); + void UpdateDefaultLockWait(); + void ToggleMultiUserMode(); + #endif + + // 3 - FileMenu options + void ListFilesInDataDirectory(); + void Open(); + void Close(); + void CloseAllTables(); + void DisplayTableStats(); + void Pack(); + void ZapTable(); + void CopyDbfStructure(); + void UpdateTableAutoCommit(); + void DisplayTableInfo(); + + // 4 - RecordMenu options + void GetRecord(); + void BlankRecord(); + void AppendRecord(); + void PutRecord(); + void DeleteRecord(); + void UndeleteRecord(); + void SelectActiveTable(); + void GetFirstRecord(); + void GetNextRecord(); + void GetPrevRecord(); + void GetLastRecord(); + void DumpRecord(); + void AbortRecord(); + void CommitRecord(); + + #ifdef XB_FILTER_SUPPORT + void SetFilter(); + void GetFirstFilterRec(); + void GetNextFilterRec(); + void GetPrevFilterRec(); + void GetLastFilterRec(); + #endif // XB_FILTER_SUPPORT + + // 5 - FieldMenu options + void ListFieldInfo(); + void UpdateFieldData(); + #ifdef XB_MEMO_SUPPORT + void ShowMemoFieldData(); + void UpdateMemoFieldData(); + void DeleteMemoField(); + #endif + + // 6 - LockMenu options + #ifdef XB_LOCKING_SUPPORT + void DisplayFileLockSettings(); + void UpdateFileLockRetryCount(); + void UpdateFileLockFlavor(); + void UpdateFileAutoLock(); + void LockDbf(); + void UnlockDbf(); + void LockRecord(); + void UnlockRecord(); + void LockAppend(); + void UnlockAppend(); + #ifdef XB_INDEX_SUPPORT + void LockIndices(); + void UnlockIndices(); + #endif // XB_INDEX_SUPPORT + + #ifdef XB_MEMO_SUPPORT + void LockMemo(); + void UnlockMemo(); + #endif + + void LockHeader(); + void UnlockHeader(); + void xbFileLock(); + void xbFileUnlock(); + #endif + + // 7 - Expression Menu options + #ifdef XB_EXPRESSION_SUPPORT + void ParseExpression( xbInt16 iOption ); + void ProcessParsedExpression( xbInt16 iOption ); + void ParseAndProcessExpression(); + void JulToDate8(); + void Date8ToJul(); + void IsLeapYear(); + #ifdef XB_DEBUG_SUPPORT + void DumpExpressionTree(); + #endif + #endif + + // 8 - Index Menu Options + #ifdef XB_INDEX_SUPPORT + void DisplayOpenIndexFiles(); + void DisplaySupportedIndexTypes(); + void SelectTag(); + void OpenIndex(); + void CloseIndexFile(); + void CreateIndexTag(); + + void GetFirstKey(); + void GetNextKey(); + void GetPrevKey(); + void GetLastKey(); + void FindKey(); + + void CheckIntegrity(); + void Reindex(); + void DeleteTag(); + void AssociateNonProdIx(); + + void DumpIxHeader(); + void DumpIxNode(); + void DumpIxNodeChain(); + void DumpRecsByIx( xbInt16 iOpt ); + void DumpFreeBlocks(); + + #endif // XB_INDEX_SUPPORT + + + // 9 - SQL Menu Options + #ifdef XB_SQL_SUPPORT + void ExecSqlNonQuery(); + void ExecSqlQuery(); + #endif // XB_SQL_SUPPORT + + + // 10 - DebugMenu options + #ifdef XB_MEMO_SUPPORT + void DumpDbtHeader(); + void DumpMemoFreeChain(); + #endif + + // 11 - InfFileMenu options + #ifdef XB_NDXINF_SUPPORT + void ListInfFileData(); + void AddInfFileData(); + void DeleteInfFileData(); + void InfFileHelp(); + #endif + + + private: + xbXBase x; + xbDbf * dActiveTable; + + #ifdef XB_EXPRESSION_SUPPORT + xbExp *exp; + #endif // XB_EXPRESSION_SUPPORT + + #ifdef XB_SQL_SUPPORT + xbSql *sql; + #endif // XB_SQL_SUPPORT + + #ifdef XB_FILTER_SUPPORT + xbFilter *filt; + #endif // XB_FILTER_SUPPORT + +}; + +/*************************************************************************************/ +xbUtil::xbUtil() +{ + dActiveTable = NULL; + x.EnableMsgLogging(); + x.SetLogSize( 10000000L ); + + #ifdef XB_EXPRESSION_SUPPORT + exp = NULL; + #endif + + #ifdef XB_SQL_SUPPORT + sql = new xbSql( &x ); + #endif // XB_SQL_SUPPORT + + #ifdef XB_FILTER_SUPPORT + filt = NULL; + #endif // XB_FILTER_SUPPORT +} + + +/*************************************************************************************/ +xbUtil::~xbUtil(){ + + x.CloseAllTables(); + + if( dActiveTable ) + delete dActiveTable; + + #ifdef XB_SQL_SUPPORT + if( sql ) + delete sql; + #endif // XB_SQL_SUPPORT + + #ifdef XB_FILTER_SUPPORT + if( filt ) + delete filt; + #endif // XB_FILTER_SUPPORT +} + +/*************************************************************************************/ +#ifdef XB_NDXINF_SUPPORT + +void xbUtil::ListInfFileData() +{ + xbLinkListNode *llN = dActiveTable->GetNdxInfList(); + + xbString s; + while( llN ){ + s = llN->GetKey(); + std::cout << s.Str() << std::endl; + llN = llN->GetNextNode(); + } + +} + +void xbUtil::AddInfFileData() +{ + char cBuf[128]; + std::cout << "Enter NDX index file name (FILENAME.NDX)" << std::endl; + std::cin.getline( cBuf, 128 ); + xbInt16 iRc = dActiveTable->AssociateIndex( "NDX", cBuf, 0 ); + x.DisplayError( iRc ); +} + +void xbUtil::DeleteInfFileData() +{ + char cBuf[128]; + std::cout << "Enter NDX index file name (FILENAME.NDX)" << std::endl; + std::cin.getline( cBuf, 128 ); + xbInt16 iRc = dActiveTable->AssociateIndex( "NDX", cBuf, 1 ); + x.DisplayError( iRc ); +} + +void xbUtil::InfFileHelp() +{ + std::cout << std::endl; + std::cout << "Xbase64 uses an .INF file to link non production (NDX) index files to their associated DBF data file" << std::endl; +} +#endif // XB_NDXINF_SUPPORT + + +/*************************************************************************************/ +#ifdef XB_INDEX_SUPPORT +#ifdef XB_LOCKING_SUPPORT + +void xbUtil::LockIndices(){ + + // verify active table selected, if not, select one + if( !dActiveTable ) + dActiveTable = GetTablePtr( " - select table" ); + + if( !dActiveTable ){ + std::cout << "No table selected" << std::endl; + return; + } + xbInt16 iRc = dActiveTable->LockIndices( XB_LOCK ); + x.DisplayError( iRc ); +} + +void xbUtil::UnlockIndices(){ + + // verify active table selected, if not, select one + if( !dActiveTable ) + dActiveTable = GetTablePtr( " - select table" ); + + if( !dActiveTable ){ + std::cout << "No table selected" << std::endl; + return; + } + xbInt16 iRc = dActiveTable->LockIndices( XB_UNLOCK ); + x.DisplayError( iRc ); +} + +#endif // XB_LOCKING_SUPPORT + + +/*************************************************************************************/ +void xbUtil::ParseExpression( xbInt16 iOption ){ + + if( iOption == 0 ) + std::cout << "ParseExpression()\n"; + + if( !dActiveTable ) + dActiveTable = GetTablePtr( " - select table" ); + + if( !dActiveTable ){ + std::cout << "No table selected" << std::endl; + return; + } + xbInt16 iRc; + char sExpression[256]; + memset( sExpression, 0x00, 256 ); + + std::cout << "Enter expresion:\n"; + std::cin.getline( sExpression, 255 ); + + if( strlen( sExpression ) == 0 ){ + std::cout << "Invalid expression" << std::endl; + return; + } + + if( exp ){ + delete exp; + exp = NULL; + } + + exp = new xbExp( &x, dActiveTable ); + iRc = exp->ParseExpression( sExpression ); + + if( iOption == 0 ) + x.DisplayError( iRc ); +} + +/*************************************************************************************/ +void xbUtil::ProcessParsedExpression( xbInt16 iOption ){ + + if( iOption == 0 ) + std::cout << "ProcessParsedExpression()\n"; + if( !exp ) + ParseExpression( iOption ); + if( !exp ) + return; + + std::cout << "Dump from w/i dbfutil before processing expression\n"; + exp->DumpTree( xbTrue ); + std::cout << "-- end of dumptree in dbfutil before processExpression\n"; + + xbInt16 iRc = exp->ProcessExpression(); + if( iRc != XB_NO_ERROR ){ + x.DisplayError( iRc ); + return; + } + + std::cout << "Dump from w/i dbfutil after processing expression\n"; + exp->DumpTree( xbTrue ); + std::cout << "-- end of dumptree in dbfutil\n"; + + xbString sResult; + xbDouble dResult; + xbDate dtResult; + xbBool bResult; + + switch ( exp->GetReturnType()){ + case XB_EXP_CHAR: + exp->GetStringResult( sResult ); + std::cout << sResult.Str() << "\n"; + break; + case XB_EXP_DATE: + exp->GetDateResult( dtResult ); + std::cout << dtResult.Str() << "\n"; + break; + case XB_EXP_LOGICAL: + exp->GetBoolResult( bResult ); + std::cout << bResult << "\n"; + break; + case XB_EXP_NUMERIC: + exp->GetNumericResult( dResult ); + std::cout << dResult << "\n"; + break; + default: + std::cout << "Unknown result type [" << exp->GetReturnType() << std::endl; + break; + } + return; +} + +/*************************************************************************************/ +void xbUtil::ParseAndProcessExpression(){ + ParseExpression( 1 ); + if( exp ) + ProcessParsedExpression( 1 ); +} +/*************************************************************************************/ +#ifdef XB_DEBUG_SUPPORT +void xbUtil::DumpExpressionTree(){ + + if( exp ) + exp->DumpTree( xbTrue ); + else + std::cout << "No expression defined\n"; +} +#endif // XB_DEBUG_SUPPORT + + +void xbUtil::JulToDate8(){ + std::cout << "Convert Julian Date to Date8 (CCYYMMDD) format" << std::endl; + std::cout << "Enter Julian Value" << std::endl; + xbInt32 l = GetLong(); + xbDate d( l ); + std::cout << "Date8 value = [" << d.Str() << "]" << std::endl; +} + +void xbUtil::Date8ToJul(){ + char cLine[12]; + std::cout << "Convert Date8 (CCYYMMDD) format to Julian Date value" << std::endl; + std::cout << "Enter Date8 value:" << std::endl; + memset( cLine, 0x00, 12 ); + std::cin.getline( cLine, 9 ); + + if( strlen( cLine ) == 8 ){ + xbDate d( cLine ); + std::cout << "Julian Value = [" << d.JulianDays() << "]" << std::endl; + } else { + std::cout << "Invalid length, expecting 8 characters" << std::endl; + } +} + +void xbUtil::IsLeapYear(){ + std::cout << "Check leap year status for a given year" << std::endl; + std::cout << "Enter a four digit year" << std::endl; + xbInt32 l = GetLong(); + xbDate d( l ); + if( d.IsLeapYear( (xbInt16) l )) + std::cout << l << " is a leap year" << std::endl; + else + std::cout << l << " is not a leap year" << std::endl; +} +#endif // XB_EXPRESSION_SUPPORT +/*************************************************************************************/ +#ifdef XB_SQL_SUPPORT +void xbUtil::ExecSqlNonQuery(){ + + xbSql sql( &x ); + std::cout << "ExecSqlNonQuery\n"; + + char sSql[2048]; + std::cout << "Enter an SQL command (2K max byte max)" << std::endl; + std::cin.getline( sSql, 2048 ); + + xbInt16 iRc = sql.ExecuteNonQuery( sSql ); + x.DisplayError( iRc ); +} + +void xbUtil::ExecSqlQuery(){ + std::cout << "ExecSqlQuery\n"; +} +#endif // XB_SQL_SUPPORT + +/*************************************************************************************/ +#ifdef XB_LOCKING_SUPPORT +void xbUtil::DisplayFileLockSettings(){ + + if( !dActiveTable ) + dActiveTable = GetTablePtr( " - select table" ); + + if( !dActiveTable ){ + std::cout << "No table selected" << std::endl; + return; + } + std::cout << "File Lock Retry Count = [" << dActiveTable->GetLockRetryCount() << "]" << std::endl; + std::cout << "File Lock Flavor = ["; + switch (dActiveTable->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( dActiveTable->GetAutoLock()) + std::cout << "ON]" << std::endl; + else + std::cout << "OFF]" << std::endl; + if( dActiveTable->GetHeaderLocked()) + std::cout << "Header Locked = [TRUE]\n"; + else + std::cout << "Header Locked = [FALSE]\n"; + + if( dActiveTable->GetTableLocked()) + std::cout << "Table Locked = [TRUE]\n"; + else + std::cout << "Table Locked = [FALSE]\n"; + + if( dActiveTable->GetAppendLocked() > 0 ) + std::cout << "Append Locked = [" << dActiveTable->GetAppendLocked() << "]\n"; + else + std::cout << "Append Locked = [FALSE]\n"; + + #ifdef XB_MEMO_SUPPORT + if( dActiveTable->GetMemoLocked()) + std::cout << "Memo Locked = [TRUE]\n"; + else + std::cout << "Memo Locked = [FALSE]\n"; + #endif + + xbLinkListNode * llN = dActiveTable->GetFirstRecLock(); + if( llN ){ + while( llN ){ + std::cout << "Record Locked = [" << llN->GetKey() << "]\n"; + llN = llN->GetNextNode(); + } + } else { + std::cout << "Record Locked = [None]\n"; + } +} + +void xbUtil::UpdateFileLockRetryCount(){ + + if( !dActiveTable ) + dActiveTable = GetTablePtr( " - select table" ); + + if( !dActiveTable ){ + std::cout << "No table selected" << std::endl; + return; + } + + char cBuf[15]; + std::cout << std::endl << "Enter new File Lock Retry Count: " << std::endl; + std::cin.getline( cBuf, 15 ); + if( strlen( cBuf ) == 0 ){ + std::cout << "File Lock Retry Count not updated" << std::endl; + return; + } else { + dActiveTable->SetLockRetryCount( atoi( cBuf )); + std::cout << "File Lock Retry Count updated to [" + << dActiveTable->GetLockRetryCount() << "]" << std::endl; + } +} + +void xbUtil::UpdateFileLockFlavor(){ + + if( !dActiveTable ) + dActiveTable = GetTablePtr( " - select table" ); + + if( !dActiveTable ){ + std::cout << "No table selected" << std::endl; + return; + } + + std::cout << std::endl; + std::cout << "Enter new File Lock Flavor: " << std::endl; + std::cout << "1 = DBase" << std::endl; + std::cout << "2 = Clipper (not implemented yet)" << std::endl; + std::cout << "3 = Fox (not implemented yet)" << std::endl; + std::cout << "9 = XBase64 (not implemented yet)" << std::endl; + + char cBuf[15]; + std::cin.getline( cBuf, 15 ); + if( strlen( cBuf ) == 0 ){ + std::cout << "File Lock Flavor not updated" << std::endl; + return; + } else { + dActiveTable->SetLockFlavor( atoi( cBuf )); + std::cout << "File Lock Flavor updated to [" + << dActiveTable->GetLockFlavor() << "]" << std::endl; + } +} + +void xbUtil::UpdateFileAutoLock(){ + + if( !dActiveTable ) + dActiveTable = GetTablePtr( " - select table" ); + + if( !dActiveTable ){ + std::cout << "No table selected" << std::endl; + return; + } + + std::cout << "Enter new File Auto Lock: " << std::endl; + std::cout << "-1 = Use System Default" << std::endl; + std::cout << " 0 = Disable Auto Lock for this DBF file" << std::endl; + std::cout << " 1 = Enable Auto Lock for this DBF file" << std::endl; + + char cBuf[15]; + std::cin.getline( cBuf, 15 ); + if( strlen( cBuf ) == 0 ){ + std::cout << "File Auto Lock Flavor not updated" << std::endl; + return; + } + xbInt16 iOption = atoi( cBuf ); + if( iOption < -1 || iOption > 1 ){ + std::cout << "Must be one of:" << std::endl; + std::cout << " -1 = Use Default Auto Lock" << std::endl; + std::cout << " 0 = File Auto Lock Off" << std::endl; + std::cout << " 1 = File Auto Lock On" << std::endl; + std::cout << "File Auto Lock Flavor not updated" << std::endl; + return; + } else { + dActiveTable->SetAutoLock( iOption ); + std::cout << "File Auto Lock updated to [" + << dActiveTable->GetAutoLock() << "]" << std::endl; + } +} + +void xbUtil::LockDbf(){ + + if( !dActiveTable ) + dActiveTable = GetTablePtr( " - select table" ); + + if( !dActiveTable ){ + std::cout << "No table selected" << std::endl; + return; + } + xbInt16 iRc; + std::cout << std::endl << "Lock Table" << std::endl; + iRc = dActiveTable->LockTable( XB_LOCK ); + x.DisplayError( iRc ); +} + +void xbUtil::LockAppend(){ + + if( !dActiveTable ) + dActiveTable = GetTablePtr( " - select table" ); + + if( !dActiveTable ){ + std::cout << "No table selected" << std::endl; + return; + } + xbInt16 iRc; + std::cout << std::endl << "Lock Append" << std::endl; + iRc = dActiveTable->LockAppend( XB_LOCK ); + x.DisplayError( iRc ); +} + +void xbUtil::UnlockAppend(){ + + if( !dActiveTable ) + dActiveTable = GetTablePtr( " - select table" ); + + if( !dActiveTable ){ + std::cout << "No table selected" << std::endl; + return; + } + xbInt16 iRc; + std::cout << std::endl << "Unlock Append" << std::endl; + iRc = dActiveTable->LockAppend( XB_UNLOCK ); + x.DisplayError( iRc ); +} +#ifdef XB_MEMO_SUPPORT +void xbUtil::LockMemo(){ + + if( !dActiveTable ) + dActiveTable = GetTablePtr( " - select table" ); + + if( !dActiveTable ){ + std::cout << "No table selected" << std::endl; + return; + } + xbInt16 iRc; + std::cout << std::endl << "Lock Memo" << std::endl; + iRc = dActiveTable->LockMemo( XB_LOCK ); + x.DisplayError( iRc ); +} + +void xbUtil::UnlockMemo(){ + + if( !dActiveTable ) + dActiveTable = GetTablePtr( " - select table" ); + + if( !dActiveTable ){ + std::cout << "No table selected" << std::endl; + return; + } + xbInt16 iRc; + std::cout << std::endl << "Unlock Memo" << std::endl; + iRc = dActiveTable->LockMemo( XB_UNLOCK ); + x.DisplayError( iRc ); +} +#endif + +void xbUtil::LockRecord(){ + if( !dActiveTable ) + dActiveTable = GetTablePtr( " - select table" ); + + if( !dActiveTable ){ + std::cout << "No table selected" << std::endl; + return; + } + + xbInt16 iRc; + std::cout << std::endl << "Lock Record" << std::endl; + std::cout << "Enter record number to lock specific record" << std::endl; + + char cBuf[15]; + std::cin.getline( cBuf, 15 ); + iRc = dActiveTable->LockRecord( XB_LOCK, atol( cBuf )); + x.DisplayError( iRc ); +} + +void xbUtil::UnlockRecord(){ + if( !dActiveTable ) + dActiveTable = GetTablePtr( " - select table" ); + + if( !dActiveTable ){ + std::cout << "No table selected" << std::endl; + return; + } + + xbInt16 iRc; + std::cout << std::endl << "Unlock Record" << std::endl; + std::cout << "Enter record number to unlock specific record" << std::endl; + + char cBuf[15]; + std::cin.getline( cBuf, 15 ); + iRc = dActiveTable->LockRecord( XB_UNLOCK, atol( cBuf )); + x.DisplayError( iRc ); +} + +void xbUtil::UnlockDbf(){ + + if( !dActiveTable ) + dActiveTable = GetTablePtr( " - select table" ); + + if( !dActiveTable ){ + std::cout << "No table selected" << std::endl; + return; + } + + xbInt16 iRc; + std::cout << std::endl << "Unlock Table" << std::endl; + iRc = dActiveTable->LockTable( XB_UNLOCK ); + x.DisplayError( iRc ); +} + +void xbUtil::UnlockHeader(){ + + if( !dActiveTable ) + dActiveTable = GetTablePtr( " - select table" ); + + if( !dActiveTable ){ + std::cout << "No table selected" << std::endl; + return; + } + + xbInt16 iRc; + std::cout << std::endl << "Unlock Table Header" << std::endl; + iRc = dActiveTable->LockHeader( XB_UNLOCK ); + x.DisplayError( iRc ); +} + +void xbUtil::LockHeader(){ + if( !dActiveTable ) + dActiveTable = GetTablePtr( " - select table" ); + if( !dActiveTable ){ + std::cout << "No table selected" << std::endl; + return; + } + + xbInt16 iRc; + std::cout << std::endl << "Lock Table Header" << std::endl; + iRc = dActiveTable->LockHeader( XB_LOCK ); + x.DisplayError( iRc ); +} + +#ifdef XB_DEBUG_SUPPORT + +void xbUtil::xbFileLock(){ + if( !dActiveTable ) + dActiveTable = GetTablePtr( " - select table" ); + + if( !dActiveTable ){ + std::cout << "No table selected" << std::endl; + return; + } + + char cBufOffset[30]; + char cBufLen[30]; + + std::cout << std::endl << "Enter new File Lock Offset (in bytes from BOF): " << std::endl; + std::cin.getline( cBufOffset, 30 ); + std::cout << std::endl << "Enter new File Lock Len (in Bytes): " << std::endl; + std::cin.getline( cBufLen, 30 ); + + if( strlen( cBufOffset ) == 0 || strlen( cBufLen ) == 0 ){ + std::cout << "Offset and length required." << std::endl; + return; + } + + xbInt64 llSpos; + xbInt64 llLen; + xbString s1 = cBufOffset; + s1.CvtLongLong( llSpos ); + s1 = cBufLen; + s1.CvtLongLong( llLen ); + xbInt16 iRc = dActiveTable->xbLock( XB_LOCK, llSpos, (size_t) llLen ); + x.DisplayError( iRc ); +} + +void xbUtil::xbFileUnlock(){ + + if( !dActiveTable ) + dActiveTable = GetTablePtr( " - select table" ); + + if( !dActiveTable ){ + std::cout << "No table selected" << std::endl; + return; + } + char cBufOffset[30]; + char cBufLen[30]; + std::cout << std::endl << "Enter new File Lock Offset (in bytes from BOF): " << std::endl; + std::cin.getline( cBufOffset, 30 ); + std::cout << std::endl << "Enter new File Lock Len (in Bytes): " << std::endl; + std::cin.getline( cBufLen, 30 ); + if( strlen( cBufOffset ) == 0 || strlen( cBufLen ) == 0 ){ + std::cout << "Offset and length required." << std::endl; + return; + } + xbInt16 iRc; + iRc = dActiveTable->xbLock( XB_UNLOCK, atol( cBufOffset ), (size_t) atol( cBufLen )); + x.DisplayError( iRc ); +} + +#endif +#endif +/*************************************************************************************/ +#ifdef XB_DEBUG_SUPPORT +#ifdef XB_MEMO_SUPPORT + +void xbUtil::DumpDbtHeader(){ + + if( !dActiveTable ) + dActiveTable = GetTablePtr( " - select table" ); + + if( !dActiveTable ){ + std::cout << "No table selected" << std::endl; + return; + } + + if( !dActiveTable->MemoFieldsExist()){ + std::cout << "Table has no memo fields" << std::endl; + return; + } + + xbMemo * mPtr; + mPtr = dActiveTable->GetMemoPtr(); + mPtr->DumpMemoHeader(); + +} +#endif +#endif + +/*************************************************************************************/ +#ifdef XB_DEBUG_SUPPORT +#ifdef XB_MEMO_SUPPORT + +void xbUtil::DumpMemoFreeChain(){ + + if( !dActiveTable ) + dActiveTable = GetTablePtr( " - select table" ); + if( !dActiveTable ){ + std::cout << "No table selected" << std::endl; + return; + } + if( !dActiveTable->MemoFieldsExist()){ + std::cout << "Table has no memo fields" << std::endl; + return; + } + xbMemo * mPtr; + mPtr = dActiveTable->GetMemoPtr(); + mPtr->DumpMemoFreeChain(); +} +#endif +#endif + +/*************************************************************************************/ +void xbUtil::ListFieldInfo(){ + if( !dActiveTable ) + dActiveTable = GetTablePtr( " - select table" ); + if( !dActiveTable ){ + std::cout << "No table selected" << std::endl; + return; + } + DisplayActiveTable(); + dActiveTable->DumpHeader( 2 ); +} +/*************************************************************************************/ +void xbUtil::UpdateFieldData(){ + xbInt16 rc; + char cFldName[40]; + char cFldData[256]; + if( !dActiveTable ) + dActiveTable = GetTablePtr( " - select table" ); + if( !dActiveTable ){ + std::cout << "No table selected" << std::endl; + return; + } + std::cout << "Enter Field Name " << std::endl; + std::cin.getline( cFldName, 40 ); + + std::cout << "Enter Field Data " << std::endl; + std::cin.getline( cFldData, 256 ); + + if(( rc = dActiveTable->PutField( cFldName, cFldData )) != XB_NO_ERROR ){ + x.DisplayError( rc ); + return; + } + std::cout << "Success" << std::endl; +} +/*************************************************************************************/ +#ifdef XB_MEMO_SUPPORT +void xbUtil::ShowMemoFieldData(){ + xbInt16 rc; + char cFldName[40]; + char cBuf[15]; + xbString sMemoData; + xbUInt32 ulRecNo; + xbUInt32 ulFldLen; + if( !dActiveTable ) + dActiveTable = GetTablePtr( " - select table" ); + if( !dActiveTable ){ + std::cout << "No table selected" << std::endl; + return; + } + if( !dActiveTable->MemoFieldsExist()){ + std::cout << "Table has no memo fields" << std::endl; + return; + } + if( dActiveTable->GetCurRecNo() == 0 ){ + std::cout << "Enter Record number" << std::endl; + std::cin.getline( cBuf, 15 ); + if( strlen( cBuf ) == 0 ){ + std::cout << "Record number not entered" << std::endl; + return; + } + ulRecNo = atol( cBuf ); + if(( rc = dActiveTable->GetRecord( ulRecNo )) != XB_NO_ERROR ){ + x.DisplayError( rc ); + return; + } + } + std::cout << "Enter Memo Field Name " << std::endl; + std::cin.getline( cFldName, 40 ); + if(( rc = dActiveTable->GetMemoField( cFldName, sMemoData )) != XB_NO_ERROR ){ + std::cout << "rc = " << rc << std::endl; + x.DisplayError( rc ); + return; + } + if(( rc = dActiveTable->GetMemoFieldLen( cFldName, ulFldLen )) != XB_NO_ERROR ){ + std::cout << "rc = " << rc << std::endl; + x.DisplayError( rc ); + return; + } + std::cout << sMemoData.Str() << std::endl; + std::cout << "Data length = [" << ulFldLen << "]" << std::endl; +} +#endif +/*************************************************************************************/ +#ifdef XB_MEMO_SUPPORT +void xbUtil::UpdateMemoFieldData(){ + + xbInt16 rc; + char cFldName[40]; + char cBuf[15]; + char cMemoData[2048]; + xbUInt32 ulRecNo; + if( !dActiveTable ) + dActiveTable = GetTablePtr( " - select table" ); + if( !dActiveTable ){ + std::cout << "No table selected" << std::endl; + return; + } + if( !dActiveTable->MemoFieldsExist()){ + std::cout << "Table has no memo fields" << std::endl; + return; + } + if( dActiveTable->GetCurRecNo() == 0 ){ + std::cout << "Enter Record number" << std::endl; + std::cin.getline( cBuf, 15 ); + if( strlen( cBuf ) == 0 ){ + std::cout << "Record number not entered" << std::endl; + return; + } + ulRecNo = atol( cBuf ); + if(( rc = dActiveTable->GetRecord( ulRecNo )) != XB_NO_ERROR ){ + x.DisplayError( rc ); + return; + } + } + std::cout << "Enter Memo Field Name " << std::endl; + std::cin.getline( cFldName, 40 ); + std::cout << "Enter Memo Data (2048 bytes max) " << std::endl; + std::cin.getline( cMemoData, 2048 ); + if(( rc = dActiveTable->UpdateMemoField( cFldName, cMemoData )) != XB_NO_ERROR ){ + std::cout << "rc = " << rc << std::endl; + x.DisplayError( rc ); + return; + } + + if(( rc = dActiveTable->PutRecord( dActiveTable->GetCurRecNo())) != XB_NO_ERROR ){ + std::cout << "rc = " << rc << std::endl; + x.DisplayError( rc ); + return; + } +} +#endif + +/*************************************************************************************/ +#ifdef XB_MEMO_SUPPORT +void xbUtil::DeleteMemoField(){ + + xbInt16 rc; + char cFldName[40]; + char cBuf[15]; + xbUInt32 ulRecNo; + if( !dActiveTable ) + dActiveTable = GetTablePtr( " - select table" ); + if( !dActiveTable ){ + std::cout << "No table selected" << std::endl; + return; + } + + if( !dActiveTable->MemoFieldsExist()){ + std::cout << "Table has no memo fields" << std::endl; + return; + } + + if( dActiveTable->GetCurRecNo() == 0 ){ + std::cout << "Enter Record number" << std::endl; + std::cin.getline( cBuf, 15 ); + if( strlen( cBuf ) == 0 ){ + std::cout << "Record number not entered" << std::endl; + return; + } + ulRecNo = atol( cBuf ); + if(( rc = dActiveTable->GetRecord( ulRecNo )) != XB_NO_ERROR ){ + x.DisplayError( rc ); + return; + } + } + std::cout << "Enter Memo Field Name " << std::endl; + std::cin.getline( cFldName, 40 ); + if(( rc = dActiveTable->UpdateMemoField( cFldName, "" )) != XB_NO_ERROR ){ + std::cout << "rc = " << rc << std::endl; + x.DisplayError( rc ); + return; + } + if(( rc = dActiveTable->PutRecord( dActiveTable->GetCurRecNo())) != XB_NO_ERROR ){ + std::cout << "rc = " << rc << std::endl; + x.DisplayError( rc ); + return; + } +} +#endif +/*************************************************************************************/ +void xbUtil::SelectActiveTable(){ + + char cBuf[15]; + xbInt16 iLineNo; + if( x.GetOpenTableCount() == 0 ){ + std::cout << "No open tables" << std::endl; + std::cout << "Use menu option =3.4 to open a table" << std::endl; + return; + } + + x.DisplayTableList(); + std::cout << std::endl << "Enter line number:" << std::endl; + std::cin.getline( cBuf, 15 ); + if( strlen( cBuf ) == 0 ){ + std::cout << "Table not selected" << std::endl; + return; + } + iLineNo = atoi( cBuf ); + dActiveTable = x.GetDbfPtr( iLineNo ); +} + +/*************************************************************************************/ +void xbUtil::CommitRecord(){ + + xbInt16 rc = XB_NO_ERROR; + if( !dActiveTable ) + dActiveTable = GetTablePtr( " - select table" ); + + if( !dActiveTable ){ + std::cout << "No table selected" << std::endl; + return; + } + + DisplayActiveTable(); + + if(( rc = dActiveTable->Commit()) == XB_NO_ERROR ) + std::cout << "Success" << std::endl; + else + x.DisplayError( rc ); +} +/*************************************************************************************/ +void xbUtil::AbortRecord(){ + + xbInt16 rc = XB_NO_ERROR; + if( !dActiveTable ) + dActiveTable = GetTablePtr( " - select table" ); + + if( !dActiveTable ){ + std::cout << "No table selected" << std::endl; + return; + } + DisplayActiveTable(); + + + if(( rc = dActiveTable->Abort()) == XB_NO_ERROR ) + std::cout << "Success" << std::endl; + else + x.DisplayError( rc ); +} +/*************************************************************************************/ +void xbUtil::DumpRecord(){ + + char cBuf[15]; + xbInt16 rc; + xbUInt32 lRecNo; + if( !dActiveTable ) + dActiveTable = GetTablePtr( " - select table" ); + if( !dActiveTable ){ + std::cout << "No table selected" << std::endl; + return; + } + DisplayActiveTable(); + + if( dActiveTable->GetCurRecNo() == 0 ){ + std::cout << "Enter Record number" << std::endl; + std::cin.getline( cBuf, 15 ); + if( strlen( cBuf ) == 0 ){ + std::cout << "Record number not entered" << std::endl; + return; + } + lRecNo = atol( cBuf ); + if(( rc = dActiveTable->GetRecord( lRecNo )) != XB_NO_ERROR ){ + x.DisplayError( rc ); + return; + } + } + + if(( rc = dActiveTable->DumpRecord( dActiveTable->GetCurRecNo(), 2 )) == XB_NO_ERROR ) + std::cout << "Success" << std::endl; + else + x.DisplayError( rc ); +} +/*************************************************************************************/ +void xbUtil::GetFirstRecord(){ + + xbInt16 iRc; + + if( !dActiveTable ) + dActiveTable = GetTablePtr( " - select table" ); + + if( !dActiveTable ){ + std::cout << "No table selected" << std::endl; + return; + } + + if(( iRc = dActiveTable->GetFirstRecord()) == XB_NO_ERROR ) + dActiveTable->DumpRecord( dActiveTable->GetCurRecNo(), 2); + else + x.DisplayError( iRc ); +} +/*************************************************************************************/ +void xbUtil::GetNextRecord(){ + + xbInt16 iRc; + if( !dActiveTable ) + dActiveTable = GetTablePtr( " - select table" ); + if( !dActiveTable ){ + std::cout << "No table selected" << std::endl; + return; + } + if(( iRc = dActiveTable->GetNextRecord()) == XB_NO_ERROR ) + dActiveTable->DumpRecord( dActiveTable->GetCurRecNo(), 2); + else + x.DisplayError( iRc ); +} +/*************************************************************************************/ +void xbUtil::GetPrevRecord(){ + + xbInt16 iRc; + if( !dActiveTable ) + dActiveTable = GetTablePtr( " - select table" ); + if( !dActiveTable ){ + std::cout << "No table selected" << std::endl; + return; + } + if(( iRc = dActiveTable->GetPrevRecord()) == XB_NO_ERROR ) + dActiveTable->DumpRecord( dActiveTable->GetCurRecNo(), 2); + else + x.DisplayError( iRc ); +} +/*************************************************************************************/ +void xbUtil::GetLastRecord(){ + + xbInt16 iRc; + + if( !dActiveTable ) + dActiveTable = GetTablePtr( " - select table" ); + + if( !dActiveTable ){ + std::cout << "No table selected" << std::endl; + return; + } + if(( iRc = dActiveTable->GetLastRecord()) == XB_NO_ERROR ) + dActiveTable->DumpRecord( dActiveTable->GetCurRecNo(),2); + else + x.DisplayError( iRc ); +} + +/*************************************************************************************/ +void xbUtil::UndeleteRecord(){ + + char cBuf[15]; + xbInt16 rc; + xbUInt32 lRecNo; + + if( !dActiveTable ) + dActiveTable = GetTablePtr( " - select table" ); + + if( !dActiveTable ){ + std::cout << "No table selected" << std::endl; + return; + } + DisplayActiveTable(); + + if( dActiveTable->GetCurRecNo() == 0 ){ + std::cout << "Enter Record number" << std::endl; + std::cin.getline( cBuf, 15 ); + if( strlen( cBuf ) == 0 ){ + std::cout << "Record number not entered" << std::endl; + return; + } + lRecNo = atol( cBuf ); + if(( rc = dActiveTable->GetRecord( lRecNo )) != XB_NO_ERROR ){ + x.DisplayError( rc ); + return; + } + } + + if(( dActiveTable->RecordDeleted()) == xbFalse ) + std::cout << "Record is not flagged for deletion" << std::endl; + else{ + if(( rc = dActiveTable->UndeleteRecord()) == XB_NO_ERROR ) + std::cout << "Success" << std::endl; + else + x.DisplayError( rc ); + } +} + +/*************************************************************************************/ +void xbUtil::DeleteRecord(){ + + char cBuf[15]; + xbInt16 rc; + xbUInt32 lRecNo; + if( !dActiveTable ) + dActiveTable = GetTablePtr( " - select table" ); + if( !dActiveTable ){ + std::cout << "No table selected" << std::endl; + return; + } + DisplayActiveTable(); + if( dActiveTable->GetCurRecNo() == 0 ){ + std::cout << "Enter Record number" << std::endl; + std::cin.getline( cBuf, 15 ); + if( strlen( cBuf ) == 0 ){ + std::cout << "Record number not entered" << std::endl; + return; + } + lRecNo = atol( cBuf ); + if(( rc = dActiveTable->GetRecord( lRecNo )) != XB_NO_ERROR ){ + x.DisplayError( rc ); + return; + } + } + + if(( dActiveTable->RecordDeleted()) == xbTrue ) + std::cout << "Record is already flagged for deletion" << std::endl; + else{ + if(( rc = dActiveTable->DeleteRecord()) == XB_NO_ERROR ) + std::cout << "Success" << std::endl; + else + x.DisplayError( rc ); + } +} + +/*************************************************************************************/ +void xbUtil::PutRecord(){ + + char cBuf[15]; + xbInt16 rc; + xbUInt32 lRecNo; + + if( !dActiveTable ) + dActiveTable = GetTablePtr( " - select table" ); + + if( !dActiveTable ){ + std::cout << "No table selected" << std::endl; + return; + } + DisplayActiveTable(); + + std::cout << "Enter Record number" << std::endl; + std::cin.getline( cBuf, 15 ); + if( strlen( cBuf ) == 0 ){ + std::cout << "Record number not entered" << std::endl; + return; + } + lRecNo = atol( cBuf ); + if(( rc = dActiveTable->PutRecord( lRecNo )) == XB_NO_ERROR ) + std::cout << "Success" << std::endl; + else + x.DisplayError( rc ); +} + +/*************************************************************************************/ +void xbUtil::AppendRecord(){ + + xbInt16 rc; + if( !dActiveTable ) + dActiveTable = GetTablePtr( " - select table" ); + if( !dActiveTable ){ + std::cout << "No table selected" << std::endl; + return; + } + if(( rc = dActiveTable->AppendRecord()) == XB_NO_ERROR ) + std::cout << "Success" << std::endl; + else + x.DisplayError( rc ); +} + +/*************************************************************************************/ +void xbUtil::BlankRecord(){ + + xbInt16 rc; + if( !dActiveTable ) + dActiveTable = GetTablePtr( " - select table" ); + if( !dActiveTable ){ + std::cout << "No table selected" << std::endl; + return; + } + if(( rc = dActiveTable->BlankRecord()) == XB_NO_ERROR ) + std::cout << "Success" << std::endl; + else + x.DisplayError( rc ); +} + +/*************************************************************************************/ +void xbUtil::DisplayTableInfo(){ + + if( !dActiveTable ) + dActiveTable = GetTablePtr( " - select table" ); + + if( !dActiveTable ){ + std::cout << "No table selected" << std::endl; + return; + } + dActiveTable->DumpHeader( 1 ); +} + +/*************************************************************************************/ +void xbUtil::GetRecord(){ + + char cBuf[15]; + xbInt16 iRc; + xbUInt32 lRecNo; + + if( !dActiveTable ) + dActiveTable = GetTablePtr( " - select table" ); + + if( !dActiveTable ){ + std::cout << "No table selected" << std::endl; + return; + } + + DisplayActiveTable(); + + std::cout << "Enter Record number" << std::endl; + std::cin.getline( cBuf, 15 ); + if( strlen( cBuf ) == 0 ){ + std::cout << "Record number not entered" << std::endl; + return; + } + lRecNo = atol( cBuf ); + if(( iRc = dActiveTable->GetRecord( lRecNo )) == XB_NO_ERROR ) + dActiveTable->DumpRecord( dActiveTable->GetCurRecNo(), 2); + else + x.DisplayError( iRc ); +} +/*************************************************************************************/ +void xbUtil::DisplayActiveTable() const{ + if( dActiveTable ){ + std::cout << "Active Table = [" << dActiveTable->GetTblAlias().Str() << "] "; + xbUInt32 ulRecCnt = 0; + dActiveTable->ReadHeader( 0, 1 ); + dActiveTable->GetRecordCnt( ulRecCnt ); + std::cout << "Total Records = [" << ulRecCnt << "] "; + std::cout << "Current Record = [" << dActiveTable->GetCurRecNo() << "] "; + + if( dActiveTable->GetAutoCommit()) + std::cout << " Auto Commit = [Enabled]"; + else + std::cout << " Auto Commit = [Disabled]"; + + std::cout << std::endl; + #ifdef XB_INDEX_SUPPORT + xbIx *pIx = dActiveTable->GetCurIx(); + if( pIx ){ + void *vpTag = dActiveTable->GetCurTag(); + std::cout << "Active Tag = [" << pIx->GetTagName( vpTag ).Str() << "] Type = [" << dActiveTable->GetCurIxType().Str() << + "] \tFile Name = [" << pIx->GetFileName().Str() << "] Key = [" << pIx->GetKeyExpression( vpTag ).Str() << "]" << std::endl; + } + #endif // XB_INDEX_SUPPORT + std::cout << std::endl; + } +} +/*************************************************************************************/ +xbDbf * xbUtil::GetTablePtr( const char * cTitle ){ + + xbDbf *d; + xbInt16 iOpenTableCnt = x.GetOpenTableCount(); + char cBuf[15]; + xbInt16 iLineNo; + + if( iOpenTableCnt == 0 ){ + std::cout << "No open tables" << std::endl; + return NULL; + } else if( iOpenTableCnt == 1 ){ + d = x.GetDbfPtr( 1 ); + } else { + std::cout << "Select file/table " << cTitle << std::endl; + x.DisplayTableList(); + std::cout << std::endl << "Enter line number:" << std::endl; + memset( cBuf, 0x00, 15 ); + std::cin.getline( cBuf, 15 ); + if( strlen( cBuf ) == 0 ){ + std::cout << "Table not selected" << std::endl; + return NULL; + } + iLineNo = atoi( cBuf ); + if( iLineNo < 1 || iLineNo > iOpenTableCnt ){ + std::cout << "Invalid selection. Valid line numbers are 1 through " << iOpenTableCnt << std::endl; + return NULL; + } + d = x.GetDbfPtr( iLineNo ); + } + return d; +} +/*************************************************************************************/ +void xbUtil::UpdateTableAutoCommit(){ + xbDbf *d; + d = GetTablePtr( "" ); + if( d ){ + std::cout << "Xbase64 AutoCommit is functionality to determine if table updates should be posted" << std::endl; + std::cout << " to the table automatically, even if no xbDbf::PutRecord explicitly executed" << std::endl; + std::cout << " If unsure, leave the option turned on at the DBMS level (default)" << std::endl; + std::cout << " and don't over ride the setting at the table level" << std::endl << std::endl; + std::cout << " -1 ==> Use DBMS setting which is currently ["; + if( x.GetDefaultAutoCommit() ) + std::cout << "ON]" << std::endl; + else + std::cout << "OFF]" << std::endl; + std::cout << " 0 ==> Disable Auto Commit for table" << std::endl; + std::cout << " 1 ==> Enable Auto Commit for table" << std::endl; + std::cout << "Current setting is [" << d->GetAutoCommit() << "]" << std::endl; + char cBuf[15]; + xbInt16 iAuto; + std::cout << "Enter new Table Auto Commit: " << std::endl; + std::cin.getline( cBuf, 15 ); + if( strlen( cBuf ) == 0 ) { + std::cout << "Not updated" << std::endl; + return; + } + iAuto = atoi( cBuf ); + if( iAuto < -1 || iAuto > 1 ){ + std::cout << "Invalid value. Must be one of -1, 0 or 1" << std::endl; + return; + } + d->SetAutoCommit( iAuto ); + std::cout << "Auto Commit set to [" << d->GetAutoCommit(0) << "]" << std::endl; + if( d->GetAutoCommit() ) + std::cout << "Auto Commit enabled for table" << std::endl; + else + std::cout << "Auto Commit disabled for table" << std::endl; + } +} +/*************************************************************************************/ +void xbUtil::CopyDbfStructure(){ + xbDbf *d; + xbInt16 rc; + char filename[128]; + xbFile f( &x ); + xbDbf *dNewTable; + d = GetTablePtr( "" ); + + if( d ){ + + if( d->GetVersion() == 3 ){ + #ifdef XB_DBF3_SUPPORT + dNewTable = new xbDbf3( &x ); + #else + std::cout << "Dbase III file support not build into library. See XB_DBF3_SUPPORT" << std::endl; + return; + #endif + } else if( d->GetVersion() == 4 ){ + #ifdef XB_DBF4_SUPPORT + dNewTable = new xbDbf4( &x ); + #else + std::cout << "Dbase IV file support not build into library. See XB_DBF4_SUPPORT" << std::endl; + return; + #endif + } else { + std::cout << "Unsupported file type file = " << d->GetVersion() << std::endl; + return; + } + std::cout << "Copy Table" << std::endl; + std::cout << "Enter new DBF file name (ie; myfile.dbf or MYFILE.DBF): "; + std::cin.getline( filename, 128 ); + f.SetFileName( filename ); + if( strlen( filename ) == 0 ){ + std::cout << "No file name entered" << std::endl; + return; + } + if(( rc = f.FileExists( f.GetFqFileName() )) == xbTrue ){ + std::cout << "File [" << f.GetFqFileName().Str() << "] already exists " << std::endl; + return; + } + if(( rc = d->CopyDbfStructure( dNewTable, filename, filename, 0, XB_MULTI_USER )) != XB_NO_ERROR ){ + std::cout << "Error " << rc << " creating new file" << std::endl; + x.DisplayError( rc ); + return; + } + std::cout << "Table " << f.GetFqFileName().Str() << " created" << std::endl; + if(( rc = dNewTable->Close()) != XB_NO_ERROR ){ + std::cout << "Error " << rc << " closing new file" << std::endl; + x.DisplayError( rc ); + return; + } + } +} +/*************************************************************************************/ +void xbUtil::ZapTable(){ + + xbInt16 iRc; + if( !dActiveTable ) + dActiveTable = GetTablePtr( " - select table" ); + if( !dActiveTable ){ + std::cout << "No table selected" << std::endl; + return; + } + DisplayActiveTable(); + + iRc = dActiveTable->Zap(); + if( iRc == XB_NO_ERROR ) + std::cout << "Table Zapped (all rows deleted)" << std::endl; + else + x.DisplayError( iRc ); +} +/*************************************************************************************/ +void xbUtil::Pack(){ + + if( !dActiveTable ) + dActiveTable = GetTablePtr( " - select table" ); + if( !dActiveTable ){ + std::cout << "No table selected" << std::endl; + return; + } + DisplayActiveTable(); + xbInt16 iRc; + iRc = dActiveTable->Pack(); + if( iRc == XB_NO_ERROR ) + std::cout << "Table Packed" << std::endl; + else + x.DisplayError( iRc ); +} +/*************************************************************************************/ +void xbUtil::DisplayTableStats(){ + + xbDbf *d; + char cBuf[15]; + xbInt16 iOptionNo; + d = GetTablePtr( "" ); + if( d ){ + std::cout << "Enter option" << std::endl; + std::cout << "1 - Header data only" << std::endl; + std::cout << "2 - Field data only" << std::endl; + std::cout << "3 - Header and Field data" << std::endl; + std::cout << "4 - Header, Field and Memo Header if applicable" << std::endl; + std::cout << "5 - DBMS Settings (not stored in the file)" << std::endl << std::endl; + std::cin.getline( cBuf, 15 ); + if( strlen( cBuf ) == 0 ){ + std::cout << "Option not entered" << std::endl; + return; + } + iOptionNo = atoi( cBuf ); + if( iOptionNo < 1 || iOptionNo > 5 ){ + std::cout << "Invalid option [" << cBuf << "] entered. Defaulting to 1" << std::endl; + iOptionNo = 1; + } + if( iOptionNo < 5 ) { + d->ReadHeader( xbTrue, 0 ); + d->DumpHeader( iOptionNo ); + } else { + // DBMS settings + if( d->GetAutoCommit( 0 ) == -1 ){ + std::cout << "Table Auto Commit = [Use DBMS Setting]" << std::endl; + if( x.GetDefaultAutoCommit() ) + std::cout << "DBMS Auto Commit = [ON]" << std::endl; + else + std::cout << "DBMS Auto Commit = [OFF]" << std::endl; + } + else if( d->GetAutoCommit( 0 ) == 0 ) + std::cout << "Table Auto Commit = [OFF]" << std::endl; + else + std::cout << "Table Auto Commit = [ON]" << std::endl; + } + } +} +/*************************************************************************************/ +void xbUtil::CloseAllTables(){ + + xbInt16 sOpenTableCnt = x.GetOpenTableCount(); + xbInt16 rc; + if( sOpenTableCnt == 0 ){ + std::cout << "No open tables" << std::endl; + return; + } + rc = x.CloseAllTables(); + if( rc == XB_NO_ERROR ) + std::cout << "All open tables closed" << std::endl; + else + x.DisplayError( rc ); + dActiveTable = NULL; +} + +/*************************************************************************************/ +void xbUtil::Close(){ + xbDbf *d; +// xbInt16 sOpenTableCnt = x.GetOpenTableCount(); + + d = GetTablePtr( "to close" ); + if( d ){ + d->Close(); + std::cout << "Table closed" << std::endl; + if( d == dActiveTable ) + dActiveTable = NULL; + } + else + printf( "Can't identify table\n"); +} +/*************************************************************************************/ +void xbUtil::Open(){ + xbInt16 rc; + xbFile f( &x ); + xbDbf *dTable; + char filename[128]; + unsigned char cFileTypeByte; + + std::cout << "Open Table" << std::endl; + std::cout << "Enter DBF file name (.dbf or .DBF): "; + std::cin.getline( filename, 128 ); + f.SetFileName( filename ); + + if( strlen( filename ) == 0 ){ + std::cout << "No file name entered" << std::endl; + return; + } + if(( rc = f.FileExists( f.GetFqFileName() )) != xbTrue ){ + std::cout << "File [" << f.GetFqFileName().Str() << "] does not exist " << std::endl; + return; + } + if(( rc = f.GetXbaseFileTypeByte( f.GetFqFileName(), cFileTypeByte )) != XB_NO_ERROR ){ + std::cout << "Could not open file or determine file type rc = " << rc << " file = " << filename << std::endl; + x.DisplayError( rc ); + return; + } + std:: cout << "File Type Byte "; + x.BitDump( cFileTypeByte ); + std::cout << "\n"; + std::cout << "Table Type = [" << f.DetermineXbaseTableVersion( cFileTypeByte ) << "]\n"; + std::cout << "Memo Type = [" << f.DetermineXbaseMemoVersion( cFileTypeByte ) << "]\n"; + + if( f.DetermineXbaseTableVersion( cFileTypeByte ) == 4 ){ + #ifdef XB_DBF4_SUPPORT + dTable = new xbDbf4( &x ); + #else + std::cout << "Dbase IV file support not build into library. See XB_DBF4_SUPPORT" << std::endl; + return; + #endif + + } else if( f.DetermineXbaseTableVersion( cFileTypeByte ) == 3 ){ + #ifdef XB_DBF3_SUPPORT + dTable = new xbDbf3( &x ); + #else + std::cout << "Dbase III file support not build into library. See XB_DBF3_SUPPORT" << std::endl; + return; + #endif + } else { + std::cout << "Unsupported file type file = " << filename << " type = "; + x.BitDump( cFileTypeByte ); + std::cout << std::endl; + return; + } + + if(( rc = dTable->Open( filename )) != 0 ){ + std::cout << "Could not open file rc = " << rc << " file = " << filename << std::endl; + x.DisplayError( rc ); + return; + } + + dActiveTable = dTable; + dActiveTable->GetFirstRecord(); + + if( dActiveTable ) + std::cout << dActiveTable->GetTblAlias().Str() << " opened" << std::endl; + else + std::cout << "dActiveTable not set" << std::endl; +} +/*************************************************************************************/ +#ifdef XB_LOCKING_SUPPORT +void xbUtil::UpdateDefaultLockRetries(){ + char cBuf[15]; + std::cout << std::endl << "Enter new Default Lock Retry Count: " << std::endl; + std::cin.getline( cBuf, 15 ); + if( strlen( cBuf ) == 0 ){ + std::cout << "Default Lock Retry Count not updated" << std::endl; + return; + } else { + x.SetDefaultLockRetries( atoi( cBuf )); + std::cout << "Default Lock Retry Count updated to [" + << x.GetDefaultLockRetries() << "]" << std::endl; + } +} + +void xbUtil::UpdateDefaultLockWait(){ + char cBuf[15]; + std::cout << std::endl << "Enter new Default Lock Wait Time (in millisecs 1000=1 second): " << std::endl; + std::cin.getline( cBuf, 15 ); + if( strlen( cBuf ) == 0 ){ + std::cout << "Default Lock Wait Time not updated" << std::endl; + return; + } else { + x.SetDefaultLockWait( atoi( cBuf )); + std::cout << "Default Lock Wait Time updated to [" + << x.GetDefaultLockWait() << "]" << std::endl; + } +} + +void xbUtil::UpdateDefaultLockFlavor(){ + char cBuf[15]; + std::cout << std::endl; + std::cout << "Enter new Default Lock Flavor: " << std::endl; + std::cout << "1 = DBase" << std::endl; + std::cout << "2 = Clipper (not implemented yet)" << std::endl; + std::cout << "3 = Fox (not implemented yet)" << std::endl; + std::cout << "9 = XBase64 (not implemented yet)" << std::endl; + std::cin.getline( cBuf, 15 ); + if( strlen( cBuf ) == 0 ){ + std::cout << "Default Lock Flavor not updated" << std::endl; + return; + } else { + x.SetDefaultLockFlavor( atoi( cBuf )); + std::cout << "Default Lock Flavor updated to [" + << x.GetDefaultLockFlavor() << "]" << std::endl; + } +} +void xbUtil::ToggleDefaultAutoLock(){ + if( x.GetDefaultAutoLock()){ + x.DisableDefaultAutoLock(); + x.WriteLogMessage( "xb_dbfutil - Default Auto Lock disabled" ); + std::cout << "Default Auto Lock disabled" << std::endl; + } else { + x.EnableDefaultAutoLock(); + x.WriteLogMessage( "xb_dbfutil - Default Auto Lock enabled" ); + std::cout << "Default Auto Lock enabled" << std::endl; + } +} + +void xbUtil::ToggleMultiUserMode(){ + if( x.GetMultiUser()){ + x.SetMultiUser( xbFalse ); + x.WriteLogMessage( "xb_dbfutil - Multi user mode disabled" ); + std::cout << "Multi user mode disabled" << std::endl; + } else { + x.SetMultiUser( xbTrue ); + x.WriteLogMessage( "xb_dbfutil - Multu user mode enabled" ); + std::cout << "Multi user mode enabled" << std::endl; + } +} +#endif +/*************************************************************************************/ +void xbUtil::ListFilesInDataDirectory(){ + std::cout << "List files for [" << x.GetDataDirectory().Str() << "]" << std::endl << std::endl; + xbString s1; + xbInt16 iCnt = 0; + +#ifdef WIN32 + + WIN32_FIND_DATA fData; + HANDLE hFile; + + xbString sSearchPath = x.GetDataDirectory(); + xbUInt32 l = sSearchPath.Len(); + char cPathSeperator = sSearchPath.GetPathSeparator(); + char cLastChar = sSearchPath.GetCharacter( l ); + + if( cLastChar == cPathSeperator ) + sSearchPath += "*.*"; + else if( cPathSeperator ){ + sSearchPath += cPathSeperator; + sSearchPath += "*.*"; + } + else + sSearchPath += "\\*.*"; + + hFile = FindFirstFile( sSearchPath.Str(), &fData ); + if( hFile == INVALID_HANDLE_VALUE ){ + std::cout << "Could not open directory" << std::endl; + return; + } + + do{ + s1 = fData.cFileName; + if( s1 != "." && s1 != ".." ){ + std::cout << fData.cFileName << std::endl; + iCnt++; + } + } while( FindNextFile( hFile, &fData )); + std::cout << std::endl << iCnt << " entries" << std::endl; +#else + DIR *dir; + struct dirent *ent; + + if(( dir = opendir( x.GetDataDirectory() )) != NULL ){ + while(( ent = readdir( dir )) != NULL ){ + s1 = ent->d_name; + if( s1 != "." && s1 != ".." ){ + std::cout << ent->d_name << std::endl; + iCnt++; + } + } + std::cout << std::endl << iCnt << " entries" << std::endl; + closedir( dir ); + } + else + std::cout << "Could not open directory" << std::endl; +#endif +} +/*************************************************************************************/ +void xbUtil::UpdateLogDirectory(){ + + char cNewDir[256]; + cNewDir[0] = 0x00; + std::cout << std::endl << "Update Log Directory" << std::endl; + std::cout << "Current Log File Directory = [" << x.GetLogDirectory().Str() << "]" << std::endl; + std::cout << "Enter '1' to erase the Log File Directory" << std::endl; + std::cout << "Enter '2' to change Log File Directory to [" << x.GetDefaultLogDirectory().Str() << "]" << std::endl; + std::cout << "Enter new log directory. Enter for no updates." << std::endl; + std::cin.getline( cNewDir, 256 ); + if( strlen( cNewDir ) > 0 ){ + if( cNewDir[0] == '1' ) + x.SetLogDirectory( "" ); + else if( cNewDir[0] == '2' ) + x.SetLogDirectory( x.GetDefaultLogDirectory()); + else + x.SetLogDirectory( cNewDir ); + + std::cout << "Log File Directory is [" << x.GetLogDirectory().Str() << "]" << std::endl; + } + else + std::cout << "Log Directory not updated" << std::endl; +} +/*************************************************************************************/ +void xbUtil::UpdateLogFileName(){ + char cNewFile[256]; + cNewFile[0] = 0x00; + + std::cout << std::endl << "Update Log File Name" << std::endl; + std::cout << "Current Log File Name = [" << x.GetLogFileName().Str() << "]" << std::endl; + std::cout << "Enter '1' to change change Log File Name to default [" << x.GetDefaultLogFileName().Str() << "]" << std::endl; + std::cout << "Enter new Log File Name" << std::endl; + std::cin.getline( cNewFile, 256 ); + if( strlen( cNewFile ) > 0 ){ + if( cNewFile[0] == '1' ) + x.SetLogFileName( x.GetDefaultLogFileName()); + else + x.SetLogFileName( cNewFile ); + + std::cout << "Log File Name is [" << x.GetLogFileName().Str() << "]" << std::endl; + } else + std::cout << "Log File Name not updated" << std::endl; +} +/*************************************************************************************/ +void xbUtil::WriteLogMessage(){ + if( x.GetLogStatus()) { + char cMsg[256]; + std::cout << "Enter a message to write to the log file (256 byte max)" << std::endl; + std::cin.getline( cMsg, 256 ); + x.WriteLogMessage( cMsg ); + } else + std::cout << "Logging disabled" << std::endl; +} +/*************************************************************************************/ +void xbUtil::ToggleLoggingStatus(){ + if( x.GetLogStatus()){ + x.DisableMsgLogging(); + x.WriteLogMessage( "xb_dbfutil - Logging disabled" ); + std::cout << "Logging disabled" << std::endl; + } else { + x.EnableMsgLogging(); + x.WriteLogMessage( "xb_dbfutil - Logging enabled" ); + std::cout << "Logging enabled" << std::endl; + } +} +/*************************************************************************************/ +void xbUtil::ToggleDefaultAutoCommit(){ + if( x.GetDefaultAutoCommit()){ + x.SetDefaultAutoCommit( xbFalse ); + x.WriteLogMessage( "xb_dbfutil - Default Auto Commit disabled" ); + std::cout << "Default Auto Commit disabled" << std::endl; + } else { + x.SetDefaultAutoCommit( xbTrue ); + x.WriteLogMessage( "xb_dbfutil - Default Auto Commit enabled" ); + std::cout << "Default Auto Commit enabled" << std::endl; + } +} +/*************************************************************************************/ +void xbUtil::UpdateDataDirectory(){ + char cNewDir[256]; + cNewDir[0] = 0x00; + std::cout << std::endl << "Update Default Data Directory" << std::endl; + std::cout << "Current Default Data Directory = [" << x.GetDataDirectory().Str() << "]" << std::endl; + std::cout << "Enter '1' to erase the Default Data Directory" << std::endl; + std::cout << "Enter '2' to change Default Data Directory to [" << PROJECT_DATA_DIR << "]" << std::endl; + std::cout << "Enter new directory" << std::endl; + std::cin.getline( cNewDir, 256 ); + if( strlen( cNewDir ) > 0 ){ + if( cNewDir[0] == '1' ) + x.SetDataDirectory( "" ); + else if( cNewDir[0] == '2' ) + x.SetDataDirectory( PROJECT_DATA_DIR ); + else + x.SetDataDirectory( cNewDir ); + std::cout << "Default Data Directory is [" << x.GetDataDirectory().Str() << "]" << std::endl; + } + else + std::cout << "Default Data Directory not updated" << std::endl; +} +/*************************************************************************************/ +void xbUtil::ListSystemSettings(){ + std::cout << std::endl << "List System Settings" << std::endl; + std::cout << "Default Data Directory = [" << x.GetDataDirectory().Str() << "]" << std::endl; +// std::cout << "Default File Version = [" << x.GetDefaultFileVersion() << "]" << std::endl; + #ifdef XB_LOCKING_SUPPORT + std::cout << "Default Auto Locking = ["; + if( x.GetDefaultAutoLock()) + std::cout << "ON]" << std::endl; + else + std::cout << "OFF]" << std::endl; + + std::cout << "Default Lock Retries = [" << x.GetDefaultLockRetries() << "]" << std::endl; + std::cout << "Default Lock Wait Time = [" << x.GetDefaultLockWait() << "] (millisecs)" << std::endl; + + std::cout << "Default Lock Flavor = ["; + switch (x.GetDefaultLockFlavor()){ + 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; + } + #endif + std::cout << "Default Log Directory = [" << x.GetDefaultLogDirectory().Str() << "]" << std::endl; + std::cout << "Default Logfile Name = [" << x.GetDefaultLogFileName().Str() << "]" << std::endl; + std::cout << "Default Auto Commit = ["; + if( x.GetDefaultAutoCommit()) + std::cout << "ON]" << std::endl; + else + std::cout << "OFF]" << std::endl; + + std::cout << "Logging Status = ["; + if( x.GetLogStatus()) + std::cout << "ON]" << std::endl; + else + std::cout << "OFF]" << std::endl; + std::cout << "Endian Type = ["; + if( x.GetEndianType() == 'L' ) + std::cout << "Little Endian]" << std::endl; + else + std::cout << "Big Endian]" << std::endl; + + if( x.GetMultiUser()) + std::cout << "Multi User Mode = [ON]"; + else + std::cout << "Multi User Mode = [OFF]"; +} +/*************************************************************************************/ +/* This method handles all the complex menu option commands */ +void xbUtil::ProcessOption( const xbString &sOption ){ + + if( sOption[1] == '=' ){ + if( sOption == "=0" ) + MainMenu(); + else if( sOption == "=0.99" ) + // exit right now, now cleanup or termination of anything + exit(0); + else if( sOption == "=2" ) + SystemMenu(); + else if( sOption == "=2.1" ) + ListSystemSettings(); + else if( sOption == "=2.2" ) + UpdateDataDirectory(); + else if( sOption == "=2.3" ) + ToggleDefaultAutoCommit(); + else if( sOption == "=2.4" ) + UpdateLogDirectory(); + else if( sOption == "=2.5" ) + UpdateLogFileName(); + else if( sOption == "=2.6" ) + ToggleLoggingStatus(); + else if( sOption == "=2.7" ) + WriteLogMessage(); + #ifdef XB_LOCKING_SUPPORT + else if( sOption == "=2.8" ) + UpdateDefaultLockRetries(); + else if( sOption == "=2.9" ) + ToggleDefaultAutoLock(); + else if( sOption == "=2.10" ) + UpdateDefaultLockFlavor(); + else if( sOption == "=2.11" ) + UpdateDefaultLockWait(); + else if( sOption == "=2.12" ) + ToggleMultiUserMode(); + #endif + else if( sOption == "=3" ) + FileMenu(); + else if( sOption == "=3.1" ) + ListFilesInDataDirectory(); + else if( sOption == "=3.2" ) + UpdateDataDirectory(); + else if( sOption == "=3.3" ) + x.DisplayTableList(); + else if( sOption == "=3.4" ) + Open(); + else if( sOption == "=3.5" ) + Close(); + else if( sOption == "=3.6" ) + CloseAllTables(); + else if( sOption == "=3.7" ) + SelectActiveTable(); + else if( sOption == "=3.8" ) + DisplayTableStats(); + else if( sOption == "=3.10" ) + Pack(); + else if( sOption == "=3.11" ) + ZapTable(); + else if( sOption == "=3.12" ) + CopyDbfStructure(); + else if( sOption == "=3.13" ) + UpdateTableAutoCommit(); + else if( sOption == "=3.14" ) + DisplayTableInfo(); + else if( sOption == "=4" ) + RecordMenu(); + else if( sOption == "=4.1" ) + SelectActiveTable(); + else if( sOption == "=4.2" ) + GetRecord(); + else if( sOption == "=4.3" ) + BlankRecord(); + else if( sOption == "=4.4" ) + AppendRecord(); + else if( sOption == "=4.5" ) + PutRecord(); + else if( sOption == "=4.6" ) + DeleteRecord(); + else if( sOption == "=4.7" ) + UndeleteRecord(); + else if( sOption == "=4.8" ) + GetFirstRecord(); + else if( sOption == "=4.9" ) + GetNextRecord(); + else if( sOption == "=4.10" ) + GetPrevRecord(); + else if( sOption == "=4.11" ) + GetLastRecord(); + else if( sOption == "=4.12" ) + DumpRecord(); + else if( sOption == "=4.13" ) + AbortRecord(); + else if( sOption == "=4.14" ) + CommitRecord(); + #ifdef XB_FILTER_SUPPORT + else if( sOption == "=4.20" ) + SetFilter(); + else if( sOption == "=4.21" ) + GetFirstFilterRec(); + else if( sOption == "=4.22" ) + GetNextFilterRec(); + else if( sOption == "=4.23" ) + GetPrevFilterRec(); + else if( sOption == "=4.24" ) + GetLastFilterRec(); + #endif // XB_FILTER_SUPPORT + else if( sOption == "=5" ) + FieldMenu(); + else if( sOption == "=5.1" ) + SelectActiveTable(); + else if( sOption == "=5.2" ) + ListFieldInfo(); + else if( sOption == "=5.3" ) + DumpRecord(); + else if( sOption == "=5.4" ) + UpdateFieldData(); + #ifdef XB_MEMO_SUPPORT + else if( sOption == "=5.5" ) + ShowMemoFieldData(); + else if( sOption == "=5.6" ) + UpdateMemoFieldData(); + else if( sOption == "=5.7" ) + DeleteMemoField(); + #endif + + #ifdef XB_LOCKING_SUPPORT + else if( sOption == "=6" ) + LockingMenu(); + else if( sOption == "=6.1" ) + DisplayFileLockSettings(); + else if( sOption == "=6.2" ) + UpdateFileLockRetryCount(); + else if( sOption == "=6.3" ) + UpdateFileLockFlavor(); + else if( sOption == "=6.4" ) + UpdateFileAutoLock(); + else if( sOption == "=6.5" ) + LockDbf(); + else if( sOption == "=6.6" ) + UnlockDbf(); + else if( sOption == "=6.7" ) + LockRecord(); + else if( sOption == "=6.8" ) + UnlockRecord(); + else if( sOption == "=6.9" ) + LockAppend(); + else if( sOption == "=6.10" ) + UnlockAppend(); + else if( sOption == "=6.11" ) + LockHeader(); + else if( sOption == "=6.12" ) + UnlockHeader(); + #ifdef XB_MEMO_SUPPORT + else if( sOption == "=6.13" ) + LockMemo(); + else if( sOption == "=6.14" ) + UnlockMemo(); + #endif + + #ifdef XB_DEBUG_SUPPORT + else if( sOption == "=6.20" ) + xbFileLock(); + else if( sOption == "=6.21" ) + xbFileUnlock(); + #endif + #endif + + #ifdef XB_EXPRESSION_SUPPORT + else if( sOption == "=7" ) + ExpressionMenu(); + else if( sOption == "=7.1" ) + ParseExpression( 0 ); + else if( sOption == "=7.2" ) + ProcessParsedExpression( 0 ); + else if( sOption == "=7.3" ) + ParseAndProcessExpression(); + #ifdef XB_DEBUG_SUPPORT + else if (sOption == "=7.4" ) + DumpExpressionTree(); + #endif // XB_DEBUG_SUPPORT + else if (sOption == "=7.10" ) + Date8ToJul(); + else if (sOption == "=7.11" ) + JulToDate8(); + else if (sOption == "=7.12" ) + IsLeapYear(); + #endif // XB_EXPRESSION_SUPPORT + + #ifdef XB_INDEX_SUPPORT + + else if( sOption == "=8" ) + IndexMenu(); + else if( sOption == "=8.1" ) + SelectActiveTable(); + else if( sOption == "=8.2" ) + DisplayOpenIndexFiles(); + else if( sOption == "=8.3" ) + DisplaySupportedIndexTypes(); + else if( sOption == "=8.4" ) + SelectTag(); + else if( sOption == "=8.5" ) + OpenIndex(); + else if( sOption == "=8.6" ) + CloseIndexFile(); + else if( sOption == "=8.7" ) + CreateIndexTag(); + else if( sOption == "=8.8" ) + GetFirstKey(); + else if( sOption == "=8.9" ) + GetPrevKey(); + else if( sOption == "=8.10" ) + GetNextKey(); + else if( sOption == "=8.11" ) + GetLastKey(); + else if( sOption == "=8.12" ) + FindKey(); + + else if( sOption == "=8.13" ) + CheckIntegrity(); + else if( sOption == "=8.14" ) + Reindex(); + else if( sOption == "=8.15" ) + DeleteTag(); + else if( sOption == "=8.16" ) + AssociateNonProdIx(); + + #ifdef XB_DEBUG_SUPPORT + else if( sOption == "=8.20" ) + DumpIxHeader(); + else if( sOption == "=8.21" ) + DumpIxNode(); + else if( sOption == "=8.22" ) + DumpIxNodeChain(); + else if( sOption == "=8.23" ) + DumpRecsByIx(0); + else if( sOption == "=8.24" ) + DumpRecsByIx(1); + else if( sOption == "=8.25" ) + DumpFreeBlocks(); + #endif // XB_DEBUG_SUPPORT + #endif // XB_INDEX_SUPPORT + + #ifdef XB_SQL_SUPPORT + else if( sOption == "=10" ) + SqlMenu(); + else if( sOption == "=10.1" ) + ExecSqlNonQuery(); + else if( sOption == "=10.2" ) + ExecSqlQuery(); + #endif // XB_SQL_SUPPORT + + #ifdef XB_NDXINF_SUPPORT + else if( sOption == "=11" ) + InfFileMenu(); + else if( sOption == "=11.1" ) + ListInfFileData(); + else if( sOption == "=11.2" ) + AddInfFileData(); + else if( sOption == "=11.3" ) + DeleteInfFileData(); + else if( sOption == "=11.10" ) + InfFileHelp(); + #endif // XB_NDXINF_SUPPORT + + #ifdef XB_DEBUG_SUPPORT + else if( sOption == "=20" ) + DebugMenu(); + else if( sOption == "=20.1" ) + SelectActiveTable(); +/* + #ifdef XB_MEMO_SUPPORT + else if( sOption == "=10.2" ) + DumpDbtHeader(); + else if( sOption == "=10.3" ) + DumpMemoFreeChain(); + #endif +*/ + + #endif + else + return; + } +} + +/*************************************************************************************/ +xbInt32 xbUtil::GetLong(){ + char cLine[256]; + xbString sLine; + memset( cLine, 0x00, 256 ); + std::cin.getline( cLine, 256 ); + sLine = cLine; + sLine.Trim(); + return atol( cLine ); +} +/*************************************************************************************/ +xbInt16 xbUtil::GetOption(){ + char cLine[256]; + xbString sLine; + memset( cLine, 0x00, 256 ); + std::cin.getline( cLine, 256 ); + sLine = cLine; + sLine.Trim(); + + if( sLine[1] == '=' || sLine.Pos(".") != 0 ){ + ProcessOption( sLine ); + return 0; + } + else + return atoi( cLine ); +} +/************************************************************************/ +#ifdef XB_INDEX_SUPPORT +void xbUtil::IndexMenu() +{ + int option = 0; + + while( option != 99 ) { + + std::cout << std::endl << std::endl << " 8 - Index Menu" << std::endl; + std::cout << "---------------------" << std::endl; + DisplayActiveTable(); + + std::cout << " 1 - Select Active Table" << std::endl; + std::cout << " 2 - Display Open Index Files" << std::endl; + std::cout << " 3 - Display Supported Index Types" << std::endl; + std::cout << " 4 - Select Active Tag" << std::endl; + std::cout << " 5 - Open Index File" << std::endl; + std::cout << " 6 - Close Index File" << std::endl; + std::cout << " 7 - Create Index Tag" << std::endl; + std::cout << " 8 - Get First Key" << std::endl; + std::cout << " 9 - Get Next Key" << std::endl; + std::cout << " 10 - Get Prev Key" << std::endl; + std::cout << " 11 - Get Last Key" << std::endl; + std::cout << " 12 - Find Key" << std::endl; + std::cout << " 13 - Check Index Integrity" << std::endl; + std::cout << " 14 - Reindex" << std::endl; + std::cout << " 15 - Delete Tag" << std::endl; + std::cout << " 16 - Associate NDX file" << std::endl; + + #ifdef XB_DEBUG_SUPPORT + std::cout << std::endl; + std::cout << " 20 - Dump Header" << std::endl; + std::cout << " 21 - Dump Node(s)" << std::endl; + std::cout << " 22 - Dump Node Chain" << std::endl; + std::cout << " 23 - Dump Recs by Tag Fwd" << std::endl; + std::cout << " 24 - Dump Recs by Tag Bwd" << std::endl; + std::cout << " 25 - Dump Free Blocks / MDX ulBlock2" << std::endl; + #endif // XB_DEBUG_SUPPORT + + std::cout << std::endl; + std::cout << std::endl; + + std::cout << " 99 - Exit Menu" << std::endl; + option = GetOption(); + + switch( option ){ + case 0: break; + case 1: SelectActiveTable(); break; + case 2: DisplayOpenIndexFiles(); break; + case 3: DisplaySupportedIndexTypes(); break; + case 4: SelectTag(); break; + case 5: OpenIndex(); break; + case 6: CloseIndexFile(); break; + case 7: CreateIndexTag(); break; + + case 8: GetFirstKey(); break; + case 9: GetNextKey(); break; + case 10: GetPrevKey(); break; + case 11: GetLastKey(); break; + + case 12: FindKey(); break; + case 13: CheckIntegrity(); break; + case 14: Reindex(); break; + case 15: DeleteTag(); break; + case 16: AssociateNonProdIx(); break; + + #ifdef XB_DEBUG_SUPPORT + case 20: DumpIxHeader(); break; + case 21: DumpIxNode(); break; + case 22: DumpIxNodeChain(); break; + case 23: DumpRecsByIx(0); break; + case 24: DumpRecsByIx(1); break; + case 25: DumpFreeBlocks(); break; + #endif + + case 99: break; + default: std::cout << "Invalid option" << std::endl; break; + } + } +} +#endif +/************************************************************************/ +#ifdef XB_EXPRESSION_SUPPORT +void xbUtil::ExpressionMenu() +{ + int option = 0; + + while( option != 99 ) { + std::cout << std::endl << std::endl << " 7 - Expression Menu" << std::endl; + std::cout << " --------------" << std::endl; + DisplayActiveTable(); + std::cout << " 1 - Parse Expression" << std::endl; + std::cout << " 2 - Process Parsed Expression" << std::endl; + std::cout << " 3 - Parse and Process Expression" << std::endl; + #ifdef XB_DEBUG_SUPPORT + std::cout << " 4 - Dump Expression Internals" << std::endl; + #endif + std::cout << " 10 - Date8 To Julian Date" << std::endl; + std::cout << " 11 - Julian Date to Date8" << std::endl; + std::cout << " 12 - Check Leap Year" << std::endl; + std::cout << " 99 - Exit Menu" << std::endl; + option = GetOption(); + switch( option ){ + case 0: break; + case 1: ParseExpression( 0 ); break; + case 2: ProcessParsedExpression( 0 ); break; + case 3: ParseAndProcessExpression(); break; + #ifdef XB_DEBUG_SUPPORT + case 4: DumpExpressionTree(); break; + #endif + case 10: Date8ToJul(); break; + case 11: JulToDate8(); break; + case 12: IsLeapYear(); break; + case 99: break; + default: std::cout << "Invalid option" << std::endl; break; + } + } +} +#endif +/************************************************************************/ +#ifdef XB_LOCKING_SUPPORT +void xbUtil::LockingMenu() +{ + int option = 0; + + while( option != 99 ) { + std::cout << std::endl << std::endl << " 6 - Locking Menu" << std::endl; + std::cout << "-------------" << std::endl; + DisplayActiveTable(); + + std::cout << " 1 - Display File Specific Settings" << std::endl; + std::cout << " 2 - Update File Retry Count" << std::endl; + std::cout << " 3 - Update Locking Flavor" << std::endl; + std::cout << " 4 - Update Auto Lock" << std::endl; + std::cout << " 5 - Lock table (dbf file)" << std::endl; + std::cout << " 6 - Unlock table (dbf file)" << std::endl; + std::cout << " 7 - Lock Record" << std::endl; + std::cout << " 8 - Unlock Record" << std::endl; + std::cout << " 9 - Lock Append" << std::endl; + std::cout << " 10 - Unlock Append" << std::endl; + std::cout << " 11 - Lock Header" << std::endl; + std::cout << " 12 - Unlock Header" << std::endl; + + + #ifdef XB_MEMO_SUPPORT + std::cout << " 13 - Lock Memo File" << std::endl; + std::cout << " 14 - Unlock Memo File" << std::endl; + #endif // XB_MEMO_SUPPORT + + #ifdef XB_INDEX_SUPPORT + std::cout << " 15 - Lock Index File(s)" << std::endl; + std::cout << " 16 - Unlock Index File(s)" << std::endl; + #endif // XB_INDEX_SUPPORT + + #ifdef XB_DEBUG_SUPPORT + std::cout << " 20 - Native xbFile - Lock Bytes" << std::endl; + std::cout << " 21 - Native xbFile - Unlock Bytes" << std::endl; + #endif // XB_DEBUG_SUPPORT + + std::cout << " 99 - Exit Menu" << std::endl; + option = GetOption(); + + switch( option ) { + case 0: break; + case 1: DisplayFileLockSettings(); break; + case 2: UpdateFileLockRetryCount(); break; + case 3: UpdateFileLockFlavor(); break; + case 4: UpdateFileAutoLock(); break; + case 5: LockDbf(); break; + case 6: UnlockDbf(); break; + case 7: LockRecord(); break; + case 8: UnlockRecord(); break; + case 9: LockAppend(); break; + case 10: UnlockAppend(); break; + case 11: LockHeader(); break; + case 12: UnlockHeader(); break; + + #ifdef XB_MEMO_SUPPORT + case 13: LockMemo(); break; + case 14: UnlockMemo(); break; + #endif // XB_MEMO_SUPPORT + + #ifdef XB_INDEX_SUPPORT + case 15: LockIndices(); break; + case 16: UnlockIndices(); break; + #endif // XB_INDEX_SUPPORT + + #ifdef XB_DEBUG_SUPPORT + case 20: xbFileLock(); break; + case 21: xbFileUnlock(); break; + #endif // XB_DEBUG_SUPPORT + + case 99: break; + default: std::cout << "Invalid Option" << std::endl; + } + } +} +#endif + +/************************************************************************/ +#ifdef XB_SQL_SUPPORT +void xbUtil::SqlMenu() +{ + int option = 0; + + while( option != 99 ) { + std::cout << std::endl << std::endl << "9 - Sql Menu" << std::endl; + std::cout << "-------------" << std::endl; + DisplayActiveTable(); + + std::cout << " 1 - Execute SQL Non Query" << std::endl; + std::cout << " 2 - Execute SQL Query" << std::endl; + std::cout << "99 - Exit Menu" << std::endl; + + option = GetOption(); + switch( option ){ + case 0: break; + case 1: ExecSqlNonQuery(); break; + case 2: ExecSqlQuery(); break; + case 99: break; + default: std::cout << "Invalid option" << std::endl; break; + } + } +} +#endif // XB_SQL_SUPPORT + +/************************************************************************/ +#ifdef XB_DEBUG_SUPPORT +void xbUtil::DebugMenu() +{ + int option = 0; + + while( option != 99 ) { + std::cout << std::endl << std::endl << "10 - Debug Menu" << std::endl; + std::cout << "-------------" << std::endl; + DisplayActiveTable(); + + std::cout << " 1 - Select Active Table" << std::endl; + #ifdef XB_MEMO_SUPPORT + std::cout << " 2 - Dislay Memo Header Info" << std::endl; + std::cout << " 3 - Dump Memo Free Chain" << std::endl; + #endif + +// //std::cout << "4 - Dump index node chains to file xbase.dmp" << std::endl; +// //std::cout << "5 - Dump index node chain" << std::endl; + std::cout << "99 - Exit Menu" << std::endl; + option = GetOption(); + switch( option ){ + case 0: break; + case 1: SelectActiveTable(); break; + #ifdef XB_MEMO_SUPPORT + case 2: DumpDbtHeader(); break; + case 3: DumpMemoFreeChain(); break; + #endif + case 99: break; + default: std::cout << "Invalid option" << std::endl; break; + } + } +} +#endif // XBASE_DEBUG +/************************************************************************/ +void xbUtil::FieldMenu() +{ + int option = 0; + + while( option != 99 ) { + std::cout << std::endl << std::endl << " 5 - Field Menu" << std::endl; + std::cout << " --------------" << std::endl; + DisplayActiveTable(); + std::cout << " 1 - Select Active Table" << std::endl; + std::cout << " 2 - List Field Info" << std::endl; + std::cout << " 3 - Show Field Data (non memo field)" << std::endl; + std::cout << " 4 - Update Field Data" << std::endl; + #ifdef XB_MEMO_SUPPORT + std::cout << " 5 - Show Memo Field Data" << std::endl; + std::cout << " 6 - Update Memo Field" << std::endl; + std::cout << " 7 - Delete Memo Field" << std::endl; + #endif + + std::cout << "99 - Exit Menu" << std::endl; + option = GetOption(); + switch( option ){ + case 0: break; + case 1: SelectActiveTable(); break; + case 2: ListFieldInfo(); break; + case 3: DumpRecord(); break; + case 4: UpdateFieldData(); break; + #ifdef XB_MEMO_SUPPORT + case 5: ShowMemoFieldData(); break; + case 6: UpdateMemoFieldData(); break; + case 7: DeleteMemoField(); break; + #endif + case 99: break; + default: std::cout << "Invalid option" << std::endl; break; + } + } +} +/************************************************************************/ +void xbUtil::RecordMenu() +{ + int option = 0; + while( option != 99 ) { + std::cout << std::endl << std::endl << " 4 - Record Menu" << std::endl; + std::cout << " ---------------" << std::endl; + DisplayActiveTable(); + std::cout << " 1 - Select Active Table" << std::endl; + std::cout << " 2 - Get Record" << std::endl; + std::cout << " 3 - Blank Record" << std::endl; + std::cout << " 4 - Append Record" << std::endl; + std::cout << " 5 - Put Record" << std::endl; + std::cout << " 6 - Delete Record" << std::endl; + std::cout << " 7 - Undelete Record" << std::endl; + std::cout << " 8 - First Record" << std::endl; + std::cout << " 9 - Next Record" << std::endl; + std::cout << "10 - Prev Record" << std::endl; + std::cout << "11 - Last Record" << std::endl; + std::cout << "12 - Dump Record" << std::endl; + std::cout << "13 - Abort Record Updates" << std::endl; + std::cout << "14 - Commit Record Updates" << std::endl; + + #ifdef XB_FILTER_SUPPORT + std::cout << "20 - Set Filter" << std::endl; + std::cout << "21 - Get First Filter Rec" << std::endl; + std::cout << "22 - Get Next Filter Rec" << std::endl; + std::cout << "23 - Get Prev Filter Rec" << std::endl; + std::cout << "24 - Get Last Filter Rec" << std::endl; + #endif // XB_FILTER_SUPPORT + + std::cout << "99 - Exit Menu" << std::endl; + option = GetOption(); + + switch( option ){ + case 0: break; + case 1: SelectActiveTable(); break; + case 2: GetRecord(); break; + case 3: BlankRecord(); break; + case 4: AppendRecord(); break; + case 5: PutRecord(); break; + case 6: DeleteRecord(); break; + case 7: UndeleteRecord(); break; + case 8: GetFirstRecord(); break; + case 9: GetNextRecord(); break; + case 10: GetPrevRecord(); break; + case 11: GetLastRecord(); break; + case 12: DumpRecord(); break; + case 13: AbortRecord(); break; + case 14: CommitRecord(); break; + + #ifdef XB_FILTER_SUPPORT + case 20: SetFilter(); break; + case 21: GetFirstFilterRec(); break; + case 22: GetNextFilterRec(); break; + case 23: GetPrevFilterRec(); break; + case 24: GetLastFilterRec(); break; + #endif // XB_FILTER_SUPPORT + + case 99: break; + default: std::cout << "Invalid option" << std::endl; break; + } + } +} + +/************************************************************************/ +void xbUtil::FileMenu() +{ + int option = 0; + + while( option != 99 ) { + std::cout << std::endl << std::endl << " 3 - File / Table Menu" << std::endl; + std::cout << " ---------------------" << std::endl; + DisplayActiveTable(); + std::cout << " 1 - List files in Default Data Directory" << std::endl; + std::cout << " 2 - Update Default Data Directory" << std::endl; + std::cout << " 3 - List Open Tables/Files" << std::endl; + std::cout << " 4 - Open Table/File" << std::endl; + std::cout << " 5 - Close Table/File" << std::endl; + std::cout << " 6 - Close All Tables/Files" << std::endl; + std::cout << " 7 - Select Active Table" << std::endl; + std::cout << " 8 - Table/File Information" << std::endl; + std::cout << "10 - Pack" << std::endl; + std::cout << "11 - Zap Database" << std::endl; + std::cout << "12 - Copy Dbf Structure" << std::endl; + std::cout << "13 - Update Table Auto Commit Setting" << std::endl; + std::cout << "14 - Display Table Info" << std::endl; + + std::cout << "99 - Exit Menu" << std::endl; + option = GetOption(); + + switch( option ){ + case 0: break; + case 1: ListFilesInDataDirectory(); break; + case 2: UpdateDataDirectory(); break; + case 3: x.DisplayTableList(); break; + case 4: Open(); break; + case 5: Close(); break; + case 6: CloseAllTables(); break; + case 7: SelectActiveTable(); break; + case 8: DisplayTableStats(); break; + case 10: Pack(); break; + case 11: ZapTable(); break; + case 12: CopyDbfStructure(); break; + case 13: UpdateTableAutoCommit(); break; + case 14: DisplayTableInfo(); break; + case 99: break; + + default: std::cout << "Invalid Option" << std::endl; + } + } +} + +/************************************************************************/ +#ifdef XB_NDXINF_SUPPORT +void xbUtil::InfFileMenu() +{ + + + if( !dActiveTable ) + dActiveTable = GetTablePtr( " - select table" ); + + if( !dActiveTable ){ + std::cout << "No table selected" << std::endl; + return; + } + + int option = 0; + while( option != 99 ) { + std::cout << std::endl << std::endl << " 11 - InfFileData Menu" << std::endl; + std::cout << " ---------------------" << std::endl; + DisplayActiveTable(); + std::cout << " 1 - List Inf Contents" << std::endl; + std::cout << " 2 - Add NDX File association to [" << dActiveTable->GetTblAlias().Str() << "]" << std::endl; + std::cout << " 3 - Delete NDX File association from [" << dActiveTable->GetTblAlias().Str() << "]" << std::endl; + std::cout << "10 - Inf File Help" << std::endl; + std::cout << "99 - Exit Menu" << std::endl; + option = GetOption(); + + switch( option ){ + case 0: break; + case 1: ListInfFileData(); break; + case 2: AddInfFileData(); break; + case 3: DeleteInfFileData(); break; + case 10: InfFileHelp(); break; + case 99: break; + default: std::cout << "Invalid Option" << std::endl; + } + } +} +#endif // XB_NDXINF_SUPPORT + +/************************************************************************************/ +void xbUtil::SystemMenu() +{ + int option = 0; + while( option != 99 ) { + std::cout << std::endl << std::endl << " 2 - System Menu" << std::endl; + std::cout << " ---------------" << std::endl; + DisplayActiveTable(); + std::cout << " 1 - Display System Settings" << std::endl; + std::cout << " 2 - Update Default Data Directory" << std::endl; + + std::cout << " 3 - Toggle Default Auto Commit" << std::endl; + std::cout << " 4 - Update Default Log Directory" << std::endl; + std::cout << " 5 - Update Default Log File Name" << std::endl; + std::cout << " 6 - Toggle Logging Status" << std::endl; + std::cout << " 7 - Write Test Log Message" << std::endl; + #ifdef XB_LOCKING_SUPPORT + std::cout << " 8 - Update Default Lock Retries" << std::endl; + std::cout << " 9 - Toggle Default Auto Lock" << std::endl; + //std::cout << "10 - Update Lock Flavor" << std::endl; 3/20/17, only one flavor working + std::cout << "11 - Update Default Lock Wait Time" << std::endl; + std::cout << "12 - Toggle Multi User Mode" << std::endl; + #endif + + std::cout << "99 - Exit Menu" << std::endl; + option = GetOption(); + + switch( option ) { + case 0: break; + case 1: ListSystemSettings(); break; + case 2: UpdateDataDirectory(); break; + case 3: ToggleDefaultAutoCommit(); break; + case 4: UpdateLogDirectory(); break; + case 5: UpdateLogFileName(); break; + case 6: ToggleLoggingStatus(); break; + case 7: WriteLogMessage(); break; + #ifdef XB_LOCKING_SUPPORT + case 8: UpdateDefaultLockRetries(); break; + case 9: ToggleDefaultAutoLock(); break; + case 10: UpdateDefaultLockFlavor(); break; + case 11: UpdateDefaultLockWait(); break; + case 12: ToggleMultiUserMode(); break; + #endif + + case 99: break; + default: std::cout << "Invalid Option" << std::endl; + } + } +} +/************************************************************************************/ +void xbUtil::MainMenu() +{ + int option = 0; + std::cout << std::endl<< std::endl << "XBase64 Utility Program"; + DisplayActiveTable(); + while( option != 99 ) { + std::cout << std::endl << std::endl << " 0 - Main Menu" << std::endl; + std::cout << " -------------" << std::endl; + std::cout << " 1 - Help" << std::endl; + std::cout << " 2 - System Menu" << std::endl; + std::cout << " 3 - File / Table Menu" << std::endl; + std::cout << " 4 - Record Menu" << std::endl; + std::cout << " 5 - Field Menu" << std::endl; + //std::cout << " 6 - Index Menu" << std::endl; + #ifdef XB_LOCKING_SUPPORT + std::cout << " 6 - Locking Menu" << std::endl; + #endif + #ifdef XB_EXPRESSION_SUPPORT + std::cout << " 7 - Expression Menu" << std::endl; + #endif + #ifdef XB_INDEX_SUPPORT + std::cout << " 8 - Index Menu" << std::endl; + #endif + + #ifdef XB_FILTERS + std::cout << " 9 - Filter Menu" << std::endl; + #endif + + #ifdef XB_SQL_SUPPORT + std::cout << "10 - SQL Menu" << std::endl; + #endif // XB_SQL_SUPPORT + + #ifdef XB_NDXINF_SUPPORT + std::cout << "11 - INF File Menu" << std::endl; + #endif // XB_NDXINF_SUPPORT + + #ifdef XB_DEBUG_SUPPORT + std::cout << "20 - Debug Menu" << std::endl; + #endif + + std::cout << "99 - Exit" << std::endl; + option = GetOption(); + switch( option ){ + case 0: break; + case 2: SystemMenu(); break; + case 3: FileMenu(); break; + case 4: RecordMenu(); break; + case 5: FieldMenu(); break; + + #ifdef XB_LOCKING_SUPPORT + case 6: LockingMenu(); break; + #endif + + #ifdef XB_EXPRESSION_SUPPORT + case 7: ExpressionMenu(); break; + #endif + + #ifdef XB_INDEX_SUPPORT + case 8: IndexMenu(); break; + #endif + + #ifdef XB_FILTERS + case 9: FilterMenu(); break; + #endif + + #ifdef XB_SQL_SUPPORT + case 10: SqlMenu(); break; + #endif + + #ifdef XB_NDXINF_SUPPORT + case 11: InfFileMenu(); break; + #endif + + #ifdef XB_DEBUG_SUPPORT + case 20: DebugMenu(); break; + #endif + + case 99: std::cout << "Bye!! - Thanks for using XBase64" << std::endl; break; + default: + std::cout << option << " - Invalid function" << std::endl; + break; + } + } +} +/*************************************************************************************/ +#ifdef XB_INDEX_SUPPORT +void xbUtil::DisplayOpenIndexFiles(){ + + // verify active table selected, if not, select one + if( !dActiveTable ) + dActiveTable = GetTablePtr( " - select table" ); + + if( !dActiveTable ){ + std::cout << "Table not selected" << std::endl; + return; + } + // if not active indices, display no open NDX indices + xbInt32 lIxCnt = dActiveTable->GetPhysicalIxCnt(); + if( lIxCnt == 0 ){ + std::cout << "No open index files for table" << std::endl; + return; + } else { + std::cout << " Open Index Files = [" << lIxCnt << "]" << std::endl; + std::cout << " FileName\tFile Type\n"; + std::cout << " ========\t=========\n"; + } + // For each active index + // display File Name, Type, Key + xbIxList *p = dActiveTable->GetIxList(); + xbIx *ixp; + while( p ){ + ixp = p->ix; + std::cout << " " << ixp->GetFileName().Str() << "\t" << p->sFmt->Str() << std::endl; + p = p->next; + } +} + +void xbUtil::DisplaySupportedIndexTypes(){ + + std::cout << "Supported Index Type" << std::endl; + std::cout << "Type MaxTags Asc/Dsc Filters Description" << std::endl; + std::cout << "---- ------- -------- ------- --------------------------------" << std::endl; + #ifdef XB_NDX_SUPPORT + std::cout << "NDX 1 ASC No Dbase III single tag index file" << std::endl; + #endif + #ifdef XB_MDX_SUPPORT + std::cout << "MDX 47 ASC/DSC Yes Dbase IV multiple tag index file" << std::endl; + #endif +} + +void xbUtil::SelectTag(){ + std::cout << "SelectTag" << std::endl; + if( !dActiveTable ) + dActiveTable = GetTablePtr( " - select table" ); + + if( !dActiveTable ){ + std::cout << "Table not selected" << std::endl; + return; + } + + xbLinkListNode *llN = dActiveTable->GetTagList(); + xbTag *pTag; + xbInt16 i = 1; + std::cout << "Line\tType\tUnique\tSort\tName\t\tKey Expression\t\tFilter" << std::endl; + + char cUnique; + char cSort; + xbString s; + while( llN ){ + pTag = llN->GetKey(); + pTag->GetUnique() ? cUnique = 'Y' : cUnique = 'N'; + pTag->GetSort() ? cSort = 'D' : cSort = 'A'; + s.Sprintf( "%d\t%s\t%c\t%c\t%-12s\t%-20s\t%s \n", i++, pTag->GetType().Str(), cUnique, cSort, pTag->GetTagName().Str(), pTag->GetExpression().Str(), pTag->GetFilter().Str() ); + std::cout << s.Str(); +// std::cout << i++ << "\t" << pTag->GetType() << "\t " << cUnique << "\t " << cSort << "\t" << pTag->GetTagName() << "\t" << pTag->GetExpression() << "\t" << pTag->GetFilter() << std::endl; + llN = llN->GetNextNode(); + } + char cBuf[128]; + std::cout << std::endl << "Enter Line No:" << std::endl; + std::cin.getline( cBuf, 128 ); + + xbInt32 iSelection = atol( cBuf ); + if( iSelection < 1 || iSelection > i ){ + std::cout << "Invalid selection [" << iSelection << "]" << std::endl; + } else { + llN = dActiveTable->GetTagList(); + i = 1; + for( i = 1; i < iSelection; i++ ) + llN = llN->GetNextNode(); + pTag = llN->GetKey(); + dActiveTable->SetCurTag( pTag->GetType(), pTag->GetIx(), pTag->GetVpTag() ); + } +} + +void xbUtil::OpenIndex(){ + if( !dActiveTable ) + dActiveTable = GetTablePtr( " - select table" ); + + if( !dActiveTable ){ + std::cout << "Table not selected" << std::endl; + return; + } + xbInt16 iRc; + + char cBuf[128]; + std::cout << "Enter index type (NDX or MDX)" << std::endl; + std::cin.getline( cBuf, 128 ); + xbString sIxType = cBuf; + + std::cout << "Enter File Name" << std::endl; + std::cin.getline( cBuf, 128 ); + xbString sIxFileName = cBuf; + + iRc = dActiveTable->OpenIndex( sIxType, sIxFileName ); + x.DisplayError( iRc ); +} + +void xbUtil::CloseIndexFile(){ + + std::cout << "CloseIndex\n"; + + // verify active table selected, if not, select one + if( !dActiveTable ) + dActiveTable = GetTablePtr( " - select table" ); + + if( !dActiveTable ){ + std::cout << "Table not selected" << std::endl; + return; + } + + xbIx *pIx = dActiveTable->GetCurIx(); + if( !pIx ) + SelectTag(); + + pIx = dActiveTable->GetCurIx(); + if( !pIx ){ + std::cout << "Tag not selected" << std::endl; + return; + } + + xbInt16 iRc = dActiveTable->CloseIndexFile( pIx ); + x.DisplayError( iRc ); +} + +void xbUtil::CreateIndexTag(){ + + std::cout << "CreateIndexTag\n"; + + // verify active table selected, if not, select one + if( !dActiveTable ) + dActiveTable = GetTablePtr( " - select table" ); + + if( !dActiveTable ){ + std::cout << "Table not selected" << std::endl; + return; + } + + char cBuf[512]; + xbString sPrompt = "Enter Index Type ("; + #ifdef XB_NDX_SUPPORT + sPrompt += "NDX"; + #endif // XB_NDX_SUPPORT + #ifdef XB_MDX_SUPPORT + sPrompt += " MDX"; + #endif // XB_MDX_SUPPORT + sPrompt += "):"; + std::cout << sPrompt.Str() << std::endl; + + std::cin.getline( cBuf, 128 ); + xbString sIxType = cBuf; + + if( sIxType == "NDX" ) + std::cout << "Enter File Name" << std::endl; + else if( sIxType == "MDX" ) + std::cout << "Enter Tag Name" << std::endl; + + std::cin.getline( cBuf, 128 ); + xbString sIxName = cBuf; + + if( sIxType != "NDX" && sIxType != "MDX" ){ + std::cout << "Invalid tag type" << std::endl; + return; + } + + xbString s; + if( sIxType == "NDX" ){ + s = sIxName; + s.ToUpperCase(); + xbUInt32 lPos = s.Pos( ".NDX" ); + if( lPos == 0 ) + sIxName += ".NDX"; + } + if( sIxType == "MDX" ){ + if( sIxName.Len() > 12 ){ + std::cout << "Tag name [" << sIxName.Str() << "] to long. Must be 12 bytes or less" << std::endl; + return; + } + } + + xbInt16 iDescending = 0; + xbInt16 iUnique = 0; + + std::cout << "Enter Key Expression:" << std::endl; + std::cin.getline( cBuf, 512 ); + xbString sKeyExpression = cBuf; + + xbString sFilter; + if( sIxType == "MDX" ){ + std::cout << "Enter Filter (or enter for none):" << std::endl; + std::cin.getline( cBuf, 512 ); + sFilter = cBuf; + + std::cout << "Descending? (Enter Y for yes):" << std::endl; + std::cin.getline( cBuf, 12 ); + if( cBuf[0] == 'Y' ) + iDescending = 1; + } + + std::cout << "Unique Keys? (Enter Y for yes):" << std::endl; + std::cin.getline( cBuf, 12 ); + if( cBuf[0] == 'Y' ) + iUnique = 1; + + xbIx *pIx; + void *vpTag; + + xbInt16 iRc = dActiveTable->CreateTag( sIxType, sIxName, sKeyExpression, sFilter, iDescending, iUnique, 0, &pIx, &vpTag ); + x.DisplayError( iRc ); +} + +void xbUtil::GetFirstKey(){ + std::cout << "GetFirstKey\n"; + // verify active table selected, if not, select one + if( !dActiveTable ) + dActiveTable = GetTablePtr( " - select table" ); + if( !dActiveTable ){ + std::cout << "Table not selected" << std::endl; + return; + } + xbIx *pIx = dActiveTable->GetCurIx(); + if( !pIx ) + SelectTag(); + pIx = dActiveTable->GetCurIx(); + if( !pIx ){ + std::cout << "Tag not selected" << std::endl; + return; + } + void *vpCurTag = dActiveTable->GetCurTag(); + xbInt16 iRc = pIx->GetFirstKey( vpCurTag, 1 ); + if( iRc == XB_NO_ERROR ) + dActiveTable->DumpRecord( dActiveTable->GetCurRecNo(), 2); + else + x.DisplayError( iRc ); +} + +void xbUtil::GetNextKey(){ + std::cout << "GetNextKey\n"; + // verify active table selected, if not, select one + if( !dActiveTable ) + dActiveTable = GetTablePtr( " - select table" ); + if( !dActiveTable ){ + std::cout << "Table not selected" << std::endl; + return; + } + xbIx *pIx = dActiveTable->GetCurIx(); + if( !pIx ) + SelectTag(); + pIx = dActiveTable->GetCurIx(); + if( !pIx ){ + std::cout << "Tag not selected" << std::endl; + return; + } + void *vpCurTag = dActiveTable->GetCurTag(); + xbInt16 iRc = pIx->GetNextKey( vpCurTag, 1 ); + if( iRc == XB_NO_ERROR ) + dActiveTable->DumpRecord( dActiveTable->GetCurRecNo(), 2); + else + x.DisplayError( iRc ); +} + +void xbUtil::GetPrevKey(){ + std::cout << "GetPrevKey\n"; + // verify active table selected, if not, select one + if( !dActiveTable ) + dActiveTable = GetTablePtr( " - select table" ); + + if( !dActiveTable ){ + std::cout << "Table not selected" << std::endl; + return; + } + xbIx *pIx = dActiveTable->GetCurIx(); + if( !pIx ) + SelectTag(); + pIx = dActiveTable->GetCurIx(); + if( !pIx ){ + std::cout << "Tag not selected" << std::endl; + return; + } + void *vpCurTag = dActiveTable->GetCurTag(); + xbInt16 iRc = pIx->GetPrevKey( vpCurTag, 1 ); + if( iRc == XB_NO_ERROR ) + dActiveTable->DumpRecord( dActiveTable->GetCurRecNo(), 2); + else + x.DisplayError( iRc ); +} + +void xbUtil::GetLastKey(){ + std::cout << "GetLastKey\n"; + // verify active table selected, if not, select one + if( !dActiveTable ) + dActiveTable = GetTablePtr( " - select table" ); + if( !dActiveTable ){ + std::cout << "Table selected" << std::endl; + return; + } + xbIx *pIx = dActiveTable->GetCurIx(); + if( !pIx ) + SelectTag(); + pIx = dActiveTable->GetCurIx(); + if( !pIx ){ + std::cout << "Tag not selected" << std::endl; + return; + } + void *vpCurTag = dActiveTable->GetCurTag(); + xbInt16 iRc = pIx->GetLastKey( vpCurTag, 1 ); + if( iRc == XB_NO_ERROR ) + dActiveTable->DumpRecord( dActiveTable->GetCurRecNo(), 2); + else + x.DisplayError( iRc ); +} + +void xbUtil::FindKey(){ + std::cout << "FindKey\n"; + // verify active table selected, if not, select one + if( !dActiveTable ) + dActiveTable = GetTablePtr( " - select table" ); + if( !dActiveTable ){ + std::cout << "Table not selected" << std::endl; + return; + } + xbIx *pIx = dActiveTable->GetCurIx(); + if( !pIx ) + SelectTag(); + pIx = dActiveTable->GetCurIx(); + if( !pIx ){ + std::cout << "Tag not selected" << std::endl; + return; + } + void *vpCurTag = dActiveTable->GetCurTag(); + char cKeyType = pIx->GetKeyType( vpCurTag ); + + switch( cKeyType ){ + case 'C': + std::cout << "Enter character key value:"; + break; + case 'F': + case 'N': + std::cout << "Enter numeric key value:"; + break; + case 'D': + std::cout << "Enter date (YYYYMMDD) key value:"; + break; + default: + std::cout << "Unknown key type [" << cKeyType << "]" << std::endl; + return; + break; + } + + char cBuf[128]; + std::cin.getline( cBuf, 128 ); + xbInt16 iRc = 0; + + if( cKeyType == 'C' ){ + iRc = pIx->FindKey( vpCurTag, cBuf, (xbInt32) strlen( cBuf ), 1 ); + + } else if( cKeyType == 'F' || cKeyType == 'N' ){ + xbDouble d = atof( cBuf ); + iRc = pIx->FindKey( vpCurTag, d, 1 ); + + } else if( cKeyType == 'D' ){ + xbDate dt( cBuf ); + iRc = pIx->FindKey( vpCurTag, dt, 1 ); + } + + if( iRc == XB_NO_ERROR ) + dActiveTable->DumpRecord( dActiveTable->GetCurRecNo(), 2); + else + x.DisplayError( iRc ); +} + +void xbUtil::CheckIntegrity(){ + std::cout << "CheckIntegrity\n"; + // verify active table selected, if not, select one + if( !dActiveTable ) + dActiveTable = GetTablePtr( " - select table" ); + if( !dActiveTable ){ + std::cout << "Table not selected" << std::endl; + return; + } + xbIx *pIx = dActiveTable->GetCurIx(); + if( !pIx ) + SelectTag(); + pIx = dActiveTable->GetCurIx(); + if( !pIx ){ + std::cout << "Tag not selected" << std::endl; + return; + } + void *vpCurTag = dActiveTable->GetCurTag(); + + xbInt16 iRc = pIx->CheckTagIntegrity( vpCurTag, 2 ); + x.DisplayError( iRc ); +} + +void xbUtil::Reindex(){ + std::cout << "Reindex\n"; + + // verify active table selected, if not, select one + if( !dActiveTable ) + dActiveTable = GetTablePtr( " - select table" ); + if( !dActiveTable ){ + std::cout << "Table not selected" << std::endl; + return; + } + xbIx *pIx = dActiveTable->GetCurIx(); + if( !pIx ) + SelectTag(); + pIx = dActiveTable->GetCurIx(); + if( !pIx ){ + std::cout << "Tag not selected" << std::endl; + return; + } + void *vpCurTag = dActiveTable->GetCurTag(); + xbInt16 iRc = pIx->Reindex( &vpCurTag ); + x.DisplayError( iRc ); +} + +void xbUtil::DeleteTag(){ + std::cout << "DeleteTag\n"; + // verify active table selected, if not, select one + if( !dActiveTable ) + dActiveTable = GetTablePtr( " - select table" ); + if( !dActiveTable ){ + std::cout << "Table not selected" << std::endl; + return; + } + xbIx *pIx = dActiveTable->GetCurIx(); + if( !pIx ) + SelectTag(); + pIx = dActiveTable->GetCurIx(); + if( !pIx ){ + std::cout << "Tag not selected" << std::endl; + return; + } + void *vpCurTag = dActiveTable->GetCurTag(); + xbInt16 iRc = dActiveTable->DeleteTag( dActiveTable->GetCurIxType(), pIx->GetTagName( vpCurTag )); + x.DisplayError( iRc ); +} + +void xbUtil::AssociateNonProdIx(){ + std::cout << "See InfFile menu option, option 11 from the main menu or =11 from here\n"; +} + +#ifdef XB_DEBUG_SUPPORT + +void xbUtil::DumpRecsByIx( xbInt16 iOpt ){ + // iDirection = 0 - Forward - MDX + // = 1 - Reverse - MDX + + std::cout << "DumpRecsByIx\n"; + // verify active table selected, if not, select one + if( !dActiveTable ) + dActiveTable = GetTablePtr( " - select table" ); + if( !dActiveTable ){ + std::cout << "Table not selected" << std::endl; + return; + } + xbIx *pIx = dActiveTable->GetCurIx(); + if( !pIx ) + SelectTag(); + pIx = dActiveTable->GetCurIx(); + if( !pIx ){ + std::cout << "Tag not selected" << std::endl; + return; + } + std::cout << "Select destination:" << std::endl; + std::cout << "0 - Logfile" << std::endl; + std::cout << "1 - Screen" << std::endl; + std::cout << "2 - Both" << std::endl; + xbInt16 iDispOpt = GetOption(); + std::cout << "Select format:" << std::endl; + std::cout << "0 - With Field Names" << std::endl; + std::cout << "1 - 1 line per record" << std::endl; + xbInt16 iDispFmt = GetOption(); + + x.WriteLogMessage( "--------- Dump Recs By Index -------------", iDispOpt ); + void *vpCurTag = dActiveTable->GetCurTag(); + xbUInt32 lRecCnt = 0; + xbInt16 iRc = 0; + + if( iOpt == 0 ){ + iRc = pIx->GetFirstKey( vpCurTag, 1 ); + if( iRc == XB_NO_ERROR ){ + lRecCnt++; + dActiveTable->DumpRecord( dActiveTable->GetCurRecNo(), iDispOpt, iDispFmt ); + while(( iRc = pIx->GetNextKey( vpCurTag, 1 )) == XB_NO_ERROR ){ + dActiveTable->DumpRecord( dActiveTable->GetCurRecNo(), iDispOpt, iDispFmt ); + lRecCnt++; + } + } else { + x.DisplayError( iRc ); + } + } else if( iOpt == 1 ) { + iRc = pIx->GetLastKey( vpCurTag, 1 ); + if( iRc == XB_NO_ERROR ){ + lRecCnt++; + dActiveTable->DumpRecord( dActiveTable->GetCurRecNo(), iDispOpt, iDispFmt ); + while(( iRc = pIx->GetPrevKey( vpCurTag, 1 )) == XB_NO_ERROR ){ + dActiveTable->DumpRecord( dActiveTable->GetCurRecNo(), iDispOpt, iDispFmt ); + lRecCnt++; + } + } + } else { + std::cout << "Invalid option " << iOpt << std::endl; + return; + } + std::cout << lRecCnt << " record(s) dumped" << std::endl; +} + +void xbUtil::DumpIxHeader(){ + std::cout << "DumpIxHeader\n"; + // verify active table selected, if not, select one + if( !dActiveTable ) + dActiveTable = GetTablePtr( " - select table" ); + if( !dActiveTable ){ + std::cout << "Table not selected" << std::endl; + return; + } + xbIx *pIx = dActiveTable->GetCurIx(); + if( !pIx ) + SelectTag(); + pIx = dActiveTable->GetCurIx(); + if( !pIx ){ + std::cout << "Tag not selected" << std::endl; + return; + } + std::cout << "0 - Logfile" << std::endl; + std::cout << "1 - Screen" << std::endl; + std::cout << "2 - Both" << std::endl; + xbInt16 iDispOpt = GetOption(); + pIx->DumpHeader( iDispOpt, 3 ); + +} + +void xbUtil::DumpIxNode(){ + std::cout << "DumpIxNode\n"; + // verify active table selected, if not, select one + if( !dActiveTable ) + dActiveTable = GetTablePtr( " - select table" ); + + if( !dActiveTable ){ + std::cout << "Table not selected" << std::endl; + return; + } + + xbIx *pIx = dActiveTable->GetCurIx(); + if( !pIx ) + SelectTag(); + + pIx = dActiveTable->GetCurIx(); + if( !pIx ){ + std::cout << "Tag not selected" << std::endl; + return; + } + std::cout << "0 - Logfile" << std::endl; + std::cout << "1 - Screen" << std::endl; + std::cout << "2 - Both" << std::endl; + xbInt16 iDispOpt = GetOption(); +// std::cout << std::endl << "0 - All Blocks" << std::endl; +// std::cout << "NNN - Specific block number, 0=All" << std::endl; +// xbUInt32 iBlockOpt = (xbUInt32) GetOption(); +// if( iBlockOpt == 0 ) // dump the header if dumping all blocks + // pIx->DumpHeader( iDispOpt ); + pIx->DumpTagBlocks( iDispOpt, dActiveTable->GetCurTag() ); +} + +void xbUtil::DumpIxNodeChain(){ + + std::cout << "DumpIxNodeChain\n"; + + // verify active table selected, if not, select one + if( !dActiveTable ) + dActiveTable = GetTablePtr( " - select table" ); + + if( !dActiveTable ){ + std::cout << "Table not selected" << std::endl; + return; + } + + xbIx *pIx = dActiveTable->GetCurIx(); + if( !pIx ) + SelectTag(); + + pIx = dActiveTable->GetCurIx(); + if( !pIx ){ + std::cout << "Tag not selected" << std::endl; + return; + } + + std::cout << "0 - Logfile" << std::endl; + std::cout << "1 - Screen" << std::endl; + std::cout << "2 - Both" << std::endl; + xbInt16 iDispOpt = GetOption(); + + void *vpCurTag = dActiveTable->GetCurTag(); + pIx->DumpIxNodeChain( vpCurTag, iDispOpt ); +} + + +void xbUtil::DumpFreeBlocks(){ + std::cout << "Dump ulBlock2 - (Free Block or Split From Page) for MDX Index file\n"; + // verify active table selected, if not, select one + if( !dActiveTable ) + dActiveTable = GetTablePtr( " - select table" ); + if( !dActiveTable ){ + std::cout << "Table not selected" << std::endl; + return; + } + xbIx *pIx = dActiveTable->GetCurIx(); + if( !pIx ) + SelectTag(); + pIx = dActiveTable->GetCurIx(); + if( !pIx ){ + std::cout << "Tag not selected" << std::endl; + return; + } + std::cout << "0 - Logfile" << std::endl; + std::cout << "1 - Screen" << std::endl; + std::cout << "2 - Both" << std::endl; + xbInt16 iDispOpt = GetOption(); + pIx->DumpFreeBlocks( iDispOpt ); + +} + + + +#endif // XB_DEBUG_SUPPORT +#endif // XB_INDEX_SUPPORT + +#ifdef XB_FILTER_SUPPORT +void xbUtil::SetFilter() +{ + // verify active table selected, if not, select one + if( !dActiveTable ) + dActiveTable = GetTablePtr( " - select table" ); + + if( !dActiveTable ){ + std::cout << "Table not selected" << std::endl; + return; + } + + char cBuf[128]; + std::cout << "Enter filter expression:" << std::endl; + std::cin.getline( cBuf, 128 ); + xbString s( cBuf ); + + if( filt ) + delete filt; + + filt = new xbFilter( &x, dActiveTable ); + xbInt16 iRc = filt->Set( s ); + + x.DisplayError( iRc ); +} + +void xbUtil::GetFirstFilterRec() +{ + // verify active table selected, if not, select one + if( !dActiveTable ) + dActiveTable = GetTablePtr( " - select table" ); + + if( !dActiveTable ){ + std::cout << "Table not selected" << std::endl; + return; + } + + xbInt16 iRc; + if(( iRc = filt->GetFirstRecord()) == XB_NO_ERROR ) + dActiveTable->DumpRecord( dActiveTable->GetCurRecNo(), 2); + else + x.DisplayError( iRc ); +} + +void xbUtil::GetNextFilterRec() +{ + // verify active table selected, if not, select one + if( !dActiveTable ) + dActiveTable = GetTablePtr( " - select table" ); + + if( !dActiveTable ){ + std::cout << "Table not selected" << std::endl; + return; + } + xbInt16 iRc; + if(( iRc = filt->GetNextRecord()) == XB_NO_ERROR ) + dActiveTable->DumpRecord( dActiveTable->GetCurRecNo(), 2); + else + x.DisplayError( iRc ); +} + +void xbUtil::GetPrevFilterRec() +{ + // verify active table selected, if not, select one + if( !dActiveTable ) + dActiveTable = GetTablePtr( " - select table" ); + + if( !dActiveTable ){ + std::cout << "Table not selected" << std::endl; + return; + } + xbInt16 iRc; + if(( iRc = filt->GetPrevRecord()) == XB_NO_ERROR ) + dActiveTable->DumpRecord( dActiveTable->GetCurRecNo(), 2); + else + x.DisplayError( iRc ); +} + +void xbUtil::GetLastFilterRec() +{ + // verify active table selected, if not, select one + if( !dActiveTable ) + dActiveTable = GetTablePtr( " - select table" ); + + if( !dActiveTable ){ + std::cout << "Table not selected" << std::endl; + return; + } + xbInt16 iRc; + if(( iRc = filt->GetLastRecord()) == XB_NO_ERROR ) + dActiveTable->DumpRecord( dActiveTable->GetCurRecNo(), 2); + else + x.DisplayError( iRc ); +} +#endif // XB_FILTER_SUPPORT + +///@endcond DOXYOFF + +/*************************************************************************************/ +int main(int, char**) +{ + std::cout << "xb_dbfutil initializing" << std::endl; + xbUtil u; + u.MainMenu(); + return 0; +} diff --git a/src/utils/xb_deletall.cpp b/src/utils/xb_deletall.cpp new file mode 100755 index 0000000..720710f --- /dev/null +++ b/src/utils/xb_deletall.cpp @@ -0,0 +1,55 @@ +/* xb_deletall.cpp + +XBase64 Software Library + +Copyright (c) 1997,2003,2014,2017,2019 Gary A Kunkel + +The xb64 software library is covered under +the terms of the GPL Version 3, 2007 license. + +Email Contact: + + xb64-devel@lists.sourceforge.net + xb64-users@lists.sourceforge.net + + + +This program sets the delete flag on all records in a dbf file + +*/ + + +#include + + +using namespace xb; + +int main(int ac,char** av) +{ + xbXBase x; + xbDbf *MyFile = NULL; + xbFile f( &x ); + xbInt16 iRc = 0; + + if (ac <= 1) { + std::cout << "\nUsage: xb_deletall filename...\n"; + return 1; + } + + if(( iRc = x.OpenHighestVersion( av[1], "", &MyFile )) != XB_NO_ERROR ){ + std::cout << "Could not open file iRc = " << iRc << " file = " << av[1] << std::endl; + x.DisplayError( iRc ); + return 0; + } + + iRc = MyFile->DeleteAllRecords(); + if( iRc != XB_NO_ERROR ) { + std::cout << "Error Deleting all records - database ==> " << av[1] << "\n"; + std::cout << " Return Code = " << iRc; + } + MyFile->Close(); + std::cout << "\nDone...\n\n"; + + return 0; + +} \ No newline at end of file diff --git a/src/utils/xb_dumpdbt.cpp b/src/utils/xb_dumpdbt.cpp new file mode 100755 index 0000000..bc95ab7 --- /dev/null +++ b/src/utils/xb_dumpdbt.cpp @@ -0,0 +1,87 @@ +/* xb_dumpdbt.cpp + +XBase64 Software Library + +Copyright (c) 1997,2003,2014,2017 Gary A Kunkel + +The xb64 software library is covered under +the terms of the GPL Version 3, 2007 license. + +Email Contact: + + xb64-devel@lists.sourceforge.net + xb64-users@lists.sourceforge.net + +*/ + + +#include + +using namespace xb; + +int main( int ac, char **av ) +{ + + +#ifdef XB_MEMO_SUPPORT + xbXBase x; + + xbInt16 iRc; + char cFieldType; + xbDbf *MyFile = NULL; + xbUInt32 ulMemoFieldLen; + xbString sFldName; + xbInt32 lBlockPtr; + xbString sMemoFldData; + + if( ac <= 1 ){ + std::cout << "\nUsage: dumpdbt filename...\n"; + return 1; + } + + if(( iRc = x.OpenHighestVersion( av[1], "", &MyFile )) != XB_NO_ERROR ){ + std::cout << "Could not open file iRc = " << iRc << " file = " << av[1] << std::endl; + x.DisplayError( iRc ); + return 0; + } + + if( MyFile->GetMemoFieldCnt() == 0 ) { + std::cout << "No memo fields exist in " << av[1] << std::endl; + } else { + + xbUInt32 ulRecCnt = 0; + iRc = MyFile->GetRecordCnt( ulRecCnt ); + + for( xbUInt32 ul = 1; ul <= ulRecCnt; ul++ ){ + MyFile->GetRecord( ul ); + std::cout << "\nRecord # " << MyFile->GetCurRecNo(); + if( MyFile->RecordDeleted()) + std::cout << " (deleted) "; + + for( int j = 0; j < MyFile->GetFieldCnt(); j++ ) { + MyFile->GetFieldType( j, cFieldType ); + + if( cFieldType == 'M' ) { + MyFile->GetMemoFieldLen( j, ulMemoFieldLen ); + MyFile->GetFieldName( j, sFldName ); + MyFile->GetLongField( j, lBlockPtr ); + std::cout << "\nMemo field [" << sFldName.Str() + << "] length = [" << ulMemoFieldLen; + std::cout << "] Head Block = [" << lBlockPtr << "]\n"; + + MyFile->GetMemoField( j, sMemoFldData ); + std::cout << sMemoFldData.Str() << "\n"; + } + } + } + std::cout << "\n"; + MyFile->Close(); + delete MyFile; + } + +#else + std::cout << "\nXB_MEMO_SUPPORT is OFF\n"; +#endif + + return 0; +} diff --git a/src/utils/xb_dumphdr.cpp b/src/utils/xb_dumphdr.cpp new file mode 100755 index 0000000..267f4c5 --- /dev/null +++ b/src/utils/xb_dumphdr.cpp @@ -0,0 +1,45 @@ +/* xb_dumphdr.cpp + +XBase64 Software Library + +Copyright (c) 1997,2003,2014 Gary A Kunkel + +The xb64 software library is covered under +the terms of the GPL Version 3, 2007 license. + +Email Contact: + + xb64-devel@lists.sourceforge.net + xb64-users@lists.sourceforge.net + +*/ + + +#include + +using namespace xb; + +int main( int ac, char ** av ){ + + + xbInt16 iRc; + xbXBase x; + xbDbf *MyFile = NULL; + + if( ac <= 1 ) { + std::cout << "\nUsage: xb_dumphdr filename...\n"; + return 1; + } + + if(( iRc = x.OpenHighestVersion( av[1], "", &MyFile )) != XB_NO_ERROR ){ + std::cout << "Could not open file iRc = " << iRc << " file = " << av[1] << std::endl; + x.DisplayError( iRc ); + return 0; + } + + MyFile->DumpHeader( 4 ); + MyFile->Close(); + delete MyFile; + + return 0; +} diff --git a/src/utils/xb_dumpix.cpp b/src/utils/xb_dumpix.cpp new file mode 100755 index 0000000..afd8764 --- /dev/null +++ b/src/utils/xb_dumpix.cpp @@ -0,0 +1,56 @@ +/* xb_dumpix.cpp + +XBase64 Software Library + +Copyright (c) 1997,2003,2014 Gary A Kunkel + +The xb64 software library is covered under +the terms of the GPL Version 3, 2007 license. + +Email Contact: + + xb64-devel@lists.sourceforge.net + xb64-users@lists.sourceforge.net + +*/ + + +// This program dumps an index file's internal data blocks. +// This program is for index development and debugging purposes + + +#include + +using namespace xb; + +int main( int ac, char ** av ){ + + + xbInt16 iRc; + xbXBase x; + xbDbf *MyFile = NULL; + x.EnableMsgLogging(); + x.SetLogSize( 1000000L ); + + if( ac <= 1 ) { + std::cout << "\nUsage: xb_dumphdr filename...\n"; + return 1; + } + if(( iRc = x.OpenHighestVersion( av[1], "", &MyFile )) != XB_NO_ERROR ){ + std::cout << "Could not open file iRc = " << iRc << " file = " << av[1] << std::endl; + x.DisplayError( iRc ); + return 0; + } + xbIx *pIx = MyFile->GetCurIx(); + if( pIx ){ + // pIx->DumpHeader( 2, 3 ); + pIx->DumpTagBlocks( 2 ); + } else { + std::cout << av[1] << "No current index found" << std::endl; + } + + MyFile->Close(); + delete MyFile; + + return 0; +} diff --git a/src/utils/xb_dumprecs.cpp b/src/utils/xb_dumprecs.cpp new file mode 100755 index 0000000..0a85f45 --- /dev/null +++ b/src/utils/xb_dumprecs.cpp @@ -0,0 +1,62 @@ +/* xb_dumprecs.cpp + +XBase64 Software Library + +Copyright (c) 1997,2003,2014,2019 Gary A Kunkel + +The xb64 software library is covered under +the terms of the GPL Version 3, 2007 license. + +Email Contact: + + xb64-devel@lists.sourceforge.net + xb64-users@lists.sourceforge.net + +*/ + +#include +using namespace xb; + +int main(int ac,char** av) +{ + xbXBase x; + xbInt16 iRc; + x.EnableMsgLogging(); + x.SetLogSize( 1000000L ); +// xbUInt32 ulRecCnt = 0; + + if (ac <= 1) { + std::cout << "Usage: xb_dumprecs filename..." << std::endl; + return 1; + } + + xbDbf *MyFile = NULL; + if(( iRc = x.OpenHighestVersion( av[1], "", &MyFile )) != XB_NO_ERROR ){ + std::cout << "Could not open file iRc = " << iRc << " file = " << av[1] << std::endl; + x.DisplayError( iRc ); + return 0; + } + +// std::cout << "Processing file sequentially from beginning..." << std::endl; + + xbUInt32 j = 0; + xbUInt32 ulRecCnt = 0; + + iRc = MyFile->GetRecordCnt( ulRecCnt ); + + if( iRc < XB_NO_ERROR ) + return iRc; + while( j < ulRecCnt ){ + if( j == 0 ) + iRc = MyFile->DumpRecord(++j, 2, 2 ); + else + iRc = MyFile->DumpRecord(++j, 2, 1 ); + if( iRc != XB_NO_ERROR ){ + x.DisplayError( iRc ); + return 1; + } + } +// std::cout << j << " Records written to logile" << std::endl; + MyFile->Close(); + return 0; +} diff --git a/src/utils/xb_execsql.cpp b/src/utils/xb_execsql.cpp new file mode 100755 index 0000000..d474593 --- /dev/null +++ b/src/utils/xb_execsql.cpp @@ -0,0 +1,95 @@ +/* xb_execsql.cpp + +XBase64 Software Library + +Copyright (c) 1997,2003,2014 Gary A Kunkel + +The xb64 software library is covered under +the terms of the GPL Version 3, 2007 license. + +Email Contact: + + xb64-devel@lists.sourceforge.net + xb64-users@lists.sourceforge.net + +*/ + +#include + +using namespace xb; + +xbInt16 GetNextSqlCmd( xbFile &f, xbString &sCmd ); +xbInt16 GetNextSqlCmd( xbFile &f, xbString &sCmd ) +{ + sCmd = ""; + xbString sLine; + xbInt16 iRc = XB_NO_ERROR; + xbUInt32 lPos = 0; + xbBool bDone = xbFalse; + while( !bDone ){ + if(( iRc = f.xbFgets( 256, sLine )) != XB_NO_ERROR ){ + bDone = xbTrue; + } else { + + // don't need CR/LF chars + sLine.ZapChar( 0x0a ); + sLine.ZapChar( 0x0d ); + + // if comment, zap out everything to the right of the hash + lPos = sLine.Pos( '#' ); + if( lPos > 0 ) + sLine.Left( lPos - 1); + + if( sLine.Pos( ';' ) > 0 ){ + bDone = xbTrue; + sLine.ZapChar( ';' ); + } + } + sCmd += sLine; + } + return iRc; +} + +int main(int ac,char** av) +{ + + if (ac <= 1) { + std::cout << "Usage: xb_execsql filename..." << std::endl; + return 1; + } + + xbXBase x; + x.EnableMsgLogging(); + xbSql sql( &x ); + xbFile f( sql.GetXbasePtr() ); + xbInt16 iRc = XB_NO_ERROR; + xbString sFileName; + xbString sSqlLine; + + sFileName = av[1]; + + if(( iRc = f.xbFopen( "r", sFileName, XB_SINGLE_USER )) != XB_NO_ERROR ){ + xbString sMsg; + sMsg.Sprintf( "Error opening [%s]\n", sFileName.Str() ); + std::cout << sMsg.Str(); + sql.GetXbasePtr()->DisplayError( iRc ); + return 1; + } + + while( iRc == XB_NO_ERROR ){ + iRc = GetNextSqlCmd( f, sSqlLine ); + + if( iRc == XB_NO_ERROR ){ + + sSqlLine.Trim(); + std::cout << "Processing line [" << sSqlLine.Str() << "]\n"; + iRc = sql.ExecuteNonQuery( sSqlLine ); + if( iRc != XB_NO_ERROR ) + x.DisplayError( iRc ); + } + } + + f.xbFclose(); + return 0; +} + diff --git a/src/utils/xb_pack.cpp b/src/utils/xb_pack.cpp new file mode 100755 index 0000000..ba61837 --- /dev/null +++ b/src/utils/xb_pack.cpp @@ -0,0 +1,55 @@ +/* xb_pack.cpp + +XBase64 Software Library + +Copyright (c) 1997,2003,2014,2017 Gary A Kunkel + +The xb64 software library is covered under +the terms of the GPL Version 3, 2007 license. + +Email Contact: + + xb64-devel@lists.sourceforge.net + xb64-users@lists.sourceforge.net + +*/ + +#include +using namespace xb; + +int main(int ac,char** av) +{ + if (ac <= 1) { + std::cout << + "\nUsage: xb_pack filename...\n" + "\nThis program does not automatically reindex any NDX indexes." + "\nUse the reindex program to reindex any indexes associated" + "\nwith the database, or build your own program which executes " + "\nthe Pack() method after opening all the index files " + "\nassociated with the database.\n\n"; + return 1; + } + + xbXBase x; + xbInt16 iRc; + xbDbf * MyFile = NULL; + if(( iRc = x.OpenHighestVersion( av[1], "", &MyFile )) != XB_NO_ERROR ){ + std::cout << "Could not open file iRc = " << iRc << " file = " << av[1] << std::endl; + x.DisplayError( iRc ); + return 0; + } + + if(( iRc = MyFile->Pack()) != XB_NO_ERROR ) { + std::cout << "\nError packing DBF database ==> " << av[1] << std::endl; + std::cout << " Return Code = " << iRc << std::endl; + } + + MyFile->Close(); + delete MyFile; + + std::cout << "\nPack Database complete...\n\n"; + return 0; +} + + + diff --git a/src/utils/xb_tblinfo.cpp b/src/utils/xb_tblinfo.cpp new file mode 100755 index 0000000..884ce5e --- /dev/null +++ b/src/utils/xb_tblinfo.cpp @@ -0,0 +1,91 @@ +/* xb_dumpmdx.cpp + +XBase64 Software Library + +Copyright (c) 1997,2003,2014,2021 Gary A Kunkel + +The xb64 software library is covered under +the terms of the GPL Version 3, 2007 license. + +Email Contact: + + xb64-devel@lists.sourceforge.net + xb64-users@lists.sourceforge.net + +*/ + + +#include + +using namespace xb; + +int main( int ac, char ** av ){ + + xbXBase x; + xbBool bHdr = xbFalse; + xbBool bMdxFound = xbFalse; + xbInt16 iRc; + xbString sTagName; + xbDbf *MyFile = NULL; + + x.EnableMsgLogging(); + x.SetLogSize( 1000000L ); + + + if( ac <= 1 ) { + std::cout << "\nUsage: xb_dumpmdx filename.DBF -h -tTAGNAME...\n"; + std::cout << "-h dump mdx file header (default)\n"; + std::cout << "-tTAGNAME where TAGNAME is the name of a tag for extract\n"; + return 1; + } + + if( ac > 2 ){ + for( int i = 1; i< ac; i++ ){ + if( strstr( av[i], "-h" )) + bHdr = xbTrue; + else if( strstr( av[i], "-t" )) + sTagName.Set( av[i]+2 ); + } + } + + if( bHdr == xbFalse && sTagName == "" ) + bHdr = xbTrue; + + + // std::cout << "Hdr = " << bHdr << std::endl; + if( sTagName != "" ) + std::cout << "Tag = " << sTagName.Str() << std::endl; + + if(( iRc = x.OpenHighestVersion( av[1], "", &MyFile )) != XB_NO_ERROR ){ + std::cout << "Could not open file iRc = " << iRc << " file = " << av[1] << std::endl; + x.DisplayError( iRc ); + return 0; + } + MyFile->DumpHeader( 4 ); + + // for each mdx file, dump the header + xbIxList *ixl = MyFile->GetIxList(); + xbIx *ixp; + xbString sFileType; + while( ixl ){ + ixp = ixl->ix; + ixp->GetFileType( sFileType ); + + if( sFileType == "MDX" ){ + bMdxFound = xbTrue; + if( bHdr ){ + // std::cout << "MDX header\n"; + ixp->DumpHeader( 1, 3 ); + } + } + ixl = ixl->next; + } + + if( !bMdxFound ) + std::cout << "No MDX index for file." << std::endl; + + MyFile->Close(); + delete MyFile; + + return 0; +} diff --git a/src/utils/xb_undelall.cpp b/src/utils/xb_undelall.cpp new file mode 100755 index 0000000..4f1af12 --- /dev/null +++ b/src/utils/xb_undelall.cpp @@ -0,0 +1,55 @@ +/* xb_deletall.cpp + +XBase64 Software Library + +Copyright (c) 1997,2003,2014,2017 Gary A Kunkel + +The xb64 software library is covered under +the terms of the GPL Version 3, 2007 license. + +Email Contact: + + xb64-devel@lists.sourceforge.net + xb64-users@lists.sourceforge.net + + + +This program sets the delete flag on all records in a dbf file + +*/ + + +#include + + +using namespace xb; + +int main(int ac,char** av) +{ + + xbXBase x; + xbDbf *MyFile = NULL; + xbInt16 iRc = 0; + + if (ac <= 1) { + std::cout << "\nUsage: xb_deletall filename...\n"; + return 1; + } + + if(( iRc = x.OpenHighestVersion( av[1], "", &MyFile )) != XB_NO_ERROR ){ + std::cout << "Could not open file iRc = " << iRc << " file = " << av[1] << std::endl; + x.DisplayError( iRc ); + return 0; + } + + iRc = MyFile->UndeleteAllRecords(); + if( iRc != XB_NO_ERROR ) { + std::cout << "Error Undeleting all records - database ==> " << av[1] << "\n"; + std::cout << " Return Code = " << iRc; + } + MyFile->Close(); + delete MyFile; + std::cout << "\nDone...\n\n"; + + return 0; +} \ No newline at end of file diff --git a/src/utils/xb_zap.cpp b/src/utils/xb_zap.cpp new file mode 100755 index 0000000..4660cd8 --- /dev/null +++ b/src/utils/xb_zap.cpp @@ -0,0 +1,52 @@ +/* xb_zap.cpp + +XBase64 Software Library + +Copyright (c) 1997,2003,2014,2017 Gary A Kunkel + +The xb64 software library is covered under +the terms of the GPL Version 3, 2007 license. + +Email Contact: + + xb64-devel@lists.sourceforge.net + xb64-users@lists.sourceforge.net + +*/ + +#include +using namespace xb; + +int main(int ac,char** av) +{ + if (ac <= 1) { + std::cout << + "\nUsage: zap filename...\n" + "\nThis program does not automatically reindex any indices." + "\nUse the reindex program to reindex any indexes associated" + "\nwith the database, or build your own program which executes " + "\nthe PackDatabase() method after opening all the index files " + "\nassociated with the database.\n\n" + ; + return 1; + } + + xbXBase x; + xbInt16 iRc; + xbDbf *MyFile = NULL; + + if(( iRc = x.OpenHighestVersion( av[1], "", &MyFile )) != XB_NO_ERROR ){ + std::cout << "Could not open file " << av[1] << std::endl; + return iRc; + } + + if(( iRc = MyFile->Zap()) != XB_NO_ERROR ) { + std::cout << "\nError packing DBF database ==> " << av[1] << std::endl; + std::cout << " Return Code = " << iRc << std::endl; + } + MyFile->Close(); + delete MyFile; + + std::cout << "\nZap Database complete..." << std::endl; + return 0; +} -- cgit v1.2.3